diff options
Diffstat (limited to '')
455 files changed, 54384 insertions, 96 deletions
diff --git a/.gitignore b/.gitignore index e349901a..3bcf7b3f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,4 @@ -# .gitignore for dash - -# generated by autogen.sh -Makefile.in -/aclocal.m4 -/autom4te.cache/ -/compile -/config.h.in -/configure -/depcomp -/install-sh -/missing - -# generated by configure -Makefile -.deps -.dirstamp -/config.cache -/config.h -/config.log -/config.status -/stamp-h1 - -# generated by make -/src/token_vars.h - -# Apple debug symbol bundles -*.dSYM/ - -# backups and patch artefacts -*~ -*.bak -*.orig -*.rej - -# OS generated files -.DS_Store -.DS_Store? -._* -.Spotlight* -.Trash* -*[Tt]humbs.db +/build +/clone +/git +/trash diff --git a/TOUR.7 b/TOUR.7 new file mode 100644 index 00000000..2e77ffaa --- /dev/null +++ b/TOUR.7 @@ -0,0 +1,29 @@ +.Dd December 28, 2020 +.Dt TOUR 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm tour +.Nd things that are here +. +.Sh FILES +.Bl -tag -width Ds +.It Pa home/ +.Dq dotfiles +.It Pa bin/ +utilities of varying utility +.It Pa bin/dash/ +dash patched for interactive use +.It Pa doc/rfc/ +scripts for reading IETF RFCs +.It Pa doc/zlib/ +zlib documentation ported to +.Xr mdoc 7 +.It Pa www/ +sources for causal.agency sites +.It Pa www/git.causal.agency/cgit/ +patched cgit +.It Pa port/wcwidth/ +.Xr wcwidth 3 +replacement for macOS +.El diff --git a/agpl.c b/agpl.c new file mode 100644 index 00000000..929730c1 --- /dev/null +++ b/agpl.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2021 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> diff --git a/bin/.gitignore b/bin/.gitignore new file mode 100644 index 00000000..be0a8004 --- /dev/null +++ b/bin/.gitignore @@ -0,0 +1,37 @@ +*.html +*.o +beef +bibsort +bit +bri +c +config.mk +dtch +ever +fbatt +fbclock +freecell +glitch +hilex +htagml +htmltags +modem +mtags +nudge +order +pbd +pngo +psf2png +psfed +ptee +relay +scheme +scheme.h +shotty +sup +tags +title +typer +up +when +xx diff --git a/bin/LICENSE b/bin/LICENSE new file mode 100644 index 00000000..dba13ed2 --- /dev/null +++ b/bin/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 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 Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero 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 your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + 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 AGPL, see +<http://www.gnu.org/licenses/>. diff --git a/bin/Makefile b/bin/Makefile new file mode 100644 index 00000000..14ad94eb --- /dev/null +++ b/bin/Makefile @@ -0,0 +1,153 @@ +PREFIX ?= ~/.local +MANDIR ?= ${PREFIX}/share/man + +LIBS_PREFIX ?= /usr/local +CFLAGS += -I${LIBS_PREFIX}/include +LDFLAGS += -L${LIBS_PREFIX}/lib + +CFLAGS += -Wall -Wextra -Wpedantic -Wno-gnu-case-range + +BINS += beef +BINS += bibsort +BINS += bit +BINS += c +BINS += dtch +BINS += glitch +BINS += hilex +BINS += htagml +BINS += modem +BINS += mtags +BINS += nudge +BINS += order +BINS += pbd +BINS += pngo +BINS += psf2png +BINS += ptee +BINS += scheme +BINS += shotty +BINS += sup +BINS += title +BINS += up +BINS += when +BINS += xx + +BSD += ever + +GAMES += freecell + +LINUX += bri +LINUX += fbatt +LINUX += fbclock +LINUX += psfed + +TLS += relay +TLS += typer + +MANS = ${BINS:%=man1/%.1} +MANS.BSD = ${BSD:%=man1/%.1} +MANS.GAMES = ${GAMES:%=man6/%.6} +MANS.LINUX = ${LINUX:%=man1/%.1} +MANS.TLS = ${TLS:%=man1/%.1} + +LDLIBS.dtch = -lutil +LDLIBS.fbclock = -lz +LDLIBS.freecell = -lcurses +LDLIBS.glitch = -lz +LDLIBS.modem = -lutil +LDLIBS.pngo = -lz +LDLIBS.ptee = -lutil +LDLIBS.relay = -ltls +LDLIBS.scheme = -lm +LDLIBS.title = -lcurl +LDLIBS.typer = -ltls + +ALL ?= meta any + +-include config.mk + +all: ${ALL} + +meta: .gitignore tags + +any: ${BINS} + +bsd: ${BSD} + +games: ${GAMES} + +linux: ${LINUX} + +tls: ${TLS} + +IGNORE = *.o *.html +IGNORE += ${BINS} ${BSD} ${GAMES} ${LINUX} ${TLS} +IGNORE += scheme.h tags htmltags + +.gitignore: Makefile + echo config.mk '${IGNORE}' | tr ' ' '\n' | sort > $@ + +tags: *.[chly] + ctags -w *.[chly] + +clean: + rm -f ${IGNORE} + +install: ${ALL:%=install-%} + +install-meta: + install -d ${PREFIX}/bin ${MANDIR}/man1 + +install-any: install-meta ${BINS} ${MANS} + install ${BINS} ${PREFIX}/bin + install -m 644 ${MANS} ${MANDIR}/man1 + +install-bsd: install-meta ${BSD} ${MANS.BSD} + install ${BSD} ${PREFIX}/bin + install -m 644 ${MANS.BSD} ${MANDIR}/man1 + +install-games: install-meta ${GAMES} ${MANS.GAMES} + install ${GAMES} ${PREFIX}/bin + install -m 644 ${MANS.GAMES} ${MANDIR}/man6 + +install-linux: install-meta ${LINUX} ${MANS.LINUX} + install ${LINUX} ${PREFIX}/bin + install -m 644 ${MANS.LINUX} ${MANDIR}/man1 + +install-tls: install-meta ${TLS} ${MANS.TLS} + install ${TLS} ${PREFIX}/bin + install -m 644 ${MANS.TLS} ${MANDIR}/man1 + +uninstall: + rm -f ${BINS:%=${PREFIX}/bin/%} ${MANS:%=${MANDIR}/%} + rm -f ${BSD:%=${PREFIX}/bin/%} ${MANS.BSD:%=${MANDIR}/%} + rm -f ${GAMES:%=${PREFIX}/bin/%} ${MANS.GAMES:%=${MANDIR}/%} + rm -f ${LINUX:%=${PREFIX}/bin/%} ${MANS.LINUX:%=${MANDIR}/%} + rm -f ${TLS:%=${PREFIX}/bin/%} ${MANS.TLS:%=${MANDIR}/%} + +.SUFFIXES: .pl + +.c: + ${CC} ${CFLAGS} ${LDFLAGS} $< ${LDLIBS.$@} -o $@ + +.o: + ${CC} ${LDFLAGS} $< ${LDLIBS.$@} -o $@ + +.pl: + cp -f $< $@ + chmod a+x $@ + +OBJS.hilex = c11.o hilex.o make.o mdoc.o sh.o + +hilex: ${OBJS.hilex} + ${CC} ${LDFLAGS} ${OBJS.$@} ${LDLIBS.$@} -o $@ + +${OBJS.hilex}: hilex.h + +fbatt.o fbclock.o: scheme.h + +psf2png.o scheme.o: png.h + +scheme.h: scheme + ./scheme -c > $@ + +include html.mk diff --git a/bin/README.7 b/bin/README.7 new file mode 100644 index 00000000..0ff46eab --- /dev/null +++ b/bin/README.7 @@ -0,0 +1,87 @@ +.Dd April 26, 2021 +.Dt BIN 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm bin +.Nd various utilities +. +.Sh DESCRIPTION +Various tools primarily targeting +Darwin, +.Fx +and +.Ox . +Some tools target Linux. +. +.Pp +.Bl -tag -width "fbclock(1)" -compact +.It Xr beef 1 +Befunge-93 interpreter +.It Xr bibsort 1 +reformat bibliography +.It Xr bit 1 +calculator +.It Xr bri 1 +backlight brightness control +.It Xr c 1 +run C statements +.It Xr dtch 1 +detached sessions +.It Xr ever 1 +watch files +.It Xr fbatt 1 +framebuffer battery indicator +.It Xr fbclock 1 +framebuffer clock +.It Xr freecell 6 +patience game +.It Xr glitch 1 +PNG glitcher +.It Xr hilex 1 +syntax highlighter +.It Xr htagml 1 +tags HTMLizer +.It Xr modem 1 +fixed baud rate wrapper +.It Xr mtags 1 +miscellaneous tags +.It Xr nudge 1 +terminal vibrator +.It Xr order 1 +operator precedence +.It Xr pbd 1 +macOS pasteboard daemon +.It Xr pngo 1 +PNG optimizer +.It Xr psf2png 1 +PSF2 to PNG renderer +.It Xr psfed 1 +PSF2 font editor +.It Xr ptee 1 +tee for PTYs +.It Xr relay 1 +IRC relay bot +.It Xr scheme 1 +color scheme +.It Xr shotty 1 +terminal capture +.It Xr sup 1 +single-use passwords +.It Xr title 1 +page titles +.It Xr up 1 +upload file +.It Xr when 1 +date calculator +.It Xr xx 1 +hexdump +.El +. +.Pp +One piece of reused code. +.Pp +.Bl -tag -width "png(3)" -compact +.It Xr png 3 +basic PNG output +.El diff --git a/bin/beef.c b/bin/beef.c new file mode 100644 index 00000000..b2579b73 --- /dev/null +++ b/bin/beef.c @@ -0,0 +1,141 @@ +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <time.h> + +enum { + Cols = 80, + Rows = 25, +}; +static char page[Rows][Cols]; + +static char get(int y, int x) { + if (y < 0 || y >= Rows) return 0; + if (x < 0 || x >= Cols) return 0; + return page[y][x]; +} +static void put(int y, int x, char v) { + if (y < 0 || y >= Rows) return; + if (x < 0 || x >= Cols) return; + page[y][x] = v; +} + +enum { StackLen = 1024 }; +static long stack[StackLen]; +static size_t top = StackLen; + +static void push(long val) { + if (!top) errx(EX_SOFTWARE, "stack overflow"); + stack[--top] = val; +} +static long pop(void) { + if (top == StackLen) return 0; + return stack[top++]; +} + +static struct { + int y, x; + int dy, dx; +} pc = { .dx = 1 }; + +static void inc(void) { + pc.y += pc.dy; + pc.x += pc.dx; + if (pc.y == -1) pc.y += Rows; + if (pc.x == -1) pc.x += Cols; + if (pc.y == Rows) pc.y -= Rows; + if (pc.x == Cols) pc.x -= Cols; +} + +static bool string; + +static bool step(void) { + char ch = page[pc.y][pc.x]; + + if (ch == '"') { + string ^= true; + } else if (string) { + push(ch); + inc(); + return true; + } + + if (ch == '?') ch = "><^v"[rand() % 4]; + + long x, y, v; + switch (ch) { + break; case '+': push(pop() + pop()); + break; case '-': y = pop(); x = pop(); push(x - y); + break; case '*': push(pop() * pop()); + break; case '/': y = pop(); x = pop(); push(x / y); + break; case '%': y = pop(); x = pop(); push(x % y); + break; case '!': push(!pop()); + break; case '`': y = pop(); x = pop(); push(x > y); + break; case '>': pc.dy = 0; pc.dx = +1; + break; case '<': pc.dy = 0; pc.dx = -1; + break; case '^': pc.dy = -1; pc.dx = 0; + break; case 'v': pc.dy = +1; pc.dx = 0; + break; case '_': pc.dy = 0; pc.dx = (pop() ? -1 : +1); + break; case '|': pc.dx = 0; pc.dy = (pop() ? -1 : +1); + break; case ':': x = pop(); push(x); push(x); + break; case '\\': y = pop(); x = pop(); push(y); push(x); + break; case '$': pop(); + break; case '.': printf("%ld ", pop()); fflush(stdout); + break; case ',': printf("%c", (char)pop()); fflush(stdout); + break; case '#': inc(); + break; case 'g': y = pop(); x = pop(); push(get(y, x)); + break; case 'p': y = pop(); x = pop(); v = pop(); put(y, x, v); + break; case '&': x = EOF; scanf("%ld", &x); push(x); + break; case '~': push(getchar()); + break; case '@': return false; + break; default: if (ch >= '0' && ch <= '9') push(ch - '0'); + } + + inc(); + return true; +} + +int main(int argc, char *argv[]) { + srand(time(NULL)); + memset(page, ' ', sizeof(page)); + + FILE *file = stdin; + if (argc > 1) { + file = fopen(argv[1], "r"); + if (!file) err(EX_NOINPUT, "%s", argv[1]); + } + + int y = 0; + char *line = NULL; + size_t cap = 0; + while (y < Rows && 0 < getline(&line, &cap, file)) { + for (int x = 0; x < Cols; ++x) { + if (line[x] == '\n' || !line[x]) break; + page[y][x] = line[x]; + } + y++; + } + free(line); + + while (step()); + return pop(); +} diff --git a/bin/bibsort.pl b/bin/bibsort.pl new file mode 100644 index 00000000..0d132c19 --- /dev/null +++ b/bin/bibsort.pl @@ -0,0 +1,68 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +while (<>) { + print; + last if /^[.]Sh STANDARDS$/; +} + +my ($ref, @refs); +while (<>) { + next if /^[.](Bl|It|$)/; + last if /^[.]El$/; + if (/^[.]Rs$/) { + $ref = {}; + } elsif (/^[.]%(.) (.*)/) { + $ref->{$1} = [] unless $ref->{$1}; + push @{$ref->{$1}}, $2; + } elsif (/^[.]Re$/) { + push @refs, $ref; + } else { + print; + } +} + +sub byLast { + my ($af, $al) = split /\s(\S+)(,.*)?$/, $a; + my ($bf, $bl) = split /\s(\S+)(,.*)?$/, $b; + ($al // $af) cmp ($bl // $bf) || $af cmp $bf; +} + +foreach $ref (@refs) { + @{$ref->{A}} = sort byLast @{$ref->{A}}; + @{$ref->{Q}} = sort @{$ref->{Q}} if $ref->{Q}; + if ($ref->{N} && $ref->{N}[0] =~ /RFC/) { + $ref->{R} = $ref->{N}; + delete $ref->{N}; + } + if ($ref->{R} && $ref->{R}[0] =~ /RFC (\d+)/ && !$ref->{U}) { + $ref->{U} = ["https://tools.ietf.org/html/rfc${1}"]; + } +} + +sub byAuthor { + local ($a, $b) = ($a->{A}[0], $b->{A}[0]); + byLast(); +} + +{ + local ($,, $\) = (' ', "\n"); + print '.Bl', '-item'; + foreach $ref (sort byAuthor @refs) { + print '.It'; + print '.Rs'; + foreach my $key (qw(A T B I J R N V U P Q C D O)) { + next unless $ref->{$key}; + foreach (@{$ref->{$key}}) { + print ".%${key}", $_; + } + } + print '.Re'; + } + print '.El'; +} + +while (<>) { + print; +} diff --git a/bin/bit.y b/bin/bit.y new file mode 100644 index 00000000..ab310492 --- /dev/null +++ b/bin/bit.y @@ -0,0 +1,202 @@ +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +%{ + +#include <ctype.h> +#include <err.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> + +#define MASK(b) ((1ULL << (b)) - 1) + +#define YYSTYPE uint64_t + +static int yylex(void); +static void yyerror(const char *str); +static void print(uint64_t val); + +static uint64_t vars[128]; + +%} + +%token Int Var + +%left '$' +%right '=' +%left '|' +%left '^' +%left '&' +%left Shl Shr Sar +%left '+' '-' +%left '*' '/' '%' +%right '~' +%left 'K' 'M' 'G' 'T' + +%% + +stmt: + | stmt expr '\n' { print(vars['_'] = $2); printf("\n"); } + | stmt expr ',' { print(vars['_'] = $2); } + | stmt '\n' + ; + +expr: + Int + | Var { $$ = vars[$1]; } + | '(' expr ')' { $$ = $2; } + | expr 'K' { $$ = $1 << 10; } + | expr 'M' { $$ = $1 << 20; } + | expr 'G' { $$ = $1 << 30; } + | expr 'T' { $$ = $1 << 40; } + | '~' expr { $$ = ~$2; } + | '&' expr %prec '~' { $$ = MASK($2); } + | '+' expr %prec '~' { $$ = +$2; } + | '-' expr %prec '~' { $$ = -$2; } + | expr '*' expr { $$ = $1 * $3; } + | expr '/' expr { $$ = $1 / $3; } + | expr '%' expr { $$ = $1 % $3; } + | expr '+' expr { $$ = $1 + $3; } + | expr '-' expr { $$ = $1 - $3; } + | expr Shl expr { $$ = $1 << $3; } + | expr Shr expr { $$ = $1 >> $3; } + | expr Sar expr { $$ = (int64_t)$1 >> $3; } + | expr '&' expr { $$ = $1 & $3; } + | expr '^' expr { $$ = $1 ^ $3; } + | expr '|' expr { $$ = $1 | $3; } + | Var '=' expr { $$ = vars[$1] = $3; } + | expr '$' { $$ = $1; } + ; + +%% + +static int lexInt(uint64_t base) { + yylval = 0; + for (int ch; EOF != (ch = getchar());) { + uint64_t digit = base; + if (ch == '_') { + continue; + } else if (isdigit(ch)) { + digit = ch - '0'; + } else if (isxdigit(ch)) { + digit = 0xA + toupper(ch) - 'A'; + } + if (digit >= base) { + ungetc(ch, stdin); + return Int; + } + yylval *= base; + yylval += digit; + } + return Int; +} + +static int yylex(void) { + int ch; + while (isblank(ch = getchar())); + if (ch == '\'') { + yylval = 0; + while (EOF != (ch = getchar()) && ch != '\'') { + yylval <<= 8; + yylval |= ch; + } + return Int; + } else if (ch == '0') { + ch = getchar(); + if (ch == 'b') { + return lexInt(2); + } else if (ch == 'x') { + return lexInt(16); + } else { + ungetc(ch, stdin); + return lexInt(8); + } + } else if (isdigit(ch)) { + ungetc(ch, stdin); + return lexInt(10); + } else if (ch == '_' || islower(ch)) { + yylval = ch; + return Var; + } else if (ch == '<') { + char ne = getchar(); + if (ne == '<') { + return Shl; + } else { + ungetc(ne, stdin); + return ch; + } + } else if (ch == '-' || ch == '>') { + char ne = getchar(); + if (ne == '>') { + return (ch == '-' ? Sar : Shr); + } else { + ungetc(ne, stdin); + return ch; + } + } else { + return ch; + } +} + +static void yyerror(const char *str) { + warnx("%s", str); +} + +static const char *Codes[128] = { + "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", + "BS", "HT", "NL", "VT", "NP", "CR", "SO", "SI", + "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", + "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US", + [127] = "DEL", +}; + +static void print(uint64_t val) { + int bits = val > UINT32_MAX ? 64 + : val > UINT16_MAX ? 32 + : val > UINT8_MAX ? 16 + : 8; + printf("0x%0*"PRIX64" %"PRId64"", bits >> 2, val, (int64_t)val); + if (bits == 8) { + char bin[9] = {0}; + for (int i = 0; i < 8; ++i) { + bin[i] = '0' + (val >> (7 - i) & 1); + } + printf(" %#"PRIo64" 0b%s", val, bin); + } + if (val < 128) { + if (isprint(val)) printf(" '%c'", (char)val); + if (Codes[val]) printf(" %s", Codes[val]); + } + if (val) { + if (!(val & MASK(40))) { + printf(" %"PRIu64"T", val >> 40); + } else if (!(val & MASK(30))) { + printf(" %"PRIu64"G", val >> 30); + } else if (!(val & MASK(20))) { + printf(" %"PRIu64"M", val >> 20); + } else if (!(val & MASK(10))) { + printf(" %"PRIu64"K", val >> 10); + } + } + printf("\n"); +} + +int main(void) { + while (yyparse()); +} diff --git a/bin/bri.c b/bin/bri.c new file mode 100644 index 00000000..81df2995 --- /dev/null +++ b/bin/bri.c @@ -0,0 +1,85 @@ +/* Copyright (C) 2017 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +typedef unsigned uint; + +static const char *Class = "/sys/class/backlight"; + +int main(int argc, char *argv[]) { + int error; + + const char *input = (argc > 1) ? argv[1] : NULL; + + error = chdir(Class); + if (error) err(EX_OSFILE, "%s", Class); + + DIR *dir = opendir("."); + if (!dir) err(EX_OSFILE, "%s", Class); + + struct dirent *entry; + while (NULL != (errno = 0, entry = readdir(dir))) { + if (entry->d_name[0] == '.') continue; + + error = chdir(entry->d_name); + if (error) err(EX_OSFILE, "%s/%s", Class, entry->d_name); + break; + } + if (!entry) { + if (errno) err(EX_IOERR, "%s", Class); + errx(EX_CONFIG, "%s: empty", Class); + } + + FILE *actual = fopen("actual_brightness", "r"); + if (!actual) err(EX_OSFILE, "actual_brightness"); + + uint value; + int match = fscanf(actual, "%u", &value); + if (match == EOF) err(EX_IOERR, "actual_brightness"); + if (match < 1) errx(EX_DATAERR, "actual_brightness"); + + if (!input) { + printf("%u\n", value); + return EX_OK; + } + + if (input[0] == '+' || input[0] == '-') { + size_t count = strnlen(input, 16); + if (input[0] == '+') { + value += 16 * count; + } else { + value -= 16 * count; + } + } else { + value = strtoul(input, NULL, 0); + } + + FILE *brightness = fopen("brightness", "w"); + if (!brightness) err(EX_OSFILE, "brightness"); + + int size = fprintf(brightness, "%u", value); + if (size < 0) err(EX_IOERR, "brightness"); + + return EX_OK; +} diff --git a/bin/c.sh b/bin/c.sh new file mode 100644 index 00000000..ff059437 --- /dev/null +++ b/bin/c.sh @@ -0,0 +1,121 @@ +#!/bin/sh +set -eu + +temp=$(mktemp -d) +trap 'rm -r "${temp}"' EXIT + +exec 3>>"${temp}/run.c" + +cat >&3 <<EOF +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <locale.h> +#include <math.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <wchar.h> +#include <wctype.h> + +#include <dirent.h> +#include <fcntl.h> +#include <strings.h> +#include <unistd.h> +EOF + +expr= +type= +while getopts 'e:i:t' opt; do + case "${opt}" in + (e) expr=$OPTARG;; + (i) echo "#include <${OPTARG}>" >&3;; + (t) type=1;; + (?) exit 1;; + esac +done +shift $((OPTIND - 1)) + +cat >&3 <<EOF +int main(int argc, char *argv[]) { + (void)argc; + (void)argv; + $*; +EOF + +if [ -n "${type}" ]; then + cat >&3 <<EOF + printf( + _Generic( + ${expr}, + char: "(char) ", + char *: "(char *) ", + const char *: "(const char *) ", + wchar_t *: "(wchar_t *) ", + const wchar_t *: "(const wchar_t *) ", + signed char: "(signed char) ", + short: "(short) ", + int: "(int) ", + long: "(long) ", + long long: "(long long) ", + unsigned char: "(unsigned char) ", + unsigned short: "(unsigned short) ", + unsigned int: "(unsigned int) ", + unsigned long: "(unsigned long) ", + unsigned long long: "(unsigned long long) ", + float: "(float) ", + double: "(double) ", + long double: "(long double) ", + default: "(void *) " + ) + ); +EOF +fi + +if [ -n "${expr}" ]; then + cat >&3 <<EOF + printf( + _Generic( + ${expr}, + char: "%c\n", + char *: "%s\n", + const char *: "%s\n", + wchar_t *: "%ls\n", + const wchar_t *: "%ls\n", + signed char: "%hhd\n", + short: "%hd\n", + int: "%d\n", + long: "%ld\n", + long long: "%lld\n", + unsigned char: "%hhu\n", + unsigned short: "%hu\n", + unsigned int: "%u\n", + unsigned long: "%lu\n", + unsigned long long: "%llu\n", + float: "%g\n", + double: "%g\n", + long double: "%Lg\n", + default: "%p\n" + ), + ${expr} + ); +EOF +fi + +if [ $# -eq 0 -a -z "${expr}" ]; then + cat >&3 +fi + +echo '}' >&3 + +cat >"${temp}/Makefile" <<EOF +CFLAGS += -Wall -Wextra -Wpedantic +EOF + +make -s -C "${temp}" run +"${temp}/run" diff --git a/bin/c11.l b/bin/c11.l new file mode 100644 index 00000000..b9490f2e --- /dev/null +++ b/bin/c11.l @@ -0,0 +1,149 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +%option prefix="c11" +%option noyywrap + +%{ +#include "hilex.h" +%} + +%s MacroLine MacroInclude +%x CharLiteral StringLiteral + +ident [_[:alpha:]][_[:alnum:]]* +width "*"|[0-9]+ + +%% + static int pop = INITIAL; + +[[:blank:]]+ { return Normal; } + +^"%"[%{}]? { + BEGIN(pop = MacroLine); + return Macro; +} + +([-+*/%&|^=!<>]|"<<"|">>")"="? | +[=~.?:]|"["|"]"|"++"|"--"|"&&"|"||"|"->" | +sizeof|(_A|alignof) { + return Operator; +} + +([1-9][0-9]*|"0"[0-7]*|"0x"[[:xdigit:]]+)([ulUL]{0,3}) | +([0-9]+("."[0-9]*)?|[0-9]*"."[0-9]+)([eE][+-]?[0-9]+)?[flFL]? | +"0x"[[:xdigit:]]*("."[[:xdigit:]]*)?([pP][+-]?[0-9]+)[flFL]? { + return Number; +} + +auto|break|case|const|continue|default|do|else|enum|extern|for|goto|if|inline | +register|restrict|return|static|struct|switch|typedef|union|volatile|while | +(_A|a)lignas|_Atomic|_Generic|(_N|n)oreturn|(_S|s)tatic_assert | +(_T|t)hread_local { + return Keyword; +} + +^"#"[[:blank:]]*(include|import) { + BEGIN(pop = MacroInclude); + return Macro; +} +^"#"[[:blank:]]*{ident} { + BEGIN(pop = MacroLine); + return Macro; +} +<MacroInclude>"<"[^>]+">" { + return String; +} +<MacroLine,MacroInclude>{ + "\n" { + BEGIN(pop = INITIAL); + return Normal; + } + "\\\n" { return Macro; } + {ident} { return Macro; } +} + +{ident} { return Ident; } + +"//"([^\n]|"\\\n")* | +"/*"([^*]|"*"[^/])*"*"+"/" { + return Comment; +} + +[LUu]?"'"/[^\\] { + BEGIN(CharLiteral); + yymore(); +} +[LUu]?"'" { + BEGIN(CharLiteral); + return String; +} +([LU]|u8?)?"\""/[^\\%] { + BEGIN(StringLiteral); + yymore(); +} +([LU]|u8?)?"\"" { + BEGIN(StringLiteral); + return String; +} + +<CharLiteral,StringLiteral>{ + "\\\n" | + "\\"[''""?\\abfnrtv] | + "\\"([0-7]{1,3}) | + "\\x"([[:xdigit:]]{2}) | + "\\u"([[:xdigit:]]{4}) | + "\\U"([[:xdigit:]]{8}) { + return Escape; + } +} +<StringLiteral>{ + "%%" | + "%"[EO]?[ABCDFGHIMRSTUVWXYZabcdeghjmnprtuwxyz] | + "%"[ #+-0]*{width}?("."{width})?([Lhjltz]|hh|ll)?[AEFGXacdefginopsux] { + return Format; + } +} + +<CharLiteral>{ + [^\\'']*"'" { + BEGIN(pop); + return String; + } + [^\\'']+|. { return String; } +} +<StringLiteral>{ + [^%\\""]*"\"" { + BEGIN(pop); + return String; + } + [^%\\""]+|. { return String; } +} + +<MacroLine,MacroInclude>. { + return Macro; +} + +.|\n { return Normal; } + +%{ + (void)yyunput; + (void)input; +%} + +%% + +const struct Lexer LexC = { yylex, &yyin, &yytext }; diff --git a/bin/dash/.gitignore b/bin/dash/.gitignore new file mode 100644 index 00000000..63fccdc3 --- /dev/null +++ b/bin/dash/.gitignore @@ -0,0 +1,34 @@ +# .gitignore for dash + +# generated by autogen.sh +/autom4te.cache/ + +# generated by configure +Makefile +.deps +.dirstamp +/config.cache +/config.h +/config.log +/config.status +/stamp-h1 + +# generated by make +/src/token_vars.h + +# Apple debug symbol bundles +*.dSYM/ + +# backups and patch artefacts +*~ +*.bak +*.orig +*.rej + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight* +.Trash* +*[Tt]humbs.db diff --git a/COPYING b/bin/dash/COPYING index 37f8189c..37f8189c 100644 --- a/COPYING +++ b/bin/dash/COPYING diff --git a/ChangeLog b/bin/dash/ChangeLog index 406e20c0..406e20c0 100644 --- a/ChangeLog +++ b/bin/dash/ChangeLog diff --git a/ChangeLog.O b/bin/dash/ChangeLog.O index dfdb2cec..dfdb2cec 100644 --- a/ChangeLog.O +++ b/bin/dash/ChangeLog.O diff --git a/Makefile.am b/bin/dash/Makefile.am index af437a64..af437a64 100644 --- a/Makefile.am +++ b/bin/dash/Makefile.am diff --git a/bin/dash/Makefile.in b/bin/dash/Makefile.in new file mode 100644 index 00000000..49084bfd --- /dev/null +++ b/bin/dash/Makefile.in @@ -0,0 +1,776 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(am__DIST_COMMON) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope distdir distdir-am dist dist-all distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ + config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in COPYING \ + ChangeLog compile install-sh missing +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +# Exists only to be overridden by the user if desired. +AM_DISTCHECK_DVI_TARGET = dvi +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = src +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-zstd: distdir + tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + *.tar.zst*) \ + zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=../.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile config.h +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) all install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-am clean clean-cscope clean-generic \ + cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ + dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ + dist-zstd distcheck distclean distclean-generic distclean-hdr \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/dash/aclocal.m4 b/bin/dash/aclocal.m4 new file mode 100644 index 00000000..6715ee0e --- /dev/null +++ b/bin/dash/aclocal.m4 @@ -0,0 +1,1132 @@ +# generated automatically by aclocal 1.16.3 -*- Autoconf -*- + +# Copyright (C) 1996-2020 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# Copyright (C) 2002-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.16' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.16.3], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.16.3])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + AS_CASE([$CONFIG_FILES], + [*\'*], [eval set x "$CONFIG_FILES"], + [*], [set x $CONFIG_FILES]) + shift + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf + do + # Strip MF so we end up with the name of the file. + am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`AS_DIRNAME(["$am_mf"])` + am_filepart=`AS_BASENAME(["$am_mf"])` + AM_RUN_LOG([cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles]) || am_rc=$? + done + if test $am_rc -ne 0; then + AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. If GNU make was not used, consider + re-running the configure script with MAKE="gmake" (or whatever is + necessary). You can also try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking).]) + fi + AS_UNSET([am_dirpart]) + AS_UNSET([am_filepart]) + AS_UNSET([am_mf]) + AS_UNSET([am_rc]) + rm -f conftest-deps.mk +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking is enabled. +# This creates each '.Po' and '.Plo' makefile fragment that we'll need in +# order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> +# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542> + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: <https://www.gnu.org/software/coreutils/>. + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check whether make has an 'include' directive that can support all +# the idioms we need for our automatic dependency tracking code. +AC_DEFUN([AM_MAKE_INCLUDE], +[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) +cat > confinc.mk << 'END' +am__doit: + @echo this is the am__doit target >confinc.out +.PHONY: am__doit +END +am__include="#" +am__quote= +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) + AS_CASE([$?:`cat confinc.out 2>/dev/null`], + ['0:this is the am__doit target'], + [AS_CASE([$s], + [BSD], [am__include='.include' am__quote='"'], + [am__include='include' am__quote=''])]) + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +AC_MSG_RESULT([${_am_result}]) +AC_SUBST([am__include])]) +AC_SUBST([am__quote])]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + MISSING="\${SHELL} '$am_aux_dir/missing'" +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar <conftest.tar]) + AM_RUN_LOG([cat conftest.dir/file]) + grep GrepMe conftest.dir/file >/dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + diff --git a/autogen.sh b/bin/dash/autogen.sh index 9879c53e..5e9006ca 100755 --- a/autogen.sh +++ b/bin/dash/autogen.sh @@ -2,5 +2,5 @@ aclocal \ && autoheader \ -&& automake --add-missing \ +&& automake --add-missing --copy \ && autoconf diff --git a/bin/dash/compile b/bin/dash/compile new file mode 100755 index 00000000..23fcba01 --- /dev/null +++ b/bin/dash/compile @@ -0,0 +1,348 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# Written by Tom Tromey <tromey@cygnus.com>. +# +# 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 2, 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/>. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to <bug-automake@gnu.org> or send patches to +# <automake-patches@gnu.org>. + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN* | MSYS*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/* | msys/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to <bug-automake@gnu.org>. +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/bin/dash/config.h.in b/bin/dash/config.h.in new file mode 100644 index 00000000..6fb0b3fb --- /dev/null +++ b/bin/dash/config.h.in @@ -0,0 +1,205 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if __attribute__((__alias__())) is supported */ +#undef HAVE_ALIAS_ATTRIBUTE + +/* Define to 1 if you have the <alloca.h> header file. */ +#undef HAVE_ALLOCA_H + +/* Define to 1 if you have the `bsearch' function. */ +#undef HAVE_BSEARCH + +/* Define to 1 if you have the declaration of `isblank', and to 0 if you + don't. */ +#undef HAVE_DECL_ISBLANK + +/* Define to 1 if you have the `faccessat' function. */ +#undef HAVE_FACCESSAT + +/* Define to 1 if you have the `fnmatch' function. */ +#undef HAVE_FNMATCH + +/* Define to 1 if you have the `getpwnam' function. */ +#undef HAVE_GETPWNAM + +/* Define to 1 if you have the `getrlimit' function. */ +#undef HAVE_GETRLIMIT + +/* Define to 1 if you have the `glob' function. */ +#undef HAVE_GLOB + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `isalpha' function. */ +#undef HAVE_ISALPHA + +/* Define to 1 if you have the `killpg' function. */ +#undef HAVE_KILLPG + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mempcpy' function. */ +#undef HAVE_MEMPCPY + +/* Define to 1 if you have the <paths.h> header file. */ +#undef HAVE_PATHS_H + +/* Define to 1 if you have the `sigsetmask' function. */ +#undef HAVE_SIGSETMASK + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `stpcpy' function. */ +#undef HAVE_STPCPY + +/* Define to 1 if you have the `strchrnul' function. */ +#undef HAVE_STRCHRNUL + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strsignal' function. */ +#undef HAVE_STRSIGNAL + +/* Define to 1 if you have the `strtod' function. */ +#undef HAVE_STRTOD + +/* Define to 1 if you have the `strtoimax' function. */ +#undef HAVE_STRTOIMAX + +/* Define to 1 if you have the `strtoumax' function. */ +#undef HAVE_STRTOUMAX + +/* Define if your `struct stat' has `st_mtim' */ +#undef HAVE_ST_MTIM + +/* Define to 1 if you have the `sysconf' function. */ +#undef HAVE_SYSCONF + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define if your faccessat tells root all files are executable */ +#undef HAVE_TRADITIONAL_FACCESSAT + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `_el_fn_sh_complete' function. */ +#undef HAVE__EL_FN_SH_COMPLETE + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to printf format string for intmax_t */ +#undef PRIdMAX + +/* The size of `intmax_t', as computed by sizeof. */ +#undef SIZEOF_INTMAX_T + +/* The size of `long long int', as computed by sizeof. */ +#undef SIZEOF_LONG_LONG_INT + +/* Define if you build with -DSMALL */ +#undef SMALL + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Version number of package */ +#undef VERSION + +/* Define if you build with -DWITH_LINENO */ +#undef WITH_LINENO + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to system shell path */ +#undef _PATH_BSHELL + +/* Define to devnull device node path */ +#undef _PATH_DEVNULL + +/* Define to tty device node path */ +#undef _PATH_TTY + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* 64-bit operations are the same as 32-bit */ +#undef dirent64 + +/* 64-bit operations are the same as 32-bit */ +#undef fstat64 + +/* 64-bit operations are the same as 32-bit */ +#undef lstat64 + +/* 64-bit operations are the same as 32-bit */ +#undef open64 + +/* 64-bit operations are the same as 32-bit */ +#undef readdir64 + +/* klibc has bsd_signal instead of signal */ +#undef signal + +/* 64-bit operations are the same as 32-bit */ +#undef stat64 diff --git a/bin/dash/configure b/bin/dash/configure new file mode 100755 index 00000000..83889b3b --- /dev/null +++ b/bin/dash/configure @@ -0,0 +1,6450 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for dash 0.5.11.2. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 </dev/null +exec 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='dash' +PACKAGE_TARNAME='dash' +PACKAGE_VERSION='0.5.11.2' +PACKAGE_STRING='dash 0.5.11.2' +PACKAGE_BUGREPORT='' +PACKAGE_URL='' + +ac_unique_file="src/main.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +CC_FOR_BUILD +EGREP +GREP +CPP +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL +am__quote' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +enable_dependency_tracking +enable_static +enable_fnmatch +enable_glob +enable_test_workaround +with_libedit +enable_lineno +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures dash 0.5.11.2 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/dash] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of dash 0.5.11.2:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-static Build statical linked program + --enable-fnmatch Use fnmatch(3) from libc + --enable-glob Use glob(3) from libc + --enable-test-workaround + Guard against faccessat(2) that tells root all files + are executable + --disable-lineno Disable LINENO support + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --without-libedit Compile without libedit support + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +dash configure 0.5.11.2 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES +# --------------------------------------------- +# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR +# accordingly. +ac_fn_c_check_decl () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + as_decl_name=`echo $2|sed 's/ *(.*//'` + as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_decl + +# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include <stdio.h> +#include <stdlib.h> +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + echo >>conftest.val; read $3 <conftest.val; ac_retval=0 +else + ac_retval=1 +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f conftest.val + + fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_compute_int + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case <limits.h> declares $2. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by dash $as_me 0.5.11.2, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +am__api_version='1.16' + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +if test x"${MISSING+set}" != xset; then + MISSING="\${SHELL} '$am_aux_dir/missing'" +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='dash' + VERSION='0.5.11.2' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> +# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542> + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: <https://www.gnu.org/software/coreutils/>. + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + + + +ac_config_headers="$ac_config_headers config.h" + + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=0;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdio.h> +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 +$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; } +cat > confinc.mk << 'END' +am__doit: + @echo this is the am__doit target >confinc.out +.PHONY: am__doit +END +am__include="#" +am__quote= +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 + (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + case $?:`cat confinc.out 2>/dev/null` in #( + '0:this is the am__doit target') : + case $s in #( + BSD) : + am__include='.include' am__quote='"' ;; #( + *) : + am__include='include' am__quote='' ;; +esac ;; #( + *) : + ;; +esac + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 +$as_echo "${_am_result}" >&6; } + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" +if test "x$ac_cv_header_minix_config_h" = xyes; then : + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + + +$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + + +$as_echo "#define _MINIX 1" >>confdefs.h + + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if ${ac_cv_safe_to_define___extensions__+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_safe_to_define___extensions__=yes +else + ac_cv_safe_to_define___extensions__=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + + $as_echo "#define _GNU_SOURCE 1" >>confdefs.h + + $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h + + $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for build system compiler" >&5 +$as_echo_n "checking for build system compiler... " >&6; } +if test "$cross_compiling" = yes; then + CC_FOR_BUILD=${CC_FOR_BUILD-cc} +else + CC_FOR_BUILD=${CC} +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${CC_FOR_BUILD}" >&5 +$as_echo "${CC_FOR_BUILD}" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((__alias__()))" >&5 +$as_echo_n "checking for __attribute__((__alias__()))... " >&6; } +dash_cv_have_attribute_alias=no +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +void t() {} + void a() __attribute__((__alias__("t"))); +int +main () +{ +a(); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + dash_cv_have_attribute_alias=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dash_cv_have_attribute_alias" >&5 +$as_echo "$dash_cv_have_attribute_alias" >&6; } +if test "x$dash_cv_have_attribute_alias" = xyes; then + +$as_echo "#define HAVE_ALIAS_ATTRIBUTE 1" >>confdefs.h + +fi + +# Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; +fi + +if test "$enable_static" = "yes"; then + export LDFLAGS="-static -Wl,--fatal-warnings" +fi + +# Check whether --enable-fnmatch was given. +if test "${enable_fnmatch+set}" = set; then : + enableval=$enable_fnmatch; +fi + +# Check whether --enable-glob was given. +if test "${enable_glob+set}" = set; then : + enableval=$enable_glob; +fi + + + +for ac_header in alloca.h paths.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +ac_fn_c_check_decl "$LINENO" "_PATH_BSHELL" "ac_cv_have_decl__PATH_BSHELL" " +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +" +if test "x$ac_cv_have_decl__PATH_BSHELL" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define _PATH_BSHELL "/bin/sh" +_ACEOF + +fi + +ac_fn_c_check_decl "$LINENO" "_PATH_DEVNULL" "ac_cv_have_decl__PATH_DEVNULL" " +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +" +if test "x$ac_cv_have_decl__PATH_DEVNULL" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define _PATH_DEVNULL "/dev/null" +_ACEOF + +fi + +ac_fn_c_check_decl "$LINENO" "_PATH_TTY" "ac_cv_have_decl__PATH_TTY" " +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +" +if test "x$ac_cv_have_decl__PATH_TTY" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define _PATH_TTY "/dev/tty" +_ACEOF + +fi + + +ac_fn_c_check_decl "$LINENO" "isblank" "ac_cv_have_decl_isblank" "#include <ctype.h> +" +if test "x$ac_cv_have_decl_isblank" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_ISBLANK $ac_have_decl +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of intmax_t" >&5 +$as_echo_n "checking size of intmax_t... " >&6; } +if ${ac_cv_sizeof_intmax_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (intmax_t))" "ac_cv_sizeof_intmax_t" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_intmax_t" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (intmax_t) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_intmax_t=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_intmax_t" >&5 +$as_echo "$ac_cv_sizeof_intmax_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_INTMAX_T $ac_cv_sizeof_intmax_t +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long int" >&5 +$as_echo_n "checking size of long long int... " >&6; } +if ${ac_cv_sizeof_long_long_int+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long int))" "ac_cv_sizeof_long_long_int" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_long_long_int" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (long long int) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_long_long_int=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long_int" >&5 +$as_echo "$ac_cv_sizeof_long_long_int" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_LONG_LONG_INT $ac_cv_sizeof_long_long_int +_ACEOF + + + +if test "x$ac_cv_sizeof_intmax_t" = "x$ac_cv_sizeof_long_long_int"; then + intmax_fstr="lld" +else + intmax_fstr="jd" +fi + +ac_fn_c_check_decl "$LINENO" "PRIdMAX" "ac_cv_have_decl_PRIdMAX" " +#include <inttypes.h> + +" +if test "x$ac_cv_have_decl_PRIdMAX" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define PRIdMAX "$intmax_fstr" +_ACEOF + +fi + + +for ac_func in bsearch faccessat getpwnam getrlimit isalpha killpg \ + mempcpy \ + sigsetmask stpcpy strchrnul strsignal strtod strtoimax \ + strtoumax sysconf +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + +# Check whether --enable-test-workaround was given. +if test "${enable_test_workaround+set}" = set; then : + enableval=$enable_test_workaround; +else + enable_test_workaround=auto +fi + + +if test "enable_test_workaround" = "auto" && + test "$ac_cv_func_faccessat" = yes; then + case `uname -s 2>/dev/null` in + GNU/kFreeBSD | \ + FreeBSD) + enable_test_workaround=yes + esac +fi +if test "$enable_test_workaround" = "yes"; then + +$as_echo "#define HAVE_TRADITIONAL_FACCESSAT 1" >>confdefs.h + +fi + +if test "$enable_fnmatch" = yes; then + use_fnmatch= + for ac_func in fnmatch +do : + ac_fn_c_check_func "$LINENO" "fnmatch" "ac_cv_func_fnmatch" +if test "x$ac_cv_func_fnmatch" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_FNMATCH 1 +_ACEOF + use_fnmatch=yes +fi +done + +fi + +if test "$use_fnmatch" = yes && test "$enable_glob" = yes; then + for ac_func in glob +do : + ac_fn_c_check_func "$LINENO" "glob" "ac_cv_func_glob" +if test "x$ac_cv_func_glob" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_GLOB 1 +_ACEOF + +fi +done + +fi + +ac_fn_c_check_func "$LINENO" "signal" "ac_cv_func_signal" +if test "x$ac_cv_func_signal" = xyes; then : + +fi + +if test "$ac_cv_func_signal" != yes; then + ac_fn_c_check_func "$LINENO" "bsd_signal" "ac_cv_func_bsd_signal" +if test "x$ac_cv_func_bsd_signal" = xyes; then : + +$as_echo "#define signal bsd_signal" >>confdefs.h + +fi + +fi + +ac_fn_c_check_func "$LINENO" "stat64" "ac_cv_func_stat64" +if test "x$ac_cv_func_stat64" = xyes; then : + +else + + +$as_echo "#define fstat64 fstat" >>confdefs.h + + +$as_echo "#define lstat64 lstat" >>confdefs.h + + +$as_echo "#define stat64 stat" >>confdefs.h + + +fi + + +ac_fn_c_check_func "$LINENO" "open64" "ac_cv_func_open64" +if test "x$ac_cv_func_open64" = xyes; then : + +else + + +$as_echo "#define open64 open" >>confdefs.h + + +$as_echo "#define readdir64 readdir" >>confdefs.h + + +$as_echo "#define dirent64 dirent" >>confdefs.h + + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stat::st_mtim" >&5 +$as_echo_n "checking for stat::st_mtim... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <time.h> +#include <sys/time.h> +#include <sys/stat.h> +int +main () +{ +struct stat foo; return sizeof(foo.st_mtim.tv_sec) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + have_st_mtim=yes +else + have_st_mtim=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_st_mtim" >&5 +$as_echo "$have_st_mtim" >&6; } +if test "$have_st_mtim" = "yes"; then + +$as_echo "#define HAVE_ST_MTIM 1" >>confdefs.h + +fi + + +# Check whether --with-libedit was given. +if test "${with_libedit+set}" = set; then : + withval=$with_libedit; +fi + +use_libedit= +if test "$with_libedit" != "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for history_init in -ledit" >&5 +$as_echo_n "checking for history_init in -ledit... " >&6; } +if ${ac_cv_lib_edit_history_init+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ledit $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char history_init (); +int +main () +{ +return history_init (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_edit_history_init=yes +else + ac_cv_lib_edit_history_init=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_edit_history_init" >&5 +$as_echo "$ac_cv_lib_edit_history_init" >&6; } +if test "x$ac_cv_lib_edit_history_init" = xyes; then : + + ac_fn_c_check_header_mongrel "$LINENO" "histedit.h" "ac_cv_header_histedit_h" "$ac_includes_default" +if test "x$ac_cv_header_histedit_h" = xyes; then : + use_libedit="yes" +else + as_fn_error $? "Can't find required header files." "$LINENO" 5 +fi + + +else + + as_fn_error $? "Can't find libedit." "$LINENO" 5 +fi + +fi +if test "$use_libedit" != "yes"; then + +$as_echo "#define SMALL 1" >>confdefs.h + +else + export LIBS="$LIBS -ledit" + for ac_func in _el_fn_sh_complete +do : + ac_fn_c_check_func "$LINENO" "_el_fn_sh_complete" "ac_cv_func__el_fn_sh_complete" +if test "x$ac_cv_func__el_fn_sh_complete" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE__EL_FN_SH_COMPLETE 1 +_ACEOF + +fi +done + +fi +# Check whether --enable-lineno was given. +if test "${enable_lineno+set}" = set; then : + enableval=$enable_lineno; +fi + +if test "$enable_lineno" != "no"; then + +$as_echo "#define WITH_LINENO 1" >>confdefs.h + +fi +ac_config_files="$ac_config_files Makefile src/Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by dash $as_me 0.5.11.2, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +dash config.status 0.5.11.2 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' <conf$$subs.awk | sed ' +/^[^""]/{ + N + s/\n// +} +' >>$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' <confdefs.h | sed ' +s/'"$ac_delim"'/"\\\ +"/g' >>$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + case $CONFIG_FILES in #( + *\'*) : + eval set x "$CONFIG_FILES" ;; #( + *) : + set x $CONFIG_FILES ;; #( + *) : + ;; +esac + shift + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf + do + # Strip MF so we end up with the name of the file. + am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`$as_dirname -- "$am_mf" || +$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$am_mf" : 'X\(//\)[^/]' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$am_mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + am_filepart=`$as_basename -- "$am_mf" || +$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$am_mf" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { echo "$as_me:$LINENO: cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles" >&5 + (cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } || am_rc=$? + done + if test $am_rc -ne 0; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. If GNU make was not used, consider + re-running the configure script with MAKE=\"gmake\" (or whatever is + necessary). You can also try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking). +See \`config.log' for more details" "$LINENO" 5; } + fi + { am_dirpart=; unset am_dirpart;} + { am_filepart=; unset am_filepart;} + { am_mf=; unset am_mf;} + { am_rc=; unset am_rc;} + rm -f conftest-deps.mk +} + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/configure.ac b/bin/dash/configure.ac index d06b9044..dc96b748 100644 --- a/configure.ac +++ b/bin/dash/configure.ac @@ -169,18 +169,21 @@ if test "$have_st_mtim" = "yes"; then [Define if your `struct stat' has `st_mtim']) fi -AC_ARG_WITH(libedit, AS_HELP_STRING(--with-libedit, [Compile with libedit support])) +AC_ARG_WITH(libedit, + AS_HELP_STRING(--without-libedit, [Compile without libedit support])) use_libedit= -if test "$with_libedit" = "yes"; then +if test "$with_libedit" != "no"; then AC_CHECK_LIB(edit, history_init, [ AC_CHECK_HEADER([histedit.h], [use_libedit="yes"], AC_MSG_ERROR( - [Can't find required header files.]))]) + [Can't find required header files.]))], [ + AC_MSG_ERROR([Can't find libedit.])]) fi if test "$use_libedit" != "yes"; then AC_DEFINE([SMALL], 1, [Define if you build with -DSMALL]) else export LIBS="$LIBS -ledit" + AC_CHECK_FUNCS(_el_fn_sh_complete) fi AC_ARG_ENABLE(lineno, AS_HELP_STRING(--disable-lineno, \ [Disable LINENO support])) diff --git a/bin/dash/depcomp b/bin/dash/depcomp new file mode 100755 index 00000000..6b391623 --- /dev/null +++ b/bin/dash/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 1999-2020 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, 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/>. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>. + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to <bug-automake@gnu.org>. +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/bin/dash/install-sh b/bin/dash/install-sh new file mode 100755 index 00000000..ec298b53 --- /dev/null +++ b/bin/dash/install-sh @@ -0,0 +1,541 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2020-11-14.01; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +tab=' ' +nl=' +' +IFS=" $tab$nl" + +# Set DOITPROG to "echo" to test this script. + +doit=${DOITPROG-} +doit_exec=${doit:-exec} + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +# Create dirs (including intermediate dirs) using mode 755. +# This is like GNU 'install' as of coreutils 8.32 (2020). +mkdir_umask=22 + +backupsuffix= +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +is_target_a_directory=possibly + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -p pass -p to $cpprog. + -s $stripprog installed files. + -S SUFFIX attempt to back up existing files, with suffix SUFFIX. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG + +By default, rm is invoked with -f; when overridden with RMPROG, +it's up to you to specify -f if you want it. + +If -S is not specified, no backups are attempted. + +Email bug reports to bug-automake@gnu.org. +Automake home page: https://www.gnu.org/software/automake/ +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -p) cpprog="$cpprog -p";; + + -s) stripcmd=$stripprog;; + + -S) backupsuffix="$2" + shift;; + + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + # Don't chown directories that already exist. + if test $dstdir_status = 0; then + chowncmd="" + fi + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename. + if test -d "$dst"; then + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac + dstdir_status=0 + else + dstdir=`dirname "$dst"` + test -d "$dstdir" + dstdir_status=$? + fi + fi + + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + # The $RANDOM variable is not portable (e.g., dash). Use it + # here however when possible just to lower collision chance. + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + + trap ' + ret=$? + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null + exit $ret + ' 0 + + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p'. + if (umask $mkdir_umask && + $mkdirprog $mkdir_mode "$tmpdir" && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + test_tmpdir="$tmpdir/a" + ls_ld_tmpdir=`ls -ld "$test_tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null + fi + trap '' 0;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + oIFS=$IFS + IFS=/ + set -f + set fnord $dstdir + shift + set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && + { test -z "$stripcmd" || { + # Create $dsttmp read-write so that cp doesn't create it read-only, + # which would cause strip to fail. + if test -z "$doit"; then + : >"$dsttmp" # No need to fork-exec 'touch'. + else + $doit touch "$dsttmp" + fi + } + } && + $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + set +f && + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # If $backupsuffix is set, and the file being installed + # already exists, attempt a backup. Don't worry if it fails, + # e.g., if mv doesn't support -f. + if test -n "$backupsuffix" && test -f "$dst"; then + $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null + fi + + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/bin/dash/missing b/bin/dash/missing new file mode 100755 index 00000000..8d0eaad2 --- /dev/null +++ b/bin/dash/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996. + +# 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 2, 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/>. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to <bug-automake@gnu.org>." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=https://www.perl.org/ +flex_URL=https://github.com/westes/flex +gnu_software_URL=https://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/src/.gitignore b/bin/dash/src/.gitignore index 644eccb8..644eccb8 100644 --- a/src/.gitignore +++ b/bin/dash/src/.gitignore diff --git a/src/Makefile.am b/bin/dash/src/Makefile.am index 17324653..fb8229f1 100644 --- a/src/Makefile.am +++ b/bin/dash/src/Makefile.am @@ -34,7 +34,7 @@ HELPERS = mkinit mksyntax mknodes mksignames BUILT_SOURCES = builtins.h nodes.h syntax.h token.h token_vars.h CLEANFILES = \ - $(BUILT_SOURCES) $(patsubst %.o,%.c,$(dash_LDADD)) \ + $(BUILT_SOURCES) $(dash_LDADD:%.o=%.c) \ $(HELPERS) builtins.def man_MANS = dash.1 @@ -44,27 +44,27 @@ EXTRA_DIST = \ mknodes.c nodetypes nodes.c.pat mksyntax.c mksignames.c token.h token_vars.h: mktokens - $(AM_V_GEN)$(SHELL) $^ + $(AM_V_GEN)$(SHELL) mktokens builtins.def: builtins.def.in $(top_builddir)/config.h - $(AM_V_CC)$(COMPILE) -E -x c -o $@ $< + $(AM_V_CC)$(COMPILE) -E -x c -o $@ builtins.def.in builtins.c builtins.h: mkbuiltins builtins.def - $(AM_V_GEN)$(SHELL) $^ + $(AM_V_GEN)$(SHELL) mkbuiltins builtins.def init.c: mkinit $(dash_CFILES) - $(AM_V_GEN)./$^ + $(AM_V_GEN)./mkinit $(dash_CFILES) nodes.c nodes.h: mknodes nodetypes nodes.c.pat - $(AM_V_GEN)./$^ + $(AM_V_GEN)./mknodes nodetypes nodes.c.pat syntax.c syntax.h: mksyntax - $(AM_V_GEN)./$^ + $(AM_V_GEN)./mksyntax signames.c: mksignames - $(AM_V_GEN)./$^ + $(AM_V_GEN)./mksignames mksyntax: token.h -$(HELPERS): %: %.c - $(AM_V_CC)$(COMPILE_FOR_BUILD) -o $@ $< +$(HELPERS): + $(AM_V_CC)$(COMPILE_FOR_BUILD) -o $@ $@.c diff --git a/bin/dash/src/Makefile.in b/bin/dash/src/Makefile.in new file mode 100644 index 00000000..ae25f565 --- /dev/null +++ b/bin/dash/src/Makefile.in @@ -0,0 +1,834 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +bin_PROGRAMS = dash$(EXEEXT) +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" +PROGRAMS = $(bin_PROGRAMS) +am__dirstamp = $(am__leading_dot)dirstamp +am__objects_1 = alias.$(OBJEXT) arith_yacc.$(OBJEXT) \ + arith_yylex.$(OBJEXT) cd.$(OBJEXT) error.$(OBJEXT) \ + eval.$(OBJEXT) exec.$(OBJEXT) expand.$(OBJEXT) \ + histedit.$(OBJEXT) input.$(OBJEXT) jobs.$(OBJEXT) \ + mail.$(OBJEXT) main.$(OBJEXT) memalloc.$(OBJEXT) \ + miscbltin.$(OBJEXT) mystring.$(OBJEXT) options.$(OBJEXT) \ + parser.$(OBJEXT) redir.$(OBJEXT) show.$(OBJEXT) trap.$(OBJEXT) \ + output.$(OBJEXT) bltin/printf.$(OBJEXT) system.$(OBJEXT) \ + bltin/test.$(OBJEXT) bltin/times.$(OBJEXT) var.$(OBJEXT) +am_dash_OBJECTS = $(am__objects_1) +dash_OBJECTS = $(am_dash_OBJECTS) +dash_DEPENDENCIES = builtins.o init.o nodes.o signames.o syntax.o +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/alias.Po ./$(DEPDIR)/arith_yacc.Po \ + ./$(DEPDIR)/arith_yylex.Po ./$(DEPDIR)/cd.Po \ + ./$(DEPDIR)/error.Po ./$(DEPDIR)/eval.Po ./$(DEPDIR)/exec.Po \ + ./$(DEPDIR)/expand.Po ./$(DEPDIR)/histedit.Po \ + ./$(DEPDIR)/input.Po ./$(DEPDIR)/jobs.Po ./$(DEPDIR)/mail.Po \ + ./$(DEPDIR)/main.Po ./$(DEPDIR)/memalloc.Po \ + ./$(DEPDIR)/miscbltin.Po ./$(DEPDIR)/mystring.Po \ + ./$(DEPDIR)/options.Po ./$(DEPDIR)/output.Po \ + ./$(DEPDIR)/parser.Po ./$(DEPDIR)/redir.Po ./$(DEPDIR)/show.Po \ + ./$(DEPDIR)/system.Po ./$(DEPDIR)/trap.Po ./$(DEPDIR)/var.Po \ + bltin/$(DEPDIR)/printf.Po bltin/$(DEPDIR)/test.Po \ + bltin/$(DEPDIR)/times.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(dash_SOURCES) +DIST_SOURCES = $(dash_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +man1dir = $(mandir)/man1 +NROFF = nroff +MANS = $(man_MANS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_YFLAGS = -d +COMMON_CFLAGS = -Wall +COMMON_CPPFLAGS = \ + -DBSD=1 -DSHELL + +AM_CFLAGS = $(COMMON_CFLAGS) +AM_CPPFLAGS = -include $(top_builddir)/config.h $(COMMON_CPPFLAGS) +AM_CFLAGS_FOR_BUILD = -g -O2 $(COMMON_CFLAGS) +AM_CPPFLAGS_FOR_BUILD = $(COMMON_CPPFLAGS) +COMPILE_FOR_BUILD = \ + $(CC_FOR_BUILD) $(DEFAULT_INCLUDES) $(AM_CPPFLAGS_FOR_BUILD) \ + $(CPPFLAGS_FOR_BUILD) \ + $(AM_CFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) + +dash_CFILES = \ + alias.c arith_yacc.c arith_yylex.c cd.c error.c eval.c exec.c expand.c \ + histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \ + mystring.c options.c parser.c redir.c show.c trap.c output.c \ + bltin/printf.c system.c bltin/test.c bltin/times.c var.c + +dash_SOURCES = \ + $(dash_CFILES) \ + alias.h arith_yacc.h bltin/bltin.h cd.h error.h eval.h exec.h \ + expand.h \ + init.h input.h jobs.h machdep.h mail.h main.h memalloc.h miscbltin.h \ + myhistedit.h mystring.h options.h output.h parser.h redir.h shell.h \ + show.h system.h trap.h var.h + +dash_LDADD = builtins.o init.o nodes.o signames.o syntax.o +HELPERS = mkinit mksyntax mknodes mksignames +BUILT_SOURCES = builtins.h nodes.h syntax.h token.h token_vars.h +CLEANFILES = \ + $(BUILT_SOURCES) $(dash_LDADD:%.o=%.c) \ + $(HELPERS) builtins.def + +man_MANS = dash.1 +EXTRA_DIST = \ + $(man_MANS) \ + mktokens mkbuiltins builtins.def.in mkinit.c \ + mknodes.c nodetypes nodes.c.pat mksyntax.c mksignames.c + +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +bltin/$(am__dirstamp): + @$(MKDIR_P) bltin + @: > bltin/$(am__dirstamp) +bltin/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) bltin/$(DEPDIR) + @: > bltin/$(DEPDIR)/$(am__dirstamp) +bltin/printf.$(OBJEXT): bltin/$(am__dirstamp) \ + bltin/$(DEPDIR)/$(am__dirstamp) +bltin/test.$(OBJEXT): bltin/$(am__dirstamp) \ + bltin/$(DEPDIR)/$(am__dirstamp) +bltin/times.$(OBJEXT): bltin/$(am__dirstamp) \ + bltin/$(DEPDIR)/$(am__dirstamp) + +dash$(EXEEXT): $(dash_OBJECTS) $(dash_DEPENDENCIES) $(EXTRA_dash_DEPENDENCIES) + @rm -f dash$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dash_OBJECTS) $(dash_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f bltin/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alias.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arith_yacc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arith_yylex.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exec.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expand.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/histedit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jobs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memalloc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/miscbltin.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mystring.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/redir.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/show.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/system.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trap.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/var.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@bltin/$(DEPDIR)/printf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@bltin/$(DEPDIR)/test.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@bltin/$(DEPDIR)/times.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +install-man1: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man1dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.1[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ + done; } + +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man1dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.1[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f bltin/$(DEPDIR)/$(am__dirstamp) + -rm -f bltin/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/alias.Po + -rm -f ./$(DEPDIR)/arith_yacc.Po + -rm -f ./$(DEPDIR)/arith_yylex.Po + -rm -f ./$(DEPDIR)/cd.Po + -rm -f ./$(DEPDIR)/error.Po + -rm -f ./$(DEPDIR)/eval.Po + -rm -f ./$(DEPDIR)/exec.Po + -rm -f ./$(DEPDIR)/expand.Po + -rm -f ./$(DEPDIR)/histedit.Po + -rm -f ./$(DEPDIR)/input.Po + -rm -f ./$(DEPDIR)/jobs.Po + -rm -f ./$(DEPDIR)/mail.Po + -rm -f ./$(DEPDIR)/main.Po + -rm -f ./$(DEPDIR)/memalloc.Po + -rm -f ./$(DEPDIR)/miscbltin.Po + -rm -f ./$(DEPDIR)/mystring.Po + -rm -f ./$(DEPDIR)/options.Po + -rm -f ./$(DEPDIR)/output.Po + -rm -f ./$(DEPDIR)/parser.Po + -rm -f ./$(DEPDIR)/redir.Po + -rm -f ./$(DEPDIR)/show.Po + -rm -f ./$(DEPDIR)/system.Po + -rm -f ./$(DEPDIR)/trap.Po + -rm -f ./$(DEPDIR)/var.Po + -rm -f bltin/$(DEPDIR)/printf.Po + -rm -f bltin/$(DEPDIR)/test.Po + -rm -f bltin/$(DEPDIR)/times.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-man + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man1 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/alias.Po + -rm -f ./$(DEPDIR)/arith_yacc.Po + -rm -f ./$(DEPDIR)/arith_yylex.Po + -rm -f ./$(DEPDIR)/cd.Po + -rm -f ./$(DEPDIR)/error.Po + -rm -f ./$(DEPDIR)/eval.Po + -rm -f ./$(DEPDIR)/exec.Po + -rm -f ./$(DEPDIR)/expand.Po + -rm -f ./$(DEPDIR)/histedit.Po + -rm -f ./$(DEPDIR)/input.Po + -rm -f ./$(DEPDIR)/jobs.Po + -rm -f ./$(DEPDIR)/mail.Po + -rm -f ./$(DEPDIR)/main.Po + -rm -f ./$(DEPDIR)/memalloc.Po + -rm -f ./$(DEPDIR)/miscbltin.Po + -rm -f ./$(DEPDIR)/mystring.Po + -rm -f ./$(DEPDIR)/options.Po + -rm -f ./$(DEPDIR)/output.Po + -rm -f ./$(DEPDIR)/parser.Po + -rm -f ./$(DEPDIR)/redir.Po + -rm -f ./$(DEPDIR)/show.Po + -rm -f ./$(DEPDIR)/system.Po + -rm -f ./$(DEPDIR)/trap.Po + -rm -f ./$(DEPDIR)/var.Po + -rm -f bltin/$(DEPDIR)/printf.Po + -rm -f bltin/$(DEPDIR)/test.Po + -rm -f bltin/$(DEPDIR)/times.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-man + +uninstall-man: uninstall-man1 + +.MAKE: all check install install-am install-exec install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \ + distclean distclean-compile distclean-generic distclean-tags \ + distdir dvi dvi-am html html-am info info-am install \ + install-am install-binPROGRAMS install-data install-data-am \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-man install-man1 install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-man uninstall-man1 + +.PRECIOUS: Makefile + + +token.h token_vars.h: mktokens + $(AM_V_GEN)$(SHELL) mktokens + +builtins.def: builtins.def.in $(top_builddir)/config.h + $(AM_V_CC)$(COMPILE) -E -x c -o $@ builtins.def.in + +builtins.c builtins.h: mkbuiltins builtins.def + $(AM_V_GEN)$(SHELL) mkbuiltins builtins.def + +init.c: mkinit $(dash_CFILES) + $(AM_V_GEN)./mkinit $(dash_CFILES) + +nodes.c nodes.h: mknodes nodetypes nodes.c.pat + $(AM_V_GEN)./mknodes nodetypes nodes.c.pat + +syntax.c syntax.h: mksyntax + $(AM_V_GEN)./mksyntax + +signames.c: mksignames + $(AM_V_GEN)./mksignames + +mksyntax: token.h + +$(HELPERS): + $(AM_V_CC)$(COMPILE_FOR_BUILD) -o $@ $@.c + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/TOUR b/bin/dash/src/TOUR index e30836e1..e30836e1 100644 --- a/src/TOUR +++ b/bin/dash/src/TOUR diff --git a/src/alias.c b/bin/dash/src/alias.c index daeacbb8..daeacbb8 100644 --- a/src/alias.c +++ b/bin/dash/src/alias.c diff --git a/src/alias.h b/bin/dash/src/alias.h index fb841d64..fb841d64 100644 --- a/src/alias.h +++ b/bin/dash/src/alias.h diff --git a/src/arith_yacc.c b/bin/dash/src/arith_yacc.c index 1a087c32..1a087c32 100644 --- a/src/arith_yacc.c +++ b/bin/dash/src/arith_yacc.c diff --git a/src/arith_yacc.h b/bin/dash/src/arith_yacc.h index ff34d524..ff34d524 100644 --- a/src/arith_yacc.h +++ b/bin/dash/src/arith_yacc.h diff --git a/src/arith_yylex.c b/bin/dash/src/arith_yylex.c index ec5b5b25..ec5b5b25 100644 --- a/src/arith_yylex.c +++ b/bin/dash/src/arith_yylex.c diff --git a/src/bltin/bltin.h b/bin/dash/src/bltin/bltin.h index f5ac06f2..f5ac06f2 100644 --- a/src/bltin/bltin.h +++ b/bin/dash/src/bltin/bltin.h diff --git a/src/bltin/echo.1 b/bin/dash/src/bltin/echo.1 index fbc7fb43..fbc7fb43 100644 --- a/src/bltin/echo.1 +++ b/bin/dash/src/bltin/echo.1 diff --git a/src/bltin/printf.1 b/bin/dash/src/bltin/printf.1 index 38731732..38731732 100644 --- a/src/bltin/printf.1 +++ b/bin/dash/src/bltin/printf.1 diff --git a/src/bltin/printf.c b/bin/dash/src/bltin/printf.c index 7785735b..7785735b 100644 --- a/src/bltin/printf.c +++ b/bin/dash/src/bltin/printf.c diff --git a/src/bltin/test.1 b/bin/dash/src/bltin/test.1 index 42435fb3..42435fb3 100644 --- a/src/bltin/test.1 +++ b/bin/dash/src/bltin/test.1 diff --git a/src/bltin/test.c b/bin/dash/src/bltin/test.c index c7fc479d..c7fc479d 100644 --- a/src/bltin/test.c +++ b/bin/dash/src/bltin/test.c diff --git a/src/bltin/times.c b/bin/dash/src/bltin/times.c index 1166a68e..1166a68e 100644 --- a/src/bltin/times.c +++ b/bin/dash/src/bltin/times.c diff --git a/src/builtins.def.in b/bin/dash/src/builtins.def.in index 95e420cc..95e420cc 100644 --- a/src/builtins.def.in +++ b/bin/dash/src/builtins.def.in diff --git a/src/cd.c b/bin/dash/src/cd.c index 1ef1dc56..1ef1dc56 100644 --- a/src/cd.c +++ b/bin/dash/src/cd.c diff --git a/src/cd.h b/bin/dash/src/cd.h index 87631619..87631619 100644 --- a/src/cd.h +++ b/bin/dash/src/cd.h diff --git a/src/dash.1 b/bin/dash/src/dash.1 index 32f6ac0d..9bc51cd6 100644 --- a/src/dash.1 +++ b/bin/dash/src/dash.1 @@ -32,7 +32,7 @@ .\" .\" @(#)sh.1 8.6 (Berkeley) 5/4/95 .\" -.Dd January 19, 2003 +.Dd December 22, 2020 .Os .Dt DASH 1 .Sh NAME @@ -2291,6 +2291,10 @@ The secondary prompt string, which defaults to Output before each line when execution trace (set -x) is enabled, defaults to .Dq +\ . +.It Ev RPS1 +The primary right prompt string. +.It Ev RPS2 +The secondary right prompt string. .It Ev IFS Input Field Separators. This is normally set to diff --git a/src/error.c b/bin/dash/src/error.c index 728ff885..728ff885 100644 --- a/src/error.c +++ b/bin/dash/src/error.c diff --git a/src/error.h b/bin/dash/src/error.h index 94e30a27..94e30a27 100644 --- a/src/error.h +++ b/bin/dash/src/error.h diff --git a/src/eval.c b/bin/dash/src/eval.c index d4190f95..d4190f95 100644 --- a/src/eval.c +++ b/bin/dash/src/eval.c diff --git a/src/eval.h b/bin/dash/src/eval.h index 63e7d865..63e7d865 100644 --- a/src/eval.h +++ b/bin/dash/src/eval.h diff --git a/src/exec.c b/bin/dash/src/exec.c index 87354d49..87354d49 100644 --- a/src/exec.c +++ b/bin/dash/src/exec.c diff --git a/src/exec.h b/bin/dash/src/exec.h index 423b07e6..423b07e6 100644 --- a/src/exec.h +++ b/bin/dash/src/exec.h diff --git a/src/expand.c b/bin/dash/src/expand.c index 1730670e..1730670e 100644 --- a/src/expand.c +++ b/bin/dash/src/expand.c diff --git a/src/expand.h b/bin/dash/src/expand.h index c44b8481..c44b8481 100644 --- a/src/expand.h +++ b/bin/dash/src/expand.h diff --git a/src/funcs/cmv b/bin/dash/src/funcs/cmv index 91a67c53..91a67c53 100644 --- a/src/funcs/cmv +++ b/bin/dash/src/funcs/cmv diff --git a/src/funcs/dirs b/bin/dash/src/funcs/dirs index 5f6ce635..5f6ce635 100644 --- a/src/funcs/dirs +++ b/bin/dash/src/funcs/dirs diff --git a/src/funcs/kill b/bin/dash/src/funcs/kill index c5df95f5..c5df95f5 100644 --- a/src/funcs/kill +++ b/bin/dash/src/funcs/kill diff --git a/src/funcs/login b/bin/dash/src/funcs/login index 215e5352..215e5352 100644 --- a/src/funcs/login +++ b/bin/dash/src/funcs/login diff --git a/src/funcs/newgrp b/bin/dash/src/funcs/newgrp index ec0e7e5a..ec0e7e5a 100644 --- a/src/funcs/newgrp +++ b/bin/dash/src/funcs/newgrp diff --git a/src/funcs/popd b/bin/dash/src/funcs/popd index 7bccf50d..7bccf50d 100644 --- a/src/funcs/popd +++ b/bin/dash/src/funcs/popd diff --git a/src/funcs/pushd b/bin/dash/src/funcs/pushd index 19ac8e08..19ac8e08 100644 --- a/src/funcs/pushd +++ b/bin/dash/src/funcs/pushd diff --git a/src/funcs/suspend b/bin/dash/src/funcs/suspend index 44844678..44844678 100644 --- a/src/funcs/suspend +++ b/bin/dash/src/funcs/suspend diff --git a/src/histedit.c b/bin/dash/src/histedit.c index f5c90aba..1b7ad238 100644 --- a/src/histedit.c +++ b/bin/dash/src/histedit.c @@ -116,6 +116,14 @@ histedit(void) if (hist) el_set(el, EL_HIST, history, hist); el_set(el, EL_PROMPT, getprompt); + el_set(el, EL_RPROMPT, getrprompt); +#ifdef HAVE__EL_FN_SH_COMPLETE + el_set(el, EL_ADDFN, "sh-complete", "Filename completion", + _el_fn_sh_complete); +#else + el_set(el, EL_ADDFN, "sh-complete", "Filename completion", + _el_fn_complete); +#endif } else { bad: out2str("sh: can't initialize editing\n"); @@ -133,6 +141,7 @@ bad: else if (Eflag) el_set(el, EL_EDITOR, "emacs"); el_source(el, NULL); + el_set(el, EL_BIND, "^I", "sh-complete", NULL); } } else { INTOFF; @@ -160,6 +169,7 @@ sethistsize(const char *hs) (histsize = atoi(hs)) < 0) histsize = 100; history(hist, &he, H_SETSIZE, histsize); + history(hist, &he, H_SETUNIQUE, 1); } } diff --git a/src/init.h b/bin/dash/src/init.h index d56fb28e..d56fb28e 100644 --- a/src/init.h +++ b/bin/dash/src/init.h diff --git a/src/input.c b/bin/dash/src/input.c index 17544e78..4167bd18 100644 --- a/src/input.c +++ b/bin/dash/src/input.c @@ -152,12 +152,8 @@ retry: static const char *rl_cp; static int el_len; - if (rl_cp == NULL) { - struct stackmark smark; - pushstackmark(&smark, stackblocksize()); + if (rl_cp == NULL) rl_cp = el_gets(el, &el_len); - popstackmark(&smark); - } if (rl_cp == NULL) nr = 0; else { diff --git a/src/input.h b/bin/dash/src/input.h index 8acc6e9f..8acc6e9f 100644 --- a/src/input.h +++ b/bin/dash/src/input.h diff --git a/src/jobs.c b/bin/dash/src/jobs.c index f30313be..8fb9d4b2 100644 --- a/src/jobs.c +++ b/bin/dash/src/jobs.c @@ -1207,12 +1207,12 @@ stoppedjobs(void) int retval; retval = 0; - if (job_warning) + if (job_warning > 1) goto out; jp = curjob; if (jp && jp->state == JOBSTOPPED) { out2str("You have stopped jobs.\n"); - job_warning = 2; + job_warning++; retval++; } diff --git a/src/jobs.h b/bin/dash/src/jobs.h index 6ac6c56d..6ac6c56d 100644 --- a/src/jobs.h +++ b/bin/dash/src/jobs.h diff --git a/src/machdep.h b/bin/dash/src/machdep.h index f2ff0ad8..f2ff0ad8 100644 --- a/src/machdep.h +++ b/bin/dash/src/machdep.h diff --git a/src/mail.c b/bin/dash/src/mail.c index 8eacb2d0..8eacb2d0 100644 --- a/src/mail.c +++ b/bin/dash/src/mail.c diff --git a/src/mail.h b/bin/dash/src/mail.h index 3c6b21d2..3c6b21d2 100644 --- a/src/mail.h +++ b/bin/dash/src/mail.h diff --git a/src/main.c b/bin/dash/src/main.c index 7a285346..7a285346 100644 --- a/src/main.c +++ b/bin/dash/src/main.c diff --git a/src/main.h b/bin/dash/src/main.h index 19e49835..19e49835 100644 --- a/src/main.h +++ b/bin/dash/src/main.h diff --git a/src/memalloc.c b/bin/dash/src/memalloc.c index 60637da1..60637da1 100644 --- a/src/memalloc.c +++ b/bin/dash/src/memalloc.c diff --git a/src/memalloc.h b/bin/dash/src/memalloc.h index b9adf764..b9adf764 100644 --- a/src/memalloc.h +++ b/bin/dash/src/memalloc.h diff --git a/src/miscbltin.c b/bin/dash/src/miscbltin.c index 5ccbbcb8..5ccbbcb8 100644 --- a/src/miscbltin.c +++ b/bin/dash/src/miscbltin.c diff --git a/src/miscbltin.h b/bin/dash/src/miscbltin.h index dd9a8d1c..dd9a8d1c 100644 --- a/src/miscbltin.h +++ b/bin/dash/src/miscbltin.h diff --git a/src/mkbuiltins b/bin/dash/src/mkbuiltins index f1f25932..f1f25932 100644 --- a/src/mkbuiltins +++ b/bin/dash/src/mkbuiltins diff --git a/src/mkinit.c b/bin/dash/src/mkinit.c index 9025862e..9025862e 100644 --- a/src/mkinit.c +++ b/bin/dash/src/mkinit.c diff --git a/src/mknodes.c b/bin/dash/src/mknodes.c index 1903a605..1903a605 100644 --- a/src/mknodes.c +++ b/bin/dash/src/mknodes.c diff --git a/src/mksignames.c b/bin/dash/src/mksignames.c index a832eab9..a832eab9 100644 --- a/src/mksignames.c +++ b/bin/dash/src/mksignames.c diff --git a/src/mksyntax.c b/bin/dash/src/mksyntax.c index a23c18ca..a23c18ca 100644 --- a/src/mksyntax.c +++ b/bin/dash/src/mksyntax.c diff --git a/src/mktokens b/bin/dash/src/mktokens index 78055be8..78055be8 100644 --- a/src/mktokens +++ b/bin/dash/src/mktokens diff --git a/src/myhistedit.h b/bin/dash/src/myhistedit.h index 22e5c438..22e5c438 100644 --- a/src/myhistedit.h +++ b/bin/dash/src/myhistedit.h diff --git a/src/mystring.c b/bin/dash/src/mystring.c index de624b89..de624b89 100644 --- a/src/mystring.c +++ b/bin/dash/src/mystring.c diff --git a/src/mystring.h b/bin/dash/src/mystring.h index 083ea98c..083ea98c 100644 --- a/src/mystring.h +++ b/bin/dash/src/mystring.h diff --git a/src/nodes.c.pat b/bin/dash/src/nodes.c.pat index 9125bc73..9125bc73 100644 --- a/src/nodes.c.pat +++ b/bin/dash/src/nodes.c.pat diff --git a/src/nodetypes b/bin/dash/src/nodetypes index ceaf478c..ceaf478c 100644 --- a/src/nodetypes +++ b/bin/dash/src/nodetypes diff --git a/src/options.c b/bin/dash/src/options.c index a46c23b9..a46c23b9 100644 --- a/src/options.c +++ b/bin/dash/src/options.c diff --git a/src/options.h b/bin/dash/src/options.h index 975fe339..975fe339 100644 --- a/src/options.h +++ b/bin/dash/src/options.h diff --git a/src/output.c b/bin/dash/src/output.c index e9ee9b4d..e9ee9b4d 100644 --- a/src/output.c +++ b/bin/dash/src/output.c diff --git a/src/output.h b/bin/dash/src/output.h index c43d4937..c43d4937 100644 --- a/src/output.h +++ b/bin/dash/src/output.h diff --git a/src/parser.c b/bin/dash/src/parser.c index a47022e0..499f7f8e 100644 --- a/src/parser.c +++ b/bin/dash/src/parser.c @@ -107,6 +107,10 @@ struct heredoc *heredoc; int quoteflag; /* set if (part of) last token was quoted */ +static char *promptcache; +static char *rpromptcache; + + STATIC union node *list(int); STATIC union node *andor(void); STATIC union node *pipeline(void); @@ -1541,27 +1545,6 @@ synerror(const char *msg) /* NOTREACHED */ } -STATIC void -setprompt(int which) -{ - struct stackmark smark; - int show; - - needprompt = 0; - whichprompt = which; - -#ifdef SMALL - show = 1; -#else - show = !el; -#endif - if (show) { - pushstackmark(&smark, stackblocksize()); - out2str(getprompt(NULL)); - popstackmark(&smark); - } -} - const char * expandstr(const char *ps) { @@ -1611,31 +1594,78 @@ out: return result; } -/* - * called by editline -- any expansions to the prompt - * should be added here. - */ -const char * -getprompt(void *unused) +STATIC void +setprompt(int which) { - const char *prompt; + struct stackmark smark; + const char *prompt, *rprompt, *nl; + int show; + + needprompt = 0; + whichprompt = which; switch (whichprompt) { default: #ifdef DEBUG - return "<internal prompt error>"; + prompt = "<internal prompt error>"; + rprompt = prompt; + break; #endif case 0: - return nullstr; + prompt = nullstr; + rprompt = nullstr; + break; case 1: prompt = ps1val(); + rprompt = rps1val(); break; case 2: prompt = ps2val(); + rprompt = rps2val(); break; } - return expandstr(prompt); +#ifdef SMALL + show = 1; +#else + show = !el; +#endif + pushstackmark(&smark, stackblocksize()); + if (show) { + out2str(expandstr(prompt)); + } else { + free(promptcache); + free(rpromptcache); + promptcache = savestr(expandstr(prompt)); + rpromptcache = savestr(expandstr(rprompt)); + + nl = strrchr(promptcache, '\n'); + if (nl) + outmem(promptcache, &nl[1] - promptcache, out2); + } + popstackmark(&smark); +} + +/* + * called by editline -- return the cached prompt expansion. + */ +const char * +getprompt(void *unused) +{ + const char *nl; + + nl = strrchr(promptcache, '\n'); + + if (nl) + return &nl[1]; + else + return promptcache; +} + +const char * +getrprompt(void *unused) +{ + return rpromptcache; } const char *const * diff --git a/src/parser.h b/bin/dash/src/parser.h index 524ac1c7..6597faa8 100644 --- a/src/parser.h +++ b/bin/dash/src/parser.h @@ -86,6 +86,7 @@ int isassignment(const char *p); union node *parsecmd(int); void fixredir(union node *, const char *, int); const char *getprompt(void *); +const char *getrprompt(void *); const char *const *findkwd(const char *); char *endofname(const char *); const char *expandstr(const char *); diff --git a/src/redir.c b/bin/dash/src/redir.c index 895140c3..895140c3 100644 --- a/src/redir.c +++ b/bin/dash/src/redir.c diff --git a/src/redir.h b/bin/dash/src/redir.h index 1cf27616..1cf27616 100644 --- a/src/redir.h +++ b/bin/dash/src/redir.h diff --git a/src/shell.h b/bin/dash/src/shell.h index 98edc8be..98edc8be 100644 --- a/src/shell.h +++ b/bin/dash/src/shell.h diff --git a/src/show.c b/bin/dash/src/show.c index 4a049e93..4a049e93 100644 --- a/src/show.c +++ b/bin/dash/src/show.c diff --git a/src/show.h b/bin/dash/src/show.h index d0ccac79..d0ccac79 100644 --- a/src/show.h +++ b/bin/dash/src/show.h diff --git a/src/system.c b/bin/dash/src/system.c index 844a6410..844a6410 100644 --- a/src/system.c +++ b/bin/dash/src/system.c diff --git a/src/system.h b/bin/dash/src/system.h index 007952c5..007952c5 100644 --- a/src/system.h +++ b/bin/dash/src/system.h diff --git a/src/trap.c b/bin/dash/src/trap.c index cd84814f..cd84814f 100644 --- a/src/trap.c +++ b/bin/dash/src/trap.c diff --git a/src/trap.h b/bin/dash/src/trap.h index beaf6605..beaf6605 100644 --- a/src/trap.h +++ b/bin/dash/src/trap.h diff --git a/src/var.c b/bin/dash/src/var.c index ef9c2bde..ca9504ef 100644 --- a/src/var.c +++ b/bin/dash/src/var.c @@ -93,6 +93,8 @@ struct var varinit[] = { { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 }, { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 }, { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 }, + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "RPS1\0", 0 }, + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "RPS2\0", 0 }, { 0, VSTRFIXED|VTEXTFIXED, defoptindvar, getoptsreset }, #ifdef WITH_LINENO { 0, VSTRFIXED|VTEXTFIXED, linenovar, 0 }, diff --git a/src/var.h b/bin/dash/src/var.h index aa7575a7..1d1d87a5 100644 --- a/src/var.h +++ b/bin/dash/src/var.h @@ -87,7 +87,9 @@ extern struct var varinit[]; #define vps1 (&vpath)[1] #define vps2 (&vps1)[1] #define vps4 (&vps2)[1] -#define voptind (&vps4)[1] +#define vrps1 (&vps4)[1] +#define vrps2 (&vrps1)[1] +#define voptind (&vrps2)[1] #ifdef WITH_LINENO #define vlineno (&voptind)[1] #endif @@ -122,6 +124,8 @@ extern char linenovar[]; #define ps1val() (vps1.text + 4) #define ps2val() (vps2.text + 4) #define ps4val() (vps4.text + 4) +#define rps1val() (vrps1.text + 5) +#define rps2val() (vrps2.text + 5) #define optindval() (voptind.text + 7) #define linenoval() (vlineno.text + 7) #ifndef SMALL diff --git a/bin/dtch.c b/bin/dtch.c new file mode 100644 index 00000000..2aea53ae --- /dev/null +++ b/bin/dtch.c @@ -0,0 +1,271 @@ +/* Copyright (C) 2017-2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/wait.h> +#include <sysexits.h> +#include <termios.h> +#include <unistd.h> + +#if defined __FreeBSD__ +#include <libutil.h> +#elif defined __linux__ +#include <pty.h> +#else +#include <util.h> +#endif + +static char _; +static struct iovec iov = { .iov_base = &_, .iov_len = 1 }; + +static ssize_t sendfd(int sock, int fd) { + size_t len = CMSG_SPACE(sizeof(int)); + char buf[len]; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = buf, + .msg_controllen = len, + }; + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cmsg) = fd; + + return sendmsg(sock, &msg, 0); +} + +static int recvfd(int sock) { + size_t len = CMSG_SPACE(sizeof(int)); + char buf[len]; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = buf, + .msg_controllen = len, + }; + if (0 > recvmsg(sock, &msg, 0)) return -1; + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS) { + errno = ENOMSG; + return -1; + } + return *(int *)CMSG_DATA(cmsg); +} + +static struct sockaddr_un addr = { .sun_family = AF_UNIX }; + +static void handler(int sig) { + unlink(addr.sun_path); + _exit(-sig); +} + +static void detach(int server, bool sink, char *argv[]) { + int pty; + pid_t pid = forkpty(&pty, NULL, NULL, NULL); + if (pid < 0) err(EX_OSERR, "forkpty"); + + if (!pid) { + execvp(argv[0], argv); + err(EX_NOINPUT, "%s", argv[0]); + } + + signal(SIGINT, handler); + signal(SIGTERM, handler); + + int error = listen(server, 0); + if (error) err(EX_OSERR, "listen"); + + struct pollfd fds[] = { + { .events = POLLIN, .fd = server }, + { .events = POLLIN, .fd = pty }, + }; + while (0 < poll(fds, (sink ? 2 : 1), -1)) { + if (fds[0].revents) { + int client = accept(server, NULL, NULL); + if (client < 0) err(EX_IOERR, "accept"); + + ssize_t len = sendfd(client, pty); + if (len < 0) warn("sendfd"); + + len = recv(client, &_, sizeof(_), 0); + if (len < 0) warn("recv"); + + close(client); + } + + if (fds[1].revents) { + char buf[4096]; + ssize_t len = read(pty, buf, sizeof(buf)); + if (len < 0) err(EX_IOERR, "read"); + } + + int status; + pid_t dead = waitpid(pid, &status, WNOHANG); + if (dead < 0) err(EX_OSERR, "waitpid"); + if (dead) { + unlink(addr.sun_path); + exit(WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status)); + } + } + err(EX_IOERR, "poll"); +} + +static struct termios saveTerm; +static void restoreTerm(void) { + tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm); + fprintf(stderr, "\33c"); // RIS + warnx("detached"); +} + +static void nop(int sig) { + (void)sig; +} + +static void attach(int client) { + int error; + + int pty = recvfd(client); + if (pty < 0) err(EX_IOERR, "recvfd"); + warnx("attached"); + + struct winsize window; + error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window); + if (error) err(EX_IOERR, "ioctl"); + + struct winsize redraw = { .ws_row = 1, .ws_col = 1 }; + error = ioctl(pty, TIOCSWINSZ, &redraw); + if (error) err(EX_IOERR, "ioctl"); + + error = ioctl(pty, TIOCSWINSZ, &window); + if (error) err(EX_IOERR, "ioctl"); + + error = tcgetattr(STDIN_FILENO, &saveTerm); + if (error) err(EX_IOERR, "tcgetattr"); + atexit(restoreTerm); + + struct termios raw = saveTerm; + cfmakeraw(&raw); + error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw); + if (error) err(EX_IOERR, "tcsetattr"); + + signal(SIGWINCH, nop); + + char buf[4096]; + struct pollfd fds[] = { + { .events = POLLIN, .fd = STDIN_FILENO }, + { .events = POLLIN, .fd = pty }, + }; + for (;;) { + int nfds = poll(fds, 2, -1); + if (nfds < 0) { + if (errno != EINTR) err(EX_IOERR, "poll"); + + error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window); + if (error) err(EX_IOERR, "ioctl"); + + error = ioctl(pty, TIOCSWINSZ, &window); + if (error) err(EX_IOERR, "ioctl"); + + continue; + } + + if (fds[0].revents) { + ssize_t len = read(STDIN_FILENO, buf, sizeof(buf)); + if (len < 0) err(EX_IOERR, "read"); + if (!len) break; + + if (len == 1 && buf[0] == CTRL('Q')) break; + + len = write(pty, buf, len); + if (len < 0) err(EX_IOERR, "write"); + } + + if (fds[1].revents) { + ssize_t len = read(pty, buf, sizeof(buf)); + if (len < 0) err(EX_IOERR, "read"); + if (!len) break; + + len = write(STDOUT_FILENO, buf, len); + if (len < 0) err(EX_IOERR, "write"); + } + } +} + +int main(int argc, char *argv[]) { + int error; + + bool atch = false; + bool sink = false; + + int opt; + while (0 < (opt = getopt(argc, argv, "as"))) { + switch (opt) { + break; case 'a': atch = true; + break; case 's': sink = true; + break; default: return EX_USAGE; + } + } + if (optind == argc) errx(EX_USAGE, "no session name"); + const char *name = argv[optind++]; + + if (optind == argc) { + argv[--optind] = getenv("SHELL"); + if (!argv[optind]) errx(EX_CONFIG, "SHELL unset"); + } + + const char *home = getenv("HOME"); + if (!home) errx(EX_CONFIG, "HOME unset"); + + int fd = open(home, 0); + if (fd < 0) err(EX_CANTCREAT, "%s", home); + + error = mkdirat(fd, ".dtch", 0700); + if (error && errno != EEXIST) err(EX_CANTCREAT, "%s/.dtch", home); + + close(fd); + + int sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) err(EX_OSERR, "socket"); + fcntl(sock, F_SETFD, FD_CLOEXEC); + + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/.dtch/%s", home, name); + + if (atch) { + error = connect(sock, (struct sockaddr *)&addr, SUN_LEN(&addr)); + if (error) err(EX_NOINPUT, "%s", addr.sun_path); + attach(sock); + } else { + error = bind(sock, (struct sockaddr *)&addr, SUN_LEN(&addr)); + if (error) err(EX_CANTCREAT, "%s", addr.sun_path); + detach(sock, sink, &argv[optind]); + } +} diff --git a/bin/ever.c b/bin/ever.c new file mode 100644 index 00000000..f983912b --- /dev/null +++ b/bin/ever.c @@ -0,0 +1,119 @@ +/* Copyright (C) 2017 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> + +#include <err.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdlib.h> +#include <sys/event.h> +#include <sys/wait.h> +#include <sysexits.h> +#include <unistd.h> + +static int watch(int kq, char *path) { + int fd = open(path, O_CLOEXEC); + if (fd < 0) err(EX_NOINPUT, "%s", path); + + struct kevent event; + EV_SET( + &event, + fd, + EVFILT_VNODE, + EV_ADD | EV_CLEAR, + NOTE_WRITE | NOTE_DELETE, + 0, + path + ); + int nevents = kevent(kq, &event, 1, NULL, 0, NULL); + if (nevents < 0) err(EX_OSERR, "kevent"); + + return fd; +} + +static bool quiet; +static void exec(int fd, char *const argv[]) { + pid_t pid = fork(); + if (pid < 0) err(EX_OSERR, "fork"); + + if (!pid) { + dup2(fd, STDIN_FILENO); + execvp(*argv, argv); + err(EX_NOINPUT, "%s", *argv); + } + + int status; + pid = wait(&status); + if (pid < 0) err(EX_OSERR, "wait"); + + if (quiet) return; + if (WIFEXITED(status)) { + warnx("exit %d\n", WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + warnx("signal %d\n", WTERMSIG(status)); + } else { + warnx("status %d\n", status); + } +} + +int main(int argc, char *argv[]) { + bool input = false; + + for (int opt; 0 < (opt = getopt(argc, argv, "iq"));) { + switch (opt) { + break; case 'i': input = true; + break; case 'q': quiet = true; + break; default: return EX_USAGE; + } + } + argc -= optind; + argv += optind; + if (argc < 2) return EX_USAGE; + + int kq = kqueue(); + if (kq < 0) err(EX_OSERR, "kqueue"); + + int i; + for (i = 0; i < argc - 1; ++i) { + if (argv[i][0] == '-') { + i++; + break; + } + watch(kq, argv[i]); + } + + if (!input) { + exec(STDIN_FILENO, &argv[i]); + } + + for (;;) { + struct kevent event; + int nevents = kevent(kq, NULL, 0, &event, 1, NULL); + if (nevents < 0) err(EX_OSERR, "kevent"); + + if (event.fflags & NOTE_DELETE) { + close(event.ident); + sleep(1); + event.ident = watch(kq, (char *)event.udata); + } else if (input) { + off_t off = lseek(event.ident, 0, SEEK_SET); + if (off < 0) err(EX_IOERR, "lseek"); + } + + exec((input ? event.ident : STDIN_FILENO), &argv[i]); + } +} diff --git a/bin/fbatt.c b/bin/fbatt.c new file mode 100644 index 00000000..9feffe47 --- /dev/null +++ b/bin/fbatt.c @@ -0,0 +1,123 @@ +/* Copyright (C) 2018 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/fb.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sysexits.h> +#include <unistd.h> + +#include "scheme.h" + +static const char *Class = "/sys/class/power_supply"; + +static const uint32_t Right = 5 * 8 + 1; // fbclock width. +static const uint32_t Width = 8; +static const uint32_t Height = 16; + +int main() { + int error; + + DIR *dir = opendir(Class); + if (!dir) err(EX_OSFILE, "%s", Class); + + FILE *chargeFull = NULL; + FILE *chargeNow = NULL; + + const struct dirent *entry; + while (NULL != (errno = 0, entry = readdir(dir))) { + if (entry->d_name[0] == '.') continue; + + error = chdir(Class); + if (error) err(EX_OSFILE, "%s", Class); + + error = chdir(entry->d_name); + if (error) err(EX_OSFILE, "%s/%s", Class, entry->d_name); + + chargeFull = fopen("charge_full", "r"); + chargeNow = fopen("charge_now", "r"); + if (chargeFull && chargeNow) break; + } + if (!chargeFull || !chargeNow) { + if (errno) err(EX_OSFILE, "%s", Class); + errx(EX_CONFIG, "%s: empty", Class); + } + closedir(dir); + + const char *path = getenv("FRAMEBUFFER"); + if (!path) path = "/dev/fb0"; + + int fb = open(path, O_RDWR); + if (fb < 0) err(EX_OSFILE, "%s", path); + + struct fb_var_screeninfo info; + error = ioctl(fb, FBIOGET_VSCREENINFO, &info); + if (error) err(EX_IOERR, "%s", path); + + size_t size = 4 * info.xres * info.yres; + uint32_t *buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0); + if (buf == MAP_FAILED) err(EX_IOERR, "%s", path); + + for (;;) { + int match; + + rewind(chargeFull); + fflush(chargeFull); + uint32_t full; + match = fscanf(chargeFull, "%u", &full); + if (match == EOF) err(EX_IOERR, "charge_full"); + if (match < 1) errx(EX_DATAERR, "charge_full"); + + rewind(chargeNow); + fflush(chargeNow); + uint32_t now; + match = fscanf(chargeNow, "%u", &now); + if (match == EOF) err(EX_IOERR, "charge_now"); + if (match < 1) errx(EX_DATAERR, "charge_now"); + + uint32_t percent = 100 * now / full; + uint32_t height = 16 * now / full; + + for (int i = 0; i < 60; ++i, sleep(1)) { + uint32_t left = info.xres - Right - Width; + + for (uint32_t y = 0; y <= Height; ++y) { + buf[y * info.xres + left - 1] = DarkWhite; + buf[y * info.xres + left + Width] = DarkWhite; + } + for (uint32_t x = left; x < left + Width; ++x) { + buf[Height * info.xres + x] = DarkWhite; + } + + for (uint32_t y = 0; y < Height; ++y) { + for (uint32_t x = left; x < left + Width; ++x) { + buf[y * info.xres + x] = + (Height - 1 - y > height) ? DarkBlack + : (percent <= 10) ? DarkRed + : (percent <= 30) ? DarkYellow + : LightBlack; + } + } + } + } +} diff --git a/bin/fbclock.c b/bin/fbclock.c new file mode 100644 index 00000000..ddc32db6 --- /dev/null +++ b/bin/fbclock.c @@ -0,0 +1,132 @@ +/* Copyright (C) 2018 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <err.h> +#include <fcntl.h> +#include <linux/fb.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sysexits.h> +#include <time.h> +#include <unistd.h> +#include <zlib.h> + +#include "scheme.h" + +static const uint32_t PSF2Magic = 0x864AB572; +struct PSF2Header { + uint32_t magic; + uint32_t version; + uint32_t headerSize; + uint32_t flags; + uint32_t glyphCount; + uint32_t glyphSize; + uint32_t glyphHeight; + uint32_t glyphWidth; +}; + +int main() { + size_t len; + + const char *fontPath = getenv("FONT"); + if (!fontPath) { + fontPath = "/usr/share/kbd/consolefonts/Lat2-Terminus16.psfu.gz"; + } + + gzFile font = gzopen(fontPath, "r"); + if (!font) err(EX_NOINPUT, "%s", fontPath); + + struct PSF2Header header; + len = gzfread(&header, sizeof(header), 1, font); + if (!len && gzeof(font)) errx(EX_DATAERR, "%s: missing header", fontPath); + if (!len) errx(EX_IOERR, "%s", gzerror(font, NULL)); + + if (header.magic != PSF2Magic) { + errx( + EX_DATAERR, "%s: invalid header magic %08X", + fontPath, header.magic + ); + } + if (header.headerSize != sizeof(struct PSF2Header)) { + errx( + EX_DATAERR, "%s: weird header size %d", + fontPath, header.headerSize + ); + } + + uint8_t glyphs[128][header.glyphSize]; + len = gzfread(glyphs, header.glyphSize, 128, font); + if (!len && gzeof(font)) errx(EX_DATAERR, "%s: missing glyphs", fontPath); + if (!len) errx(EX_IOERR, "%s", gzerror(font, NULL)); + + gzclose(font); + + const char *fbPath = getenv("FRAMEBUFFER"); + if (!fbPath) fbPath = "/dev/fb0"; + + int fb = open(fbPath, O_RDWR); + if (fb < 0) err(EX_OSFILE, "%s", fbPath); + + struct fb_var_screeninfo info; + int error = ioctl(fb, FBIOGET_VSCREENINFO, &info); + if (error) err(EX_IOERR, "%s", fbPath); + + size_t size = 4 * info.xres * info.yres; + uint32_t *buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0); + if (buf == MAP_FAILED) err(EX_IOERR, "%s", fbPath); + + for (;;) { + time_t t = time(NULL); + if (t < 0) err(EX_OSERR, "time"); + const struct tm *local = localtime(&t); + if (!local) err(EX_OSERR, "localtime"); + + char str[64]; + len = strftime(str, sizeof(str), "%H:%M", local); + assert(len); + + for (int i = 0; i < (60 - local->tm_sec); ++i, sleep(1)) { + uint32_t left = info.xres - header.glyphWidth * len; + uint32_t bottom = header.glyphHeight; + + for (uint32_t y = 0; y < bottom; ++y) { + buf[y * info.xres + left - 1] = DarkWhite; + } + for (uint32_t x = left - 1; x < info.xres; ++x) { + buf[bottom * info.xres + x] = DarkWhite; + } + + for (const char *s = str; *s; ++s) { + const uint8_t *glyph = glyphs[(unsigned)*s]; + uint32_t stride = header.glyphSize / header.glyphHeight; + for (uint32_t y = 0; y < header.glyphHeight; ++y) { + for (uint32_t x = 0; x < header.glyphWidth; ++x) { + uint8_t bits = glyph[y * stride + x / 8]; + uint8_t bit = bits >> (7 - x % 8) & 1; + buf[y * info.xres + left + x] = bit + ? DarkWhite + : DarkBlack; + } + } + left += header.glyphWidth; + } + } + } +} diff --git a/bin/freecell.c b/bin/freecell.c new file mode 100644 index 00000000..11bed1c0 --- /dev/null +++ b/bin/freecell.c @@ -0,0 +1,388 @@ +/* Copyright (C) 2019, 2021 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <ctype.h> +#include <curses.h> +#include <err.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> +#include <time.h> +#include <unistd.h> + +typedef unsigned uint; +typedef unsigned char byte; + +typedef byte Card; +enum { + A = 1, + J = 11, + Q = 12, + K = 13, + Rank = 0x0F, + Suit = 0x30, + Color = 0x10, + Club = 0x00, + Diamond = 0x10, + Spade = 0x20, + Heart = 0x30, +}; + +enum { StackCap = 52 }; +struct Stack { + byte len; + Card cards[StackCap]; +}; +static void push(struct Stack *stack, Card card) { + assert(stack->len < StackCap); + stack->cards[stack->len++] = card; +} +static Card pop(struct Stack *stack) { + if (!stack->len) return 0; + return stack->cards[--stack->len]; +} +static Card peek(struct Stack *stack) { + if (!stack->len) return 0; + return stack->cards[stack->len-1]; +} + +enum { + Foundation, + Cell = Foundation + 4, + Tableau = Cell + 4, + Stacks = Tableau + 8, +}; +static struct Stack stacks[Stacks]; + +struct Move { + byte dst; + byte src; +}; + +enum { QCap = 16 }; +static struct { + struct Move moves[QCap]; + uint r, w, u; +} q; +static void enq(byte dst, byte src) { + q.moves[q.w % QCap].dst = dst; + q.moves[q.w % QCap].src = src; + q.w++; +} +static void deq(void) { + struct Move move = q.moves[q.r++ % QCap]; + push(&stacks[move.dst], pop(&stacks[move.src])); +} +static void undo(void) { + uint len = q.w - q.u; + if (!len || len > QCap) return; + for (uint i = len-1; i < len; --i) { + struct Move move = q.moves[(q.u+i) % QCap]; + push(&stacks[move.src], pop(&stacks[move.dst])); + } + q.r = q.w = q.u; +} + +// https://rosettacode.org/wiki/Deal_cards_for_FreeCell +static uint lcgState; +static uint lcg(void) { + lcgState = (214013 * lcgState + 2531011) % (1 << 31); + return lcgState >> 16; +} +static void deal(uint game) { + lcgState = game; + struct Stack deck = {0}; + for (Card i = A; i <= K; ++i) { + push(&deck, Club | i); + push(&deck, Diamond | i); + push(&deck, Heart | i); + push(&deck, Spade | i); + } + for (uint stack = 0; deck.len; ++stack) { + uint i = lcg() % deck.len; + Card card = deck.cards[i]; + deck.cards[i] = deck.cards[--deck.len]; + push(&stacks[Tableau + stack%8], card); + } +} + +static bool win(void) { + for (uint i = Foundation; i < Cell; ++i) { + if (stacks[i].len != 13) return false; + } + return true; +} + +static bool valid(uint dst, Card card) { + Card top = peek(&stacks[dst]); + if (dst < Cell) { + if (!top) return (card & Rank) == A; + return (card & Suit) == (top & Suit) + && (card & Rank) == (top & Rank) + 1; + } + if (!top) return true; + if (dst >= Tableau) { + return (card & Color) != (top & Color) + && (card & Rank) == (top & Rank) - 1; + } + return false; +} + +static void autoEnq(void) { + Card min[] = { K, K }; + for (uint i = Cell; i < Stacks; ++i) { + for (uint j = 0; j < stacks[i].len; ++j) { + Card card = stacks[i].cards[j]; + if ((card & Rank) < min[!!(card & Color)]) { + min[!!(card & Color)] = card & Rank; + } + } + } + for (uint src = Cell; src < Stacks; ++src) { + Card card = peek(&stacks[src]); + if (!card) continue; + if (min[!(card & Color)] < (card & Rank)-1) continue; + for (uint dst = Foundation; dst < Cell; ++dst) { + if (valid(dst, card)) { + enq(dst, src); + return; + } + } + } +} + +static void moveSingle(uint dst, uint src) { + if (!valid(dst, peek(&stacks[src]))) return; + q.u = q.w; + enq(dst, src); +} + +static uint freeCells(uint cells[static 4]) { + uint len = 0; + for (uint i = Cell; i < Tableau; ++i) { + if (!stacks[i].len) cells[len++] = i; + } + return len; +} + +static uint moveDepth(uint src) { + struct Stack stack = stacks[src]; + if (stack.len < 2) return stack.len; + uint n = 1; + for (uint i = stack.len-2; i < stack.len; --i, ++n) { + if ((stack.cards[i] & Color) == (stack.cards[i+1] & Color)) break; + if ((stack.cards[i] & Rank) != (stack.cards[i+1] & Rank) + 1) break; + } + return n; +} + +static void moveColumn(uint dst, uint src) { + uint depth; + uint cells[4]; + uint free = freeCells(cells); + for (depth = moveDepth(src); depth; --depth) { + if (free < depth-1) continue; + if (valid(dst, stacks[src].cards[stacks[src].len-depth])) break; + } + if (depth < 2 || dst < Tableau) { + moveSingle(dst, src); + return; + } + q.u = q.w; + for (uint i = 0; i < depth-1; ++i) { + enq(cells[i], src); + } + enq(dst, src); + for (uint i = depth-2; i < depth-1; --i) { + enq(dst, cells[i]); + } +} + +static void curse(void) { + setlocale(LC_CTYPE, ""); + initscr(); + cbreak(); + noecho(); + curs_set(0); + start_color(); + use_default_colors(); + init_pair(1, COLOR_BLACK, COLOR_WHITE); + init_pair(2, COLOR_RED, COLOR_WHITE); + init_pair(3, COLOR_GREEN, -1); +} + +static void drawCard(bool hi, int y, int x, Card card) { + if (!card) return; + move(y, x); + attr_set(hi ? A_REVERSE : A_NORMAL, (card & Color) ? 2 : 1, NULL); + switch (card & Suit) { + break; case Club: addstr("\u2663"); + break; case Diamond: addstr("\u2666"); + break; case Spade: addstr("\u2660"); + break; case Heart: addstr("\u2665"); + break; default:; + } + switch (card & Rank) { + break; case A: addstr(" A"); + break; case 10: addstr("10"); + break; case J: addstr(" J"); + break; case Q: addstr(" Q"); + break; case K: addstr(" K"); + break; default: { + addch(' '); + addch('0' + (card & Rank)); + } + } + attr_set(A_NORMAL, 0, NULL); +} + +static void drawStack(bool hi, int y, int x, const struct Stack *stack) { + for (uint i = 0; i < stack->len; ++i) { + drawCard(hi && i == stack->len-1, y++, x, stack->cards[i]); + } +} + +enum { + Padding = 1, + CardWidth = 3, + CardHeight = 1, + CellX = Padding, + CellY = 2*CardHeight, + FoundationX = CellX + 4*(CardWidth+Padding), + FoundationY = CellY, + TableauX = CellX, + TableauY = CellY + 2*CardHeight, +}; + +static uint game; +static uint srcStack = Stacks; + +static void draw(void) { + erase(); + static char buf[256]; + if (!buf[0]) snprintf(buf, sizeof(buf), "Game #%u", game); + attr_set(A_NORMAL, 3, NULL); + mvaddstr(0, Padding, buf); + for (uint i = 0; i < Stacks; ++i) { + int y, x; + char key; + if (i < Cell) { + y = FoundationY; + x = FoundationX + (3-(i-Foundation)) * (CardWidth+Padding); + key = '_'; + } else if (i < Tableau) { + y = CellY; + x = CellX + (i-Cell) * (CardWidth+Padding); + key = '1' + i-Cell; + } else { + y = TableauY; + x = TableauX + (i-Tableau) * (CardWidth+Padding); + key = "QWERASDF"[i-Tableau]; + } + if (i < Tableau) { + mvaddch(y, x+1, COLOR_PAIR(3) | key); + } else { + mvaddch(y + 8*CardHeight, x+1, COLOR_PAIR(3) | key); + } + if (i < Cell) { + drawCard(false, y, x, peek(&stacks[i])); + } else { + drawStack(i == srcStack, y, x, &stacks[i]); + } + } +} + +static void input(void) { + char ch = getch(); + uint stack = Stacks; + switch (tolower(ch)) { + break; case '\33': srcStack = Stacks; + break; case 'u': case '\b': case '\177': undo(); + break; case '1': case '!': stack = Cell+0; + break; case '2': case '@': stack = Cell+1; + break; case '3': case '#': stack = Cell+2; + break; case '4': case '$': stack = Cell+3; + break; case '_': case ' ': stack = Foundation; + break; case 'q': stack = Tableau+0; + break; case 'w': stack = Tableau+1; + break; case 'e': stack = Tableau+2; + break; case 'r': stack = Tableau+3; + break; case 'a': stack = Tableau+4; + break; case 's': stack = Tableau+5; + break; case 'd': stack = Tableau+6; + break; case 'f': stack = Tableau+7; + } + if (stack == Stacks) return; + + if (srcStack < Stacks) { + Card card = peek(&stacks[srcStack]); + if (stack == Foundation) { + for (; stack < Cell; ++stack) { + if (valid(stack, card)) break; + } + if (stack == Cell) return; + } + if (stack == srcStack) { + for (stack = Cell; stack < Stacks; ++stack) { + if (!stacks[stack].len) break; + } + if (stack == Stacks) return; + } + if (isupper(ch)) { + moveSingle(stack, srcStack); + } else { + moveColumn(stack, srcStack); + } + srcStack = Stacks; + + } else if (stack >= Cell && stacks[stack].len) { + srcStack = stack; + } +} + +static void status(void) { + printf("Game #%u %s!\n", game, win() ? "win" : "lose"); +} + +int main(int argc, char *argv[]) { + game = 1 + time(NULL) % 32000; + uint delay = 50; + for (int opt; 0 < (opt = getopt(argc, argv, "d:n:"));) { + switch (opt) { + break; case 'd': delay = strtoul(optarg, NULL, 10); + break; case 'n': game = strtoul(optarg, NULL, 10); + break; default: return EX_USAGE; + } + } + curse(); + deal(game); + atexit(status); + while (!win()) { + while (q.r < q.w) { + deq(); + draw(); + refresh(); + usleep(delay * 1000); + if (q.r == q.w) autoEnq(); + } + draw(); + input(); + } + endwin(); +} diff --git a/bin/glitch.c b/bin/glitch.c new file mode 100644 index 00000000..9747f35a --- /dev/null +++ b/bin/glitch.c @@ -0,0 +1,538 @@ +/* Copyright (C) 2018 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> +#include <err.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <zlib.h> + +#define PACKED __attribute__((packed)) + +#define CRC_INIT (crc32(0, Z_NULL, 0)) + +static const char *path; +static FILE *file; +static uint32_t crc; + +static void readExpect(void *ptr, size_t size, const char *expect) { + fread(ptr, size, 1, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + if (feof(file)) errx(EX_DATAERR, "%s: missing %s", path, expect); + crc = crc32(crc, ptr, size); +} + +static void writeExpect(const void *ptr, size_t size) { + fwrite(ptr, size, 1, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + crc = crc32(crc, ptr, size); +} + +static const uint8_t Signature[8] = "\x89PNG\r\n\x1A\n"; + +static void readSignature(void) { + uint8_t signature[8]; + readExpect(signature, 8, "signature"); + if (0 != memcmp(signature, Signature, 8)) { + errx(EX_DATAERR, "%s: invalid signature", path); + } +} + +static void writeSignature(void) { + writeExpect(Signature, sizeof(Signature)); +} + +struct PACKED Chunk { + uint32_t size; + char type[4]; +}; + +static const char *typeStr(struct Chunk chunk) { + static char buf[5]; + memcpy(buf, chunk.type, 4); + return buf; +} + +static struct Chunk readChunk(void) { + struct Chunk chunk; + readExpect(&chunk, sizeof(chunk), "chunk"); + chunk.size = ntohl(chunk.size); + crc = crc32(CRC_INIT, (Byte *)chunk.type, sizeof(chunk.type)); + return chunk; +} + +static void writeChunk(struct Chunk chunk) { + chunk.size = htonl(chunk.size); + writeExpect(&chunk, sizeof(chunk)); + crc = crc32(CRC_INIT, (Byte *)chunk.type, sizeof(chunk.type)); +} + +static void readCrc(void) { + uint32_t expected = crc; + uint32_t found; + readExpect(&found, sizeof(found), "CRC32"); + found = ntohl(found); + if (found != expected) { + errx( + EX_DATAERR, "%s: expected CRC32 %08X, found %08X", + path, expected, found + ); + } +} + +static void writeCrc(void) { + uint32_t net = htonl(crc); + writeExpect(&net, sizeof(net)); +} + +static void skipChunk(struct Chunk chunk) { + uint8_t discard[chunk.size]; + readExpect(discard, sizeof(discard), "chunk data"); + readCrc(); +} + +static struct PACKED { + uint32_t width; + uint32_t height; + uint8_t depth; + enum PACKED { + Grayscale = 0, + Truecolor = 2, + Indexed = 3, + GrayscaleAlpha = 4, + TruecolorAlpha = 6, + } color; + uint8_t compression; + uint8_t filter; + uint8_t interlace; +} header; +_Static_assert(13 == sizeof(header), "header size"); + +static size_t pixelBits(void) { + switch (header.color) { + case Grayscale: return 1 * header.depth; + case Truecolor: return 3 * header.depth; + case Indexed: return 1 * header.depth; + case GrayscaleAlpha: return 2 * header.depth; + case TruecolorAlpha: return 4 * header.depth; + default: abort(); + } +} + +static size_t pixelSize(void) { + return (pixelBits() + 7) / 8; +} + +static size_t lineSize(void) { + return (header.width * pixelBits() + 7) / 8; +} + +static size_t dataSize(void) { + return (1 + lineSize()) * header.height; +} + +static void readHeader(void) { + struct Chunk ihdr = readChunk(); + if (0 != memcmp(ihdr.type, "IHDR", 4)) { + errx(EX_DATAERR, "%s: expected IHDR, found %s", path, typeStr(ihdr)); + } + if (ihdr.size != sizeof(header)) { + errx( + EX_DATAERR, "%s: expected IHDR size %zu, found %u", + path, sizeof(header), ihdr.size + ); + } + readExpect(&header, sizeof(header), "header"); + readCrc(); + header.width = ntohl(header.width); + header.height = ntohl(header.height); + if (!header.width) errx(EX_DATAERR, "%s: invalid width 0", path); + if (!header.height) errx(EX_DATAERR, "%s: invalid height 0", path); +} + +static void writeHeader(void) { + struct Chunk ihdr = { .size = sizeof(header), .type = "IHDR" }; + writeChunk(ihdr); + header.width = htonl(header.width); + header.height = htonl(header.height); + writeExpect(&header, sizeof(header)); + writeCrc(); + header.width = ntohl(header.width); + header.height = ntohl(header.height); +} + +static struct { + uint32_t len; + uint8_t entries[256][3]; +} palette; + +static void readPalette(void) { + struct Chunk chunk; + for (;;) { + chunk = readChunk(); + if (0 == memcmp(chunk.type, "PLTE", 4)) break; + skipChunk(chunk); + } + palette.len = chunk.size / 3; + readExpect(palette.entries, chunk.size, "palette data"); + readCrc(); +} + +static void writePalette(void) { + struct Chunk plte = { .size = 3 * palette.len, .type = "PLTE" }; + writeChunk(plte); + writeExpect(palette.entries, plte.size); + writeCrc(); +} + +static uint8_t *data; + +static void readData(void) { + data = malloc(dataSize()); + if (!data) err(EX_OSERR, "malloc(%zu)", dataSize()); + + struct z_stream_s stream = { .next_out = data, .avail_out = dataSize() }; + int error = inflateInit(&stream); + if (error != Z_OK) errx(EX_SOFTWARE, "%s: inflateInit: %s", path, stream.msg); + + for (;;) { + struct Chunk chunk = readChunk(); + if (0 == memcmp(chunk.type, "IDAT", 4)) { + uint8_t *idat = malloc(chunk.size); + if (!idat) err(EX_OSERR, "malloc"); + + readExpect(idat, chunk.size, "image data"); + readCrc(); + + stream.next_in = idat; + stream.avail_in = chunk.size; + int error = inflate(&stream, Z_SYNC_FLUSH); + free(idat); + + if (error == Z_STREAM_END) break; + if (error != Z_OK) errx(EX_DATAERR, "%s: inflate: %s", path, stream.msg); + + } else if (0 == memcmp(chunk.type, "IEND", 4)) { + errx(EX_DATAERR, "%s: missing IDAT chunk", path); + } else { + skipChunk(chunk); + } + } + + inflateEnd(&stream); + if ((size_t)stream.total_out != dataSize()) { + errx( + EX_DATAERR, "%s: expected data size %zu, found %zu", + path, dataSize(), (size_t)stream.total_out + ); + } +} + +static void writeData(void) { + uLong size = compressBound(dataSize()); + uint8_t *deflate = malloc(size); + if (!deflate) err(EX_OSERR, "malloc"); + + int error = compress2(deflate, &size, data, dataSize(), Z_BEST_SPEED); + if (error != Z_OK) errx(EX_SOFTWARE, "%s: compress2: %d", path, error); + + struct Chunk idat = { .size = size, .type = "IDAT" }; + writeChunk(idat); + writeExpect(deflate, size); + writeCrc(); + + free(deflate); +} + +static void writeEnd(void) { + struct Chunk iend = { .size = 0, .type = "IEND" }; + writeChunk(iend); + writeCrc(); +} + +enum PACKED Filter { + None, + Sub, + Up, + Average, + Paeth, + FilterCount, +}; + +static struct { + bool brokenPaeth; + bool filt; + bool recon; + uint8_t declareFilter; + uint8_t applyFilter; + enum Filter declareFilters[255]; + enum Filter applyFilters[255]; + bool invert; + bool mirror; + bool zeroX; + bool zeroY; +} options; + +struct Bytes { + uint8_t x; + uint8_t a; + uint8_t b; + uint8_t c; +}; + +static uint8_t paethPredictor(struct Bytes f) { + int32_t p = (int32_t)f.a + (int32_t)f.b - (int32_t)f.c; + int32_t pa = abs(p - (int32_t)f.a); + int32_t pb = abs(p - (int32_t)f.b); + int32_t pc = abs(p - (int32_t)f.c); + if (pa <= pb && pa <= pc) return f.a; + if (options.brokenPaeth) { + if (pb < pc) return f.b; + } else { + if (pb <= pc) return f.b; + } + return f.c; +} + +static uint8_t recon(enum Filter type, struct Bytes f) { + switch (type) { + case None: return f.x; + case Sub: return f.x + f.a; + case Up: return f.x + f.b; + case Average: return f.x + ((uint32_t)f.a + (uint32_t)f.b) / 2; + case Paeth: return f.x + paethPredictor(f); + default: abort(); + } +} + +static uint8_t filt(enum Filter type, struct Bytes f) { + switch (type) { + case None: return f.x; + case Sub: return f.x - f.a; + case Up: return f.x - f.b; + case Average: return f.x - ((uint32_t)f.a + (uint32_t)f.b) / 2; + case Paeth: return f.x - paethPredictor(f); + default: abort(); + } +} + +static struct Line { + enum Filter type; + uint8_t data[]; +} **lines; + +static void scanlines(void) { + lines = calloc(header.height, sizeof(*lines)); + if (!lines) err(EX_OSERR, "calloc(%u, %zu)", header.height, sizeof(*lines)); + + size_t stride = 1 + lineSize(); + for (uint32_t y = 0; y < header.height; ++y) { + lines[y] = (struct Line *)&data[y * stride]; + if (lines[y]->type >= FilterCount) { + errx(EX_DATAERR, "%s: invalid filter type %hhu", path, lines[y]->type); + } + } +} + +static struct Bytes origBytes(uint32_t y, size_t i) { + bool a = (i >= pixelSize()), b = (y > 0), c = (a && b); + return (struct Bytes) { + .x = lines[y]->data[i], + .a = a ? lines[y]->data[i - pixelSize()] : 0, + .b = b ? lines[y - 1]->data[i] : 0, + .c = c ? lines[y - 1]->data[i - pixelSize()] : 0, + }; +} + +static void reconData(void) { + for (uint32_t y = 0; y < header.height; ++y) { + for (size_t i = 0; i < lineSize(); ++i) { + if (options.filt) { + lines[y]->data[i] = filt(lines[y]->type, origBytes(y, i)); + } else { + lines[y]->data[i] = recon(lines[y]->type, origBytes(y, i)); + } + } + lines[y]->type = None; + } +} + +static void filterData(void) { + for (uint32_t y = header.height - 1; y < header.height; --y) { + uint8_t filter[FilterCount][lineSize()]; + uint32_t heuristic[FilterCount] = {0}; + enum Filter minType = None; + for (enum Filter type = None; type < FilterCount; ++type) { + for (size_t i = 0; i < lineSize(); ++i) { + if (options.recon) { + filter[type][i] = recon(type, origBytes(y, i)); + } else { + filter[type][i] = filt(type, origBytes(y, i)); + } + heuristic[type] += abs((int8_t)filter[type][i]); + } + if (heuristic[type] < heuristic[minType]) minType = type; + } + + if (options.declareFilter) { + lines[y]->type = options.declareFilters[y % options.declareFilter]; + } else { + lines[y]->type = minType; + } + + if (options.applyFilter) { + enum Filter type = options.applyFilters[y % options.applyFilter]; + memcpy(lines[y]->data, filter[type], lineSize()); + } else { + memcpy(lines[y]->data, filter[minType], lineSize()); + } + } +} + +static void invert(void) { + for (uint32_t y = 0; y < header.height; ++y) { + for (size_t i = 0; i < lineSize(); ++i) { + lines[y]->data[i] ^= 0xFF; + } + } +} + +static void mirror(void) { + for (uint32_t y = 0; y < header.height; ++y) { + for (size_t i = 0, j = lineSize() - 1; i < j; ++i, --j) { + uint8_t t = lines[y]->data[i]; + lines[y]->data[i] = lines[y]->data[j]; + lines[y]->data[j] = t; + } + } +} + +static void zeroX(void) { + for (uint32_t y = 0; y < header.height; ++y) { + memset(lines[y]->data, 0, pixelSize()); + } +} + +static void zeroY(void) { + memset(lines[0]->data, 0, lineSize()); +} + +static void glitch(const char *inPath, const char *outPath) { + if (inPath) { + path = inPath; + file = fopen(path, "r"); + if (!file) err(EX_NOINPUT, "%s", path); + } else { + path = "(stdin)"; + file = stdin; + } + + readSignature(); + readHeader(); + if (header.color == Indexed) readPalette(); + readData(); + fclose(file); + + scanlines(); + reconData(); + filterData(); + if (options.invert) invert(); + if (options.mirror) mirror(); + if (options.zeroX) zeroX(); + if (options.zeroY) zeroY(); + free(lines); + + if (outPath) { + path = outPath; + file = fopen(path, "w"); + if (!file) err(EX_CANTCREAT, "%s", path); + } else { + path = "(stdout)"; + file = stdout; + } + + writeSignature(); + writeHeader(); + if (header.color == Indexed) writePalette(); + writeData(); + writeEnd(); + free(data); + + int error = fclose(file); + if (error) err(EX_IOERR, "%s", path); +} + +static enum Filter parseFilter(const char *s) { + switch (s[0]) { + case 'N': case 'n': return None; + case 'S': case 's': return Sub; + case 'U': case 'u': return Up; + case 'A': case 'a': return Average; + case 'P': case 'p': return Paeth; + default: errx(EX_USAGE, "invalid filter type %s", s); + } +} + +static uint8_t parseFilters(enum Filter *filters, const char *s) { + uint8_t len = 0; + do { + filters[len++] = parseFilter(s); + s = strchr(s, ','); + } while (s++); + return len; +} + +int main(int argc, char *argv[]) { + bool stdio = false; + char *output = NULL; + + int opt; + while (0 < (opt = getopt(argc, argv, "a:cd:fimo:prxy"))) { + switch (opt) { + break; case 'a': + options.applyFilter = parseFilters(options.applyFilters, optarg); + break; case 'c': stdio = true; + break; case 'd': + options.declareFilter = parseFilters(options.declareFilters, optarg); + break; case 'f': options.filt = true; + break; case 'i': options.invert = true; + break; case 'm': options.mirror = true; + break; case 'o': output = optarg; + break; case 'p': options.brokenPaeth = true; + break; case 'r': options.recon = true; + break; case 'x': options.zeroX = true; + break; case 'y': options.zeroY = true; + break; default: return EX_USAGE; + } + } + + if (argc - optind == 1 && (output || stdio)) { + glitch(argv[optind], output); + } else if (optind < argc) { + for (int i = optind; i < argc; ++i) { + glitch(argv[i], argv[i]); + } + } else { + glitch(NULL, output); + } + + return EX_OK; +} diff --git a/bin/hilex.c b/bin/hilex.c new file mode 100644 index 00000000..91a5e889 --- /dev/null +++ b/bin/hilex.c @@ -0,0 +1,391 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <ctype.h> +#include <err.h> +#include <regex.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <sysexits.h> +#include <unistd.h> + +#include "hilex.h" + +#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) + +static const char *Class[] = { +#define X(class) [class] = #class, + ENUM_CLASS +#undef X +}; + +static FILE *yyin; +static char *yytext; +static int yylex(void) { + static size_t cap = 0; + return (getline(&yytext, &cap, yyin) < 0 ? None : Normal); +} +static const struct Lexer LexText = { yylex, &yyin, &yytext }; + +static const struct { + const struct Lexer *lexer; + const char *name; + const char *namePatt; + const char *linePatt; +} Lexers[] = { + { &LexC, "c", "[.][chlmy]$", NULL }, + { &LexMake, "make", "[.](mk|am)$|^Makefile$", NULL }, + { &LexMdoc, "mdoc", "[.][1-9]$", "^[.]Dd" }, + { &LexSh, "sh", "[.]sh$|^[.](profile|shrc)$", "^#![ ]?/bin/sh" }, + { &LexText, "text", "[.]txt$", NULL }, +}; + +static const struct Lexer *parseLexer(const char *name) { + for (size_t i = 0; i < ARRAY_LEN(Lexers); ++i) { + if (!strcmp(name, Lexers[i].name)) return Lexers[i].lexer; + } + errx(EX_USAGE, "unknown lexer %s", name); +} + +static void ungets(const char *str, FILE *file) { + size_t len = strlen(str); + for (size_t i = len-1; i < len; --i) { + int ch = ungetc(str[i], file); + if (ch == EOF) errx(EX_IOERR, "cannot push back string"); + } +} + +static const struct Lexer *matchLexer(const char *name, FILE *file) { + char buf[256]; + regex_t regex; + for (size_t i = 0; i < ARRAY_LEN(Lexers); ++i) { + int error = regcomp( + ®ex, Lexers[i].namePatt, REG_EXTENDED | REG_NOSUB + ); + assert(!error); + error = regexec(®ex, name, 0, NULL, 0); + regfree(®ex); + if (!error) return Lexers[i].lexer; + } + char *line = fgets(buf, sizeof(buf), file); + if (!line) return NULL; + for (size_t i = 0; i < ARRAY_LEN(Lexers); ++i) { + if (!Lexers[i].linePatt) continue; + int error = regcomp( + ®ex, Lexers[i].linePatt, REG_EXTENDED | REG_NOSUB + ); + assert(!error); + error = regexec(®ex, line, 0, NULL, 0); + regfree(®ex); + if (!error) { + ungets(line, file); + return Lexers[i].lexer; + } + } + ungets(line, file); + return NULL; +} + +#define ENUM_OPTION \ + X(Document, "document") \ + X(Inline, "inline") \ + X(Monospace, "monospace") \ + X(Pre, "pre") \ + X(Style, "style") \ + X(Tab, "tab") \ + X(Title, "title") + +enum Option { +#define X(option, key) option, + ENUM_OPTION +#undef X + OptionCap, +}; + +typedef void Header(const char *opts[]); +typedef void Output(const char *opts[], enum Class class, const char *text); + +static bool pager; +static void ansiHeader(const char *opts[]) { + (void)opts; + if (!pager) return; + const char *shell = getenv("SHELL"); + const char *pager = getenv("PAGER"); + if (!shell) shell = "/bin/sh"; + if (!pager) pager = "less"; + setenv("LESS", "FRX", 0); + + int rw[2]; + int error = pipe(rw); + if (error) err(EX_OSERR, "pipe"); + + pid_t pid = fork(); + if (pid < 0) err(EX_OSERR, "fork"); + if (!pid) { + dup2(rw[0], STDIN_FILENO); + close(rw[0]); + close(rw[1]); + execl(shell, shell, "-c", pager, NULL); + err(EX_CONFIG, "%s", shell); + } + dup2(rw[1], STDOUT_FILENO); + close(rw[0]); + close(rw[1]); + setlinebuf(stdout); +} + +static void ansiFooter(const char *opts[]) { + (void)opts; + if (!pager) return; + int status; + fclose(stdout); + wait(&status); +} + +static const char *SGR[ClassCap] = { + [Keyword] = "37", + [Macro] = "32", + [Comment] = "34", + [String] = "36", + [Format] = "36;1;96", + [Subst] = "33", +}; + +static void ansiFormat(const char *opts[], enum Class class, const char *text) { + (void)opts; + if (!SGR[class]) { + printf("%s", text); + return; + } + // Set color on each line for piping to less -R: + for (const char *nl; (nl = strchr(text, '\n')); text = &nl[1]) { + printf("\33[%sm%.*s\33[m\n", SGR[class], (int)(nl - text), text); + } + if (*text) printf("\33[%sm%s\33[m", SGR[class], text); +} + +static void +debugFormat(const char *opts[], enum Class class, const char *text) { + if (class != Normal) { + printf("%s(", Class[class]); + ansiFormat(opts, class, text); + printf(")"); + } else { + printf("%s", text); + } +} + +static const char *IRC[ClassCap] = { + [Keyword] = "\00315", + [Macro] = "\0033", + [Comment] = "\0032", + [String] = "\00310", + [Format] = "\00311", + [Subst] = "\0037", +}; + +static void ircHeader(const char *opts[]) { + if (opts[Monospace]) printf("\21"); +} + +static const char *stop(const char *text) { + return (*text == ',' || isdigit(*text) ? "\2\2" : ""); +} + +static void ircFormat(const char *opts[], enum Class class, const char *text) { + for (const char *nl; (nl = strchr(text, '\n')); text = &nl[1]) { + if (IRC[class]) printf("%s%s", IRC[class], stop(text)); + printf("%.*s\n", (int)(nl - text), text); + if (opts[Monospace]) printf("\21"); + } + if (*text) { + if (IRC[class]) { + printf("%s%s%s\17", IRC[class], stop(text), text); + if (opts[Monospace]) printf("\21"); + } else { + printf("%s", text); + } + } +} + +static void htmlEscape(const char *text) { + while (*text) { + switch (*text) { + break; case '"': text++; printf("""); + break; case '&': text++; printf("&"); + break; case '<': text++; printf("<"); + } + size_t len = strcspn(text, "\"&<"); + if (len) fwrite(text, len, 1, stdout); + text += len; + } +} + +static const char *Styles[ClassCap] = { + [Keyword] = "color: dimgray;", + [Macro] = "color: green;", + [Comment] = "color: navy;", + [String] = "color: teal;", + [Format] = "color: teal; font-weight: bold;", + [Subst] = "color: olive;", +}; + +static void styleTabSize(const char *tab) { + printf("-moz-tab-size: "); + htmlEscape(tab); + printf("; tab-size: "); + htmlEscape(tab); + printf(";"); +} + +static void htmlHeader(const char *opts[]) { + if (!opts[Document]) goto body; + + printf("<!DOCTYPE html>\n<title>"); + if (opts[Title]) htmlEscape(opts[Title]); + printf("</title>\n"); + + if (opts[Style]) { + printf("<link rel=\"stylesheet\" href=\""); + htmlEscape(opts[Style]); + printf("\">\n"); + } else if (!opts[Inline]) { + printf("<style>\n"); + if (opts[Tab]) { + printf("pre.hilex { "); + styleTabSize(opts[Tab]); + printf(" }\n"); + } + for (enum Class class = 0; class < ClassCap; ++class) { + if (!Styles[class]) continue; + printf("pre.hilex .%.2s { %s }\n", Class[class], Styles[class]); + } + printf("</style>\n"); + } + +body: + if ((opts[Document] || opts[Pre]) && opts[Inline] && opts[Tab]) { + printf("<pre class=\"hilex\" style=\""); + styleTabSize(opts[Tab]); + printf("\">"); + } else if (opts[Document] || opts[Pre]) { + printf("<pre class=\"hilex\">"); + } +} + +static void htmlFooter(const char *opts[]) { + if (opts[Document] || opts[Pre]) printf("</pre>"); + if (opts[Document]) printf("\n"); +} + +static void htmlFormat(const char *opts[], enum Class class, const char *text) { + if (class != Normal) { + if (opts[Inline]) { + printf("<span style=\"%s\">", Styles[class] ? Styles[class] : ""); + } else { + printf("<span class=\"%.2s\">", Class[class]); + } + htmlEscape(text); + printf("</span>"); + } else { + htmlEscape(text); + } +} + +static const struct Formatter { + const char *name; + Header *header; + Output *format; + Header *footer; +} Formatters[] = { + { "ansi", ansiHeader, ansiFormat, ansiFooter }, + { "debug", NULL, debugFormat, NULL }, + { "html", htmlHeader, htmlFormat, htmlFooter }, + { "irc", ircHeader, ircFormat, NULL }, +}; + +static const struct Formatter *parseFormatter(const char *name) { + for (size_t i = 0; i < ARRAY_LEN(Formatters); ++i) { + if (!strcmp(name, Formatters[i].name)) return &Formatters[i]; + } + errx(EX_USAGE, "unknown formatter %s", name); +} + +static char *const OptionKeys[OptionCap + 1] = { +#define X(option, key) [option] = key, + ENUM_OPTION +#undef X + NULL, +}; + +int main(int argc, char *argv[]) { + bool text = false; + const char *name = NULL; + const struct Lexer *lexer = NULL; + const struct Formatter *formatter = &Formatters[0]; + const char *opts[OptionCap] = {0}; + + for (int opt; 0 < (opt = getopt(argc, argv, "f:l:n:o:t"));) { + switch (opt) { + break; case 'f': formatter = parseFormatter(optarg); + break; case 'l': lexer = parseLexer(optarg); + break; case 'n': name = optarg; + break; case 'o': { + while (*optarg) { + char *val; + int key = getsubopt(&optarg, OptionKeys, &val); + if (key < 0) errx(EX_USAGE, "no such option %s", val); + opts[key] = (val ? val : ""); + } + } + break; case 't': text = true; + break; default: return EX_USAGE; + } + } + + const char *path = "(stdin)"; + FILE *file = stdin; + if (optind < argc) { + path = argv[optind]; + file = fopen(path, "r"); + if (!file) err(EX_NOINPUT, "%s", path); + pager = isatty(STDOUT_FILENO); + } + + if (!name) { + if (NULL != (name = strrchr(path, '/'))) { + name++; + } else { + name = path; + } + } + if (!opts[Title]) opts[Title] = name; + if (!lexer) lexer = matchLexer(name, file); + if (!lexer && text) lexer = &LexText; + if (!lexer) errx(EX_USAGE, "cannot infer lexer for %s", name); + + *lexer->in = file; + if (formatter->header) formatter->header(opts); + for (enum Class class; None != (class = lexer->lex());) { + assert(class < ClassCap); + formatter->format(opts, class, *lexer->text); + } + if (formatter->footer) formatter->footer(opts); +} diff --git a/bin/hilex.h b/bin/hilex.h new file mode 100644 index 00000000..882b5f95 --- /dev/null +++ b/bin/hilex.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> + +#define ENUM_CLASS \ + X(None) \ + X(Normal) \ + X(Operator) \ + X(Number) \ + X(Keyword) \ + X(Ident) \ + X(Macro) \ + X(Comment) \ + X(String) \ + X(Escape) \ + X(Format) \ + X(Subst) + +enum Class { +#define X(class) class, + ENUM_CLASS +#undef X + ClassCap, +}; + +typedef int Lex(void); +struct Lexer { + Lex *lex; + FILE **in; + char **text; +}; + +extern const struct Lexer LexC; +extern const struct Lexer LexMake; +extern const struct Lexer LexMdoc; +extern const struct Lexer LexSh; diff --git a/bin/htagml.c b/bin/htagml.c new file mode 100644 index 00000000..eb5128d1 --- /dev/null +++ b/bin/htagml.c @@ -0,0 +1,207 @@ +/* Copyright (C) 2021 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <ctype.h> +#include <err.h> +#include <regex.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +static char *nomagic(const char *pattern) { + char *buf = malloc(2 * strlen(pattern) + 1); + if (!buf) err(EX_OSERR, "malloc"); + char *ptr = buf; + for (const char *ch = pattern; *ch; ++ch) { + if (strchr(".[*", *ch)) *ptr++ = '\\'; + *ptr++ = *ch; + } + *ptr = '\0'; + return buf; +} + +static size_t escape(bool esc, const char *ptr, size_t len) { + if (!esc) { + fwrite(ptr, len, 1, stdout); + return len; + } + for (size_t i = 0; i < len; ++i) { + switch (ptr[i]) { + break; case '&': printf("&"); + break; case '<': printf("<"); + break; case '"': printf("""); + break; default: putchar(ptr[i]); + } + } + return len; +} + +static void id(const char *tag) { + for (const char *ch = tag; *ch; ++ch) { + if (isalnum(*ch) || strchr("-._", *ch)) { + putchar(*ch); + } else { + putchar('_'); + } + } +} + +static char *hstrstr(const char *haystack, const char *needle) { + while (haystack) { + char *elem = strchr(haystack, '<'); + char *match = strstr(haystack, needle); + if (!match) return NULL; + if (!elem || match < elem) return match; + haystack = strchr(elem, '>'); + } + return NULL; +} + +int main(int argc, char *argv[]) { + bool pre = false; + bool pipe = false; + bool index = false; + const char *tagsFile = "tags"; + for (int opt; 0 < (opt = getopt(argc, argv, "f:ipx"));) { + switch (opt) { + break; case 'f': tagsFile = optarg; + break; case 'i': pipe = true; + break; case 'p': pre = true; + break; case 'x': index = true; + break; default: return EX_USAGE; + } + } + if (optind == argc) errx(EX_USAGE, "name required"); + const char *name = argv[optind]; + + FILE *file = fopen(tagsFile, "r"); + if (!file) err(EX_NOINPUT, "%s", tagsFile); + + size_t len = 0; + size_t cap = 256; + struct Tag { + char *tag; + int num; + regex_t regex; + } *tags = malloc(cap * sizeof(*tags)); + if (!tags) err(EX_OSERR, "malloc"); + + char *buf = NULL; + size_t bufCap = 0; + while (0 < getline(&buf, &bufCap, file)) { + char *line = buf; + char *tag = strsep(&line, "\t"); + char *file = strsep(&line, "\t"); + char *def = strsep(&line, "\n"); + if (!tag || !file || !def) errx(EX_DATAERR, "malformed tags file"); + + if (strcmp(file, name)) continue; + if (len == cap) { + tags = realloc(tags, (cap *= 2) * sizeof(*tags)); + if (!tags) err(EX_OSERR, "realloc"); + } + tags[len].tag = strdup(tag); + if (!tags[len].tag) err(EX_OSERR, "strdup"); + + tags[len].num = 0; + if (def[0] == '/' || def[0] == '?') { + def++; + def[strlen(def)-1] = '\0'; + char *search = nomagic(def); + int error = regcomp( + &tags[len].regex, search, REG_NEWLINE | REG_NOSUB + ); + free(search); + if (error) { + warnx("invalid regex for tag %s: %s", tag, def); + continue; + } + } else { + tags[len].num = strtol(def, &def, 10); + if (*def) { + warnx("invalid line number for tag %s: %s", tag, def); + continue; + } + } + len++; + } + fclose(file); + + file = fopen(name, "r"); + if (!file) err(EX_NOINPUT, "%s", name); + + int num = 0; + printf(pre ? "<pre>" : index ? "<ul class=\"index\">\n" : ""); + while (0 < getline(&buf, &bufCap, file) && ++num) { + struct Tag *tag = NULL; + for (size_t i = 0; i < len; ++i) { + if (tags[i].num) { + if (num != tags[i].num) continue; + } else { + if (regexec(&tags[i].regex, buf, 0, NULL, 0)) continue; + } + tag = &tags[i]; + tag->num = num; + break; + } + if (index) { + if (!tag) continue; + printf("<li><a class=\"tag\" href=\"#"); + id(tag->tag); + printf("\">"); + escape(true, tag->tag, strlen(tag->tag)); + printf("</a></li>\n"); + continue; + } + if (pipe) { + ssize_t len = getline(&buf, &bufCap, stdin); + if (len < 0) { + errx(EX_DATAERR, "missing line %d on standard input", num); + } + } + if (!tag) { + escape(!pipe, buf, strlen(buf)); + continue; + } + + size_t mlen = strlen(tag->tag); + char *match = (pipe ? hstrstr : strstr)(buf, tag->tag); + while (match > buf && isalnum(match[-1])) { + match = (pipe ? hstrstr : strstr)(&match[mlen], tag->tag); + } + if (!match && tag->tag[0] == 'M') { + mlen = 4; + match = (pipe ? hstrstr : strstr)(buf, "main"); + } + if (!match) { + mlen = strlen(buf) - 1; + match = buf; + } + escape(!pipe, buf, match - buf); + printf("<a class=\"tag\" id=\""); + id(tag->tag); + printf("\" href=\"#"); + id(tag->tag); + printf("\">"); + match += escape(!pipe, match, mlen); + printf("</a>"); + escape(!pipe, match, strlen(match)); + } + printf(pre ? "</pre>" : index ? "</ul>\n" : ""); +} diff --git a/bin/html.mk b/bin/html.mk new file mode 100644 index 00000000..15e5257a --- /dev/null +++ b/bin/html.mk @@ -0,0 +1,45 @@ +WEBROOT ?= /usr/local/www/causal.agency + +HTMLS = index.html png.html +HTMLS += ${BINS:=.html} +HTMLS += ${BSD:=.html} +HTMLS += ${GAMES:=.html} +HTMLS += ${LINUX:=.html} +HTMLS += ${TLS:=.html} + +html: ${HTMLS} + @true + +install-html: ${HTMLS} + install -d ${WEBROOT}/bin + install -C -m 644 ${HTMLS} ${WEBROOT}/bin + +${HTMLS}: html.sh scheme hilex htagml htmltags + +htmltags: *.[chly] mtags Makefile html.mk *.sh + rm -f $@ + for f in *.[chly]; do ctags -aw -f $@ $$f; done + ./mtags -a -f $@ Makefile html.mk *.sh + +index.html: README.7 Makefile html.mk html.sh + sh html.sh README.7 Makefile html.mk html.sh > $@ + +.SUFFIXES: .html + +.c.html: + sh html.sh man1/${<:.c=.1} $< > $@ + +.h.html: + sh html.sh man3/${<:.h=.3} $< > $@ + +.y.html: + sh html.sh man1/${<:.y=.1} $< > $@ + +.sh.html: + sh html.sh man1/${<:.sh=.1} $< > $@ + +.pl.html: + sh html.sh man1/${<:.pl=.1} $< > $@ + +freecell.html: freecell.c man6/freecell.6 + sh html.sh man6/freecell.6 freecell.c > $@ diff --git a/bin/html.sh b/bin/html.sh new file mode 100644 index 00000000..d76fcc8c --- /dev/null +++ b/bin/html.sh @@ -0,0 +1,65 @@ +#!/bin/sh +set -eu + +readonly GitURL='https://git.causal.agency/src/tree/bin' + +man=$1 +shift +title=${man##*/} +title=${title%.[1-9]} + +cat <<EOF +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>${title}</title> +<style> +html { line-height: 1.25em; font-family: monospace; } +body { max-width: 80ch; margin: 1em auto; padding: 0 1ch; } + +table.head, table.foot { width: 100%; } +td.head-rtitle, td.foot-os { text-align: right; } +td.head-vol { text-align: center; } +div.Pp { margin: 1ex 0ex; } +div.Nd, div.Bf, div.Op { display: inline; } +span.Pa, span.Ad { font-style: italic; } +span.Ms { font-weight: bold; } +dl.Bl-diag > dt { font-weight: bold; } +code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn, +code.Cd { font-weight: bold; font-family: inherit; } + +table { border-collapse: collapse; } +table.Nm code.Nm { padding-right: 1ch; } +table.foot { margin-top: 1em; } + +ul.index { padding: 0; } +ul.index li { display: inline; list-style-type: none; } +pre { -moz-tab-size: 4; tab-size: 4; } + +$(./scheme -st) +html { background-color: var(--ansi16); color: var(--ansi17); } +a { color: var(--ansi4); } +a:visited { color: var(--ansi5); } +a.permalink, a.tag { color: var(--ansi3); text-decoration: none; } +a.permalink > code:target, *:target > a.permalink, +a.tag:target { color: var(--ansi11); } +pre .Ke { color: var(--ansi7); } +pre .Ma { color: var(--ansi2); } +pre .Co { color: var(--ansi4); } +pre .St { color: var(--ansi6); } +pre .Fo { color: var(--ansi14); } +pre .Su { color: var(--ansi1); } +</style> +EOF + +opts=fragment +[ "${man}" = "README.7" ] && opts=${opts},man=%N.html +mandoc -T html -O ${opts} "${man}" + +for src; do + cat <<-EOF + <p> + <a href="${GitURL}/${src}">${src} in git</a> + EOF + ./htagml -x -f htmltags "${src}" + ./hilex -t -f html "${src}" | ./htagml -ip -f htmltags "${src}" +done diff --git a/bin/make.l b/bin/make.l new file mode 100644 index 00000000..027fefa3 --- /dev/null +++ b/bin/make.l @@ -0,0 +1,132 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +%option prefix="make" +%option noyywrap + +%{ +#include "hilex.h" +%} + +%s Assign Preproc +%x Variable Shell + +ident [._[:alnum:]]+ +assign [+?:!]?= +target [-._/?*\[\][:alnum:]]+ +operator [:!]|:: + +%% + static int pop = INITIAL; + static int depth = 0; + +^"\t"+ { + BEGIN(pop = Shell); + return Normal; +} +<Shell>{ + "\n" { + BEGIN(pop = INITIAL); + return Normal; + } + "\\\n" { return Normal; } + [^\\\n$]+|. { return Normal; } +} + +[[:blank:]]+ { return Normal; } + +{operator} { return Operator; } + +"."(PHONY|PRECIOUS|SUFFIXES)/{operator}? { + return Keyword; +} + +{target}/{operator} { return Ident; } + +^"."{ident} | +^"-"?include { + BEGIN(pop = Preproc); + return Macro; +} +<Preproc>{ + "\n" { + BEGIN(pop = INITIAL); + return Normal; + } + "\\\n""\t"? { return Normal; } + + "\""[^""]*"\"" | + "<"[^>]*">" { + return String; + } + + [!<>=]"="?|"||"|"&&" { return Operator; } + [0-9]+|"0x"[[:xdigit:]]+ { return Number; } + defined|make|empty|exists|target|commands|in { return Keyword; } +} + +^{ident}/[[:blank:]]*{assign} { + return Ident; +} + +{assign} { + BEGIN(pop = Assign); + return Operator; +} +<Assign>{ + "\n" { + BEGIN(pop = INITIAL); + return Normal; + } + "\\\n""\t"? { return Escape; } + [^\\$[:space:]]+|. { return String; } +} + +{target} { return Ident; } + +"#"([^\\\n]|"\\"[^\n]|"\\\n")* { return Comment; } + +<*>{ + "$"("{"|"(")/[^$] { + depth++; + BEGIN(Variable); + yymore(); + } + "$"("{"|"(") { + depth++; + BEGIN(Variable); + return Subst; + } + "$". { return Subst; } +} +<Variable>{ + [^${}()]*"}"|")" { + if (!--depth) BEGIN(pop); + return Subst; + } + [^${}()]+ { return Subst; } +} + +.|\n { return Normal; } + +%{ + (void)yyunput; + (void)input; +%} + +%% + +const struct Lexer LexMake = { yylex, &yyin, &yytext }; diff --git a/bin/man1/beef.1 b/bin/man1/beef.1 new file mode 100644 index 00000000..ea52cfa0 --- /dev/null +++ b/bin/man1/beef.1 @@ -0,0 +1,91 @@ +.Dd August 28, 2019 +.Dt BEEF 1 +.Os +. +.Sh NAME +.Nm beef +.Nd Befunge-93 interpreter +. +.Sh SYNOPSIS +.Nm +.Op Ar file +. +.Sh DESCRIPTION +.Nm +is a Befunge-93 interpreter. +If no +.Ar file +is provided, +the program is read from standard input. +. +.Ss Befunge-93 Command Summary +.Bl -tag -width "0-9" -compact +.It \(dq +toggle string mode +.It 0-9 +push value +.It + +add +.It - +subtract +.It * +multiply +.It / +divide +.It % +modulo +.It ! +not +.It ` +greater than +.It > +right +.It < +left +.It ^ +up +.It v +down +.It ? +random +.It _ +horizontal (left) if +.It | +vertical (up) if +.It : +duplicate +.It \e +swap +.It $ +drop +.It . +output integer +.It , +output ASCII +.It # +bridge +.It g +get (y, x) +.It p +put (y, x) = v +.It & +input integer +.It ~ +input ASCII +.It @ +exit +.El +. +.Sh EXIT STATUS +.Nm +exits with the top value left on the stack, +or 0 if the stack is left empty. +. +.Sh STANDARDS +.Rs +.%A Chris Pressey +.%Q Cat's Eye Technologies +.%T Befunge-93 +.%D September, 1993 +.%U https://github.com/catseye/Befunge-93/blob/master/doc/Befunge-93.markdown +.Re diff --git a/bin/man1/bibsort.1 b/bin/man1/bibsort.1 new file mode 100644 index 00000000..07ed91ef --- /dev/null +++ b/bin/man1/bibsort.1 @@ -0,0 +1,40 @@ +.Dd February 16, 2021 +.Dt BIBSORT 1 +.Os +. +.Sh NAME +.Nm bibsort +.Nd reformat bibliography +. +.Sh SYNOPSIS +.Nm +.Op Ar file +. +.Sh DESCRIPTION +.Nm +reformats on standard output +the +.Em STANDARDS +section of the +.Xr mdoc 7 +manual page +.Ar file +or standard input. +Bibliographic references +are sorted by author last names, +and formatted in an item list +with macro lines appearing +in the order they are formatted by +.Xr mandoc 1 . +Additionally, +.Ic \&%N +macros referencing RFC numbers +are rewritten to +.Ic \&%R +macros +and missing +.Ic \&%U +macros are added for RFCs. +. +.Sh EXAMPLES +.Dl :%!bibsort diff --git a/bin/man1/bit.1 b/bin/man1/bit.1 new file mode 100644 index 00000000..b91a10e1 --- /dev/null +++ b/bin/man1/bit.1 @@ -0,0 +1,55 @@ +.Dd December 30, 2020 +.Dt BIT 1 +.Os +. +.Sh NAME +.Nm bit +.Nd a calculator +. +.Sh SYNOPSIS +.Nm +. +.Sh DESCRIPTION +.Nm +is an integer calculator. +Its syntax resembles that of C expressions, +with the following changes: +. +.Bl -bullet +.It +Underscores are allowed in integer literals. +.It +The +.Sy 0b +prefix is used for binary literals. +.It +The +.Sy -> +operator is used for arithmetic shift. +.It +The unary +.Sy & +operator is equivalent to +.Sy (1 << x) - 1 . +.It +The postfix operators +.Sy K , +.Sy M , +.Sy G , +.Sy T +are used as constant multipliers. +.It +The postfix operator +.Sy $ +is of lowest precedence and is equivalent to +wrapping the preceding expression in parentheses. +.It +Single-letter (lower case) variables +can be assigned. +The variable +.Sy _ +stores the previous result. +.El +. +.Sh SEE ALSO +.Xr operator 7 diff --git a/bin/man1/bri.1 b/bin/man1/bri.1 new file mode 100644 index 00000000..54a02322 --- /dev/null +++ b/bin/man1/bri.1 @@ -0,0 +1,44 @@ +.Dd September 7, 2018 +.Dt BRI 1 +.Os +. +.Sh NAME +.Nm bri +.Nd backlight brightness control +. +.Sh SYNOPSIS +.Nm +.Op Ar brightness +.Nm +.Cm + +.Nm +.Cm - +. +.Sh DESCRIPTION +.Nm +controls the backlight brightness on Linux. +. +.Pp +With no argument, +the current brightness is printed. +With a numerical +.Ar brightness +argument, +the brightness is set. +. +.Pp +The +.Cm + +and +.Cm - +arguments +may be repeated any number of times +and adjust the brightness +in increments of 16. +. +.Sh FILES +.Bl -tag +.It Pa /sys/class/backlight +Files under the first subdirectory found +are used to control the backlight brightness. +.El diff --git a/bin/man1/c.1 b/bin/man1/c.1 new file mode 100644 index 00000000..97384ebe --- /dev/null +++ b/bin/man1/c.1 @@ -0,0 +1,45 @@ +.Dd January 9, 2021 +.Dt C 1 +.Os +. +.Sh NAME +.Nm c +.Nd run C +. +.Sh SYNOPSIS +.Nm +.Op Fl t +.Op Fl e Ar expr +.Op Fl i Ar include +.Op Ar stmts ... +. +.Sh DESCRIPTION +The +.Nm +utility compiles and runs +C statements wrapped in +.Fn main +with common includes. +If no +.Ar expr +or +.Ar stmts +are provided, +statements are read from standard input. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl e Ar expr +Print the result of the C expression +.Ar expr +after executing +.Ar stmts . +.It Fl i Ar include +Add the include file +.Ar include . +.It Fl t +With +.Fl e , +print the type of the expression. +.El diff --git a/bin/man1/dtch.1 b/bin/man1/dtch.1 new file mode 100644 index 00000000..e27713e1 --- /dev/null +++ b/bin/man1/dtch.1 @@ -0,0 +1,67 @@ +.Dd August 12, 2019 +.Dt DTCH 1 +.Os +. +.Sh NAME +.Nm dtch +.Nd detached sessions +. +.Sh SYNOPSIS +.Nm +.Op Fl s +.Ar name +.Op Ar command ... +.Nm +.Fl a +.Ar name +. +.Sh DESCRIPTION +.Nm +spawns a +.Ar command +in a detachable session. +If no +.Ar command +is given, +the value of +.Ev SHELL +is used. +The +.Nm +process +should be run as a background job +or with +.Xr nohup 1 . +. +.Pp +To attach to an existing session, +pass the +.Fl a +flag. +To detach from the session, +type +.Ic ^Q . +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl a +Attach to an existing session. +.It Fl s +Sink the output of +.Ar command +while detached. +.El +. +.Sh FILES +.Bl -tag -width Ds +.It Pa ~/.dtch +Directory of UNIX-domain sockets +for each session. +.El +. +.Sh EXAMPLES +.Bd -literal -offset indent +dtch foo vim & +dtch -a foo +.Ed diff --git a/bin/man1/ever.1 b/bin/man1/ever.1 new file mode 100644 index 00000000..8cdab99b --- /dev/null +++ b/bin/man1/ever.1 @@ -0,0 +1,51 @@ +.Dd February 24, 2021 +.Dt EVER 1 +.Os +. +.Sh NAME +.Nm ever +.Nd watch files +. +.Sh SYNOPSIS +.Nm +.Op Fl iq +.Ar +.Ar command +.Nm +.Op Fl i +.Ar +.Fl - +.Ar command +.Op Ar argument ... +. +.Sh DESCRIPTION +.Nm +executes the +.Ar command +whenever +.Ar file +is modified. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl i +Attach the +.Ar file +which was modified +to the standard input of +.Ar command . +.It Fl q +Suppress exit status output. +.El +. +.Sh EXAMPLES +.Dl ever ever.c make +.Dl ever when.y ever.c -- make when ever +.Dl ever -i ever.1 mandoc +. +.Sh CAVEATS +.Nm +does not support Linux +since it uses +.Xr kqueue 2 . diff --git a/bin/man1/fbatt.1 b/bin/man1/fbatt.1 new file mode 100644 index 00000000..2d30cba7 --- /dev/null +++ b/bin/man1/fbatt.1 @@ -0,0 +1,34 @@ +.Dd September 7, 2018 +.Dt FBATT 1 +.Os +. +.Sh NAME +.Nm fbatt +.Nd framebuffer battery indicator +. +.Sh SYNOPSIS +.Nm +. +.Sh DESCRIPTION +.Nm +displays a battery charge indicator +in the top-right corner +of the Linux framebuffer. +. +.Sh ENVIRONMENT +.Bl -tag +.It Ev FRAMEBUFFER +The framebuffer device path. +.El +. +.Sh FILES +.Bl -tag +.It Pa /dev/fb0 +The default framebuffer device path. +.It Pa /sys/class/power_supply +The first subdirectory containing +.Pa charge_full +and +.Pa charge_now +is used to read the battery charge. +.El diff --git a/bin/man1/fbclock.1 b/bin/man1/fbclock.1 new file mode 100644 index 00000000..3195eb42 --- /dev/null +++ b/bin/man1/fbclock.1 @@ -0,0 +1,36 @@ +.Dd September 7, 2018 +.Dt FBCLOCK 1 +.Os +. +.Sh NAME +.Nm fbclock +.Nd framebuffer clock +. +.Sh SYNOPSIS +.Nm +. +.Sh DESCRIPTION +.Nm +displays a clock +in the top-right corner +of the Linux framebuffer. +. +.Sh ENVIRONMENT +.Bl -tag +.It Ev FONT +Path to gzipped PSF file. +. +.It Ev FRAMEBUFFER +The framebuffer device path. +.El +. +.Sh FILES +.Bl -tag +.It Pa /dev/fb0 +The default framebuffer device path. +.It Pa /usr/share/kbd/consolefonts/Lat2-Terminus16.psfu.gz +The default font path. +.El +. +.Sh SEE ALSO +.Xr setfont 8 diff --git a/bin/man1/glitch.1 b/bin/man1/glitch.1 new file mode 100644 index 00000000..6562c4dc --- /dev/null +++ b/bin/man1/glitch.1 @@ -0,0 +1,77 @@ +.Dd September 7, 2018 +.Dt GLITCH 1 +.Os +. +.Sh NAME +.Nm glitch +.Nd PNG glitcher +. +.Sh SYNOPSIS +.Nm +.Op Fl cfimprxy +.Op Fl a Ar filters +.Op Fl d Ar filters +.Op Fl o Ar file +.Op Ar +. +.Sh DESCRIPTION +.Nm +misinterprets PNG files +according to the options given +to create natural glitch effects. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl a Ar filters +Apply a pattern of comma-separated filters. +Filters are +.Cm none , +.Cm sub , +.Cm up , +.Cm average , +.Cm paeth . +. +.It Fl c +Write to standard output. +. +.It Fl d Ar filters +Declare a pattern of comma-separated filters. +See +.Fl a +for list of filters. +. +.It Fl f +Apply filtering in place of reconstruction. +. +.It Fl i +Invert image data after filtering. +. +.It Fl m +Mirror scanlines after filtering. +. +.It Fl o Ar file +Write to +.Ar file . +. +.It Fl p +Use a broken Paeth predictor function. +. +.It Fl r +Apply reconstruction in place of filtering. +. +.It Fl x +Zero first pixel of each scanline after filtering. +. +.It Fl y +Zero first scanline after filtering. +.El +. +.Sh EXAMPLES +.Dl glitch -m -a sub -d sub +. +.Sh SEE ALSO +.Xr pngo 1 +. +.Sh BUGS +More wanted. diff --git a/bin/man1/hilex.1 b/bin/man1/hilex.1 new file mode 100644 index 00000000..80b3155b --- /dev/null +++ b/bin/man1/hilex.1 @@ -0,0 +1,218 @@ +.Dd January 20, 2021 +.Dt HILEX 1 +.Os +. +.Sh NAME +.Nm hilex +.Nd syntax highlighter +. +.Sh SYNOPSIS +.Nm +.Op Fl t +.Op Fl f Ar format +.Op Fl l Ar lexer +.Op Fl n Ar name +.Op Fl o Ar opts +.Op Ar file +. +.Sh DESCRIPTION +The +.Nm +utility +syntax highlights +the contents of +.Ar file +or standard input +and formats it on standard output. +. +.Pp +The arguments are as follows: +.Bl -tag -width "-f format" +.It Fl f Ar format +Set the output format. +See +.Sx Output Formats . +The default format is +.Cm ansi . +. +.It Fl l Ar lexer +Set the input lexer. +See +.Sx Input Lexers . +The default input lexer is inferred from +.Ar name +or the first line of input. +. +.It Fl n Ar name +Set the name used to infer the input lexer. +The default is the final component of +.Ar file . +. +.It Fl o Ar opts +Set output format options. +.Ar opts +is a comma-separated list of options. +Options for each output format are documented in +.Sx Output Formats . +. +.It Fl t +Default to the +.Cm text +input lexer if one cannot be inferred. +.El +. +.Ss Output Formats +.Bl -tag -width Ds +.It Cm ansi +Output ANSI terminal control sequences. +If standard output is a terminal +and standard input is not being read, +output is piped to +.Ev PAGER +with +.Ev LESS=FRX +if it is not already set. +. +.It Cm html +Output HTML +.Sy span +elements +with the following classes: +.Pp +.Bl -hang -width "\&Op" -compact +.It Sy \&Op +operators +.It Sy \&Nu +numbers +.It Sy \&Ke +keywords +.It Sy \&Id +identifiers +.It Sy \&Ma +macros +.It Sy \&Co +comments +.It Sy \&St +strings +.It Sy \&Es +character escapes +.It Sy \&Fo +format strings +.It Sy \&Su +variable substitutions +.El +.Pp +The options are as follows: +.Bl -tag -width "title=..." +.It Cm document +Output an HTML document containing a +.Sy pre +element. +.It Cm inline +Output inline style attributes +rather than classes. +.It Cm pre +Wrap the output in a +.Sy pre +element with the class +.Sy hilex . +.It Cm style Ns = Ns Ar url +With +.Cm document , +use the external stylesheet +.Ar url . +If unset, +default styles are included in a +.Sy style +element. +.It Cm tab Ns = Ns Ar n +With +.Cm document , +.Cm inline +or +.Cm pre , +set the +.Sy tab-size +property to +.Ar n . +.It Cm title Ns = Ns Ar ... +With +.Cm document , +set the +.Sy title +element text. +The default title is the same as +.Ar name . +.El +. +.It Cm irc +Output IRC formatting codes. +The options are as follows: +.Bl -tag -width "monospace" +.It Cm monospace +Use the IRCCloud monospace formatting code. +.El +.El +. +.Ss Input Lexers +.Bl -tag -width Ds +.It Cm c +The C11 language, +with minimal support for +.Xr lex 1 , +.Xr yacc 1 +and Objective-C input. +Inferred for +.Pa *.[chlmy] +files. +. +.It Cm make +BSD +.Xr make 1 . +Inferred for +.Pa Makefile , +.Pa *.mk +and +.Pa *.am +files. +. +.It Cm mdoc +The +.Xr mdoc 7 +language. +Inferred for +.Pa *.[1-9] +files +and files starting with +.Dq .Dd . +. +.It Cm sh +POSIX +.Xr sh 1 . +Since lexical analysis of +the shell command language +is effectively impossible, +this is best-effort only. +Inferred for +.Pa *.sh , +.Pa .profile , +.Pa .shrc +files +and files starting with +.Dq #!/bin/sh . +. +.It Cm text +Plain text. +Inferred for +.Pa *.txt +files. +.El +. +.Sh ENVIRONMENT +.Bl -tag -width "PAGER" +.It Ev PAGER +The command to pipe ANSI output to +if standard output is a terminal. +The default is +.Ev PAGER=less . +.El diff --git a/bin/man1/htagml.1 b/bin/man1/htagml.1 new file mode 100644 index 00000000..97e8f3f2 --- /dev/null +++ b/bin/man1/htagml.1 @@ -0,0 +1,69 @@ +.Dd January 12, 2021 +.Dt HTAGML 1 +.Os +. +.Sh NAME +.Nm htagml +.Nd format tagged file as HTML +. +.Sh SYNOPSIS +.Nm +.Op Fl ip | x +.Op Fl f Ar tagsfile +.Ar file +. +.Sh DESCRIPTION +The +.Nm +utility formats a file tagged with +.Xr ctags 1 +as HTML. +Tags are output as fragment hyperlinks +with the class +.Qq tag . +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl f Ar tagsfile +Read the tag descriptions from a file called +.Ar tagsfile . +The default behavior is +to read them from a file called +.Pa tags . +.It Fl i +Assume +.Ar file +has been pre-formatted +on standard input, +such as by a syntax highlighter. +Only tag hyperlinks are added. +.It Fl p +Wrap the output in a +.Sy pre +element. +.It Fl x +Instead produce an index of tags +ordered by their occurrence in +.Ar file . +The index is formatted as a +.Sy ul +element with the class +.Qq index . +.El +. +.Sh FILES +.Bl -tag -width Ds +.It Pa tags +default input tags file +.El +. +.Sh EXAMPLES +.Bd -literal -offset indent +ctags htagml.c && htagml htagml.c +hilex -f html htagml.c | htagml -i htagml.c +.Ed +. +.Sh SEE ALSO +.Xr ctags 1 , +.Xr hilex 1 diff --git a/bin/man1/modem.1 b/bin/man1/modem.1 new file mode 100644 index 00000000..a4bbc3f1 --- /dev/null +++ b/bin/man1/modem.1 @@ -0,0 +1,31 @@ +.Dd December 8, 2020 +.Dt MODEM 1 +.Os +. +.Sh NAME +.Nm modem +.Nd fixed baud rate wrapper +. +.Sh SYNOPSIS +.Nm +.Op Fl r Ar rate +.Ar command ... +. +.Sh DESCRIPTION +.Nm +runs the +.Ar command +in a new PTY +with a fixed baud rate. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl r Ar rate +Set the baud rate. +The default is 19200. +.El +. +.Sh BUGS +Window size changes are not propagated +to the child PTY. diff --git a/bin/man1/mtags.1 b/bin/man1/mtags.1 new file mode 100644 index 00000000..57856ba0 --- /dev/null +++ b/bin/man1/mtags.1 @@ -0,0 +1,76 @@ +.Dd January 20, 2021 +.Dt MTAGS 1 +.Os +. +.Sh NAME +.Nm mtags +.Nd miscellaneous tags +. +.Sh SYNOPSIS +.Nm +.Op Fl a +.Op Fl f Ar tagsfile +.Ar +. +.Sh DESCRIPTION +The +.Nm +utility +makes a +.Pa tags +file for +.Xr ex 1 +from the specified +.Xr make 1 , +.Xr mdoc 7 +.Xr sh 1 +sources. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl a +Append to +.Pa tags +file. +.It Fl f Ar tagsfile +Place the tag descriptions +in a file called +.Ar tagsfile . +The default behaviour is +to place them in a file called +.Pa tags . +.El +. +.Pp +Files whose names are +.Pa Makefile +or end in +.Pa .mk +are assumed to be +.Xr make 1 +files. +Files whose names end in +.Pa .[1-9] +are assumed to be +.Xr mdoc 7 +files. +Files whose names are +.Pa .profile , +.Pa .shrc +or end in +.Pa .sh +are assumed to be +.Xr sh 1 +files. +. +.Sh FILES +.Bl -tag -width Ds +.It Pa tags +default output tags file +.El +. +.Sh SEE ALSO +.Xr ctags 1 , +.Xr ex 1 , +.Xr vi 1 diff --git a/bin/man1/nudge.1 b/bin/man1/nudge.1 new file mode 100644 index 00000000..3ca4294a --- /dev/null +++ b/bin/man1/nudge.1 @@ -0,0 +1,44 @@ +.Dd September 4, 2020 +.Dt NUDGE 1 +.Os +. +.Sh NAME +.Nm nudge +.Nd terminal vibrator +. +.Sh SYNOPSIS +.Nm +.Op Fl f Ar file +.Op Fl n Ar count +.Op Fl s Ar shake +.Op Fl t Ar delay +. +.Sh DESCRIPTION +The +.Nm +utility +nudges the terminal. +An +.Xr xterm 1 +compatible terminal is required. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl f Ar file +Open the terminal named by +.Ar file . +The default is +.Pa /dev/tty . +.It Fl n Ar count +Set the number of times +to nudge the terminal. +The default is 2. +.It Fl s Ar shake +Set the shake intensity in pixels. +The default is 10. +.It Fl t Ar delay +Set the interval between shakes +in milliseconds. +The default is 20. +.El diff --git a/bin/man1/order.1 b/bin/man1/order.1 new file mode 100644 index 00000000..89fcbda5 --- /dev/null +++ b/bin/man1/order.1 @@ -0,0 +1,38 @@ +.Dd July 18, 2020 +.Dt ORDER 1 +.Os +. +.Sh NAME +.Nm order +.Nd operator precedence +. +.Sh SYNOPSIS +.Nm +.Op Ar expr ... +. +.Sh DESCRIPTION +.Nm +parses C expressions +and prints them with parentheses +according to the precedence rules in +.Xr operator 7 . +If no +.Ar expr +are given, +an expression is read +from standard input. +. +.Sh EXAMPLES +.Bd -literal +$ order 'a & b << 1' +(a & (b << 1)) +.Ed +. +.Sh SEE ALSO +.Xr operator 7 +. +.Sh CAVEATS +.Nm +does not support the +.Sy (type) +operator. diff --git a/bin/man1/pbd.1 b/bin/man1/pbd.1 new file mode 100644 index 00000000..f0665891 --- /dev/null +++ b/bin/man1/pbd.1 @@ -0,0 +1,66 @@ +.Dd February 9, 2021 +.Dt PBD 1 +.Os +. +.Sh NAME +.Nm pbd +.Nd macOS pasteboard daemon +. +.Sh SYNOPSIS +.Nm Op Fl s | c | p | o Ar url +. +.Sh DESCRIPTION +.Nm +is a daemon which pipes into macOS +.Xr pbcopy 1 , +from +.Xr pbpaste 1 +and invokes +.Xr open 1 +in response to messages +sent over TCP port 7062. +. +.Pp +The socket can be forwarded through +.Xr ssh 1 +and the flags can be used remotely +to access the local pasteboard +and open URLs. +. +.Pp +Forwarding can be configured with: +.Pp +.Dl RemoteForward 7062 127.0.0.1:7062 +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl c +Behave as +.Xr pbcopy 1 . +.It Fl o Ar url +Behave as +.Xr open 1 . +.It Fl p +Behave as +.Xr pbpaste 1 . +.It Fl s +Run the server. +This is the default. +.El +.Pp +ACAB. +. +.Sh EXAMPLES +.Bd -literal -offset indent +pbd & +ssh -R 7062:127.0.0.1:7062 tux.local +pbd -p +.Ed +. +.Sh SEE ALSO +.Xr open 1 , +.Xr pbcopy 1 , +.Xr pbpaste 1 , +.Xr ssh 1 , +.Xr ssh_config 5 diff --git a/bin/man1/pngo.1 b/bin/man1/pngo.1 new file mode 100644 index 00000000..cec13160 --- /dev/null +++ b/bin/man1/pngo.1 @@ -0,0 +1,56 @@ +.Dd September 17, 2018 +.Dt PNGO 1 +.Os +. +.Sh NAME +.Nm pngo +.Nd PNG optimizer +. +.Sh SYNOPSIS +.Nm +.Op Fl cv +.Op Fl o Ar file +.Op Ar +. +.Sh DESCRIPTION +.Nm +optimizes PNG files for size. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl c +Write to standard output. +.It Fl o Ar file +Write to +.Ar file . +.It Fl v +Output PNG header information. +.El +. +.Pp +.Nm +performs the following optimizations: +.Bl -bullet +.It +Discard ancillary chunks. +.It +Discard unnecessary alpha channel. +.It +Convert unnecessary truecolor to grayscale. +.It +Palletize color and alpha if possible. +.It +Reduce bit depth if possible. +.It +Apply a simple filter heuristic. +.It +Apply zlib's best compresion. +.El +. +.Sh SEE ALSO +.Xr glitch 1 +. +.Sh CAVEATS +.Nm +does not support interlaced PNGs. diff --git a/bin/man1/psf2png.1 b/bin/man1/psf2png.1 new file mode 100644 index 00000000..db74c6e2 --- /dev/null +++ b/bin/man1/psf2png.1 @@ -0,0 +1,53 @@ +.Dd September 28, 2018 +.Dt PSF2PNG 1 +.Os +. +.Sh NAME +.Nm psf2png +.Nd PSF2 to PNG renderer +. +.Sh SYNOPSIS +.Nm +.Op Fl b Ar bg +.Op Fl c Ar cols +.Op Fl f Ar fg +.Op Fl s Ar str +.Op Ar file +. +.Sh DESCRIPTION +.Nm +renders the PSF2 font +.Ar file +or standard input +to PNG +on standard output. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl b Ar bg +Use +.Ar bg +(hexadecimal RGB) +as background color. +The default background color is black. +.It Fl c Ar cols +Arrange glyphs in +.Ar cols +columns. +The default number of columns is 32. +.It Fl f Ar fg +Use +.Ar fg +(hexadecimal RGB) +as foreground color. +The default foreground color is white. +.It Fl s Ar str +Render glyphs for string +.Ar str +rather than all glyphs. +.El +. +.Sh SEE ALSO +.Xr pngo 1 , +.Xr psfed 1 diff --git a/bin/man1/psfed.1 b/bin/man1/psfed.1 new file mode 100644 index 00000000..3fbc4710 --- /dev/null +++ b/bin/man1/psfed.1 @@ -0,0 +1,166 @@ +.Dd January 14, 2019 +.Dt PSFED 1 +.Os +. +.Sh NAME +.Nm psfed +.Nd PSF2 font editor +. +.Sh SYNOPSIS +.Nm +.Op Fl H Ar height +.Op Fl g Ar glyphs +.Op Fl h Ar height +.Op Fl w Ar width +.Ar file +. +.Sh DESCRIPTION +.Nm +is a PSF2 font editor +for the Linux framebuffer. +. +.Pp +The arguments are as follows: +. +.Bl -tag -width Ds +.It Fl H Ar height +Modify the height of an existing font. +Only increasing the height is allowed. +. +.It Fl g Ar glyphs +Set the number of glyphs in a new font. +The default number of glyphs is 256. +. +.It Fl h Ar height +Set the height of a new font. +The default height is 16. +. +.It Fl w Ar width +Set the width of a new font. +The default width is 8. +.El +. +.Ss Normal Mode +In normal mode, +each glyph is displayed in a grid. +. +.Pp +.Bl -tag -width Ds -compact +.It Ic q +Quit. +.Nm +will ask for confirmation +if the font has been modified +since the last write. +. +.It Ic w +Write font to +.Ar file . +. +.It Ic - Ic + +Adjust display scale. +. +.It Ic h Ic l +Select previous/next glyph. +. +.It Ic k Ic j +Select glyph in previous/next row. +. +.It Ic f +Select glyph of next input character. +. +.It Ic ' +Return to previously selected glyph. +. +.It Ic y +Copy selected glyph. +. +.It Ic e +Edit selected glyph in +.Sx Edit Mode . +. +.It Ic i +Enter +.Sx Preview Mode . +.El +. +.Ss Edit Mode +In edit mode, +the selected glyph is displayed for editing +surrounded by a checked border. +The glyph is also displayed unscaled +in the bottom-right corner. +. +.Pp +.Bl -tag -width Ds -compact +.It Ic ESC +Return to +.Sx Normal Mode . +. +.It Ic - Ic + +Adjust display scale. +. +.It Ic g Ic G +Toggle guide on selected column/row. +. +.It Ic h Ic l +Select previous/next bit in row. +. +.It Ic k Ic j +Select previous/next bit in column. +. +.It Ic SPACE +Flip selected bit. +. +.It Ic r +Invert glyph. +. +.It Ic H Ic L +Move glyph left/right. +. +.It Ic K Ic J +Move glyph up/down. +. +.It Ic p +Paste the copied glyph. +. +.It Ic u +Revert glyph to initial state. +.El +. +.Ss Preview Mode +In preview mode, +arbitrary text may be entered +for preview. +Press +.Ic ESC +to return to +.Sx Normal Mode . +. +.Sh ENVIRONMENT +.Bl -tag -width FRAMEBUFFER +.It Ev FRAMEBUFFER +The framebuffer device path. +The default path is +.Pa /dev/fb0 . +.El +. +.Sh SEE ALSO +.Xr psfaddtable 1 , +.Xr psfgettable 1 , +.Xr psfstriptable 1 , +.Xr setfont 8 +. +.Sh CAVEATS +.Nm +does not support Unicode tables. +Use +.Xr psfaddtable 1 +to add Unicode tables +to fonts created by +.Nm . +. +.Sh BUGS +.Nm +makes no attempt to convert header fields +to and from little-endian format. diff --git a/bin/man1/ptee.1 b/bin/man1/ptee.1 new file mode 100644 index 00000000..04f9cdac --- /dev/null +++ b/bin/man1/ptee.1 @@ -0,0 +1,40 @@ +.Dd July 17, 2019 +.Dt PTEE 1 +.Os +. +.Sh NAME +.Nm ptee +.Nd tee for PTYs +. +.Sh SYNOPSIS +.Nm +.Ar command ... +.Cm > +.Ar file +. +.Sh DESCRIPTION +.Nm +runs +.Ar command +in a new PTY +which is mirrored to +the current PTY +and standard output. +Standard output must be redirected +to a file or pipe. +. +.Pp +Type +.Ic ^S +to toggle writing to standard output. +Type +.Ic ^Q +to write the media copy sequence for +.Xr shotty 1 . +. +.Sh SEE ALSO +.Xr tee 1 +. +.Sh BUGS +Window size changes are not propagated +to the child PTY. diff --git a/bin/man1/relay.1 b/bin/man1/relay.1 new file mode 100644 index 00000000..402c4726 --- /dev/null +++ b/bin/man1/relay.1 @@ -0,0 +1,48 @@ +.Dd April 28, 2019 +.Dt RELAY 1 +.Os +. +.Sh NAME +.Nm relay +.Nd IRC relay bot +. +.Sh SYNOPSIS +.Nm +.Ar host +.Ar port +.Ar nick +.Ar chan +. +.Sh DESCRIPTION +.Nm +is one half of an IRC relay pair. +It connects to +.Ar host Ns : Ns Ar port +over TLS +as +.Ar nick +and joins +.Ar chan . +. +.Pp +.Nm +outputs messages from +.Ar chan +to standard output +and sends messages to +.Ar chan +from standard input. +Two +.Nm +processes can be connected with +.Xr mkfifo 1 . +. +.Sh EXAMPLES +.Bd -literal -offset indent +mkfifo a b +relay a.example.com 6697 relay '#example' <>a >b +relay b.example.com 6697 relay '#example' <>b >a +.Ed +. +.Sh SEE ALSO +.Xr mkfifo 1 diff --git a/bin/man1/scheme.1 b/bin/man1/scheme.1 new file mode 100644 index 00000000..9f72d945 --- /dev/null +++ b/bin/man1/scheme.1 @@ -0,0 +1,59 @@ +.Dd February 6, 2021 +.Dt SCHEME 1 +.Os +. +.Sh NAME +.Nm scheme +.Nd color scheme +. +.Sh SYNOPSIS +.Nm +.Op Fl Xacghilmstx +.Op Fl p Ar n +. +.Sh DESCRIPTION +.Nm +generates a color scheme +and outputs it in a number of formats. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl X +Output Xresources for +.Xr xterm 1 . +.It Fl a +Generate the 16 ANSI colors. +This is the default. +.It Fl c +Output a C enum. +.It Fl g +Output a swatch PNG. +.It Fl h +Output floating point HSV. +.It Fl i +Swap black and white. +.It Fl l +Output Linux console OSC sequences. +.It Fl m +Output a +.Xr mintty 1 +theme. +Use with +.Fl t . +.It Fl p Ar n +Generate only the color +.Ar n . +.It Fl s +Output CSS +for classes named +.Sy fg Ns Ar n +and +.Sy bg Ns Ar n . +.It Fl t +Generate the 16 ANSI colors as well as +background, foreground, bold, selection and cursor colors. +.It Fl x +Output hexadecimal RGB. +This is the default. +.El diff --git a/bin/man1/shotty.1 b/bin/man1/shotty.1 new file mode 100644 index 00000000..d5eaa780 --- /dev/null +++ b/bin/man1/shotty.1 @@ -0,0 +1,92 @@ +.Dd November 25, 2019 +.Dt SHOTTY 1 +.Os +. +.Sh NAME +.Nm shotty +.Nd terminal capture +. +.Sh SYNOPSIS +.Nm +.Op Fl Bdns +.Op Fl b Ar bg +.Op Fl f Ar fg +.Op Fl h Ar rows +.Op Fl w Ar cols +.Op Ar file +. +.Sh DESCRIPTION +.Nm +interprets terminal output from +.Ar file +or standard input +and produces HTML +.Sy <pre> +on standard output. +. +.Pp +Terminal output +can be captured with +.Xr ptee 1 . +.Nm +targets compatibility with +.Ev TERM Ns = Ns Cm xterm +and +.Ev TERM Ns = Ns Cm xterm-256color +as used by +.Xr ncurses 3 . +A snapshot of the terminal +is output each time +a media copy sequence occurs, +or once at the end of the capture. +. +.Pp +HTML output uses the classes +.Sy bg Ns Va n +and +.Sy fg Ns Va n , +and inline styles for +bold, italic and underline. +CSS for colors can be generated with +.Xr scheme 1 . +. +.Pp +The arguments are as follows: +.Bl -tag -width "-w cols" +.It Fl B +Replace bold with bright colors. +. +.It Fl b Ar bg +Set the default background color. +The default value is 0 (black). +. +.It Fl d +Output the terminal state +following each control sequence. +. +.It Fl f Ar fg +Set the default foreground color. +The default value is 7 (white). +. +.It Fl h Ar rows +Set the terminal height. +The default value is 24. +. +.It Fl n +Do not show the cursor. +. +.It Fl s +Set the terminal size +from the current terminal size. +. +.It Fl w Ar cols +Set the terminal width. +The default value is 80. +.El +. +.Sh EXAMPLES +.Dl ptee htop | shotty -s > htop.html +. +.Sh SEE ALSO +.Xr ptee 1 , +.Xr scheme 1 diff --git a/bin/man1/sup.1 b/bin/man1/sup.1 new file mode 100644 index 00000000..4b266e06 --- /dev/null +++ b/bin/man1/sup.1 @@ -0,0 +1,48 @@ +.Dd February 21, 2021 +.Dt SUP 1 +.Os +. +.Sh NAME +.Nm sup +.Nd single-use password +. +.Sh SYNOPSIS +.Nm +.Ar service +.Op Ar email +. +.Sh DESCRIPTION +The +.Nm +utility +sets a random single-use password +for a service using the +.Dq forgot password +or +.Dq password reset +flow. +The password is copied to the clipboard +and the service login page is opened. +For passwordless services +with email-based authentication, +the emailed login link is opened. +. +.Pp +The following services are supported: +.Cm asciinema , +.Cm discogs , +.Cm freebsdbugzilla , +.Cm liberapay , +.Cm lobsters , +.Cm tildenews . +. +.Pp +The +.Nm +utility requires +.Xr curl 1 , +.Xr git-fetch-email 1 , +.Xr openssl 1 , +.Xr pbcopy 1 +and +.Xr open 1 . diff --git a/bin/man1/title.1 b/bin/man1/title.1 new file mode 100644 index 00000000..43ecc5e2 --- /dev/null +++ b/bin/man1/title.1 @@ -0,0 +1,51 @@ +.Dd September 10, 2019 +.Dt TITLE 1 +.Os +. +.Sh NAME +.Nm title +.Nd page titles +. +.Sh SYNOPSIS +.Nm +.Op Fl v +.Op Fl x Ar pattern +.Op Ar url +. +.Sh DESCRIPTION +.Nm +fetches HTML page titles +over HTTP and HTTPS. +.Nm +scans standard input for URLs +and writes their titles to standard output. +If a +.Ar url +argument is given, +.Nm +exits after fetching its title. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl x Ar pattern +Exclude URLs matching +.Ar pattern , +which is a modern regular expression. +See +.Xr re_format 7 . +.It Fl v +Enable +.Xr libcurl 3 +verbose output. +.El +. +.Sh EXAMPLES +.Bd -literal -offset indent +mkfifo snarf titles +relay irc.example.org 6697 snarf '#example' <>titles >snarf +title <snarf >titles +.Ed +. +.Sh SEE ALSO +.Xr relay 1 diff --git a/bin/man1/typer.1 b/bin/man1/typer.1 new file mode 100644 index 00000000..dd1108d1 --- /dev/null +++ b/bin/man1/typer.1 @@ -0,0 +1,74 @@ +.Dd April 28, 2021 +.Dt TYPER 1 +.Os +. +.Sh NAME +.Nm typer +.Nd type all day +. +.Sh SYNOPSIS +.Nm +.Op Fl CPRv +.Op Fl c Ar cert +.Op Fl n Ar nick +.Op Fl p Ar port +.Op Fl u Ar user +.Ar host +.Ar chan +. +.Sh DESCRIPTION +.Nm +is an IRC bot +that types all day long. +The arguments are as follows: +.Bl -tag -width Ds +.It Fl C +Copy +.Nm . +Type whenever anyone else is typing. +.It Fl P +Request the +.Sy causal.agency/passive +vendor-specific capability. +.It Fl R +Reverse +.Nm . +Send other users' typing indicators +as regular messages for all to see. +.It Fl c Ar cert +Use the TLS client certificate +and private key loaded from +.Ar cert . +.It Fl n Ar nick +Set the nickname. +The default is +.Nm . +.It Fl p Ar port +Connect to +.Ar port . +The default is 6697. +.It Fl u Ar user +Set the username. +The default is +.Nm . +.It Fl v +Log IRC protocol to standard error. +.It Ar host +Connect to +.Ar host . +.It Ar chan +Type in the channel +.Ar chan . +.El +. +.Sh STANDARDS +.Bl -item +.It +.Rs +.%A MuffinMedic +.%A James Wheare +.%T IRCv3 typing client tag +.%I IRCv3 Working Group +.%U https://ircv3.net/specs/client-tags/typing +.Re +.El diff --git a/bin/man1/up.1 b/bin/man1/up.1 new file mode 100644 index 00000000..189020de --- /dev/null +++ b/bin/man1/up.1 @@ -0,0 +1,80 @@ +.Dd February 7, 2021 +.Dt UP 1 +.Os +. +.Sh NAME +.Nm up +.Nd upload file +. +.Sh SYNOPSIS +.Nm +.Op Fl h +.Op Ar file +. +.Nm +.Fl c | t +.Ar command ... +. +.Nm +.Fl s +. +.Sh DESCRIPTION +.Nm +uploads a file +to temp.causal.agency with +.Xr scp 1 . +If no +.Ar file +is provided, +standard input is read +and uploaded as text. +. +.Pp +The destination file name +is chosen using +.Xr date 1 +and +.Xr openssl 1 +.Cm rand . +The URL of the uploaded file is printed +and copied to the pasteboard with +.Xr pbcopy 1 +if available. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl c +Run a command +to produce a text file for upload. +.It Fl h +Use +.Xr hilex 1 +to produce an HTML file for upload. +.It Fl s +Use +.Xr screencapture 1 +or +.Xr scrot 1 +to produce a PNG file for upload. +The file is optimized by +.Xr pngo 1 +if available. +.It Fl t +Run a command with +.Xr ptee 1 +and +.Xr shotty 1 +to produce an HTML file for upload. +.El +. +.Pp +Any arguments after +.Ql \-\- +are passed to +.Xr hilex 1 +and +.Xr screencapture 1 +or +.Xr scrot 1 , +respectively. diff --git a/bin/man1/when.1 b/bin/man1/when.1 new file mode 100644 index 00000000..0b473573 --- /dev/null +++ b/bin/man1/when.1 @@ -0,0 +1,76 @@ +.Dd July 24, 2019 +.Dt WHEN 1 +.Os +. +.Sh NAME +.Nm when +.Nd date calculator +. +.Sh SYNOPSIS +.Nm +.Op Ar expr +. +.Sh DESCRIPTION +.Nm +is a date calculator. +If no +.Ar expr +is given, +expressions are read +from standard input. +. +.Pp +The grammar is as follows: +.Bl -tag -width Ds +.It Sy \&. +Today's date. +. +.It Ar month Ar date Op Ar year +A full date, +or a date in the current year. +.Ar month +must be at least three letters. +. +.It Ar day +A day of the week +in the current week. +.Ar day +must be at least three letters. +. +.It Sy < Ar date +The date one week before. +. +.It Sy > Ar date +The date one week after. +. +.It Ar date Sy + Ar interval +The date after some interval. +. +.It Ar date Sy - Ar interval +The date before some interval. +. +.It Ar date Sy - Ar date +The interval between two dates. +. +.It Ar num Sy d +A number of days. +. +.It Ar num Sy w +A number of weeks. +. +.It Ar num Sy m +A number of months. +. +.It Ar num Sy y +A number of years. +.El +. +.Sh EXAMPLES +.Bl -tag -width "Dec 25 - ." +.It Ic Dec 25 - \&. +How long until Christmas. +.It Ic >Fri +The date next Friday. +.It Ic \&. + 2w +Your last day at work. +.El diff --git a/bin/man1/xx.1 b/bin/man1/xx.1 new file mode 100644 index 00000000..d38789a7 --- /dev/null +++ b/bin/man1/xx.1 @@ -0,0 +1,68 @@ +.Dd September 7, 2018 +.Dt XX 1 +.Os +. +.Sh NAME +.Nm xx +.Nd hexdump +. +.Sh SYNOPSIS +.Nm +.Op Fl arsz +.Op Fl c Ar cols +.Op Fl g Ar group +.Op Fl p Ar count +.Op Ar file +. +.Sh DESCRIPTION +.Nm +dumps the contents of a +.Ar file +or standard input +in hexadecimal format. +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl a +Toggle ASCII output. +. +.It Fl c Ar cols +Output +.Ar cols +bytes per line. +The default +.Ar cols +is 16. +. +.It Fl g Ar group +Output extra space after every +.Ar group +bytes. +The default +.Ar group +is 8. +. +.It Fl p Ar count +Output a blank line after every +.Ar count +bytes. +.Ar count +must be a multiple of +.Ar cols . +. +.It Fl r +Reverse hexdump. +Read hexadecimal input +and write byte output. +. +.It Fl s +Toggle offset output. +. +.It Fl z +Skip output of lines containing only zeros. +.El +. +.Sh SEE ALSO +.Xr hexdump 1 , +.Xr xxd 1 diff --git a/bin/man3/png.3 b/bin/man3/png.3 new file mode 100644 index 00000000..accffbd7 --- /dev/null +++ b/bin/man3/png.3 @@ -0,0 +1,90 @@ +.Dd July 25, 2019 +.Dt PNG 3 +.Os +. +.Sh NAME +.Nm png +.Nd basic PNG output +. +.Sh SYNOPSIS +.In png.h +. +.Ft void +.Fo pngHead +.Fa "FILE *file" +.Fa "uint32_t width" +.Fa "uint32_t height" +.Fa "uint8_t depth" +.Fa "uint8_t color" +.Fc +. +.Ft void +.Fn pngPalette "FILE *file" "const uint8_t *pal" "uint32_t len" +. +.Ft void +.Fn pngData "FILE *file" "const uint8_t *data" "uint32_t len" +. +.Ft void +.Fn pngTail "FILE *file" +. +.Sh DESCRIPTION +The +.Fn pngHead +function +writes the +.Sy IHDR +chunk to +.Fa file . +The +.Fa color +parameter can be one of +.Dv PNGGrayscale , +.Dv PNGTruecolor +optionally +.Em or Ns 'ed +with +.Dv PNGAlpha , +or +.Dv PNGIndexed . +. +.Pp +The +.Fn pngPalette +function +writes the +.Sy PLTE +chunk to +.Fa file . +. +.Pp +The +.Fn pngData +function +writes the +.Sy IDAT +chunk to +.Fa file +without compression. +The constants +.Dv PNGNone , +.Dv PNGSub , +.Dv PNGUp , +.Dv PNGAverage , +.Dv PNGPaeth +are defined +for use in PNG data. +. +.Pp +The +.Fn pngTail +function +writes the +.Sy IEND +chunk to +.Fa file . +. +.Sh ERRORS +Any errors from writing to +.Fa file +are handled by calling +.Xr err 3 . diff --git a/bin/man6/freecell.6 b/bin/man6/freecell.6 new file mode 100644 index 00000000..0e485a16 --- /dev/null +++ b/bin/man6/freecell.6 @@ -0,0 +1,50 @@ +.Dd April 17, 2021 +.Dt FREECELL 6 +.Os +. +.Sh NAME +.Nm freecell +.Nd patience game +. +.Sh SYNOPSIS +.Nm +.Op Fl d Ar delay +.Op Fl n Ar game +. +.Sh DESCRIPTION +.Nm +is a terminal FreeCell patience game. +The arguments are as follows: +.Bl -tag -width Ds +.It Fl d Ar delay +Set the delay in milliseconds +between queued moves. +The default is 50. +.It Fl n Ar game +Select the game number to play. +.El +. +.Pp +Moves are performed +by typing a sequence of two keys. +To automatically move a card +to a free cell, +type the same key twice. +The keys are as follows: +.Bl -tag -width Ds +.It Ic Escape +Cancel a pending move. +.It Ic u , Backspace +Undo the previous move. +.It Ic 1 , 2 , 3 , 4 +Select the cells. +.It Ic q , w , e , r , a , s , d , f +Select the tableau cascades. +.It Ic _ , Space +Manually move +the selected card +to the foundations. +.It Ic Shift +Move a single card +to an empty tableau cascade. +.El diff --git a/bin/mdoc.l b/bin/mdoc.l new file mode 100644 index 00000000..a31b6a2e --- /dev/null +++ b/bin/mdoc.l @@ -0,0 +1,65 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +%option prefix="mdoc" +%option noyywrap + +%{ +#include "hilex.h" +%} + +%s MacroLine + +%% + +[[:blank:]]+ { return Normal; } + +^"." { + BEGIN(MacroLine); + return Keyword; +} + +^".\\\"".* { return Comment; } + +<MacroLine>{ + "\n" { + BEGIN(0); + return Normal; + } + + %[ABCDIJNOPQRTUV]|A[cdnopqrt]|B[cdfkloqtx]|Br[coq]|Bsx|C[dm]|D[1bcdloqtvx] | + E[cdfklmnorsvx]|F[acdlnortx]|Hf|I[cnt]|L[bikp]|M[st]|N[dmosx]|O[copstx] | + P[acfopq]|Q[cloq]|R[esv]|S[chmoqstxy]|T[an]|U[dx]|V[at]|X[cor] { + return Keyword; + } + + "\""([^""]|"\\\"")*"\"" { return String; } +} + +"\\"(.|"("..|"["[^]]*"]") { return String; } + +[^.\\""[:space:]]+ { return Normal; } + +.|\n { return Normal; } + +%{ + (void)yyunput; + (void)input; +%} + +%% + +const struct Lexer LexMdoc = { yylex, &yyin, &yytext }; diff --git a/bin/modem.c b/bin/modem.c new file mode 100644 index 00000000..2133ae08 --- /dev/null +++ b/bin/modem.c @@ -0,0 +1,102 @@ +/* Copyright (C) 2018 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <poll.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <sysexits.h> +#include <termios.h> +#include <unistd.h> + +#if defined __FreeBSD__ +#include <libutil.h> +#elif defined __linux__ +#include <pty.h> +#else +#include <util.h> +#endif + +typedef unsigned uint; +typedef unsigned char byte; + +static struct termios saveTerm; +static void restoreTerm(void) { + tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm); +} + +int main(int argc, char *argv[]) { + int error; + + uint baudRate = 19200; + for (int opt; 0 < (opt = getopt(argc, argv, "r:"));) { + switch (opt) { + break; case 'r': baudRate = strtoul(optarg, NULL, 10); + break; default: return EX_USAGE; + } + } + if (argc - optind < 1) return EX_USAGE; + + error = tcgetattr(STDIN_FILENO, &saveTerm); + if (error) err(EX_IOERR, "tcgetattr"); + atexit(restoreTerm); + + struct termios raw = saveTerm; + cfmakeraw(&raw); + error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw); + if (error) err(EX_IOERR, "tcsetattr"); + + struct winsize window; + error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window); + if (error) err(EX_IOERR, "TIOCGWINSZ"); + + int pty; + pid_t pid = forkpty(&pty, NULL, NULL, &window); + if (pid < 0) err(EX_OSERR, "forkpty"); + + if (!pid) { + execvp(argv[optind], &argv[optind]); + err(EX_NOINPUT, "%s", argv[optind]); + } + + byte c; + struct pollfd fds[2] = { + { .events = POLLIN, .fd = STDIN_FILENO }, + { .events = POLLIN, .fd = pty }, + }; + while (usleep(8 * 1000000 / baudRate), 0 < poll(fds, 2, -1)) { + if (fds[0].revents) { + ssize_t size = read(STDIN_FILENO, &c, 1); + if (size < 0) err(EX_IOERR, "read(%d)", STDIN_FILENO); + size = write(pty, &c, 1); + if (size < 0) err(EX_IOERR, "write(%d)", pty); + } + + if (fds[1].revents) { + ssize_t size = read(pty, &c, 1); + if (size < 0) err(EX_IOERR, "read(%d)", pty); + if (!size) break; + size = write(STDOUT_FILENO, &c, 1); + if (size < 0) err(EX_IOERR, "write(%d)", STDOUT_FILENO); + } + } + + int status; + pid_t dead = waitpid(pid, &status, 0); + if (dead < 0) err(EX_OSERR, "waitpid"); + return WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE; +} diff --git a/bin/mtags.c b/bin/mtags.c new file mode 100644 index 00000000..1ebfbac5 --- /dev/null +++ b/bin/mtags.c @@ -0,0 +1,99 @@ +/* Copyright (C) 2021 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <err.h> +#include <regex.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +static void escape(FILE *file, const char *str, size_t len) { + for (size_t i = 0; i < len; ++i) { + if (str[i] == '\\' || str[i] == '/') { + putc('\\', file); + } + putc(str[i], file); + } +} + +int main(int argc, char *argv[]) { + bool append = false; + const char *path = "tags"; + for (int opt; 0 < (opt = getopt(argc, argv, "af:"));) { + switch (opt) { + break; case 'a': append = true; + break; case 'f': path = optarg; + break; default: return EX_USAGE; + } + } + + FILE *tags = fopen(path, (append ? "a" : "w")); + if (!tags) err(EX_CANTCREAT, "%s", path); + + regex_t makeFile, makeLine; + regex_t mdocFile, mdocLine; + regex_t shFile, shLine; + int error = 0 + || regcomp(&makeFile, "(^|/)Makefile|[.]mk$", REG_EXTENDED | REG_NOSUB) + || regcomp( + &makeLine, + "^([.][^:$A-Z][^:$[:space:]]*|[^.:$][^:$[:space:]]*):", + REG_EXTENDED + ) + || regcomp(&mdocFile, "[.][1-9]$", REG_EXTENDED | REG_NOSUB) + || regcomp(&mdocLine, "^[.]S[hs] ([^\t\n]+)", REG_EXTENDED) + || regcomp( + &shFile, "(^|/)[.](profile|shrc)|[.]sh$", REG_EXTENDED | REG_NOSUB + ) + || regcomp(&shLine, "^([_[:alnum:]]+)[[:blank:]]*[(][)]", REG_EXTENDED); + assert(!error); + + size_t cap = 0; + char *buf = NULL; + for (int i = optind; i < argc; ++i) { + const regex_t *regex; + if (!regexec(&makeFile, argv[i], 0, NULL, 0)) { + regex = &makeLine; + } else if (!regexec(&mdocFile, argv[i], 0, NULL, 0)) { + regex = &mdocLine; + } else if (!regexec(&shFile, argv[i], 0, NULL, 0)) { + regex = &shLine; + } else { + warnx("skipping unknown file type %s", argv[i]); + continue; + } + + FILE *file = fopen(argv[i], "r"); + if (!file) err(EX_NOINPUT, "%s", argv[i]); + + while (0 < getline(&buf, &cap, file)) { + regmatch_t match[2]; + if (regexec(regex, buf, 2, match, 0)) continue; + fprintf( + tags, "%.*s\t%s\t/^", + (int)(match[1].rm_eo - match[1].rm_so), &buf[match[1].rm_so], + argv[i] + ); + escape(tags, buf, match[0].rm_eo); + fprintf(tags, "/\n"); + } + fclose(file); + } +} diff --git a/bin/nudge.c b/bin/nudge.c new file mode 100644 index 00000000..108d2459 --- /dev/null +++ b/bin/nudge.c @@ -0,0 +1,78 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> +#include <termios.h> +#include <unistd.h> + +static int shake = 10; +static int delay = 20000; +static int count = 2; + +static void move(int tty, int x, int y) { + dprintf(tty, "\33[3;%d;%dt", x, y); + usleep(delay); +} + +int main(int argc, char *argv[]) { + const char *path = "/dev/tty"; + for (int opt; 0 < (opt = getopt(argc, argv, "f:n:s:t:"));) { + switch (opt) { + break; case 'f': path = optarg; + break; case 'n': count = atoi(optarg); + break; case 's': shake = atoi(optarg); + break; case 't': delay = atoi(optarg) * 1000; + break; default: return EX_USAGE; + } + } + + int tty = open(path, O_RDWR); + if (tty < 0) err(EX_OSFILE, "%s", path); + + struct termios save; + int error = tcgetattr(tty, &save); + if (error) err(EX_IOERR, "tcgetattr"); + + struct termios raw = save; + cfmakeraw(&raw); + error = tcsetattr(tty, TCSAFLUSH, &raw); + if (error) err(EX_IOERR, "tcsetattr"); + + char buf[256]; + dprintf(tty, "\33[13t"); + ssize_t len = read(tty, buf, sizeof(buf) - 1); + buf[(len < 0 ? 0 : len)] = '\0'; + + int x, y; + int n = sscanf(buf, "\33[3;%d;%dt", &x, &y); + + error = tcsetattr(tty, TCSANOW, &save); + if (error) err(EX_IOERR, "tcsetattr"); + if (n < 2) return EX_CONFIG; + + dprintf(tty, "\33[5t"); + for (int i = 0; i < count; ++i) { + move(tty, x - shake, y); + move(tty, x, y + shake); + move(tty, x + shake, y); + move(tty, x, y - shake); + move(tty, x, y); + } +} diff --git a/bin/order.y b/bin/order.y new file mode 100644 index 00000000..c2e87971 --- /dev/null +++ b/bin/order.y @@ -0,0 +1,195 @@ +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +%{ + +#include <ctype.h> +#include <err.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#define YYSTYPE char * + +static char *fmt(const char *format, ...) { + char *str = NULL; + va_list ap; + va_start(ap, format); + vasprintf(&str, format, ap); + va_end(ap); + if (!str) err(EX_OSERR, "vasprintf"); + return str; +} + +static int yylex(void); +static void yyerror(const char *str); + +%} + +%token Ident + +%left ',' +%right '=' MulAss DivAss ModAss AddAss SubAss ShlAss ShrAss AndAss XorAss OrAss +%right '?' ':' +%left Or +%left And +%left '|' +%left '^' +%left '&' +%left Eq Ne +%left '<' Le '>' Ge +%left Shl Shr +%left '+' '-' +%left '*' '/' '%' +%right '!' '~' Inc Dec Sizeof +%left '(' ')' '[' ']' Arr '.' + +%% + +start: + expr { printf("%s\n", $1); } + ; + +expr: + Ident + | '(' expr ')' { $$ = $2; } + | expr '[' expr ']' { $$ = fmt("(%s[%s])", $1, $3); } + | expr Arr Ident { $$ = fmt("(%s->%s)", $1, $3); } + | expr '.' Ident { $$ = fmt("(%s.%s)", $1, $3); } + | '!' expr { $$ = fmt("(!%s)", $2); } + | '~' expr { $$ = fmt("(~%s)", $2); } + | Inc expr { $$ = fmt("(++%s)", $2); } + | Dec expr { $$ = fmt("(--%s)", $2); } + | expr Inc { $$ = fmt("(%s++)", $1); } + | expr Dec { $$ = fmt("(%s--)", $1); } + | '+' expr %prec '!' { $$ = fmt("(+%s)", $2); } + | '-' expr %prec '!' { $$ = fmt("(-%s)", $2); } + | '*' expr %prec '!' { $$ = fmt("(*%s)", $2); } + | '&' expr %prec '!' { $$ = fmt("(&%s)", $2); } + | Sizeof expr { $$ = fmt("(sizeof %s)", $2); } + | expr '*' expr { $$ = fmt("(%s * %s)", $1, $3); } + | expr '/' expr { $$ = fmt("(%s / %s)", $1, $3); } + | expr '%' expr { $$ = fmt("(%s %% %s)", $1, $3); } + | expr '+' expr { $$ = fmt("(%s + %s)", $1, $3); } + | expr '-' expr { $$ = fmt("(%s - %s)", $1, $3); } + | expr Shl expr { $$ = fmt("(%s << %s)", $1, $3); } + | expr Shr expr { $$ = fmt("(%s >> %s)", $1, $3); } + | expr '<' expr { $$ = fmt("(%s < %s)", $1, $3); } + | expr Le expr { $$ = fmt("(%s <= %s)", $1, $3); } + | expr '>' expr { $$ = fmt("(%s > %s)", $1, $3); } + | expr Ge expr { $$ = fmt("(%s >= %s)", $1, $3); } + | expr Eq expr { $$ = fmt("(%s == %s)", $1, $3); } + | expr Ne expr { $$ = fmt("(%s != %s)", $1, $3); } + | expr '&' expr { $$ = fmt("(%s & %s)", $1, $3); } + | expr '^' expr { $$ = fmt("(%s ^ %s)", $1, $3); } + | expr '|' expr { $$ = fmt("(%s | %s)", $1, $3); } + | expr And expr { $$ = fmt("(%s && %s)", $1, $3); } + | expr Or expr { $$ = fmt("(%s || %s)", $1, $3); } + | expr '?' expr ':' expr { $$ = fmt("(%s ? %s : %s)", $1, $3, $5); } + | expr ass expr %prec '=' { $$ = fmt("(%s %s %s)", $1, $2, $3); } + | expr ',' expr { $$ = fmt("(%s, %s)", $1, $3); } + ; + +ass: + '=' { $$ = "="; } + | MulAss { $$ = "*="; } + | DivAss { $$ = "/="; } + | ModAss { $$ = "%="; } + | AddAss { $$ = "+="; } + | SubAss { $$ = "-="; } + | ShlAss { $$ = "<<="; } + | ShrAss { $$ = ">>="; } + | AndAss { $$ = "&="; } + | XorAss { $$ = "^="; } + | OrAss { $$ = "|="; } + ; + +%% + +#define T(a, b) ((int)(a) << 8 | (int)(b)) + +static FILE *in; + +static int yylex(void) { + char ch; + while (isspace(ch = getc(in))); + + if (isalnum(ch)) { + char ident[64] = { ch, '\0' }; + for (size_t i = 1; i < sizeof(ident) - 1; ++i) { + ch = getc(in); + if (!isalnum(ch) && ch != '_') break; + ident[i] = ch; + } + ungetc(ch, in); + if (!strcmp(ident, "sizeof")) return Sizeof; + yylval = fmt("%s", ident); + return Ident; + } + + char ne = getc(in); + switch (T(ch, ne)) { + case T('-', '>'): return Arr; + case T('+', '+'): return Inc; + case T('-', '-'): return Dec; + case T('<', '='): return Le; + case T('>', '='): return Ge; + case T('=', '='): return Eq; + case T('!', '='): return Ne; + case T('&', '&'): return And; + case T('|', '|'): return Or; + case T('*', '='): return MulAss; + case T('/', '='): return DivAss; + case T('%', '='): return ModAss; + case T('+', '='): return AddAss; + case T('-', '='): return SubAss; + case T('&', '='): return AndAss; + case T('^', '='): return XorAss; + case T('|', '='): return OrAss; + case T('<', '<'): { + if ('=' == (ne = getc(in))) return ShlAss; + ungetc(ne, in); + return Shl; + } + case T('>', '>'): { + if ('=' == (ne = getc(in))) return ShrAss; + ungetc(ne, in); + return Shr; + } + default: { + ungetc(ne, in); + return ch; + } + } +} + +static void yyerror(const char *str) { + errx(EX_DATAERR, "%s", str); +} + +int main(int argc, char *argv[]) { + for (int i = 1; i < argc; ++i) { + in = fmemopen(argv[i], strlen(argv[i]), "r"); + if (!in) err(EX_OSERR, "fmemopen"); + yyparse(); + fclose(in); + } + if (argc > 1) return EX_OK; + in = stdin; + yyparse(); +} diff --git a/bin/pbd.c b/bin/pbd.c new file mode 100644 index 00000000..2fd401ae --- /dev/null +++ b/bin/pbd.c @@ -0,0 +1,151 @@ +/* Copyright (C) 2017 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> +#include <err.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <sysexits.h> +#include <unistd.h> + +typedef unsigned char byte; + +static void spawn(const char *cmd, const char *arg, int dest, int src) { + pid_t pid = fork(); + if (pid < 0) err(EX_OSERR, "fork"); + + if (pid) { + int status; + pid_t dead = waitpid(pid, &status, 0); + if (dead < 0) err(EX_OSERR, "waitpid(%d)", pid); + if (status) warnx("%s: status %d", cmd, status); + + } else { + int fd = dup2(src, dest); + if (fd < 0) err(EX_OSERR, "dup2"); + + execlp(cmd, cmd, arg, NULL); + err(EX_UNAVAILABLE, "%s", cmd); + } +} + +static int pbd(void) { + int error; + + int server = socket(PF_INET, SOCK_STREAM, 0); + if (server < 0) err(EX_OSERR, "socket"); + + error = fcntl(server, F_SETFD, FD_CLOEXEC); + if (error) err(EX_IOERR, "fcntl"); + + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(7062), + .sin_addr = { .s_addr = htonl(0x7F000001) }, + }; + error = bind(server, (struct sockaddr *)&addr, sizeof(addr)); + if (error) err(EX_UNAVAILABLE, "bind"); + + error = listen(server, 0); + if (error) err(EX_UNAVAILABLE, "listen"); + + for (;;) { + int client = accept(server, NULL, NULL); + if (client < 0) err(EX_IOERR, "accept"); + + error = fcntl(client, F_SETFD, FD_CLOEXEC); + if (error) err(EX_IOERR, "fcntl"); + + char c = 0; + ssize_t size = read(client, &c, 1); + if (size < 0) warn("read"); + + switch (c) { + break; case 'p': spawn("pbpaste", NULL, STDOUT_FILENO, client); + break; case 'c': spawn("pbcopy", NULL, STDIN_FILENO, client); + break; case 'o': spawn("xargs", "open", STDIN_FILENO, client); + } + + close(client); + } +} + +static int pbdClient(char c) { + int client = socket(PF_INET, SOCK_STREAM, 0); + if (client < 0) err(EX_OSERR, "socket"); + + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(7062), + .sin_addr = { .s_addr = htonl(0x7F000001) }, + }; + int error = connect(client, (struct sockaddr *)&addr, sizeof(addr)); + if (error) err(EX_UNAVAILABLE, "connect"); + + ssize_t size = write(client, &c, 1); + if (size < 0) err(EX_IOERR, "write"); + + return client; +} + +static void copy(int out, int in) { + byte buf[4096]; + ssize_t readSize; + while (0 < (readSize = read(in, buf, sizeof(buf)))) { + ssize_t writeSize = write(out, buf, readSize); + if (writeSize < 0) err(EX_IOERR, "write(%d)", out); + } + if (readSize < 0) err(EX_IOERR, "read(%d)", in); +} + +static int pbcopy(void) { + int client = pbdClient('c'); + copy(client, STDIN_FILENO); + return EX_OK; +} + +static int pbpaste(void) { + int client = pbdClient('p'); + copy(STDOUT_FILENO, client); + return EX_OK; +} + +static int open1(const char *url) { + if (!url) return EX_USAGE; + int client = pbdClient('o'); + ssize_t size = write(client, url, strlen(url)); + if (size < 0) err(EX_IOERR, "write"); + return EX_OK; +} + +int main(int argc, char *argv[]) { + for (int opt; 0 < (opt = getopt(argc, argv, "co:ps"));) { + switch (opt) { + case 'c': return pbcopy(); + case 'o': return open1(optarg); + case 'p': return pbpaste(); + case 's': return pbd(); + default: return EX_USAGE; + } + } + return pbd(); +} diff --git a/bin/png.h b/bin/png.h new file mode 100644 index 00000000..15b6d13d --- /dev/null +++ b/bin/png.h @@ -0,0 +1,108 @@ +/* Copyright (C) 2018 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> + +static inline uint32_t pngCRCTable(uint8_t n) { + static uint32_t table[256]; + if (table[1]) return table[n]; + for (int i = 0; i < 256; ++i) { + table[i] = i; + for (int j = 0; j < 8; ++j) { + table[i] = (table[i] >> 1) ^ (table[i] & 1 ? 0xEDB88320 : 0); + } + } + return table[n]; +} + +static uint32_t pngCRC; + +static inline void pngWrite(FILE *file, const uint8_t *ptr, uint32_t len) { + if (!fwrite(ptr, len, 1, file)) err(EX_IOERR, "pngWrite"); + for (uint32_t i = 0; i < len; ++i) { + pngCRC = pngCRCTable(pngCRC ^ ptr[i]) ^ (pngCRC >> 8); + } +} +static inline void pngInt32(FILE *file, uint32_t n) { + pngWrite(file, (uint8_t []) { n >> 24, n >> 16, n >> 8, n }, 4); +} +static inline void pngChunk(FILE *file, char type[static 4], uint32_t len) { + pngInt32(file, len); + pngCRC = ~0; + pngWrite(file, (uint8_t *)type, 4); +} + +enum { + PNGGrayscale, + PNGTruecolor = 2, + PNGIndexed, + PNGAlpha, +}; + +static inline void pngHead( + FILE *file, uint32_t width, uint32_t height, uint8_t depth, uint8_t color +) { + pngWrite(file, (uint8_t *)"\x89PNG\r\n\x1A\n", 8); + pngChunk(file, "IHDR", 13); + pngInt32(file, width); + pngInt32(file, height); + pngWrite(file, &depth, 1); + pngWrite(file, &color, 1); + pngWrite(file, (uint8_t []) { 0, 0, 0 }, 3); + pngInt32(file, ~pngCRC); +} + +static inline void pngPalette(FILE *file, const uint8_t *pal, uint32_t len) { + pngChunk(file, "PLTE", len); + pngWrite(file, pal, len); + pngInt32(file, ~pngCRC); +} + +enum { + PNGNone, + PNGSub, + PNGUp, + PNGAverage, + PNGPaeth, +}; + +static inline void pngData(FILE *file, const uint8_t *data, uint32_t len) { + uint32_t adler1 = 1, adler2 = 0; + for (uint32_t i = 0; i < len; ++i) { + adler1 = (adler1 + data[i]) % 65521; + adler2 = (adler1 + adler2) % 65521; + } + uint32_t zlen = 2 + 5 * ((len + 0xFFFE) / 0xFFFF) + len + 4; + pngChunk(file, "IDAT", zlen); + pngWrite(file, (uint8_t []) { 0x08, 0x1D }, 2); + for (; len > 0xFFFF; data += 0xFFFF, len -= 0xFFFF) { + pngWrite(file, (uint8_t []) { 0x00, 0xFF, 0xFF, 0x00, 0x00 }, 5); + pngWrite(file, data, 0xFFFF); + } + pngWrite(file, (uint8_t []) { 0x01, len, len >> 8, ~len, ~len >> 8 }, 5); + pngWrite(file, data, len); + pngInt32(file, adler2 << 16 | adler1); + pngInt32(file, ~pngCRC); +} + +static inline void pngTail(FILE *file) { + pngChunk(file, "IEND", 0); + pngInt32(file, ~pngCRC); +} diff --git a/bin/pngo.c b/bin/pngo.c new file mode 100644 index 00000000..322cb1ba --- /dev/null +++ b/bin/pngo.c @@ -0,0 +1,812 @@ +/* Copyright (C) 2018 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> +#include <err.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <zlib.h> + +#define PACKED __attribute__((packed)) +#define PAIR(a, b) ((uint16_t)(a) << 8 | (uint16_t)(b)) + +#define CRC_INIT (crc32(0, Z_NULL, 0)) + +static bool verbose; +static const char *path; +static FILE *file; +static uint32_t crc; + +static void readExpect(void *ptr, size_t size, const char *expect) { + fread(ptr, size, 1, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + if (feof(file)) errx(EX_DATAERR, "%s: missing %s", path, expect); + crc = crc32(crc, ptr, size); +} + +static void writeExpect(const void *ptr, size_t size) { + fwrite(ptr, size, 1, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + crc = crc32(crc, ptr, size); +} + +static const uint8_t Signature[8] = "\x89PNG\r\n\x1A\n"; + +static void readSignature(void) { + uint8_t signature[8]; + readExpect(signature, 8, "signature"); + if (0 != memcmp(signature, Signature, 8)) { + errx(EX_DATAERR, "%s: invalid signature", path); + } +} + +static void writeSignature(void) { + writeExpect(Signature, sizeof(Signature)); +} + +struct PACKED Chunk { + uint32_t size; + char type[4]; +}; + +static const char *typeStr(struct Chunk chunk) { + static char buf[5]; + memcpy(buf, chunk.type, 4); + return buf; +} + +static struct Chunk readChunk(void) { + struct Chunk chunk; + readExpect(&chunk, sizeof(chunk), "chunk"); + chunk.size = ntohl(chunk.size); + crc = crc32(CRC_INIT, (Byte *)chunk.type, sizeof(chunk.type)); + return chunk; +} + +static void writeChunk(struct Chunk chunk) { + chunk.size = htonl(chunk.size); + writeExpect(&chunk, sizeof(chunk)); + crc = crc32(CRC_INIT, (Byte *)chunk.type, sizeof(chunk.type)); +} + +static void readCrc(void) { + uint32_t expected = crc; + uint32_t found; + readExpect(&found, sizeof(found), "CRC32"); + found = ntohl(found); + if (found != expected) { + errx( + EX_DATAERR, "%s: expected CRC32 %08X, found %08X", + path, expected, found + ); + } +} + +static void writeCrc(void) { + uint32_t net = htonl(crc); + writeExpect(&net, sizeof(net)); +} + +static void skipChunk(struct Chunk chunk) { + if (!(chunk.type[0] & 0x20)) { + errx(EX_CONFIG, "%s: unsupported critical chunk %s", path, typeStr(chunk)); + } + uint8_t discard[4096]; + while (chunk.size > sizeof(discard)) { + readExpect(discard, sizeof(discard), "chunk data"); + chunk.size -= sizeof(discard); + } + if (chunk.size) readExpect(discard, chunk.size, "chunk data"); + readCrc(); +} + +static struct PACKED { + uint32_t width; + uint32_t height; + uint8_t depth; + enum PACKED { + Grayscale = 0, + Truecolor = 2, + Indexed = 3, + GrayscaleAlpha = 4, + TruecolorAlpha = 6, + } color; + enum PACKED { Deflate } compression; + enum PACKED { Adaptive } filter; + enum PACKED { Progressive, Adam7 } interlace; +} header; +_Static_assert(13 == sizeof(header), "header size"); + +static size_t pixelBits(void) { + switch (header.color) { + case Grayscale: return 1 * header.depth; + case Truecolor: return 3 * header.depth; + case Indexed: return 1 * header.depth; + case GrayscaleAlpha: return 2 * header.depth; + case TruecolorAlpha: return 4 * header.depth; + default: abort(); + } +} + +static size_t pixelSize(void) { + return (pixelBits() + 7) / 8; +} + +static size_t lineSize(void) { + return (header.width * pixelBits() + 7) / 8; +} + +static size_t dataSize(void) { + return (1 + lineSize()) * header.height; +} + +static const char *ColorStr[] = { + [Grayscale] = "grayscale", + [Truecolor] = "truecolor", + [Indexed] = "indexed", + [GrayscaleAlpha] = "grayscale alpha", + [TruecolorAlpha] = "truecolor alpha", +}; +static void printHeader(void) { + fprintf( + stderr, + "%s: %ux%u %hhu-bit %s\n", + path, + header.width, header.height, + header.depth, ColorStr[header.color] + ); +} + +static void readHeader(struct Chunk chunk) { + if (chunk.size != sizeof(header)) { + errx( + EX_DATAERR, "%s: expected IHDR size %zu, found %u", + path, sizeof(header), chunk.size + ); + } + readExpect(&header, sizeof(header), "header"); + readCrc(); + + header.width = ntohl(header.width); + header.height = ntohl(header.height); + + if (!header.width) errx(EX_DATAERR, "%s: invalid width 0", path); + if (!header.height) errx(EX_DATAERR, "%s: invalid height 0", path); + switch (PAIR(header.color, header.depth)) { + case PAIR(Grayscale, 1): + case PAIR(Grayscale, 2): + case PAIR(Grayscale, 4): + case PAIR(Grayscale, 8): + case PAIR(Grayscale, 16): + case PAIR(Truecolor, 8): + case PAIR(Truecolor, 16): + case PAIR(Indexed, 1): + case PAIR(Indexed, 2): + case PAIR(Indexed, 4): + case PAIR(Indexed, 8): + case PAIR(GrayscaleAlpha, 8): + case PAIR(GrayscaleAlpha, 16): + case PAIR(TruecolorAlpha, 8): + case PAIR(TruecolorAlpha, 16): + break; + default: + errx( + EX_DATAERR, "%s: invalid color type %hhu and bit depth %hhu", + path, header.color, header.depth + ); + } + if (header.compression != Deflate) { + errx( + EX_DATAERR, "%s: invalid compression method %hhu", + path, header.compression + ); + } + if (header.filter != Adaptive) { + errx(EX_DATAERR, "%s: invalid filter method %hhu", path, header.filter); + } + if (header.interlace > Adam7) { + errx(EX_DATAERR, "%s: invalid interlace method %hhu", path, header.interlace); + } + + if (verbose) printHeader(); +} + +static void writeHeader(void) { + if (verbose) printHeader(); + + struct Chunk ihdr = { .size = sizeof(header), .type = "IHDR" }; + writeChunk(ihdr); + header.width = htonl(header.width); + header.height = htonl(header.height); + writeExpect(&header, sizeof(header)); + writeCrc(); + + header.width = ntohl(header.width); + header.height = ntohl(header.height); +} + +static struct { + uint32_t len; + uint8_t entries[256][3]; +} palette; + +static struct { + uint32_t len; + uint8_t alpha[256]; +} trans; + +static void paletteClear(void) { + palette.len = 0; + trans.len = 0; +} + +static uint32_t paletteIndex(bool alpha, const uint8_t *rgba) { + uint32_t i; + for (i = 0; i < palette.len; ++i) { + if (alpha && i < trans.len && trans.alpha[i] != rgba[3]) continue; + if (0 == memcmp(palette.entries[i], rgba, 3)) break; + } + return i; +} + +static bool paletteAdd(bool alpha, const uint8_t *rgba) { + uint32_t i = paletteIndex(alpha, rgba); + if (i < palette.len) return true; + if (i == 256) return false; + memcpy(palette.entries[i], rgba, 3); + palette.len++; + if (alpha) { + trans.alpha[i] = rgba[3]; + trans.len++; + } + return true; +} + +static void transCompact(void) { + uint32_t i; + for (i = 0; i < trans.len; ++i) { + if (trans.alpha[i] == 0xFF) break; + } + if (i == trans.len) return; + + for (uint32_t j = i + 1; j < trans.len; ++j) { + if (trans.alpha[j] == 0xFF) continue; + + uint8_t alpha = trans.alpha[i]; + trans.alpha[i] = trans.alpha[j]; + trans.alpha[j] = alpha; + + uint8_t rgb[3]; + memcpy(rgb, palette.entries[i], 3); + memcpy(palette.entries[i], palette.entries[j], 3); + memcpy(palette.entries[j], rgb, 3); + + i++; + } + trans.len = i; +} + +static void readPalette(struct Chunk chunk) { + if (chunk.size % 3) { + errx(EX_DATAERR, "%s: PLTE size %u not divisible by 3", path, chunk.size); + } + + palette.len = chunk.size / 3; + if (palette.len > 256) { + errx(EX_DATAERR, "%s: PLTE length %u > 256", path, palette.len); + } + + readExpect(palette.entries, chunk.size, "palette data"); + readCrc(); + + if (verbose) fprintf(stderr, "%s: palette length %u\n", path, palette.len); +} + +static void writePalette(void) { + if (verbose) fprintf(stderr, "%s: palette length %u\n", path, palette.len); + struct Chunk plte = { .size = 3 * palette.len, .type = "PLTE" }; + writeChunk(plte); + writeExpect(palette.entries, plte.size); + writeCrc(); +} + +static void readTrans(struct Chunk chunk) { + trans.len = chunk.size; + if (trans.len > 256) { + errx(EX_DATAERR, "%s: tRNS length %u > 256", path, trans.len); + } + readExpect(trans.alpha, chunk.size, "transparency alpha"); + readCrc(); + if (verbose) fprintf(stderr, "%s: transparency length %u\n", path, trans.len); +} + +static void writeTrans(void) { + if (verbose) fprintf(stderr, "%s: transparency length %u\n", path, trans.len); + struct Chunk trns = { .size = trans.len, .type = "tRNS" }; + writeChunk(trns); + writeExpect(trans.alpha, trns.size); + writeCrc(); +} + +static uint8_t *data; + +static void allocData(void) { + data = malloc(dataSize()); + if (!data) err(EX_OSERR, "malloc(%zu)", dataSize()); +} + +static void readData(struct Chunk chunk) { + if (verbose) fprintf(stderr, "%s: data size %zu\n", path, dataSize()); + + struct z_stream_s stream = { .next_out = data, .avail_out = dataSize() }; + int error = inflateInit(&stream); + if (error != Z_OK) errx(EX_SOFTWARE, "%s: inflateInit: %s", path, stream.msg); + + for (;;) { + if (0 != memcmp(chunk.type, "IDAT", 4)) { + errx(EX_DATAERR, "%s: missing IDAT chunk", path); + } + + uint8_t *idat = malloc(chunk.size); + if (!idat) err(EX_OSERR, "malloc"); + + readExpect(idat, chunk.size, "image data"); + readCrc(); + + stream.next_in = idat; + stream.avail_in = chunk.size; + int error = inflate(&stream, Z_SYNC_FLUSH); + free(idat); + + if (error == Z_STREAM_END) break; + if (error != Z_OK) { + errx(EX_DATAERR, "%s: inflate: %s", path, stream.msg); + } + + chunk = readChunk(); + } + + inflateEnd(&stream); + if ((size_t)stream.total_out != dataSize()) { + errx( + EX_DATAERR, "%s: expected data size %zu, found %zu", + path, dataSize(), (size_t)stream.total_out + ); + } + + if (verbose) { + fprintf( + stderr, "%s: deflate size %zu\n", path, (size_t)stream.total_in + ); + } +} + +static void writeData(void) { + if (verbose) fprintf(stderr, "%s: data size %zu\n", path, dataSize()); + + uLong size = compressBound(dataSize()); + uint8_t *deflate = malloc(size); + if (!deflate) err(EX_OSERR, "malloc"); + + int error = compress2(deflate, &size, data, dataSize(), Z_BEST_COMPRESSION); + if (error != Z_OK) errx(EX_SOFTWARE, "%s: compress2: %d", path, error); + + struct Chunk idat = { .size = size, .type = "IDAT" }; + writeChunk(idat); + writeExpect(deflate, size); + writeCrc(); + + free(deflate); + + if (verbose) fprintf(stderr, "%s: deflate size %lu\n", path, size); +} + +static void writeEnd(void) { + struct Chunk iend = { .size = 0, .type = "IEND" }; + writeChunk(iend); + writeCrc(); +} + +enum PACKED Filter { + None, + Sub, + Up, + Average, + Paeth, + FilterCount, +}; + +struct Bytes { + uint8_t x; + uint8_t a; + uint8_t b; + uint8_t c; +}; + +static uint8_t paethPredictor(struct Bytes f) { + int32_t p = (int32_t)f.a + (int32_t)f.b - (int32_t)f.c; + int32_t pa = abs(p - (int32_t)f.a); + int32_t pb = abs(p - (int32_t)f.b); + int32_t pc = abs(p - (int32_t)f.c); + if (pa <= pb && pa <= pc) return f.a; + if (pb <= pc) return f.b; + return f.c; +} + +static uint8_t recon(enum Filter type, struct Bytes f) { + switch (type) { + case None: return f.x; + case Sub: return f.x + f.a; + case Up: return f.x + f.b; + case Average: return f.x + ((uint32_t)f.a + (uint32_t)f.b) / 2; + case Paeth: return f.x + paethPredictor(f); + default: abort(); + } +} + +static uint8_t filt(enum Filter type, struct Bytes f) { + switch (type) { + case None: return f.x; + case Sub: return f.x - f.a; + case Up: return f.x - f.b; + case Average: return f.x - ((uint32_t)f.a + (uint32_t)f.b) / 2; + case Paeth: return f.x - paethPredictor(f); + default: abort(); + } +} + +static struct Line { + enum Filter type; + uint8_t data[]; +} **lines; + +static void allocLines(void) { + lines = calloc(header.height, sizeof(*lines)); + if (!lines) err(EX_OSERR, "calloc(%u, %zu)", header.height, sizeof(*lines)); +} + +static void scanlines(void) { + size_t stride = 1 + lineSize(); + for (uint32_t y = 0; y < header.height; ++y) { + lines[y] = (struct Line *)&data[y * stride]; + if (lines[y]->type >= FilterCount) { + errx(EX_DATAERR, "%s: invalid filter type %hhu", path, lines[y]->type); + } + } +} + +static struct Bytes origBytes(uint32_t y, size_t i) { + bool a = (i >= pixelSize()), b = (y > 0), c = (a && b); + return (struct Bytes) { + .x = lines[y]->data[i], + .a = a ? lines[y]->data[i - pixelSize()] : 0, + .b = b ? lines[y - 1]->data[i] : 0, + .c = c ? lines[y - 1]->data[i - pixelSize()] : 0, + }; +} + +static void reconData(void) { + for (uint32_t y = 0; y < header.height; ++y) { + for (size_t i = 0; i < lineSize(); ++i) { + lines[y]->data[i] = + recon(lines[y]->type, origBytes(y, i)); + } + lines[y]->type = None; + } +} + +static void filterData(void) { + if (header.color == Indexed || header.depth < 8) return; + for (uint32_t y = header.height - 1; y < header.height; --y) { + uint8_t filter[FilterCount][lineSize()]; + uint32_t heuristic[FilterCount] = {0}; + enum Filter minType = None; + for (enum Filter type = None; type < FilterCount; ++type) { + for (size_t i = 0; i < lineSize(); ++i) { + filter[type][i] = filt(type, origBytes(y, i)); + heuristic[type] += abs((int8_t)filter[type][i]); + } + if (heuristic[type] < heuristic[minType]) minType = type; + } + lines[y]->type = minType; + memcpy(lines[y]->data, filter[minType], lineSize()); + } +} + +static void discardAlpha(void) { + if (header.color != GrayscaleAlpha && header.color != TruecolorAlpha) return; + size_t sampleSize = header.depth / 8; + size_t colorSize = pixelSize() - sampleSize; + for (uint32_t y = 0; y < header.height; ++y) { + for (uint32_t x = 0; x < header.width; ++x) { + for (size_t i = 0; i < sampleSize; ++i) { + if (lines[y]->data[x * pixelSize() + colorSize + i] != 0xFF) return; + } + } + } + + uint8_t *ptr = data; + for (uint32_t y = 0; y < header.height; ++y) { + *ptr++ = lines[y]->type; + for (uint32_t x = 0; x < header.width; ++x) { + memmove(ptr, &lines[y]->data[x * pixelSize()], colorSize); + ptr += colorSize; + } + } + header.color = (header.color == GrayscaleAlpha) ? Grayscale : Truecolor; + scanlines(); +} + +static void discardColor(void) { + if (header.color != Truecolor && header.color != TruecolorAlpha) return; + size_t sampleSize = header.depth / 8; + for (uint32_t y = 0; y < header.height; ++y) { + for (uint32_t x = 0; x < header.width; ++x) { + uint8_t *r = &lines[y]->data[x * pixelSize()]; + uint8_t *g = r + sampleSize; + uint8_t *b = g + sampleSize; + if (0 != memcmp(r, g, sampleSize)) return; + if (0 != memcmp(g, b, sampleSize)) return; + } + } + + uint8_t *ptr = data; + for (uint32_t y = 0; y < header.height; ++y) { + *ptr++ = lines[y]->type; + for (uint32_t x = 0; x < header.width; ++x) { + uint8_t *pixel = &lines[y]->data[x * pixelSize()]; + memmove(ptr, pixel, sampleSize); + ptr += sampleSize; + if (header.color == TruecolorAlpha) { + memmove(ptr, pixel + 3 * sampleSize, sampleSize); + ptr += sampleSize; + } + } + } + header.color = (header.color == Truecolor) ? Grayscale : GrayscaleAlpha; + scanlines(); +} + +static void indexColor(void) { + if (header.color != Truecolor && header.color != TruecolorAlpha) return; + if (header.depth != 8) return; + bool alpha = (header.color == TruecolorAlpha); + for (uint32_t y = 0; y < header.height; ++y) { + for (uint32_t x = 0; x < header.width; ++x) { + if (!paletteAdd(alpha, &lines[y]->data[x * pixelSize()])) return; + } + } + transCompact(); + + uint8_t *ptr = data; + for (uint32_t y = 0; y < header.height; ++y) { + *ptr++ = lines[y]->type; + for (uint32_t x = 0; x < header.width; ++x) { + *ptr++ = paletteIndex(alpha, &lines[y]->data[x * pixelSize()]); + } + } + header.color = Indexed; + scanlines(); +} + +static void reduceDepth8(void) { + if (header.color != Grayscale && header.color != Indexed) return; + if (header.depth != 8) return; + if (header.color == Grayscale) { + for (uint32_t y = 0; y < header.height; ++y) { + for (size_t i = 0; i < lineSize(); ++i) { + uint8_t a = lines[y]->data[i]; + if ((a >> 4) != (a & 0x0F)) return; + } + } + } else if (palette.len > 16) { + return; + } + + uint8_t *ptr = data; + for (uint32_t y = 0; y < header.height; ++y) { + *ptr++ = lines[y]->type; + for (size_t i = 0; i < lineSize(); i += 2) { + uint8_t iByte = lines[y]->data[i]; + uint8_t jByte = (i + 1 < lineSize()) ? lines[y]->data[i + 1] : 0; + uint8_t a = iByte & 0x0F; + uint8_t b = jByte & 0x0F; + *ptr++ = a << 4 | b; + } + } + header.depth = 4; + scanlines(); +} + +static void reduceDepth4(void) { + if (header.depth != 4) return; + if (header.color == Grayscale) { + for (uint32_t y = 0; y < header.height; ++y) { + for (size_t i = 0; i < lineSize(); ++i) { + uint8_t a = lines[y]->data[i] >> 4; + uint8_t b = lines[y]->data[i] & 0x0F; + if ((a >> 2) != (a & 0x03)) return; + if ((b >> 2) != (b & 0x03)) return; + } + } + } else if (palette.len > 4) { + return; + } + + uint8_t *ptr = data; + for (uint32_t y = 0; y < header.height; ++y) { + *ptr++ = lines[y]->type; + for (size_t i = 0; i < lineSize(); i += 2) { + uint8_t iByte = lines[y]->data[i]; + uint8_t jByte = (i + 1 < lineSize()) ? lines[y]->data[i + 1] : 0; + uint8_t a = iByte >> 4 & 0x03, b = iByte & 0x03; + uint8_t c = jByte >> 4 & 0x03, d = jByte & 0x03; + *ptr++ = a << 6 | b << 4 | c << 2 | d; + } + } + header.depth = 2; + scanlines(); +} + +static void reduceDepth2(void) { + if (header.depth != 2) return; + if (header.color == Grayscale) { + for (uint32_t y = 0; y < header.height; ++y) { + for (size_t i = 0; i < lineSize(); ++i) { + uint8_t a = lines[y]->data[i] >> 6; + uint8_t b = lines[y]->data[i] >> 4 & 0x03; + uint8_t c = lines[y]->data[i] >> 2 & 0x03; + uint8_t d = lines[y]->data[i] & 0x03; + if ((a >> 1) != (a & 0x01)) return; + if ((b >> 1) != (b & 0x01)) return; + if ((c >> 1) != (c & 0x01)) return; + if ((d >> 1) != (d & 0x01)) return; + } + } + } else if (palette.len > 2) { + return; + } + + uint8_t *ptr = data; + for (uint32_t y = 0; y < header.height; ++y) { + *ptr++ = lines[y]->type; + for (size_t i = 0; i < lineSize(); i += 2) { + uint8_t iByte = lines[y]->data[i]; + uint8_t jByte = (i + 1 < lineSize()) ? lines[y]->data[i + 1] : 0; + uint8_t a = iByte >> 6 & 0x01, b = iByte >> 4 & 0x01; + uint8_t c = iByte >> 2 & 0x01, d = iByte & 0x01; + uint8_t e = jByte >> 6 & 0x01, f = jByte >> 4 & 0x01; + uint8_t g = jByte >> 2 & 0x01, h = jByte & 0x01; + *ptr++ = a << 7 | b << 6 | c << 5 | d << 4 | e << 3 | f << 2 | g << 1 | h; + } + } + header.depth = 1; + scanlines(); +} + +static void reduceDepth(void) { + reduceDepth8(); + reduceDepth4(); + reduceDepth2(); +} + +static void optimize(const char *inPath, const char *outPath) { + if (inPath) { + path = inPath; + file = fopen(path, "r"); + if (!file) err(EX_NOINPUT, "%s", path); + } else { + path = "(stdin)"; + file = stdin; + } + + readSignature(); + struct Chunk ihdr = readChunk(); + if (0 != memcmp(ihdr.type, "IHDR", 4)) { + errx(EX_DATAERR, "%s: expected IHDR, found %s", path, typeStr(ihdr)); + } + readHeader(ihdr); + if (header.interlace != Progressive) { + errx( + EX_CONFIG, "%s: unsupported interlace method %hhu", + path, header.interlace + ); + } + + paletteClear(); + allocData(); + for (;;) { + struct Chunk chunk = readChunk(); + if (0 == memcmp(chunk.type, "PLTE", 4)) { + readPalette(chunk); + } else if (0 == memcmp(chunk.type, "tRNS", 4)) { + readTrans(chunk); + } else if (0 == memcmp(chunk.type, "IDAT", 4)) { + readData(chunk); + } else if (0 != memcmp(chunk.type, "IEND", 4)) { + skipChunk(chunk); + } else { + break; + } + } + + fclose(file); + + allocLines(); + scanlines(); + reconData(); + + discardAlpha(); + discardColor(); + indexColor(); + reduceDepth(); + filterData(); + free(lines); + + if (outPath) { + path = outPath; + file = fopen(path, "w"); + if (!file) err(EX_CANTCREAT, "%s", path); + } else { + path = "(stdout)"; + file = stdout; + } + + writeSignature(); + writeHeader(); + if (header.color == Indexed) { + writePalette(); + if (trans.len) writeTrans(); + } + writeData(); + writeEnd(); + free(data); + + int error = fclose(file); + if (error) err(EX_IOERR, "%s", path); +} + +int main(int argc, char *argv[]) { + bool stdio = false; + char *output = NULL; + + int opt; + while (0 < (opt = getopt(argc, argv, "co:v"))) { + switch (opt) { + break; case 'c': stdio = true; + break; case 'o': output = optarg; + break; case 'v': verbose = true; + break; default: return EX_USAGE; + } + } + + if (argc - optind == 1 && (output || stdio)) { + optimize(argv[optind], output); + } else if (optind < argc) { + for (int i = optind; i < argc; ++i) { + optimize(argv[i], argv[i]); + } + } else { + optimize(NULL, output); + } + + return EX_OK; +} diff --git a/bin/psf2png.c b/bin/psf2png.c new file mode 100644 index 00000000..1aaa8635 --- /dev/null +++ b/bin/psf2png.c @@ -0,0 +1,107 @@ +/* Copyright (C) 2018 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include "png.h" + +int main(int argc, char *argv[]) { + uint32_t cols = 32; + const char *str = NULL; + uint32_t fg = 0xFFFFFF; + uint32_t bg = 0x000000; + + int opt; + while (0 < (opt = getopt(argc, argv, "b:c:f:s:"))) { + switch (opt) { + break; case 'b': bg = strtoul(optarg, NULL, 16); + break; case 'c': cols = strtoul(optarg, NULL, 0); + break; case 'f': fg = strtoul(optarg, NULL, 16); + break; case 's': str = optarg; + break; default: return EX_USAGE; + } + } + if (!cols && str) cols = strlen(str); + if (!cols) return EX_USAGE; + + const char *path = NULL; + if (optind < argc) path = argv[optind]; + + FILE *file = path ? fopen(path, "r") : stdin; + if (!file) err(EX_NOINPUT, "%s", path); + if (!path) path = "(stdin)"; + + struct { + uint32_t magic; + uint32_t version; + uint32_t size; + uint32_t flags; + struct { + uint32_t len; + uint32_t size; + uint32_t height; + uint32_t width; + } glyph; + } header; + size_t len = fread(&header, sizeof(header), 1, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + if (len < 1) errx(EX_DATAERR, "%s: truncated header", path); + + uint32_t widthBytes = (header.glyph.width + 7) / 8; + uint8_t glyphs[header.glyph.len][header.glyph.height][widthBytes]; + len = fread(glyphs, header.glyph.size, header.glyph.len, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + if (len < header.glyph.len) { + errx(EX_DATAERR, "%s: truncated glyphs", path); + } + fclose(file); + + uint32_t count = (str ? strlen(str) : header.glyph.len); + uint32_t width = header.glyph.width * cols; + uint32_t rows = (count + cols - 1) / cols; + uint32_t height = header.glyph.height * rows; + + pngHead(stdout, width, height, 8, PNGIndexed); + uint8_t pal[] = { + bg >> 16, bg >> 8, bg, + fg >> 16, fg >> 8, fg, + }; + pngPalette(stdout, pal, sizeof(pal)); + + uint8_t data[height][1 + width]; + memset(data, PNGNone, sizeof(data)); + + for (uint32_t i = 0; i < count; ++i) { + uint32_t row = header.glyph.height * (i / cols); + uint32_t col = 1 + header.glyph.width * (i % cols); + uint32_t g = (str ? str[i] : i); + for (uint32_t y = 0; y < header.glyph.height; ++y) { + for (uint32_t x = 0; x < header.glyph.width; ++x) { + uint8_t bit = glyphs[g][y][x / 8] >> (7 - x % 8) & 1; + data[row + y][col + x] = bit; + } + } + } + + pngData(stdout, (uint8_t *)data, sizeof(data)); + pngTail(stdout); +} diff --git a/bin/psfed.c b/bin/psfed.c new file mode 100644 index 00000000..4f46b500 --- /dev/null +++ b/bin/psfed.c @@ -0,0 +1,577 @@ +/* Copyright (C) 2018 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/fb.h> +#include <locale.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sysexits.h> +#include <termios.h> +#include <unistd.h> +#include <wchar.h> + +static const wchar_t CP437[256] = + L"\0☺☻♥♦♣♠•◘○◙♂♀♪♫☼" + L"►◄↕‼¶§▬↨↑↓→←∟↔▲▼" + L" !\"#$%&'()*+,-./" + L"0123456789:;<=>?" + L"@ABCDEFGHIJKLMNO" + L"PQRSTUVWXYZ[\\]^_" + L"`abcdefghijklmno" + L"pqrstuvwxyz{|}~⌂" + L"ÇüéâäàåçêëèïîìÄÅ" + L"ÉæÆôöòûùÿÖÜ¢£¥₧ƒ" + L"áíóúñѪº¿⌐¬½¼¡«»" + L"░▒▓│┤╡╢╖╕╣║╗╝╜╛┐" + L"└┴┬├─┼╞╟╚╔╩╦╠═╬╧" + L"╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀" + L"αßΓπΣσµτΦΘΩδ∞φε∩" + L"≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\0"; + +static struct { + uint32_t width; + uint32_t height; + uint32_t *buffer; + uint32_t background; +} frame; + +static void frameClear(void) { + for (uint32_t i = 0; i < frame.width * frame.height; ++i) { + frame.buffer[i] = frame.background; + } +} + +static void frameOpen(void) { + const char *dev = getenv("FRAMEBUFFER"); + if (!dev) dev = "/dev/fb0"; + + int fd = open(dev, O_RDWR); + if (fd < 0) err(EX_OSFILE, "%s", dev); + + struct fb_var_screeninfo info; + int error = ioctl(fd, FBIOGET_VSCREENINFO, &info); + if (error) err(EX_IOERR, "%s", dev); + + frame.width = info.xres; + frame.height = 3 * info.yres / 4; + frame.buffer = mmap( + NULL, sizeof(*frame.buffer) * frame.width * frame.height, + PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0 + ); + if (frame.buffer == MAP_FAILED) err(EX_IOERR, "%s", dev); + close(fd); + + frame.background = frame.buffer[0]; + atexit(frameClear); +} + +static const uint32_t Magic = 0x864AB572; +static const uint32_t Version = 0; +static const uint32_t FlagUnicode = 1 << 0; +static uint32_t bytes(uint32_t bits) { + return (bits + 7) / 8; +} + +static char *path; +static struct { + uint32_t magic; + uint32_t version; + uint32_t size; + uint32_t flags; + struct { + uint32_t len; + uint32_t size; + uint32_t height; + uint32_t width; + } glyph; +} header; +static uint8_t *glyphs; + +static void fileRead(uint32_t newLen, uint32_t newWidth, uint32_t newHeight) { + FILE *file = fopen(path, "r"); + if (file) { + size_t len = fread(&header, sizeof(header), 1, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + if (len < 1) errx(EX_DATAERR, "%s: truncated header", path); + + } else { + if (errno != ENOENT) err(EX_NOINPUT, "%s", path); + header.magic = Magic; + header.version = Version; + header.size = sizeof(header); + header.flags = 0; + header.glyph.len = newLen; + header.glyph.size = bytes(newWidth) * newHeight; + header.glyph.height = newHeight; + header.glyph.width = newWidth; + } + + if (header.magic != Magic) { + errx(EX_DATAERR, "%s: invalid magic %08X", path, header.magic); + } + if (header.version != Version) { + errx(EX_DATAERR, "%s: unsupported version %u", path, header.version); + } + if (header.flags & FlagUnicode) { + errx(EX_DATAERR, "%s: unsupported unicode table", path); + } + if (header.flags) { + errx(EX_DATAERR, "%s: unsupported flags %08X", path, header.flags); + } + + if (file && header.size > sizeof(header)) { + int error = fseek(file, header.size, SEEK_SET); + if (error) err(EX_IOERR, "%s", path); + + warnx("%s: truncating long header", path); + header.size = sizeof(header); + } + + glyphs = calloc(header.glyph.len, header.glyph.size); + if (!glyphs) err(EX_OSERR, "calloc"); + + if (file) { + size_t len = fread(glyphs, header.glyph.size, header.glyph.len, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + if (len < header.glyph.len) { + errx(EX_DATAERR, "%s: truncated glyphs", path); + } + fclose(file); + } +} + +static void fileWrite(void) { + FILE *file = fopen(path, "w"); + if (!file) err(EX_CANTCREAT, "%s", path); + + fwrite(&header, sizeof(header), 1, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + + fwrite(glyphs, header.glyph.size, header.glyph.len, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + + int error = fclose(file); + if (error) err(EX_IOERR, "%s", path); +} + +static uint8_t *glyph(uint32_t index) { + return &glyphs[header.glyph.size * index]; +} +static uint8_t *bitByte(uint32_t index, uint32_t x, uint32_t y) { + return &glyph(index)[bytes(header.glyph.width) * y + x / 8]; +} +static uint8_t bitGet(uint32_t index, uint32_t x, uint32_t y) { + return *bitByte(index, x, y) >> (7 - x % 8) & 1; +} +static void bitFlip(uint32_t index, uint32_t x, uint32_t y) { + *bitByte(index, x, y) ^= 1 << (7 - x % 8); +} +static void bitSet(uint32_t index, uint32_t x, uint32_t y, uint8_t bit) { + *bitByte(index, x, y) &= ~(1 << (7 - x % 8)); + *bitByte(index, x, y) |= bit << (7 - x % 8); +} + +static void drawGlyph( + uint32_t destX, uint32_t destY, uint32_t scale, uint32_t index, + uint32_t selectX, uint32_t selectY, uint32_t guideX, uint32_t guideY +) { + destX <<= scale; + destY <<= scale; + + for (uint32_t y = 0; y < (header.glyph.height << scale); ++y) { + if (destY + y >= frame.height) break; + for (uint32_t x = 0; x < (header.glyph.width << scale); ++x) { + if (destX + x >= frame.width) break; + + uint32_t glyphX = x >> scale; + uint32_t glyphY = y >> scale; + uint32_t fill = -bitGet(index, glyphX, glyphY); + if (selectX & 1 << glyphX && selectY & 1 << glyphY) fill ^= 0x77; + if (guideX & 1 << glyphX || guideY & 1 << glyphY) fill ^= 0x3300; + + frame.buffer[frame.width * (destY + y) + destX + x] = fill; + } + } +} + +static void drawBorder(uint32_t destX, uint32_t destY, uint32_t scale) { + destX <<= scale; + destY <<= scale; + + for (uint32_t y = 0; y < destY; ++y) { + if (y >= frame.height) break; + uint32_t fill = -(y >> scale & 1) ^ 0x555555; + for (uint32_t x = 0; x < (uint32_t)(1 << scale); ++x) { + if (destX + x >= frame.width) break; + frame.buffer[frame.width * y + destX + x] = fill; + } + } + + for (uint32_t x = 0; x < destX; ++x) { + if (x >= frame.width) break; + uint32_t fill = -(x >> scale & 1) ^ 0x555555; + for (uint32_t y = 0; y < (uint32_t)(1 << scale); ++y) { + if (destY + y >= frame.height) break; + frame.buffer[frame.width * (destY + y) + x] = fill; + } + } +} + +enum { LF = '\n', Esc = '\33', Del = '\177' }; + +static enum { + Normal, + Edit, + Preview, + Discard, +} mode; + +static struct { + uint32_t scale; + uint32_t index; + bool modified; + bool to; + uint32_t from; +} normal; + +static struct { + uint32_t scale; + uint32_t index; + uint32_t x; + uint32_t y; + uint32_t guideX; + uint32_t guideY; + uint8_t *undo; + uint8_t *copy; +} edit = { + .scale = 4, +}; + +static const uint32_t NormalCols = 32; +static void drawNormal(void) { + for (uint32_t i = 0; i < header.glyph.len; ++i) { + drawGlyph( + header.glyph.width * (i % NormalCols), + header.glyph.height * (i / NormalCols), + normal.scale, i, + -(i == normal.index), -(i == normal.index), 0, 0 + ); + } +} + +static void normalDec(uint32_t n) { + if (normal.index >= n) normal.index -= n; +} +static void normalInc(uint32_t n) { + if (normal.index + n < header.glyph.len) normal.index += n; +} +static void normalPrint(const char *prefix) { + if (normal.index <= 256) { + printf( + "%s: %02X '%lc'\n", + prefix, normal.index, (wint_t)CP437[normal.index] + ); + } else { + printf("%s: %02X\n", prefix, normal.index); + } +} + +static void inputNormal(char ch) { + if (normal.to) { + if (ch < header.glyph.len) normal.index = ch; + normalPrint("index"); + normal.to = false; + return; + } + + switch (ch) { + break; case 'q': { + if (!normal.modified) exit(EX_OK); + mode = Discard; + } + break; case 'w': { + fileWrite(); + printf("write: %s\n", path); + normal.modified = false; + } + break; case '-': if (normal.scale) normal.scale--; frameClear(); + break; case '+': normal.scale++; + break; case 'h': normalDec(1); normalPrint("index"); + break; case 'l': normalInc(1); normalPrint("index"); + break; case 'k': normalDec(NormalCols); normalPrint("index"); + break; case 'j': normalInc(NormalCols); normalPrint("index"); + break; case 'f': normal.from = normal.index; normal.to = true; + break; case 047: normal.index = normal.from; normalPrint("index"); + break; case 'y': { + if (!edit.copy) edit.copy = malloc(header.glyph.size); + if (!edit.copy) err(EX_OSERR, "malloc"); + memcpy(edit.copy, glyph(normal.index), header.glyph.size); + normalPrint("copy"); + } + break; case 'e': { + normal.modified = true; + edit.index = normal.index; + if (!edit.undo) edit.undo = malloc(header.glyph.size); + if (!edit.undo) err(EX_OSERR, "malloc"); + memcpy(edit.undo, glyph(edit.index), header.glyph.size); + mode = Edit; + frameClear(); + } + break; case 'i': mode = Preview; frameClear(); + } +} + +static void drawEdit(void) { + drawGlyph( + 0, 0, edit.scale, edit.index, + 1 << edit.x, 1 << edit.y, edit.guideX, edit.guideY + ); + drawBorder(header.glyph.width, header.glyph.height, edit.scale); + drawGlyph( + header.glyph.width << edit.scale, + header.glyph.height << edit.scale, + 0, edit.index, + 0, 0, 0, 0 + ); +} + +static void inputEdit(char ch) { + switch (ch) { + break; case Esc: mode = Normal; frameClear(); + + break; case '-': if (edit.scale) edit.scale--; frameClear(); + break; case '+': edit.scale++; + break; case 'g': edit.guideY ^= 1 << edit.y; + break; case 'G': edit.guideX ^= 1 << edit.x; + + break; case 'h': if (edit.x) edit.x--; + break; case 'l': if (edit.x + 1 < header.glyph.width) edit.x++; + break; case 'k': if (edit.y) edit.y--; + break; case 'j': if (edit.y + 1 < header.glyph.height) edit.y++; + break; case ' ': bitFlip(edit.index, edit.x, edit.y); + + break; case 'r': { + for (uint32_t y = 0; y < header.glyph.height; ++y) { + for (uint32_t x = 0; x < header.glyph.width; ++x) { + bitFlip(edit.index, x, y); + } + } + } + + break; case 'H': { + for (uint32_t x = 0; x < header.glyph.width; ++x) { + for (uint32_t y = 0; y < header.glyph.height; ++y) { + if (x + 1 < header.glyph.width) { + bitSet(edit.index, x, y, bitGet(edit.index, x + 1, y)); + } else { + bitSet(edit.index, x, y, 0); + } + } + } + } + + break; case 'L': { + uint32_t width = header.glyph.width; + for (uint32_t x = width - 1; x < width; --x) { + for (uint32_t y = 0; y < header.glyph.height; ++y) { + if (x - 1 < width) { + bitSet(edit.index, x, y, bitGet(edit.index, x - 1, y)); + } else { + bitSet(edit.index, x, y, 0); + } + } + } + } + + break; case 'K': { + for (uint32_t y = 0; y < header.glyph.height; ++y) { + for (uint32_t x = 0; x < header.glyph.width; ++x) { + if (y + 1 < header.glyph.height) { + bitSet(edit.index, x, y, bitGet(edit.index, x, y + 1)); + } else { + bitSet(edit.index, x, y, 0); + } + } + } + } + + break; case 'J': { + uint32_t height = header.glyph.height; + for (uint32_t y = height - 1; y < height; --y) { + for (uint32_t x = 0; x < header.glyph.width; ++x) { + if (y - 1 < height) { + bitSet(edit.index, x, y, bitGet(edit.index, x, y - 1)); + } else { + bitSet(edit.index, x, y, 0); + } + } + } + } + + break; case 'p': { + if (!edit.copy) break; + memcpy(glyph(edit.index), edit.copy, header.glyph.size); + } + break; case 'u': { + if (!edit.undo) break; + memcpy(glyph(edit.index), edit.undo, header.glyph.size); + } + } +} + +enum { PreviewRows = 8, PreviewCols = 64 }; +static struct { + uint32_t glyphs[PreviewRows * PreviewCols]; + uint32_t index; +} preview; + +static void drawPreview(void) { + for (uint32_t i = 0; i < PreviewRows * PreviewCols; ++i) { + drawGlyph( + header.glyph.width * (i % PreviewCols), + header.glyph.height * (i / PreviewCols), + 0, preview.glyphs[i], + -(i == preview.index), -(i == preview.index), 0, 0 + ); + } +} + +static void inputPreview(char ch) { + switch (ch) { + break; case Esc: mode = Normal; frameClear(); + break; case Del: { + if (preview.index) preview.index--; + preview.glyphs[preview.index] = 0; + } + break; case LF: { + uint32_t tail = PreviewCols - (preview.index % PreviewCols); + memset( + &preview.glyphs[preview.index], + 0, sizeof(preview.glyphs[0]) * tail + ); + preview.index += tail; + } + break; default: preview.glyphs[preview.index++] = ch; + } + preview.index %= PreviewRows * PreviewCols; +} + +static void drawDiscard(void) { + printf("discard modifications? "); + fflush(stdout); +} + +static void inputDiscard(char ch) { + printf("%c\n", ch); + if (ch == 'Y' || ch == 'y') exit(EX_OK); + mode = Normal; +} + +static void draw(void) { + switch (mode) { + break; case Normal: drawNormal(); + break; case Edit: drawEdit(); + break; case Preview: drawPreview(); + break; case Discard: drawDiscard(); + } +} + +static void input(char ch) { + switch (mode) { + break; case Normal: inputNormal(ch); + break; case Edit: inputEdit(ch); + break; case Preview: inputPreview(ch); + break; case Discard: inputDiscard(ch); + } +} + +static struct termios saveTerm; +static void restoreTerm(void) { + tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm); +} + +int main(int argc, char *argv[]) { + setlocale(LC_CTYPE, ""); + + uint32_t newLen = 256; + uint32_t newWidth = 8; + uint32_t newHeight = 16; + uint32_t setHeight = 0; + + int opt; + while (0 < (opt = getopt(argc, argv, "H:g:h:w:"))) { + switch (opt) { + break; case 'H': setHeight = strtoul(optarg, NULL, 0); + break; case 'g': newLen = strtoul(optarg, NULL, 0); + break; case 'h': newHeight = strtoul(optarg, NULL, 0); + break; case 'w': newWidth = strtoul(optarg, NULL, 0); + break; default: return EX_USAGE; + } + } + if (!newLen || !newWidth || !newHeight) return EX_USAGE; + if (optind == argc) return EX_USAGE; + + path = strdup(argv[optind]); + fileRead(newLen, newWidth, newHeight); + + if (setHeight) { + if (setHeight < header.glyph.height) { + errx(EX_CONFIG, "cannot decrease height"); + } + + uint32_t setSize = bytes(header.glyph.width) * setHeight; + uint8_t *setGlyphs = calloc(header.glyph.len, setSize); + for (uint32_t i = 0; i < header.glyph.len; ++i) { + memcpy(&setGlyphs[setSize * i], glyph(i), header.glyph.size); + } + free(glyphs); + glyphs = setGlyphs; + + header.glyph.height = setHeight; + header.glyph.size = setSize; + normal.modified = true; + } + + frameOpen(); + frameClear(); + + int error = tcgetattr(STDIN_FILENO, &saveTerm); + if (error) err(EX_IOERR, "tcgetattr"); + atexit(restoreTerm); + + struct termios term = saveTerm; + term.c_lflag &= ~(ICANON | ECHO); + error = tcsetattr(STDIN_FILENO, TCSADRAIN, &term); + if (error) err(EX_IOERR, "tcsetattr"); + + for (;;) { + draw(); + char ch; + ssize_t size = read(STDIN_FILENO, &ch, 1); + if (size < 0) err(EX_IOERR, "read"); + if (!size) return EX_SOFTWARE; + input(ch); + } +} diff --git a/bin/ptee.c b/bin/ptee.c new file mode 100644 index 00000000..6a9a16b4 --- /dev/null +++ b/bin/ptee.c @@ -0,0 +1,116 @@ +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <poll.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <sysexits.h> +#include <termios.h> +#include <unistd.h> + +#if defined __FreeBSD__ +#include <libutil.h> +#elif defined __linux__ +#include <pty.h> +#else +#include <util.h> +#endif + +typedef unsigned char byte; + +static struct termios saveTerm; +static void restoreTerm(void) { + tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm); +} + +int main(int argc, char *argv[]) { + if (argc < 2) return EX_USAGE; + if (isatty(STDOUT_FILENO)) errx(EX_USAGE, "stdout is not redirected"); + + int error = tcgetattr(STDIN_FILENO, &saveTerm); + if (error) err(EX_IOERR, "tcgetattr"); + atexit(restoreTerm); + + struct termios raw = saveTerm; + cfmakeraw(&raw); + error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw); + if (error) err(EX_IOERR, "tcsetattr"); + + struct winsize window; + error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window); + if (error) err(EX_IOERR, "ioctl"); + + int pty; + pid_t pid = forkpty(&pty, NULL, NULL, &window); + if (pid < 0) err(EX_OSERR, "forkpty"); + + if (!pid) { + execvp(argv[1], &argv[1]); + err(EX_NOINPUT, "%s", argv[1]); + } + + bool stop = false; + + byte buf[4096]; + struct pollfd fds[2] = { + { .events = POLLIN, .fd = STDIN_FILENO }, + { .events = POLLIN, .fd = pty }, + }; + while (0 < poll(fds, 2, -1)) { + if (fds[0].revents & POLLIN) { + ssize_t rlen = read(STDIN_FILENO, buf, sizeof(buf)); + if (rlen < 0) err(EX_IOERR, "read"); + + if (rlen == 1 && buf[0] == CTRL('S')) { + stop ^= true; + continue; + } + + if (rlen == 1 && buf[0] == CTRL('Q')) { + char dump[] = "\x1B[10i"; + ssize_t wlen = write(STDOUT_FILENO, dump, sizeof(dump) - 1); + if (wlen < 0) err(EX_IOERR, "write"); + continue; + } + + ssize_t wlen = write(pty, buf, rlen); + if (wlen < 0) err(EX_IOERR, "write"); + } + + if (fds[1].revents & POLLIN) { + ssize_t rlen = read(pty, buf, sizeof(buf)); + if (rlen < 0) err(EX_IOERR, "read"); + + ssize_t wlen = write(STDIN_FILENO, buf, rlen); + if (wlen < 0) err(EX_IOERR, "write"); + + if (!stop) { + wlen = write(STDOUT_FILENO, buf, rlen); + if (wlen < 0) err(EX_IOERR, "write"); + } + } + + int status; + pid_t dead = waitpid(pid, &status, WNOHANG); + if (dead < 0) err(EX_OSERR, "waitpid"); + if (dead) return WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE; + } + err(EX_IOERR, "poll"); +} diff --git a/bin/relay.c b/bin/relay.c new file mode 100644 index 00000000..aa7913c9 --- /dev/null +++ b/bin/relay.c @@ -0,0 +1,218 @@ +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this Program, or any covered work, by linking or + * combining it with LibreSSL (or a modified version of that library), + * containing parts covered by the terms of the OpenSSL License and the + * original SSLeay license, the licensors of this Program grant you + * additional permission to convey the resulting work. Corresponding + * Source for a non-source form of such a combination shall include the + * source code for the parts of LibreSSL used as well as that of the + * covered work. + */ + +#include <err.h> +#include <netdb.h> +#include <netinet/in.h> +#include <poll.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sysexits.h> +#include <tls.h> +#include <unistd.h> + +#ifdef __FreeBSD__ +#include <sys/capsicum.h> +#endif + +static void clientWrite(struct tls *client, const char *ptr, size_t len) { + while (len) { + ssize_t ret = tls_write(client, ptr, len); + if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue; + if (ret < 0) errx(EX_IOERR, "tls_write: %s", tls_error(client)); + ptr += ret; + len -= ret; + } +} + +static void clientFormat(struct tls *client, const char *format, ...) { + char buf[1024]; + va_list ap; + va_start(ap, format); + int len = vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + if ((size_t)len > sizeof(buf) - 1) errx(EX_DATAERR, "message too large"); + clientWrite(client, buf, len); +} + +static void clientHandle(struct tls *client, const char *chan, char *line) { + char *prefix = NULL; + if (line[0] == ':') { + prefix = strsep(&line, " ") + 1; + if (!line) errx(EX_PROTOCOL, "unexpected eol"); + } + + char *command = strsep(&line, " "); + if (!strcmp(command, "001") || !strcmp(command, "INVITE")) { + clientFormat(client, "JOIN :%s\r\n", chan); + } else if (!strcmp(command, "PING")) { + clientFormat(client, "PONG %s\r\n", line); + } + if (strcmp(command, "PRIVMSG") && strcmp(command, "NOTICE")) return; + + if (!prefix) errx(EX_PROTOCOL, "message without prefix"); + char *nick = strsep(&prefix, "!"); + + if (!line) errx(EX_PROTOCOL, "message without destination"); + char *dest = strsep(&line, " "); + if (strcmp(dest, chan)) return; + + if (!line || line[0] != ':') errx(EX_PROTOCOL, "message without message"); + line = &line[1]; + + if (!strncmp(line, "\1ACTION ", 8)) { + line = &line[8]; + size_t len = strcspn(line, "\1"); + printf("* %c\u200C%s %.*s\n", nick[0], &nick[1], (int)len, line); + } else if (command[0] == 'N') { + printf("-%c\u200C%s- %s\n", nick[0], &nick[1], line); + } else { + printf("<%c\u200C%s> %s\n", nick[0], &nick[1], line); + } +} + +#ifdef __FreeBSD__ +static void limit(int fd, const cap_rights_t *rights) { + int error = cap_rights_limit(fd, rights); + if (error) err(EX_OSERR, "cap_rights_limit"); +} +#endif + +int main(int argc, char *argv[]) { + int error; + + if (argc < 5) return EX_USAGE; + const char *host = argv[1]; + const char *port = argv[2]; + const char *nick = argv[3]; + const char *chan = argv[4]; + + setlinebuf(stdout); + signal(SIGPIPE, SIG_IGN); + + struct tls_config *config = tls_config_new(); + if (!config) errx(EX_SOFTWARE, "tls_config_new"); + + error = tls_config_set_ciphers(config, "compat"); + if (error) { + errx(EX_SOFTWARE, "tls_config_set_ciphers: %s", tls_config_error(config)); + } + + struct tls *client = tls_client(); + if (!client) errx(EX_SOFTWARE, "tls_client"); + + error = tls_configure(client, config); + if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client)); + tls_config_free(config); + + struct addrinfo *head; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP, + }; + error = getaddrinfo(host, port, &hints, &head); + if (error) errx(EX_NOHOST, "getaddrinfo: %s", gai_strerror(error)); + + int sock = -1; + for (struct addrinfo *ai = head; ai; ai = ai->ai_next) { + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock < 0) err(EX_OSERR, "socket"); + + error = connect(sock, ai->ai_addr, ai->ai_addrlen); + if (!error) break; + + close(sock); + sock = -1; + } + if (sock < 0) err(EX_UNAVAILABLE, "connect"); + freeaddrinfo(head); + + error = tls_connect_socket(client, sock, host); + if (error) errx(EX_PROTOCOL, "tls_connect: %s", tls_error(client)); + +#ifdef __FreeBSD__ + error = cap_enter(); + if (error) err(EX_OSERR, "cap_enter"); + + cap_rights_t rights; + cap_rights_init(&rights, CAP_WRITE); + limit(STDOUT_FILENO, &rights); + limit(STDERR_FILENO, &rights); + + cap_rights_init(&rights, CAP_EVENT, CAP_READ); + limit(STDIN_FILENO, &rights); + + cap_rights_set(&rights, CAP_WRITE); + limit(sock, &rights); +#endif + + clientFormat(client, "NICK :%s\r\nUSER %s 0 * :%s\r\n", nick, nick, nick); + + char *input = NULL; + size_t cap = 0; + + char buf[4096]; + size_t len = 0; + + struct pollfd fds[2] = { + { .events = POLLIN, .fd = STDIN_FILENO }, + { .events = POLLIN, .fd = sock }, + }; + while (0 < poll(fds, 2, -1)) { + if (fds[0].revents) { + ssize_t len = getline(&input, &cap, stdin); + if (len < 0) err(EX_IOERR, "getline"); + input[len - 1] = '\0'; + clientFormat(client, "NOTICE %s :%s\r\n", chan, input); + } + if (!fds[1].revents) continue; + + ssize_t read = tls_read(client, &buf[len], sizeof(buf) - len); + if (read == TLS_WANT_POLLIN || read == TLS_WANT_POLLOUT) continue; + if (read < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client)); + if (!read) return EX_UNAVAILABLE; + len += read; + + char *crlf; + char *line = buf; + for (;;) { + crlf = memmem(line, &buf[len] - line, "\r\n", 2); + if (!crlf) break; + crlf[0] = '\0'; + clientHandle(client, chan, line); + line = &crlf[2]; + } + len -= line - buf; + memmove(buf, line, len); + } + err(EX_IOERR, "poll"); +} diff --git a/bin/scheme.c b/bin/scheme.c new file mode 100644 index 00000000..33fd8b60 --- /dev/null +++ b/bin/scheme.c @@ -0,0 +1,278 @@ +/* Copyright (C) 2018, 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include "png.h" + +typedef unsigned uint; +typedef unsigned char byte; + +struct HSV { + double h, s, v; +}; + +struct RGB { + byte r, g, b; +}; + +static struct RGB convert(struct HSV o) { + double c = o.v * o.s; + double h = o.h / 60.0; + double x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0)); + double m = o.v - c; + double r = m, g = m, b = m; + if (h <= 1.0) { r += c; g += x; } + else if (h <= 2.0) { r += x; g += c; } + else if (h <= 3.0) { g += c; b += x; } + else if (h <= 4.0) { g += x; b += c; } + else if (h <= 5.0) { r += x; b += c; } + else if (h <= 6.0) { r += c; b += x; } + return (struct RGB) { r * 255.0, g * 255.0, b * 255.0 }; +} + +static const struct HSV +R = { 0.0, 1.0, 1.0 }, +Y = { 60.0, 1.0, 1.0 }, +G = { 120.0, 1.0, 1.0 }, +C = { 180.0, 1.0, 1.0 }, +B = { 240.0, 1.0, 1.0 }, +M = { 300.0, 1.0, 1.0 }; + +static struct HSV x(struct HSV o, double hd, double sf, double vf) { + return (struct HSV) { + fmod(o.h + hd, 360.0), + fmin(o.s * sf, 1.0), + fmin(o.v * vf, 1.0), + }; +} + +enum { + Black, Red, Green, Yellow, Blue, Magenta, Cyan, White, + Dark = 0, + Light = 8, + Background = 16, + Foreground, + Bold, + Selection, + Cursor, + SchemeLen, +}; +static struct HSV scheme[SchemeLen]; +static struct HSV *dark = &scheme[Dark]; +static struct HSV *light = &scheme[Light]; + +static void generate(void) { + light[Black] = x(R, +45.0, 0.3, 0.3); + light[Red] = x(R, +10.0, 0.9, 0.8); + light[Green] = x(G, -55.0, 0.8, 0.6); + light[Yellow] = x(Y, -20.0, 0.8, 0.8); + light[Blue] = x(B, -55.0, 0.4, 0.5); + light[Magenta] = x(M, +45.0, 0.4, 0.6); + light[Cyan] = x(C, -60.0, 0.3, 0.6); + light[White] = x(R, +45.0, 0.3, 0.8); + + dark[Black] = x(light[Black], 0.0, 1.0, 0.3); + dark[White] = x(light[White], 0.0, 1.0, 0.75); + for (uint i = Red; i < White; ++i) { + dark[i] = x(light[i], 0.0, 1.0, 0.8); + } + + scheme[Background] = x(dark[Black], 0.0, 1.0, 0.9); + scheme[Foreground] = x(light[White], 0.0, 1.0, 0.9); + scheme[Bold] = x(light[White], 0.0, 1.0, 1.0); + scheme[Selection] = x(light[Red], +10.0, 1.0, 0.8); + scheme[Cursor] = x(dark[White], 0.0, 1.0, 0.8); +} + +static void swap(struct HSV *a, struct HSV *b) { + struct HSV c = *a; + *a = *b; + *b = c; +} + +static void invert(void) { + swap(&dark[Black], &light[White]); + swap(&dark[White], &light[Black]); +} + +typedef void OutputFn(const struct HSV *hsv, uint len); + +static void outputHSV(const struct HSV *hsv, uint len) { + for (uint i = 0; i < len; ++i) { + printf("%g,%g,%g\n", hsv[i].h, hsv[i].s, hsv[i].v); + } +} + +#define FORMAT_RGB "%02hhX%02hhX%02hhX" + +static void outputRGB(const struct HSV *hsv, uint len) { + for (uint i = 0; i < len; ++i) { + struct RGB rgb = convert(hsv[i]); + printf(FORMAT_RGB "\n", rgb.r, rgb.g, rgb.b); + } +} + +static void outputLinux(const struct HSV *hsv, uint len) { + for (uint i = 0; i < len; ++i) { + struct RGB rgb = convert(hsv[i]); + printf("\x1B]P%X" FORMAT_RGB, i, rgb.r, rgb.g, rgb.b); + } +} + +static const char *Enum[SchemeLen] = { + "DarkBlack", "DarkRed", "DarkGreen", "DarkYellow", + "DarkBlue", "DarkMagenta", "DarkCyan", "DarkWhite", + "LightBlack", "LightRed", "LightGreen", "LightYellow", + "LightBlue", "LightMagenta", "LightCyan", "LightWhite", + "Background", "Foreground", "Bold", "Selection", "Cursor", +}; + +static void outputEnum(const struct HSV *hsv, uint len) { + printf("enum {\n"); + for (uint i = 0; i < len; ++i) { + struct RGB rgb = convert(hsv[i]); + printf("\t%s = 0x" FORMAT_RGB ",\n", Enum[i], rgb.r, rgb.g, rgb.b); + } + printf("};\n"); +} + +#define FORMAT_X "rgb:%02hhX/%02hhX/%02hhX" + +static const char *Resources[SchemeLen] = { + [Background] = "background", + [Foreground] = "foreground", + [Bold] = "colorBD", + [Selection] = "highlightColor", + [Cursor] = "cursorColor", +}; + +static void outputXTerm(const struct HSV *hsv, uint len) { + for (uint i = 0; i < len; ++i) { + struct RGB rgb = convert(hsv[i]); + if (Resources[i]) { + printf("XTerm*%s: " FORMAT_X "\n", Resources[i], rgb.r, rgb.g, rgb.b); + } else { + printf("XTerm*color%u: " FORMAT_X "\n", i, rgb.r, rgb.g, rgb.b); + } + } +} + +static const char *Mintty[SchemeLen] = { + "Black", "Red", "Green", "Yellow", + "Blue", "Magenta", "Cyan", "White", + "BoldBlack", "BoldRed", "BoldGreen", "BoldYellow", + "BoldBlue", "BoldMagenta", "BoldCyan", "BoldWhite", + [Background] = "BackgroundColour", + [Foreground] = "ForegroundColour", + [Cursor] = "CursorColour", +}; + +static void outputMintty(const struct HSV *hsv, uint len) { + for (uint i = 0; i < len; ++i) { + if (!Mintty[i]) continue; + struct RGB rgb = convert(hsv[i]); + printf("%s=%hhu,%hhu,%hhu\n", Mintty[i], rgb.r, rgb.g, rgb.b); + } +} + +static void outputCSS(const struct HSV *hsv, uint len) { + printf(":root {\n"); + for (uint i = 0; i < len; ++i) { + struct RGB rgb = convert(hsv[i]); + printf("\t--ansi%u: #" FORMAT_RGB ";\n", i, rgb.r, rgb.g, rgb.b); + } + printf("}\n"); + for (uint i = 0; i < len; ++i) { + printf( + ".fg%u { color: var(--ansi%u); }\n" + ".bg%u { background-color: var(--ansi%u); }\n", + i, i, i, i + ); + } +} + +enum { + SwatchWidth = 64, + SwatchHeight = 64, + SwatchCols = 8, +}; + +static void outputPNG(const struct HSV *hsv, uint len) { + uint rows = (len + SwatchCols - 1) / SwatchCols; + uint width = SwatchWidth * SwatchCols; + uint height = SwatchHeight * rows; + pngHead(stdout, width, height, 8, PNGIndexed); + + struct RGB pal[len]; + for (uint i = 0; i < len; ++i) { + pal[i] = convert(hsv[i]); + } + pngPalette(stdout, (byte *)pal, sizeof(pal)); + + byte data[height][1 + width]; + memset(data, 0, sizeof(data)); + for (uint y = 0; y < height; ++y) { + data[y][0] = (y % SwatchHeight ? PNGUp : PNGSub); + } + for (uint i = 0; i < len; ++i) { + uint y = SwatchHeight * (i / SwatchCols); + uint x = SwatchWidth * (i % SwatchCols); + data[y][1 + x] = (x ? 1 : i); + } + pngData(stdout, (byte *)data, sizeof(data)); + pngTail(stdout); +} + +int main(int argc, char *argv[]) { + generate(); + + OutputFn *output = outputRGB; + const struct HSV *hsv = scheme; + uint len = 16; + + int opt; + while (0 < (opt = getopt(argc, argv, "Xacghilmp:stx"))) { + switch (opt) { + break; case 'X': output = outputXTerm; + break; case 'a': len = 16; + break; case 'c': output = outputEnum; + break; case 'g': output = outputPNG; + break; case 'h': output = outputHSV; + break; case 'i': invert(); + break; case 'l': output = outputLinux; + break; case 'm': output = outputMintty; + break; case 'p': { + uint p = strtoul(optarg, NULL, 0); + if (p >= SchemeLen) return EX_USAGE; + hsv = &scheme[p]; + len = 1; + } + break; case 's': output = outputCSS; + break; case 't': len = SchemeLen; + break; case 'x': output = outputRGB; + break; default: return EX_USAGE; + } + } + + output(hsv, len); +} diff --git a/bin/sh.l b/bin/sh.l new file mode 100644 index 00000000..b27ecebc --- /dev/null +++ b/bin/sh.l @@ -0,0 +1,178 @@ +/* Copyright (C) 2021 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +%option prefix="sh" +%option noyywrap + +%{ +#include <assert.h> +#include <stdbool.h> +#include <string.h> +#include "hilex.h" + +enum { Cap = 64 }; +static int len = 1; +static int stack[Cap]; +static int push(int val) { + if (len < Cap) stack[len++] = val; + return val; +} +static int pop(void) { + if (len > 1) len--; + return stack[len-1]; +} +%} + +%s Param Command Arith Backtick +%x DQuote HereDocDel HereDoc HereDocLit + +word [[:alnum:]_.-]+ +param [^:=?+%#{}-]+ +reserved [!{}]|else|do|elif|for|done|fi|then|until|while|if|case|esac + +%% + static bool first; + static char *delimiter; + +[[:blank:]]+ { return Normal; } + +"\\". { return Escape; } + +<INITIAL,DQuote,HereDoc,Param,Command,Arith>{ + "$"[*@#?$!0-9-] | + "$"[_[:alpha:][_[:alnum:]]* | + "${"[#]?{param}"}" { + return Subst; + } + "${"{param} { + BEGIN(push(Param)); + return Subst; + } + "$(" { + BEGIN(push(Command)); + return Subst; + } + "$((" { + BEGIN(push(Arith)); + return Subst; + } + "`" { + BEGIN(push(Backtick)); + return Subst; + } +} +<Param>"}" | +<Command>")" | +<Arith>"))" | +<Backtick>"`" { + BEGIN(pop()); + return Subst; +} + +"\n" { + first = true; + return Normal; +} +[&();|]|"&&"|";;"|"||" { + first = true; + return Operator; +} +[0-9]?([<>]"&"?|">|"|">>"|"<>") { + return Operator; +} + +{reserved} { + if (first) { + first = false; + return Keyword; + } + return Normal; +} + +{word}/[[:blank:]]*"()" { return Ident; } + +[0-9]?("<<"|"<<-") { + BEGIN(push(HereDocDel)); + return Operator; +} +<HereDocDel>{ + [[:blank:]]+ { return Normal; } + {word} { + delimiter = strdup(yytext); + assert(delimiter); + BEGIN(pop(), push(HereDoc)); + return Ident; + } + "'"{word}"'" { + delimiter = strndup(&yytext[1], strlen(yytext)-2); + assert(delimiter); + BEGIN(pop(), push(HereDocLit)); + return Ident; + } +} +<HereDoc,HereDocLit>{ + ^"\t"*{word} { + if (strcmp(&yytext[strspn(yytext, "\t")], delimiter)) REJECT; + free(delimiter); + BEGIN(pop()); + return Ident; + } +} +<HereDoc>{ + [^$`\n]+ { return String; } + .|\n { return String; } +} +<HereDocLit>{ + .*\n { return String; } +} + +"'"[^'']*"'" { return String; } + +"\""/[^$`\\] { + BEGIN(push(DQuote)); + yymore(); +} +"\"" { + BEGIN(push(DQuote)); + return String; +} + +<DQuote>{ + [^\\$`""]*"\"" { + BEGIN(pop()); + return String; + } + "\\"[$`""\\\n] { return Escape; } + [^\\$`""]+|. { return String; } +} + +<INITIAL,Command,Backtick,Arith>"#".* { return Comment; } + +{word} { + first = false; + return Normal; +} + +.|\n { return Normal; } + +%{ + (void)yyunput; + (void)input; +%} + +%% + +const struct Lexer LexSh = { yylex, &yyin, &yytext }; diff --git a/bin/shotty.c b/bin/shotty.c new file mode 100644 index 00000000..83b00313 --- /dev/null +++ b/bin/shotty.c @@ -0,0 +1,648 @@ +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <err.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sysexits.h> +#include <unistd.h> +#include <wchar.h> + +#define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit + +typedef unsigned uint; + +enum { + NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, + BS, HT, NL, VT, NP, CR, SO, SI, + DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, + CAN, EM, SUB, ESC, FS, GS, RS, US, + DEL = 0x7F, +}; + +enum Attr { + BIT(Bold), + BIT(Dim), + BIT(Italic), + BIT(Underline), + BIT(Blink), + BIT(Reverse), +}; + +struct Style { + enum Attr attr; + int bg, fg; +}; + +struct Cell { + struct Style style; + wchar_t ch; +}; + +static uint rows = 24, cols = 80; +static struct Cell *cells; + +static struct Cell *cell(uint y, uint x) { + assert(y <= rows); + assert(x <= cols); + assert(y * cols + x <= rows * cols); + return &cells[y * cols + x]; +} + +static uint y, x; +static struct Style style = { .bg = -1, .fg = -1 }; + +static struct { + uint y, x; +} save; + +enum { ParamCap = 16 }; +static struct { + uint s[ParamCap]; + uint n, i; +} param; + +static uint p(uint i, uint z) { + return (i < param.n ? param.s[i] : z); +} + +static uint min(uint a, uint b) { + return (a < b ? a : b); +} + +#define _ch ch __attribute__((unused)) +typedef void Action(wchar_t ch); + +static void nop(wchar_t _ch) { +} + +static void csi(wchar_t _ch) { + memset(¶m, 0, sizeof(param)); +} + +static void csiSep(wchar_t _ch) { + if (param.n == ParamCap) return; + if (!param.n) param.n++; + param.n++; + param.i++; +} + +static void csiDigit(wchar_t ch) { + param.s[param.i] *= 10; + param.s[param.i] += ch - L'0'; + if (!param.n) param.n++; +} + +static void bs(wchar_t _ch) { if (x) x--; } +static void ht(wchar_t _ch) { x = min(x - x % 8 + 8, cols - 1); } +static void cr(wchar_t _ch) { x = 0; } +static void cuu(wchar_t _ch) { y -= min(p(0, 1), y); } +static void cud(wchar_t _ch) { y = min(y + p(0, 1), rows - 1); } +static void cuf(wchar_t _ch) { x = min(x + p(0, 1), cols - 1); } +static void cub(wchar_t _ch) { x -= min(p(0, 1), x); } +static void cnl(wchar_t _ch) { x = 0; cud(0); } +static void cpl(wchar_t _ch) { x = 0; cuu(0); } +static void cha(wchar_t _ch) { x = min(p(0, 1) - 1, cols - 1); } +static void vpa(wchar_t _ch) { y = min(p(0, 1) - 1, rows - 1); } +static void cup(wchar_t _ch) { + y = min(p(0, 1) - 1, rows - 1); + x = min(p(1, 1) - 1, cols - 1); +} +static void decsc(wchar_t _ch) { + save.y = y; + save.x = x; +} +static void decrc(wchar_t _ch) { + y = save.y; + x = save.x; +} + +static void move(struct Cell *dst, struct Cell *src, size_t len) { + memmove(dst, src, sizeof(*dst) * len); +} + +static void erase(struct Cell *at, struct Cell *to) { + for (; at < to; ++at) { + at->style = style; + at->ch = L' '; + } +} + +static void ed(wchar_t _ch) { + erase( + (p(0, 0) == 0 ? cell(y, x) : cell(0, 0)), + (p(0, 0) == 1 ? cell(y, x) : cell(rows - 1, cols)) + ); +} +static void el(wchar_t _ch) { + erase( + (p(0, 0) == 0 ? cell(y, x) : cell(y, 0)), + (p(0, 0) == 1 ? cell(y, x) : cell(y, cols)) + ); +} +static void ech(wchar_t _ch) { + erase(cell(y, x), cell(y, min(x + p(0, 1), cols))); +} + +static void dch(wchar_t _ch) { + uint n = min(p(0, 1), cols - x); + move(cell(y, x), cell(y, x + n), cols - x - n); + erase(cell(y, cols - n), cell(y, cols)); +} +static void ich(wchar_t _ch) { + uint n = min(p(0, 1), cols - x); + move(cell(y, x + n), cell(y, x), cols - x - n); + erase(cell(y, x), cell(y, x + n)); +} + +static struct { + uint top, bot; +} scroll; + +static void scrollUp(uint top, uint n) { + n = min(n, scroll.bot - top); + move(cell(top, 0), cell(top + n, 0), cols * (scroll.bot - top - n)); + erase(cell(scroll.bot - n, 0), cell(scroll.bot, 0)); +} + +static void scrollDown(uint top, uint n) { + n = min(n, scroll.bot - top); + move(cell(top + n, 0), cell(top, 0), cols * (scroll.bot - top - n)); + erase(cell(top, 0), cell(top + n, 0)); +} + +static void decstbm(wchar_t _ch) { + scroll.bot = min(p(1, rows), rows); + scroll.top = min(p(0, 1) - 1, scroll.bot); +} + +static void su(wchar_t _ch) { scrollUp(scroll.top, p(0, 1)); } +static void sd(wchar_t _ch) { scrollDown(scroll.top, p(0, 1)); } +static void dl(wchar_t _ch) { scrollUp(min(y, scroll.bot), p(0, 1)); } +static void il(wchar_t _ch) { scrollDown(min(y, scroll.bot), p(0, 1)); } + +static void nl(wchar_t _ch) { + if (y + 1 == scroll.bot) { + scrollUp(scroll.top, 1); + } else { + y = min(y + 1, rows - 1); + } +} +static void ri(wchar_t _ch) { + if (y == scroll.top) { + scrollDown(scroll.top, 1); + } else { + if (y) y--; + } +} + +static enum Mode { + BIT(Insert), + BIT(Wrap), + BIT(Cursor), +} mode = Wrap | Cursor; + +static enum Mode paramMode(void) { + enum Mode mode = 0; + for (uint i = 0; i < param.n; ++i) { + switch (param.s[i]) { + break; case 4: mode |= Insert; + break; default: warnx("unhandled SM/RM %u", param.s[i]); + } + } + return mode; +} + +static enum Mode paramDECMode(void) { + enum Mode mode = 0; + for (uint i = 0; i < param.n; ++i) { + switch (param.s[i]) { + break; case 1: // DECCKM + break; case 7: mode |= Wrap; + break; case 12: // "Start Blinking Cursor" + break; case 25: mode |= Cursor; + break; default: { + if (param.s[i] < 1000) { + warnx("unhandled DECSET/DECRST %u", param.s[i]); + } + } + } + } + return mode; +} + +static void sm(wchar_t _ch) { mode |= paramMode(); } +static void rm(wchar_t _ch) { mode &= ~paramMode(); } +static void decset(wchar_t _ch) { mode |= paramDECMode(); } +static void decrst(wchar_t _ch) { mode &= ~paramDECMode(); } + +enum { + Reset, + SetBold, + SetDim, + SetItalic, + SetUnderline, + SetBlink, + SetReverse = 7, + + UnsetBoldDim = 22, + UnsetItalic, + UnsetUnderline, + UnsetBlink, + UnsetReverse = 27, + + SetFg0 = 30, + SetFg7 = 37, + SetFg, + ResetFg, + SetBg0 = 40, + SetBg7 = 47, + SetBg, + ResetBg, + + SetFg8 = 90, + SetFgF = 97, + SetBg8 = 100, + SetBgF = 107, + + Color256 = 5, +}; + +static void sgr(wchar_t _ch) { + uint n = param.i + 1; + for (uint i = 0; i < n; ++i) { + switch (param.s[i]) { + break; case Reset: style = (struct Style) { .bg = -1, .fg = -1 }; + + break; case SetBold: style.attr |= Bold; style.attr &= ~Dim; + break; case SetDim: style.attr |= Dim; style.attr &= ~Bold; + break; case SetItalic: style.attr |= Italic; + break; case SetUnderline: style.attr |= Underline; + break; case SetBlink: style.attr |= Blink; + break; case SetReverse: style.attr |= Reverse; + + break; case UnsetBoldDim: style.attr &= ~(Bold | Dim); + break; case UnsetItalic: style.attr &= ~Italic; + break; case UnsetUnderline: style.attr &= ~Underline; + break; case UnsetBlink: style.attr &= ~Blink; + break; case UnsetReverse: style.attr &= ~Reverse; + + break; case SetFg: { + if (++i < n && param.s[i] == Color256) { + if (++i < n) style.fg = param.s[i]; + } + } + break; case SetBg: { + if (++i < n && param.s[i] == Color256) { + if (++i < n) style.bg = param.s[i]; + } + } + + break; case ResetFg: style.fg = -1; + break; case ResetBg: style.bg = -1; + + break; default: { + uint p = param.s[i]; + if (p >= SetFg0 && p <= SetFg7) { + style.fg = p - SetFg0; + } else if (p >= SetBg0 && p <= SetBg7) { + style.bg = p - SetBg0; + } else if (p >= SetFg8 && p <= SetFgF) { + style.fg = 8 + p - SetFg8; + } else if (p >= SetBg8 && p <= SetBgF) { + style.bg = 8 + p - SetBg8; + } else { + warnx("unhandled SGR %u", p); + } + } + } + } +} + +static enum { + USASCII, + DECSpecial, +} charset; + +static void usascii(wchar_t _ch) { charset = USASCII; } +static void decSpecial(wchar_t _ch) { charset = DECSpecial; } + +static const wchar_t AltCharset[128] = { + ['`'] = L'◆', ['a'] = L'▒', ['f'] = L'°', ['g'] = L'±', ['i'] = L'␋', + ['j'] = L'┘', ['k'] = L'┐', ['l'] = L'┌', ['m'] = L'└', ['n'] = L'┼', + ['o'] = L'⎺', ['p'] = L'⎻', ['q'] = L'─', ['r'] = L'⎼', ['s'] = L'⎽', + ['t'] = L'├', ['u'] = L'┤', ['v'] = L'┴', ['w'] = L'┬', ['x'] = L'│', + ['y'] = L'≤', ['z'] = L'≥', ['{'] = L'π', ['|'] = L'≠', ['}'] = L'£', + ['~'] = L'·', +}; + +static void add(wchar_t ch) { + if (charset == DECSpecial && ch < 128 && AltCharset[ch]) { + ch = AltCharset[ch]; + } + + int width = wcwidth(ch); + if (width < 0) { + warnx("unhandled \\u%02X", ch); + return; + } + + if (mode & Insert) { + uint n = min(width, cols - x); + move(cell(y, x + n), cell(y, x), cols - x - n); + } + if (mode & Wrap && x + width > cols) { + cr(0); + nl(0); + } + + cell(y, x)->style = style; + cell(y, x)->ch = ch; + for (int i = 1; i < width && x + i < cols; ++i) { + cell(y, x + i)->style = style; + cell(y, x + i)->ch = L'\0'; + } + x = min(x + width, (mode & Wrap ? cols : cols - 1)); +} + +static void html(void); +static void mc(wchar_t _ch) { + if (p(0, 0) == 10) { + html(); + } else { + warnx("unhandled CSI %u MC", p(0, 0)); + } +} + +static enum { + Data, + Esc, + G0, + CSI, + CSILt, + CSIEq, + CSIGt, + CSIQm, + CSIInter, + OSC, + OSCEsc, +} state; + +static void escDefault(wchar_t ch) { + warnx("unhandled ESC %lc", ch); +} + +static void g0Default(wchar_t ch) { + warnx("unhandled G0 %lc", ch); + charset = USASCII; +} + +static void csiInter(wchar_t ch) { + switch (state) { + break; case CSI: warnx("unhandled CSI %lc ...", ch); + break; case CSILt: warnx("unhandled CSI < %lc ...", ch); + break; case CSIEq: warnx("unhandled CSI = %lc ...", ch); + break; case CSIGt: warnx("unhandled CSI > %lc ...", ch); + break; case CSIQm: warnx("unhandled CSI ? %lc ...", ch); + break; default: abort(); + } +} + +static void csiFinal(wchar_t ch) { + switch (state) { + break; case CSI: warnx("unhandled CSI %lc", ch); + break; case CSILt: warnx("unhandled CSI < %lc", ch); + break; case CSIEq: warnx("unhandled CSI = %lc", ch); + break; case CSIGt: warnx("unhandled CSI > %lc", ch); + break; case CSIQm: warnx("unhandled CSI ? %lc", ch); + break; case CSIInter: warnx("unhandled CSI ... %lc", ch); + break; default: abort(); + } +} + +#define S(s) break; case s: switch (ch) +#define A(c, a, s) break; case c: a(ch); state = s +#define D(a, s) break; default: a(ch); state = s +static void update(wchar_t ch) { + switch (state) { + default: abort(); + + S(Data) { + A(BEL, nop, Data); + A(BS, bs, Data); + A(HT, ht, Data); + A(NL, nl, Data); + A(CR, cr, Data); + A(ESC, nop, Esc); + D(add, Data); + } + + S(Esc) { + A('(', nop, G0); + A('7', decsc, Data); + A('8', decrc, Data); + A('=', nop, Data); + A('>', nop, Data); + A('M', ri, Data); + A('[', csi, CSI); + A(']', nop, OSC); + D(escDefault, Data); + } + S(G0) { + A('0', decSpecial, Data); + A('B', usascii, Data); + D(g0Default, Data); + } + + S(CSI) { + A(' ' ... '/', csiInter, CSIInter); + A('0' ... '9', csiDigit, CSI); + A(':', nop, CSI); + A(';', csiSep, CSI); + A('<', nop, CSILt); + A('=', nop, CSIEq); + A('>', nop, CSIGt); + A('?', nop, CSIQm); + A('@', ich, Data); + A('A', cuu, Data); + A('B', cud, Data); + A('C', cuf, Data); + A('D', cub, Data); + A('E', cnl, Data); + A('F', cpl, Data); + A('G', cha, Data); + A('H', cup, Data); + A('J', ed, Data); + A('K', el, Data); + A('L', il, Data); + A('M', dl, Data); + A('P', dch, Data); + A('S', su, Data); + A('T', sd, Data); + A('X', ech, Data); + A('d', vpa, Data); + A('h', sm, Data); + A('i', mc, Data); + A('l', rm, Data); + A('m', sgr, Data); + A('r', decstbm, Data); + A('t', nop, Data); + D(csiFinal, Data); + } + + S(CSILt ... CSIGt) { + A(' ' ... '/', csiInter, CSIInter); + A('0' ... '9', csiDigit, state); + A(':', nop, state); + A(';', csiSep, state); + D(csiFinal, Data); + } + + S(CSIQm) { + A(' ' ... '/', csiInter, CSIInter); + A('0' ... '9', csiDigit, CSIQm); + A(':', nop, CSIQm); + A(';', csiSep, CSIQm); + A('h', decset, Data); + A('l', decrst, Data); + D(csiFinal, Data); + } + + S(CSIInter) { + D(csiFinal, Data); + } + + S(OSC) { + A(BEL, nop, Data); + A(ESC, nop, OSCEsc); + D(nop, OSC); + } + S(OSCEsc) { + A('\\', nop, Data); + D(nop, OSC); + } + } +} + +static bool bright; +static int defaultBg = 0; +static int defaultFg = 7; + +static void span(const struct Style *prev, const struct Cell *cell) { + struct Style style = cell->style; + if (!prev || memcmp(prev, &style, sizeof(*prev))) { + if (prev) printf("</span>"); + if (style.bg < 0) style.bg = defaultBg; + if (style.fg < 0) style.fg = defaultFg; + if (bright && style.attr & Bold) { + if (style.fg < 8) style.fg += 8; + style.attr ^= Bold; + } + printf( + "<span style=\"%s%s%s\" class=\"bg%u fg%u\">", + (style.attr & Bold ? "font-weight:bold;" : ""), + (style.attr & Italic ? "font-style:italic;" : ""), + (style.attr & Underline ? "text-decoration:underline;" : ""), + (style.attr & Reverse ? style.fg : style.bg), + (style.attr & Reverse ? style.bg : style.fg) + ); + } + switch (cell->ch) { + break; case '&': printf("&"); + break; case '<': printf("<"); + break; case '>': printf(">"); + break; default: printf("%lc", (wint_t)cell->ch); + } +} + +static bool mediaCopy; +static void html(void) { + mediaCopy = true; + if (mode & Cursor) cell(y, x)->style.attr ^= Reverse; + printf( + "<pre style=\"width: %uch;\" class=\"bg%u fg%u\">", + cols, defaultBg, defaultFg + ); + for (uint y = 0; y < rows; ++y) { + for (uint x = 0; x < cols; ++x) { + if (!cell(y, x)->ch) continue; + span(x ? &cell(y, x - 1)->style : NULL, cell(y, x)); + } + printf("</span>\n"); + } + printf("</pre>\n"); + if (mode & Cursor) cell(y, x)->style.attr ^= Reverse; +} + +int main(int argc, char *argv[]) { + setlocale(LC_CTYPE, ""); + + bool debug = false; + bool size = false; + bool hide = false; + + int opt; + while (0 < (opt = getopt(argc, argv, "Bb:df:h:nsw:"))) { + switch (opt) { + break; case 'B': bright = true; + break; case 'b': defaultBg = strtol(optarg, NULL, 0); + break; case 'd': debug = true; + break; case 'f': defaultFg = strtol(optarg, NULL, 0); + break; case 'h': rows = strtoul(optarg, NULL, 0); + break; case 'n': hide = true; + break; case 's': size = true; + break; case 'w': cols = strtoul(optarg, NULL, 0); + break; default: return EX_USAGE; + } + } + + FILE *file = stdin; + if (optind < argc) { + file = fopen(argv[optind], "r"); + if (!file) err(EX_NOINPUT, "%s", argv[optind]); + } + + if (size) { + struct winsize window; + int error = ioctl(STDERR_FILENO, TIOCGWINSZ, &window); + if (error) err(EX_IOERR, "ioctl"); + rows = window.ws_row; + cols = window.ws_col; + } + scroll.bot = rows; + + cells = calloc(rows * cols, sizeof(*cells)); + if (!cells) err(EX_OSERR, "calloc"); + erase(cell(0, 0), cell(rows - 1, cols)); + + wint_t ch; + while (WEOF != (ch = getwc(file))) { + uint prev = state; + update(ch); + if (debug && state != prev && state == Data) html(); + } + if (ferror(file)) err(EX_IOERR, "getwc"); + + if (!mediaCopy) { + if (hide) mode &= ~Cursor; + html(); + } +} diff --git a/bin/sup.sh b/bin/sup.sh new file mode 100644 index 00000000..b7f28bc7 --- /dev/null +++ b/bin/sup.sh @@ -0,0 +1,169 @@ +#!/bin/sh +set -eu + +service=$1 +email=${2:-$(git config fetchemail.imapUser)} + +generate() { + openssl rand -base64 33 +} +copy() { + printf '%s' "$1" | pbcopy +} + +asciinema() { + echo 'Fetching CSRF token...' + jar=$(mktemp -t sup) + trap 'rm "${jar}"' EXIT + csrf=$( + curl -Ss -c "${jar}" 'https://asciinema.org/login/new' | + sed -n 's/.*name="_csrf_token".*value="\([^"]*\)".*/\1/p' + ) + echo 'Submitting form...' + curl -Ss -X POST -b "${jar}" \ + -F "_csrf_token=${csrf}" -F "login[email]=${email}" \ + 'https://asciinema.org/login' \ + >/dev/null + echo 'Waiting for email...' + url=$( + git fetch-email -i -M Trash \ + -F 'hello@asciinema.org' -T "${email}" \ + -S 'Login to asciinema.org' | + grep -m 1 '^https://asciinema\.org/session/new' + ) + open "${url}" +} + +bugzilla() { + echo 'Fetching CSRF token...' + csrf=$( + curl -Ss "${bugzillaBase}/" | + sed -n ' + /name="token"/N + s/.*name="token"[[:space:]]*value="\([^"]*\)".*/\1/p + ' | head -n 1 + ) + echo 'Submitting form...' + curl -Ss -X POST \ + -F "loginname=${email}" -F "token=${csrf}" -F 'a=reqpw' \ + "${bugzillaBase}/token.cgi" \ + >/dev/null + echo 'Waiting for email...' + token=$( + git fetch-email -i -M Trash \ + -F "${bugzillaFrom}" -T "${email}" \ + -S 'Bugzilla Change Password Request' | + sed -n 's/.*t=3D\([^&]*\).*/\1/p' | + head -n 1 + ) + password=$(generate) + echo 'Setting password...' + curl -Ss -X POST \ + -F "t=${token}" -F 'a=chgpw' \ + -F "password=${password}" -F "matchpassword=${password}" \ + "${bugzillaBase}/token.cgi" \ + >/dev/null + copy "${password}" + open "${bugzillaBase}/" +} + +freebsdbugzilla() { + bugzillaBase='https://bugs.freebsd.org/bugzilla' + bugzillaFrom='bugzilla-noreply@freebsd.org' + bugzilla +} + +discogs() { + echo 'Submitting form...' + curl -Ss -X POST \ + -F "email=${email}" -F 'Action.EmailResetInstructions=submit' \ + 'https://www.discogs.com/users/forgot_password' \ + >/dev/null + echo 'Waiting for email...' + url=$( + git fetch-email -i -M Trash \ + -F 'noreply@discogs.com' -T "${email}" \ + -S 'Discogs Account Password Reset Instructions' | + sed -n 's/^To proceed, follow the instructions here: \(.*\)/\1/p' + ) + echo 'Fetching token...' + token=$(curl -ISs --url "${url}" | sed -n 's/.*[?]token=\([^&]*\).*/\1/p') + password=$(generate) + echo 'Setting password...' + curl -Ss -X POST \ + -F "token=${token}" \ + -F "password0=${password}" -F "password1=${password}" \ + -F 'Action.ChangePassword=submit' \ + 'https://www.discogs.com/users/forgot_password' \ + >/dev/null + copy "${password}" + open 'https://discogs.com/login' +} + +liberapay() { + echo 'Fetching CSRF token...' + csrf=$( + curl -Ss 'https://liberapay.com/sign-in' | + sed -n 's/.*name="csrf_token".*value="\([^"]*\)".*/\1/p' + ) + echo 'Submitting form...' + curl -Ss -X POST \ + -b "csrf_token=${csrf}" -F "csrf_token=${csrf}" \ + -F "log-in.id=${email}" \ + 'https://liberapay.com/sign-in' \ + >/dev/null + echo 'Waiting for email...' + url=$( + git fetch-email -i -M Trash \ + -F 'support@liberapay.com' -T "${email}" \ + -S 'Log in to Liberapay' | + grep -m 1 '^https://liberapay\.com/' + ) + open "${url}" +} + +lobsters() { + : ${lobstersBase:=https://lobste.rs} + : ${lobstersFrom:=nobody@lobste.rs} + echo 'Fetching CSRF token...' + csrf=$( + curl -Ss "${lobstersBase}/login/forgot_password" | + sed -n 's/.*name="authenticity_token" value="\([^"]*\)".*/\1/p' + ) + echo 'Submitting form...' + curl -Ss -X POST \ + -F "authenticity_token=${csrf}" \ + -F "email=${email}" -F 'commit=submit' \ + "${lobstersBase}/login/reset_password" \ + >/dev/null + echo 'Waiting for email...' + token=$( + git fetch-email -i -M Trash \ + -F "${lobstersFrom}" -T "${email}" \ + -S 'Reset your password' | + sed -n 's|^https://.*[?]token=\(.*\)|\1|p' + ) + echo 'Fetching CSRF token...' + csrf=$( + curl -Ss "${lobstersBase}/login/set_new_password?token=${token}" | + sed -n 's/.*name="authenticity_token" value="\([^"]*\)".*/\1/p' + ) + password=$(generate) + echo 'Setting password...' + curl -Ss -X POST \ + -F "authenticity_token=${csrf}" -F "token=${token}" \ + -F "password=${password}" -F "password_confirmation=${password}" \ + -F 'commit=submit' \ + "${lobstersBase}/login/set_new_password" \ + >/dev/null + copy "${password}" + open "${lobstersBase}/login" +} + +tildenews() { + lobstersBase='https://tilde.news' + lobstersFrom='nobody@tilde.news' + lobsters +} + +$service diff --git a/bin/title.c b/bin/title.c new file mode 100644 index 00000000..82f89d95 --- /dev/null +++ b/bin/title.c @@ -0,0 +1,211 @@ +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <curl/curl.h> +#include <err.h> +#include <locale.h> +#include <regex.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <wchar.h> + +static regex_t regex(const char *pattern, int flags) { + regex_t regex; + int error = regcomp(®ex, pattern, REG_EXTENDED | flags); + if (!error) return regex; + + char buf[256]; + regerror(error, ®ex, buf, sizeof(buf)); + errx(EX_SOFTWARE, "regcomp: %s: %s", buf, pattern); +} + +static const struct Entity { + wchar_t ch; + const char *name; +} Entities[] = { + { L'"', """ }, + { L'&', "&" }, + { L'<', "<" }, + { L'>', ">" }, + { L'', " " }, +}; + +static wchar_t entity(const char *name) { + for (size_t i = 0; i < sizeof(Entities) / sizeof(Entities[0]); ++i) { + struct Entity entity = Entities[i]; + if (strncmp(name, entity.name, strlen(entity.name))) continue; + return entity.ch; + } + if (!strncmp(name, "&#x", 3)) return strtoul(&name[3], NULL, 16); + if (!strncmp(name, "&#", 2)) return strtoul(&name[2], NULL, 10); + return 0; +} + +static const char EntityPattern[] = { + "[[:space:]]+|&([[:alpha:]]+|#([[:digit:]]+|x[[:xdigit:]]+));" +}; +static regex_t EntityRegex; + +static void showTitle(const char *title) { + regmatch_t match = {0}; + for (; *title; title += match.rm_eo) { + if (regexec(&EntityRegex, title, 1, &match, 0)) break; + if (title[match.rm_so] != '&') { + printf("%.*s ", (int)match.rm_so, title); + continue; + } + wchar_t ch = entity(&title[match.rm_so]); + if (ch) { + printf("%.*s%lc", (int)match.rm_so, title, (wint_t)ch); + } else { + printf("%.*s", (int)match.rm_eo, title); + } + } + printf("%s\n", title); +} + +static CURL *curl; +static bool title; +static struct { + char buf[64 * 1024]; + size_t len; +} body; + +// HE COMES +static const char TitlePattern[] = "<title>([^<]*)</title>"; +static regex_t TitleRegex; + +static size_t handleBody(char *buf, size_t size, size_t nitems, void *user) { + (void)user; + size_t len = size * nitems; + size_t cap = sizeof(body.buf) - body.len - 1; + size_t new = (len < cap ? len : cap); + if (title || !new) return len; + + memcpy(&body.buf[body.len], buf, new); + body.len += new; + body.buf[body.len] = '\0'; + + regmatch_t match[2]; + if (regexec(&TitleRegex, body.buf, 2, match, 0)) return len; + body.buf[match[1].rm_eo] = '\0'; + showTitle(&body.buf[match[1].rm_so]); + title = true; + + return len; +} + +static CURLcode fetchTitle(const char *url) { + CURLcode code = curl_easy_setopt(curl, CURLOPT_URL, url); + if (code) return code; + + curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + code = curl_easy_perform(curl); + if (code) return code; + + char *type; + code = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &type); + if (code) return code; + if (!type || strncmp(type, "text/html", 9)) return CURLE_OK; + + char *dest; + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &dest); + dest = strdup(dest); + if (!dest) err(EX_OSERR, "strdup"); + + code = curl_easy_setopt(curl, CURLOPT_URL, dest); + if (code) return code; + free(dest); + + body.len = 0; + title = false; + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + code = curl_easy_perform(curl); + return code; +} + +int main(int argc, char *argv[]) { + EntityRegex = regex(EntityPattern, 0); + TitleRegex = regex(TitlePattern, REG_ICASE); + + setlocale(LC_CTYPE, ""); + setlinebuf(stdout); + + CURLcode code = curl_global_init(CURL_GLOBAL_ALL); + if (code) errx(EX_OSERR, "curl_global_init: %s", curl_easy_strerror(code)); + + curl = curl_easy_init(); + if (!curl) errx(EX_SOFTWARE, "curl_easy_init"); + + static char error[CURL_ERROR_SIZE]; + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error); + + curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + curl_easy_setopt( + curl, CURLOPT_USERAGENT, + "curl/7.54.0 facebookexternalhit/1.1 Twitterbot/1.0" + ); + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 3L); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handleBody); + + bool exclude = false; + regex_t excludeRegex; + + int opt; + while (0 < (opt = getopt(argc, argv, "x:v"))) { + switch (opt) { + break; case 'x': { + exclude = true; + excludeRegex = regex(optarg, REG_NOSUB); + } + break; case 'v': curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + break; default: return EX_USAGE; + } + } + + if (optind < argc) { + code = fetchTitle(argv[optind]); + if (!code) return EX_OK; + errx(EX_DATAERR, "curl_easy_perform: %s", error); + } + + char *buf = NULL; + size_t cap = 0; + + regex_t urlRegex = regex("https?://([^[:space:]>\"()]|[(][^)]*[)])+", 0); + while (0 < getline(&buf, &cap, stdin)) { + regmatch_t match = {0}; + for (char *ptr = buf; *ptr; ptr += match.rm_eo) { + if (regexec(&urlRegex, ptr, 1, &match, 0)) break; + ptr[match.rm_eo] = '\0'; + const char *url = &ptr[match.rm_so]; + if (!exclude || regexec(&excludeRegex, url, 0, NULL, 0)) { + code = fetchTitle(url); + if (code) warnx("curl_easy_perform: %s", error); + } + ptr[match.rm_eo] = ' '; + } + } + if (ferror(stdin)) err(EX_IOERR, "getline"); +} diff --git a/bin/typer.c b/bin/typer.c new file mode 100644 index 00000000..76eab784 --- /dev/null +++ b/bin/typer.c @@ -0,0 +1,197 @@ +/* Copyright (C) 2021 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this Program, or any covered work, by linking or + * combining it with LibreSSL (or a modified version of that library), + * containing parts covered by the terms of the OpenSSL License and the + * original SSLeay license, the licensors of this Program grant you + * additional permission to convey the resulting work. Corresponding + * Source for a non-source form of such a combination shall include the + * source code for the parts of LibreSSL used as well as that of the + * covered work. + */ + +#include <err.h> +#include <signal.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <sysexits.h> +#include <tls.h> +#include <unistd.h> + +static bool verbose; +static struct tls *client; +static const char *chan; + +static void clientWrite(const char *ptr, size_t len) { + if (verbose) printf("%.*s", (int)len, ptr); + while (len) { + ssize_t ret = tls_write(client, ptr, len); + if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue; + if (ret < 0) errx(EX_IOERR, "tls_write: %s", tls_error(client)); + ptr += ret; + len -= ret; + } +} + +static void format(const char *format, ...) { + char buf[1024]; + va_list ap; + va_start(ap, format); + int len = vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + if ((size_t)len > sizeof(buf) - 1) errx(EX_DATAERR, "message too large"); + clientWrite(buf, len); +} + +static bool joined; +static bool reverse; +static bool copy; + +static void handle(char *line) { + char *tags = NULL; + char *origin = NULL; + if (line && line[0] == '@') tags = 1 + strsep(&line, " "); + if (line && line[0] == ':') origin = 1 + strsep(&line, " "); + char *cmd = strsep(&line, " "); + if (!cmd) return; + if (!strcmp(cmd, "CAP")) { + char *param = strsep(&cmd, " "); + if (!param) errx(EX_PROTOCOL, "CAP missing parameter"); + if (!strcmp(param, "NAK")) { + errx(EX_CONFIG, "server does not support message-tags"); + } + format("CAP END\r\n"); + } else if (!strcmp(cmd, "001")) { + format("JOIN %s\r\n", chan); + joined = true; + } else if (!strcmp(cmd, "PING")) { + format("PONG %s\r\n", line); + } else if (copy && !strcmp(cmd, "TAGMSG") && tags) { + if (strstr(tags, "typing=")) { + format("@%s TAGMSG %s\r\n", tags, chan); + } + } else if (reverse && !strcmp(cmd, "TAGMSG") && tags && origin) { + char *nick = strsep(&origin, "!"); + if (strstr(tags, "typing=active")) { + format("PRIVMSG %s :\u2328\uFE0F %s is typing!\r\n", chan, nick); + } else if (strstr(tags, "typing=paused")) { + format("PRIVMSG %s :\U0001F914 %s is thinking!\r\n", chan, nick); + } else if (strstr(tags, "typing=done")) { + format("PRIVMSG %s :\u270B %s stopped typing!\r\n", chan, nick); + } + } +} + +static void timer(int sig) { + (void)sig; + if (!joined) return; + const char *status = (arc4random_uniform(4) ? "active" : "done"); + format( + "@+typing=%s;+draft/typing=%s TAGMSG %s\r\n", + status, status, chan + ); +} + +int main(int argc, char *argv[]) { + const char *host = NULL; + const char *port = "6697"; + const char *cert = NULL; + const char *nick = "typer"; + const char *user = "typer"; + bool passive = false; + + for (int opt; 0 < (opt = getopt(argc, argv, "CPRc:n:p:u:v"));) { + switch (opt) { + break; case 'C': copy = true; + break; case 'P': passive = true; + break; case 'R': reverse = true; + break; case 'c': cert = optarg; + break; case 'n': nick = optarg; + break; case 'p': port = optarg; + break; case 'u': user = optarg; + break; case 'v': verbose = true; + break; default: return EX_USAGE; + } + } + if (argc - optind < 2) errx(EX_USAGE, "host and chan required"); + host = argv[optind]; + chan = argv[optind + 1]; + + client = tls_client(); + if (!client) errx(EX_SOFTWARE, "tls_client"); + + struct tls_config *config = tls_config_new(); + if (!config) errx(EX_SOFTWARE, "tls_config_new"); + + if (cert) { + int error = tls_config_set_keypair_file(config, cert, cert); + if (error) errx(EX_CONFIG, "%s: %s", cert, tls_config_error(config)); + } + + int error = tls_configure(client, config); + if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client)); + tls_config_free(config); + + error = tls_connect(client, host, port); + if (error) errx(EX_UNAVAILABLE, "tls_connect: %s", tls_error(client)); + + format( + "CAP REQ :message-tags%s\r\n" + "NICK %s\r\n" + "USER %s 0 * :typer\r\n", + (passive ? " causal.agency/passive" : ""), + nick, user + ); + + if (!copy && !reverse) { + signal(SIGALRM, timer); + struct itimerval itimer = { + .it_interval.tv_sec = 5, + .it_value.tv_sec = 5 + }; + error = setitimer(ITIMER_REAL, &itimer, NULL); + if (error) err(EX_OSERR, "setitimer"); + } + + size_t len = 0; + char buf[4096]; + for (;;) { + ssize_t read = tls_read(client, &buf[len], sizeof(buf) - len); + if (read == TLS_WANT_POLLIN || read == TLS_WANT_POLLOUT) continue; + if (read < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client)); + if (!read) errx(EX_UNAVAILABLE, "server disconnected"); + len += read; + + char *crlf; + char *line = buf; + for (;;) { + crlf = memmem(line, &buf[len] - line, "\r\n", 2); + if (!crlf) break; + crlf[0] = '\0'; + if (verbose) printf("%s\n", line); + handle(line); + line = &crlf[2]; + } + len -= line - buf; + memmove(buf, line, len); + } +} diff --git a/bin/up.sh b/bin/up.sh new file mode 100644 index 00000000..f55213d4 --- /dev/null +++ b/bin/up.sh @@ -0,0 +1,80 @@ +#!/bin/sh +set -eu + +readonly Host='temp.causal.agency' + +upload() { + local src ext ts rand url + src=$1 + ext=${src##*.} + ts=$(date +'%s') + rand=$(openssl rand -hex 4) + url=$(printf '%s/%x%s.%s' "$Host" "$ts" "$rand" "$ext") + scp -q "$src" "${Host}:/usr/local/www/${url}" + echo "https://${url}" +} + +temp() { + temp=$(mktemp -d) + trap "rm -r '$temp'" EXIT +} + +uploadText() { + temp + cat > "${temp}/input.txt" + upload "${temp}/input.txt" +} + +uploadCommand() { + temp + echo "$ $*" > "${temp}/exec.txt" + "$@" >> "${temp}/exec.txt" 2>&1 || true + upload "${temp}/exec.txt" +} + +uploadHilex() { + temp + hilex -f html -o document,tab=4 "$@" > "${temp}/hilex.html" + upload "${temp}/hilex.html" +} + +uploadScreen() { + temp + if type screencapture >/dev/null; then + screencapture -i "$@" "${temp}/capture.png" + else + scrot -s "$@" "${temp}/capture.png" + fi + pngo "${temp}/capture.png" || true + upload "${temp}/capture.png" +} + +uploadTerminal() { + temp + cat > "${temp}/term.html" <<-EOF + <!DOCTYPE html> + <title>${1}</title> + <style> + $(scheme -s) + </style> + EOF + ptee "$@" | shotty -Bs >> "${temp}/term.html" + upload "${temp}/term.html" +} + +while getopts 'chst' opt; do + case "$opt" in + (c) fn=uploadCommand;; + (h) fn=uploadHilex;; + (s) fn=uploadScreen;; + (t) fn=uploadTerminal;; + (?) exit 1;; + esac +done +shift $((OPTIND - 1)) +[ $# -eq 0 ] && : ${fn:=uploadText} +: ${fn:=upload} + +url=$($fn "$@") +printf '%s' "$url" | pbcopy || true +echo "$url" diff --git a/bin/when.y b/bin/when.y new file mode 100644 index 00000000..d647d193 --- /dev/null +++ b/bin/when.y @@ -0,0 +1,255 @@ +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +%{ + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <sysexits.h> +#include <time.h> + +static void yyerror(const char *str); +static int yylex(void); + +#define YYSTYPE struct tm + +static const char *Days[7] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", +}; + +static const char *Months[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", +}; + +static const struct tm Week = { .tm_mday = 7 }; + +static struct tm normalize(struct tm date) { + time_t time = timegm(&date); + struct tm *norm = gmtime(&time); + if (!norm) err(EX_OSERR, "gmtime"); + return *norm; +} + +static struct tm today(void) { + time_t now = time(NULL); + struct tm *local = localtime(&now); + if (!local) err(EX_OSERR, "localtime"); + struct tm date = { + .tm_year = local->tm_year, + .tm_mon = local->tm_mon, + .tm_mday = local->tm_mday, + }; + return normalize(date); +} + +static struct tm monthDay(int month, int day) { + struct tm date = today(); + date.tm_mon = month; + date.tm_mday = day; + return normalize(date); +} + +static struct tm monthDayYear(int month, int day, int year) { + struct tm date = today(); + date.tm_mon = month; + date.tm_mday = day; + date.tm_year = year - 1900; + return normalize(date); +} + +static struct tm weekDay(int day) { + struct tm date = today(); + date.tm_mday += day - date.tm_wday; + return normalize(date); +} + +static struct tm scalarAdd(struct tm a, struct tm b) { + a.tm_mday += b.tm_mday; + a.tm_mon += b.tm_mon; + a.tm_year += b.tm_year; + return a; +} + +static struct tm scalarSub(struct tm a, struct tm b) { + a.tm_mday -= b.tm_mday; + a.tm_mon -= b.tm_mon; + a.tm_year -= b.tm_year; + return a; +} + +static struct tm dateAdd(struct tm date, struct tm scalar) { + return normalize(scalarAdd(date, scalar)); +} + +static struct tm dateSub(struct tm date, struct tm scalar) { + return normalize(scalarSub(date, scalar)); +} + +static struct tm dateDiff(struct tm a, struct tm b) { + struct tm diff = { + .tm_year = a.tm_year - b.tm_year, + .tm_mon = a.tm_mon - b.tm_mon, + .tm_mday = a.tm_mday - b.tm_mday, + }; + if (a.tm_mon < b.tm_mon) { + diff.tm_year--; + diff.tm_mon += 12; + } + if (a.tm_mday < b.tm_mday) { + diff.tm_mon--; + diff.tm_mday = 0; + while (dateAdd(b, diff).tm_mday != a.tm_mday) diff.tm_mday++; + } + time_t atime = timegm(&a), btime = timegm(&b); + diff.tm_yday = (atime - btime) / 24 / 60 / 60; + return diff; +} + +static void printDate(struct tm date) { + printf( + "%s %s %d %d\n", + Days[date.tm_wday], Months[date.tm_mon], + date.tm_mday, 1900 + date.tm_year + ); +} + +static void printScalar(struct tm scalar) { + if (scalar.tm_year) printf("%dy ", scalar.tm_year); + if (scalar.tm_mon) printf("%dm ", scalar.tm_mon); + if (scalar.tm_mday % 7) { + printf("%dd ", scalar.tm_mday); + } else if (scalar.tm_mday) { + printf("%dw ", scalar.tm_mday / 7); + } + if (scalar.tm_yday && scalar.tm_mon) { + if (scalar.tm_yday % 7 == 0) { + printf("(%dw) ", scalar.tm_yday / 7); + } + printf("(%dd) ", scalar.tm_yday); + } + printf("\n"); +} + +%} + +%token Number Month Day +%left '+' '-' +%right '<' '>' + +%% + +expr: + date { printDate($1); } + | scalar { printScalar($1); } + ; + +date: + dateLit + | '(' date ')' { $$ = $2; } + | '<' date { $$ = dateSub($2, Week); } + | '>' date { $$ = dateAdd($2, Week); } + | date '+' scalar { $$ = dateAdd($1, $3); } + | date '-' scalar { $$ = dateSub($1, $3); } + ; + +scalar: + scalarLit + | '(' scalar ')' { $$ = $2; } + | scalar '+' scalar { $$ = scalarAdd($1, $3); } + | scalar '-' scalar { $$ = scalarSub($1, $3); } + | date '-' date { $$ = dateDiff($1, $3); } + ; + +dateLit: + { $$ = today(); } + | '.' { $$ = today(); } + | Month Number { $$ = monthDay($1.tm_mon, $2.tm_sec); } + | Month Number Number { $$ = monthDayYear($1.tm_mon, $2.tm_sec, $3.tm_sec); } + | Day { $$ = weekDay($1.tm_wday); } + ; + +scalarLit: + Number 'd' { $$ = (struct tm) { .tm_mday = $1.tm_sec }; } + | Number 'w' { $$ = (struct tm) { .tm_mday = 7 * $1.tm_sec }; } + | Number 'm' { $$ = (struct tm) { .tm_mon = $1.tm_sec }; } + | Number 'y' { $$ = (struct tm) { .tm_year = $1.tm_sec }; } + ; + +%% + +static void yyerror(const char *str) { + warnx("%s", str); +} + +static const char *input; + +static int yylex(void) { + while (isspace(*input)) input++; + if (!*input) return EOF; + + if (isdigit(*input)) { + char *rest; + yylval.tm_sec = strtol(input, &rest, 10); + input = rest; + return Number; + } + + for (int i = 0; i < 7; ++i) { + if (strncasecmp(input, Days[i], 3)) continue; + while (isalpha(*input)) input++; + yylval.tm_wday = i; + return Day; + } + + for (int i = 0; i < 12; ++i) { + if (strncasecmp(input, Months[i], 3)) continue; + while (isalpha(*input)) input++; + yylval.tm_mon = i; + return Month; + } + + return *input++; +} + +int main(int argc, char *argv[]) { + if (argc > 1) { + input = argv[1]; + return yyparse(); + } + + struct tm date = today(); + printDate(date); + printf("\n"); + + char *line = NULL; + size_t cap = 0; + while (0 < getline(&line, &cap, stdin)) { + if (line[0] == '\n') continue; + + if (today().tm_mday != date.tm_mday) { + warnx("the date has changed"); + date = today(); + } + + input = line; + yyparse(); + printf("\n"); + } +} diff --git a/bin/xx.c b/bin/xx.c new file mode 100644 index 00000000..6d04f2f5 --- /dev/null +++ b/bin/xx.c @@ -0,0 +1,142 @@ +/* Copyright (C) 2017 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <ctype.h> +#include <err.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> +#include <unistd.h> + +typedef unsigned char byte; + +static bool zero(const byte *ptr, size_t size) { + for (size_t i = 0; i < size; ++i) { + if (ptr[i]) return false; + } + return true; +} + +static struct { + size_t cols; + size_t group; + size_t blank; + bool ascii; + bool offset; + bool skip; +} options = { 16, 8, 0, true, true, false }; + +static void dump(FILE *file) { + bool skip = false; + + byte buf[options.cols]; + size_t offset = 0; + for ( + size_t size; + (size = fread(buf, 1, sizeof(buf), file)); + offset += size + ) { + if (options.skip) { + if (zero(buf, size)) { + if (!skip) printf("*\n"); + skip = true; + continue; + } else { + skip = false; + } + } + + if (options.blank) { + if (offset && offset % options.blank == 0) { + printf("\n"); + } + } + + if (options.offset) { + printf("%08zX: ", offset); + } + + for (size_t i = 0; i < sizeof(buf); ++i) { + if (options.group) { + if (i && !(i % options.group)) { + printf(" "); + } + } + if (i < size) { + printf("%02hhX ", buf[i]); + } else { + printf(" "); + } + } + + if (options.ascii) { + printf(" "); + for (size_t i = 0; i < size; ++i) { + if (options.group) { + if (i && !(i % options.group)) { + printf(" "); + } + } + printf("%c", isprint(buf[i]) ? buf[i] : '.'); + } + } + + printf("\n"); + } +} + +static void undump(FILE *file) { + byte c; + int match; + while (0 < (match = fscanf(file, " %hhx", &c))) { + printf("%c", c); + } + if (!match) errx(EX_DATAERR, "invalid input"); +} + +int main(int argc, char *argv[]) { + bool reverse = false; + const char *path = NULL; + + int opt; + while (0 < (opt = getopt(argc, argv, "ac:g:p:rsz"))) { + switch (opt) { + break; case 'a': options.ascii ^= true; + break; case 'c': options.cols = strtoul(optarg, NULL, 0); + break; case 'g': options.group = strtoul(optarg, NULL, 0); + break; case 'p': options.blank = strtoul(optarg, NULL, 0); + break; case 'r': reverse = true; + break; case 's': options.offset ^= true; + break; case 'z': options.skip ^= true; + break; default: return EX_USAGE; + } + } + if (argc > optind) path = argv[optind]; + if (!options.cols) return EX_USAGE; + + FILE *file = path ? fopen(path, "r") : stdin; + if (!file) err(EX_NOINPUT, "%s", path); + + if (reverse) { + undump(file); + } else { + dump(file); + } + if (ferror(file)) err(EX_IOERR, "%s", path); + + return EX_OK; +} diff --git a/doc/pdf/.gitignore b/doc/pdf/.gitignore new file mode 100644 index 00000000..a1363379 --- /dev/null +++ b/doc/pdf/.gitignore @@ -0,0 +1 @@ +*.pdf diff --git a/doc/pdf/Makefile b/doc/pdf/Makefile new file mode 100644 index 00000000..7afbdcf2 --- /dev/null +++ b/doc/pdf/Makefile @@ -0,0 +1,31 @@ +PDFS += abi.pdf +PDFS += c11.pdf +PDFS += elf.pdf +PDFS += intel-64-opt.pdf +PDFS += intel-64-sdm-vol-1.pdf +PDFS += intel-64-sdm-vol-2.pdf +PDFS += intel-64-sdm-vol-3.pdf +PDFS += intel-64-sdm-vol-4.pdf +PDFS += multiboot.pdf + +ELF = https://refspecs.linuxbase.org/elf +INTEL = https://software.intel.com/sites/default/files/managed + +URL.abi.pdf = ${ELF}/x86_64-abi-0.99.pdf +URL.c11.pdf = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf +URL.elf.pdf = ${ELF}/elf.pdf +URL.intel-64-opt.pdf = ${INTEL}/9e/bc/64-ia-32-architectures-optimization-manual.pdf +URL.intel-64-sdm-vol-1.pdf = ${INTEL}/a4/60/253665-sdm-vol-1.pdf +URL.intel-64-sdm-vol-2.pdf = ${INTEL}/a4/60/325383-sdm-vol-2abcd.pdf +URL.intel-64-sdm-vol-3.pdf = ${INTEL}/a4/60/325384-sdm-vol-3abcd.pdf +URL.intel-64-sdm-vol-4.pdf = ${INTEL}/22/0d/335592-sdm-vol-4.pdf +URL.multiboot.pdf = https://www.gnu.org/software/grub/manual/multiboot/multiboot.pdf + +all: ${PDFS} + +${PDFS}: + curl -Lf -o $@ ${URL.$@} + chmod a-w $@ + +clean: + rm -f ${PDFS} diff --git a/doc/rfc/.gitignore b/doc/rfc/.gitignore new file mode 100644 index 00000000..cc3245d4 --- /dev/null +++ b/doc/rfc/.gitignore @@ -0,0 +1,2 @@ +rfc +rfctags diff --git a/doc/rfc/Makefile b/doc/rfc/Makefile new file mode 100644 index 00000000..31e238a0 --- /dev/null +++ b/doc/rfc/Makefile @@ -0,0 +1,38 @@ +PREFIX ?= ~/.local +MANDIR ?= ${PREFIX}/share/man + +MODULE = ftp.rfc-editor.org::rfcs-text-only +RFCS = ${MODULE}/rfc-index.txt ${MODULE}/'rfc[1-9]*.txt' + +all: rfc rfctags + +.SUFFIXES: .in .pl + +.in: + sed 's|%%PREFIX%%|${PREFIX}|g' $< > $@ + chmod a+x $@ + +.pl: + cp -f $< $@ + chmod a+x $@ + +clean: + rm -f rfc rfctags + +install: rfc rfctags rfc.1 + install -d ${PREFIX}/bin ${MANDIR}/man1 + install rfc rfctags ${PREFIX}/bin + install -m 644 rfc.1 ${MANDIR}/man1 + ln -fs rfc.1 ${MANDIR}/man1/rfctags.1 + +sync: + install -d ${PREFIX}/share + rsync -ptz ${RFCS} ${PREFIX}/share/rfc + +compress: + find ${PREFIX}/share/rfc -name '*.txt' | xargs gzip -9f + +uninstall: + rm -f ${PREFIX}/bin/rfc ${PREFIX}/bin/rfctags + rm -f ${MANDIR}/man1/rfc.1 ${MANDIR}/man1/rfctags.1 + rm -fr ${PREFIX}/share/rfc diff --git a/doc/rfc/rfc.1 b/doc/rfc/rfc.1 new file mode 100644 index 00000000..ece5a901 --- /dev/null +++ b/doc/rfc/rfc.1 @@ -0,0 +1,53 @@ +.Dd January 18, 2021 +.Dt RFC 1 +.Os +. +.Sh NAME +.Nm rfc , +.Nm rfctags +.Nd view IETF RFCs +. +.Sh SYNOPSIS +.Nm rfc +.Op Ar number +.Nm rfctags +.Op Ar +. +.Sh DESCRIPTION +The +.Nm rfc +utility displays +an IETF RFC by number, +or the RFC index if no number is specified. +The RFC is displayed in the +.Ev PAGER +with a tags file generated by +.Nm rfctags . +. +.Pp +The +.Nm rfctags +utility generates tags +for RFC text file +section numbers, +section names +and bracketed references. +. +.Sh ENVIRONMENT +.Bl -tag -width Ds +.It Ev PAGER +The program used to display RFCs. +It must accept the +.Fl T +flag for specifying +the path of the tags file. +The default is +.Ev PAGER=less . +.El +. +.Sh SEE ALSO +.Xr ctags 1 , +.Xr less 1 +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/doc/rfc/rfc.in b/doc/rfc/rfc.in new file mode 100644 index 00000000..16081c83 --- /dev/null +++ b/doc/rfc/rfc.in @@ -0,0 +1,19 @@ +#!/bin/sh +set -eu + +mktemp='mktemp -t rfc' +[ "$(uname)" = 'OpenBSD' ] && mktemp="${mktemp}.XXXXXXXXXX" + +rfc=%%PREFIX%%/share/rfc/"rfc${1:--index}.txt" +tags=$($mktemp) +trap 'rm "${tags}"' EXIT + +if test -f "${rfc}.gz"; then + txt=$($mktemp) + trap 'rm "${txt}" "${tags}"' EXIT + gunzip -c "${rfc}.gz" >"${txt}" + rfc=$txt +fi + +%%PREFIX%%/bin/rfctags "${rfc}" >"${tags}" +${PAGER:-less} -T "${tags}" "${rfc}" diff --git a/doc/rfc/rfctags.pl b/doc/rfc/rfctags.pl new file mode 100644 index 00000000..05173d00 --- /dev/null +++ b/doc/rfc/rfctags.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use open ':encoding(ISO-8859-1)'; + +($,, $\) = ("\t", "\n"); +while (<>) { + chomp; + # Section headings + if (/^([\d.]+|[A-Z][.])\s+([^\t]+)?/) { + print $1, $ARGV, $.; + print $2, $ARGV, $. if $2; + print $1, $ARGV, $. if $1 =~ /^([\d.]+)[.]$/; + } + # References + if (/^\s*(\[[\w-]+\])\s{2,}/) { + print $1, $ARGV, $.; + print "\\$1", $ARGV, $.; # vim ^] prepends \ to [ + } + close ARGV if eof; +} diff --git a/doc/zlib/Makefile b/doc/zlib/Makefile new file mode 100644 index 00000000..6cfd4a42 --- /dev/null +++ b/doc/zlib/Makefile @@ -0,0 +1,87 @@ +PREFIX ?= ~/.local +MANDIR ?= ${PREFIX}/share/man + +MAN += adler32.3 +MAN += adler32_combine.3 +MAN += compress.3 +MAN += compressBound.3 +MAN += crc32.3 +MAN += crc32_combine.3 +MAN += deflate.3 +MAN += deflateBound.3 +MAN += deflateCopy.3 +MAN += deflateEnd.3 +MAN += deflateGetDictionary.3 +MAN += deflateInit.3 +MAN += deflateInit2.3 +MAN += deflateParams.3 +MAN += deflatePending.3 +MAN += deflatePrime.3 +MAN += deflateReset.3 +MAN += deflateSetDictionary.3 +MAN += deflateSetHeader.3 +MAN += deflateTune.3 +MAN += gzbuffer.3 +MAN += gzclose.3 +MAN += gzdirect.3 +MAN += gzeof.3 +MAN += gzerror.3 +MAN += gzflush.3 +MAN += gzfread.3 +MAN += gzfwrite.3 +MAN += gzgetc.3 +MAN += gzgets.3 +MAN += gzoffset.3 +MAN += gzopen.3 +MAN += gzprintf.3 +MAN += gzputc.3 +MAN += gzputs.3 +MAN += gzread.3 +MAN += gzseek.3 +MAN += gzsetparams.3 +MAN += gzungetc.3 +MAN += gzwrite.3 +MAN += inflate.3 +MAN += inflateBack.3 +MAN += inflateBackEnd.3 +MAN += inflateBackInit.3 +MAN += inflateCopy.3 +MAN += inflateEnd.3 +MAN += inflateGetDictionary.3 +MAN += inflateGetHeader.3 +MAN += inflateInit.3 +MAN += inflateInit2.3 +MAN += inflateMark.3 +MAN += inflatePrime.3 +MAN += inflateReset.3 +MAN += inflateSetDictionary.3 +MAN += inflateSync.3 +MAN += uncompress.3 +MAN += zlibCompileFlags.3 +MAN += zlibVersion.3 + +MLINKS += adler32.3 adler32_z.3 +MLINKS += compress.3 compress2.3 +MLINKS += crc32.3 crc32_z.3 +MLINKS += gzclose.3 gzclose_r.3 +MLINKS += gzclose.3 gzclose_w.3 +MLINKS += gzerror.3 gzclearerr.3 +MLINKS += gzopen.3 gzdopen.3 +MLINKS += gzseek.3 gzrewind.3 +MLINKS += gzseek.3 gztell.3 +MLINKS += inflateReset.3 inflateReset2.3 +MLINKS += uncompress.3 uncompress2.3 + +lint: + mandoc -T lint ${MAN} | grep -v 'referenced manual not found' + +install: + install -d ${MANDIR}/man3 + install -m 644 ${MAN} ${MANDIR}/man3 + set -- ${MLINKS}; while [ -n "$$*" ]; do \ + ln -fs $$1 ${MANDIR}/man3/$$2; shift 2; done + +uninstall: + rm -f ${MAN:%=${MANDIR}/man3/%} + set -- ${MLINKS}; while [ -n "$$*" ]; do \ + rm -f ${MANDIR}/man3/$$2; shift 2; done diff --git a/doc/zlib/adler32.3 b/doc/zlib/adler32.3 new file mode 100644 index 00000000..d713d952 --- /dev/null +++ b/doc/zlib/adler32.3 @@ -0,0 +1,65 @@ +.Dd January 15, 2017 +.Dt ADLER32 3 +.Os +. +.Sh NAME +.Nm adler32 , +.Nm adler32_z +.Nd update Adler-32 checksum +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft uLong +.Fn adler32 "uLong adler" "const Bytef *buf" "uInt len" +.Ft uLong +.Fn adler32_z "uLong adler" "const Bytef *buf" "z_size_t len" +. +.Sh DESCRIPTION +Update a running Adler-32 checksum with the bytes +.Fa "buf[0..len-1]" +and return the updated checksum. +If +.Fa buf +is +.Dv Z_NULL , +this function returns +the required initial value for the checksum. +. +.Pp +An Adler-32 checksum is almost as reliable as a CRC-32 +but can be computed much faster. +. +.Pp +.Fn adler32_z +is the same as +.Fn adler32 , +but with a +.Vt size_t +length. +. +.Sh EXAMPLES +.Bd -literal -offset indent +uLong adler = adler32(0L, Z_NULL, 0); + +while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); +} +if (adler != original_adler) error(); +.Ed +. +.Sh SEE ALSO +.Xr adler32_combine 3 , +.Xr crc32 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/adler32_combine.3 b/doc/zlib/adler32_combine.3 new file mode 100644 index 00000000..861f235b --- /dev/null +++ b/doc/zlib/adler32_combine.3 @@ -0,0 +1,63 @@ +.Dd January 15, 2017 +.Dt ADLER32_COMBINE 3 +.Os +. +.Sh NAME +.Nm adler32_combine +.Nd combine Adler-32 checksums +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft uLong +.Fn adler32_combine "uLong adler1" "uLong adler2" "z_off_t len2" +. +.Sh DESCRIPTION +Combine two Adler-32 checksums into one. +For two sequences of bytes, +.Va seq1 +and +.Va seq2 +with lengths +.Va len1 +and +.Va len2 , +Adler-32 checksums were calculated for each, +.Va adler1 +and +.Va adler2 . +.Fn adler32_combine +returns the Adler-32 checksum of +.Va seq1 +and +.Va seq2 +concatenated, +requiring only +.Fa adler1 , +.Fa adler2 , +and +.Fa len2 . +Note that the +.Vt z_off_t +type +.Pq like Vt off_t +is a signed integer. +If +.Fa len2 +is negative, +the result has no meaning or utility. +. +.Sh SEE ALSO +.Xr adler32 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/compress.3 b/doc/zlib/compress.3 new file mode 100644 index 00000000..22b229ee --- /dev/null +++ b/doc/zlib/compress.3 @@ -0,0 +1,84 @@ +.Dd January 15, 2017 +.Dt COMPRESS 3 +.Os +. +.Sh NAME +.Nm compress , +.Nm compress2 +.Nd compress source buffer into destination buffer +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +. +.Ft int +.Fo compress +.Fa "Bytef *dest" +.Fa "uLongf *destLen" +.Fa "const Bytef *source" +.Fa "uLong sourceLen" +.Fc +. +.Ft int +.Fo compress2 +.Fa "Bytef *dest" +.Fa "uLongf *destLen" +.Fa "const Bytef *source" +.Fa "uLong sourceLen" +.Fa "int level" +.Fc +. +.Sh DESCRIPTION +Compresses the source buffer into the destination buffer. +.Fa sourceLen +is the byte length of the source buffer. +Upon entry, +.Fa destLen +is the total size of the destination buffer, +which must be at least the value returned by +.Fn compressBound sourceLen . +Upon exit, +.Fa destLen +is the actual size of the compressed data. +. +.Pp +.Fn compress +is equivalent to +.Fn compress2 +with a +.Fa level +parameter of +.Dv Z_DEFAULT_COMPRESSION . +. +.Sh RETURN VALUES +.Fn compress +and +.Fn compress2 +return +.Dv Z_OK +on success, +.Dv Z_MEM_ERROR +if there was not enough memory, +.Dv Z_BUF_ERROR +if there was not enough room in the output buffer, +.Dv Z_STREAM_ERROR +if the +.Fa level +parameter is invalid. +. +.Sh SEE ALSO +.Xr compressBound 3 , +.Xr deflateInit 3 , +.Xr uncompress 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/compressBound.3 b/doc/zlib/compressBound.3 new file mode 100644 index 00000000..5800e2ba --- /dev/null +++ b/doc/zlib/compressBound.3 @@ -0,0 +1,44 @@ +.Dd January 15, 2017 +.Dt COMPRESSBOUND 3 +.Os +. +.Sh NAME +.Nm compressBound +.Nd compressed size upper bound +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft uLong +.Fn compressBound "uLong sourceLen" +. +.Sh DESCRIPTION +.Fn compressBound +returns an upper bound on the compressed size after +.Xr compress 3 +or +.Xr compress2 3 +on +.Fa sourceLen +bytes. +It would be used before a +.Xr compress 3 +or +.Xr compress2 3 +call to allocate the destination buffer. +. +.Sh SEE ALSO +.Xr compress 3 , +.Xr deflateBound 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/crc32.3 b/doc/zlib/crc32.3 new file mode 100644 index 00000000..3c9cc8c4 --- /dev/null +++ b/doc/zlib/crc32.3 @@ -0,0 +1,66 @@ +.Dd January 15, 2017 +.Dt CRC32 3 +.Os +. +.Sh NAME +.Nm crc32 , +.Nm crc32_z +.Nd update CRC-32 checksum +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft uLong +.Fn crc32 "uLong crc" "const Bytef *buf" "uInt len" +.Ft uLong +.Fn crc32_z "uLong crc" "const Bytef *buf" "z_size_t len" +. +.Sh DESCRIPTION +Update a running CRC-32 with the bytes +.Fa "buf[0..len-1]" +and return the updated CRC-32. +If +.Fa buf +is +.Dv Z_NULL , +this function returns +the required initial value for the CRC. +Pre- and post-conditioning +(one's complement) +is performed within this function +so it shouldn't be done +by the application. +. +.Pp +.Fn crc32_z +is the same as +.Fn crc32 , +but with a +.Vt size_t +length. +. +.Sh EXAMPLES +.Bd -literal -offset indent +uLong crc = crc32(0L, Z_NULL, 0); + +while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); +} +if (crc != original_crc) error(); +.Ed +. +.Sh SEE ALSO +.Xr adler32 3 , +.Xr crc32_combine 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/crc32_combine.3 b/doc/zlib/crc32_combine.3 new file mode 100644 index 00000000..2f79f623 --- /dev/null +++ b/doc/zlib/crc32_combine.3 @@ -0,0 +1,54 @@ +.Dd January 15, 2017 +.Dt CRC32_COMBINE 3 +.Os +. +.Sh NAME +.Nm crc32_combine +.Nd combine CRC-32 checksums +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft uLong +.Fn crc32_combine "uLong crc1" "uLong crc2" "z_off_t len2" +. +.Sh DESCRIPTION +Combine two CRC-32 check values into one. +For two sequences of bytes, +.Va seq1 +and +.Va seq2 +with lengths +.Va len1 +and +.Va len2 , +CRC-32 check values were calculated for each, +.Va crc1 +and +.Va crc2 . +.Fn crc32_combine +returns the CRC-32 check value of +.Va seq1 +and +.Va seq2 +concatenated, +requiring only +.Fa crc1 , +.Fa crc2 , +and +.Fa len2 . +. +.Sh SEE ALSO +.Xr crc32 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflate.3 b/doc/zlib/deflate.3 new file mode 100644 index 00000000..7df313ee --- /dev/null +++ b/doc/zlib/deflate.3 @@ -0,0 +1,370 @@ +.Dd January 15, 2017 +.Dt DEFLATE 3 +.Os +. +.Sh NAME +.Nm deflate +.Nd deflate compression +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn deflate "z_streamp strm" "int flush" +. +.Sh DESCRIPTION +.Fn deflate +compresses as much data as possible, +and stops when the input buffer becomes empty +or the output buffer becomes full. +It may introduce some output latency +(reading input without producing any output) +except when forced the flush. +. +.Pp +The detailed semantics are as follows. +.Fn deflate +performs one or both of the following actions: +. +.Bl -dash +.It +Compress more input starting at +.Fa next_in +and update +.Fa next_in +and +.Fa avail_in +accordingly. +If not all input can be processed +(because there is not enough room in the output buffer), +.Fa next_in +and +.Fa avail_in +are updated +and processing will resume at this point +for the next call of +.Fn deflate . +. +.It +Generate more output starting at +.Fa next_out +and update +.Fa next_out +and +.Fa avail_out +accordingly. +This action is forced if the parameter +.Fa flush +is non-zero. +Forcing flush frequently degrades the compression ratio, +so this parameter should be set only when necessary. +Some output may be provided even if +.Fa flush +is zero. +.El +. +.Pp +Before the call of +.Fn deflate , +the application should ensure that +at least one of the actions is possible, +by providing more input +and/or consuming more output, +and updating +.Fa avail_in +or +.Fa avail_out +accordingly; +.Fa avail_out +should never be zero before the call. +The application can consume the compressed output +when it wants, +for example when the output buffer is full +.Po +.Fa avail_out +== 0 +.Pc , +or after each call of +.Fn deflate . +If +.Fn deflate +returns +.Dv Z_OK +and with zero +.Fa avail_out , +it must be called again after making room in the output buffer +because there might be more output pending. +See +.Xr deflatePending 3 , +which can be used if desired to determine +whether or not there is more output in that case. +. +.Pp +Normally the parameter +.Fa flush +is set to +.Dv Z_NO_FLUSH , +which allows +.Fn deflate +to decide how much data to accumulate before producing output, +in order to maximize compression. +. +.Pp +If the parameter +.Fa flush +is set to +.Dv Z_SYNC_FLUSH , +all pending output is flushed to the output buffer +and the output is aligned on a byte boundary, +so that the decompressor can get all input data available so far. +.Po +In particular +.Fa avail_in +is zero after the call if enough output space +has been provided before the call. +.Pc \& +Flushing may degrade compression for some compression algorithms +and so it should be used only when necessary. +This completes the current deflate block +and follows it with an empty stored block +that is three bits plus filler bits to the next byte, +followed by four bytes +(00 00 ff ff). +. +.Pp +If +.Fa flush +is set to +.Dv Z_PARTIAL_FLUSH , +all pending output is flushed to the output buffer, +but the output is not aligned to a byte boundary. +All of the input data so far will be available to the decompressor, +as for +.Dv Z_SYNC_FLUSH . +This completes the current deflate block +and follows it with an empty fixed codes block +that is 10 bits long. +This assures that enough bytes are output +in order for the decompressor to finish the block +before the empty fixed codes block. +. +.Pp +If +.Fa flush +is set to +.Dv Z_BLOCK , +a deflate block is completed and emitted, +as for +.Dv Z_SYNC_FLUSH , +but the output is not aligned on a byte boundary, +and up to seven bits of the current block +are held to be written as the next byte +after the next deflate block is completed. +In this case, +the decompressor may not be provided enough bits +at this point in order to complete decompression +of the data provided so far to the compressor. +It may need to wait for the next block to be emitted. +This is for advanced applications +that need to control the emission of deflate blocks. +. +.Pp +If +.Fa flush +is set to +.Dv Z_FULL_FLUSH , +all output is flushed as with +.Dv Z_SYNC_FLUSH , +and the compression state is reset +so that decompression can restart from this point +if previous compressed data has been damaged +or if random access is desired. +Using +.Dv Z_FULL_FLUSH +too often can seriously degrade compression. +. +.Pp +If +.Fn deflate +returns with +.Fa avail_out +== 0, +this function must be called again +with the same value of the +.Fa flush +parameter +and more output space +.Po +updated +.Fa avail_out +.Pc , +until the flush is complete +.Po +.Fn deflate +returns with non-zero +.Fa avail_out +.Pc . +In the case of a +.Dv Z_FULL_FLUSH +or +.Dv Z_SYNC_FLUSH , +make sure that +.Fa avail_out +is greater than six +to avoid repeated flush markers +due to +.Fa avail_out +== 0 +on return. +. +.Pp +If the parameter +.Fa flush +is set to +.Dv Z_FINISH , +pending input is processed, +pending output is flushed and +.Fn deflate +returns with +.Dv Z_STREAM_END +if there was enough output space. +If +.Fn deflate +returns with +.Dv Z_OK +or +.Dv Z_BUF_ERROR , +this function must be called again with +.Dv Z_FINISH +and more output space +.Pq updated Fa avail_out +but no more input data, +until it returns with +.Dv Z_STREAM_END +or an error. +After +.Fn deflate +has returned +.Dv Z_STREAM_END , +the only possible operations on the stream are +.Xr deflateReset 3 +or +.Xr deflateEnd 3 . +. +.Pp +.Dv Z_FINISH +can be used in the first +.Fn deflate +call after +.Xr deflateInit 3 +if all the compression is to be done in a single step. +In order to complete in one call, +.Fa avail_out +must be at least the value returned by +.Xr deflateBound 3 . +Then +.Fn deflate +is guaranteed to return +.Dv Z_STREAM_END . +If not enough output space is provided, +.Fn deflate +will not return +.Dv Z_STREAM_END , +and it must be called again as described above. +. +.Pp +.Fn deflate +sets +.Fa strm->adler +to the Adler-32 checksum +of all input read so far +.Po +that is, +.Fa total_in +bytes +.Pc . +If a gzip stream is being generated, +then +.Fa strm->adler +will be the CRC-32 checksum of the input read so far. +See +.Xr deflateInit2 3 . +. +.Pp +.Fn deflate +may update +.Fa strm->data_type +if it can make a good guess +about the input data type +.Po +.Dv Z_BINARY +or +.Dv Z_TEXT +.Pc . +If in doubt, +the date is considered binary. +This field is only for information purposes +and does not affect the compression algorithm in any manner. +. +.Sh RETURN VALUES +.Fn deflate +returns +.Dv Z_OK +if some progress has been made +(more input processed or more output produced), +.Dv Z_STREAM_END +if all input has been consumed +and all output has been produced +.Po +only when +.Fa flush +is set to +.Dv Z_FINISH +.Pc , +.Dv Z_STREAM_ERROR +if the stream state was inconsistent +.Po +for example if +.Fa next_in +or +.Fa next_out +was +.Dv Z_NULL +or the state was inadvertently written over +by the application +.Pc , +or +.Dv Z_BUF_ERROR +if no progress is possible +.Po +for example +.Fa avail_in +or +.Fa avail_out +was zero +.Pc . +Note that +.Dv Z_BUF_ERROR +is not fatal, +and +.Fn deflate +can be called again with more input and more output space +to continue compressing. +. +.Sh SEE ALSO +.Xr deflateEnd 3 , +.Xr deflateInit 3 , +.Xr deflatePending 3 , +.Xr inflate 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateBound.3 b/doc/zlib/deflateBound.3 new file mode 100644 index 00000000..63e80246 --- /dev/null +++ b/doc/zlib/deflateBound.3 @@ -0,0 +1,71 @@ +.Dd January 15, 2017 +.Dt DEFLATEBOUND 3 +.Os +. +.Sh NAME +.Nm deflateBound +.Nd compressed size upper bound +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft uLong +.Fn deflateBound "z_streamp strm" "uLong sourceLen" +. +.Sh DESCRIPTION +.Fn deflateBound +returns an upper bound +on the compressed size +after deflation of +.Fa sourceLen +bytes. +It must be called after +.Xr deflateInit 3 +or +.Xr deflateInit2 3 , +and after +.Xr deflateSetHeader 3 , +if used. +This would be used +to allocate an output buffer +for deflation in a single pass, +and so would be called before +.Xr deflate 3 . +If that first +.Fn deflate +call is provided the +.Fa sourceLen +input bytes, +an output buffer allocated +to the size returned by +.Fn deflateBound , +and the flush value +.Dv Z_FINISH , +then +.Fn deflate +is guaranteed to return +.Dv Z_STREAM_END . +Note that it is possible +for the compressed size +to be larger than the value returned by +.Fn deflateBound +if flush options other than +.Dv Z_FINISH +or +.Dv Z_NO_FLUSH +are used. +. +.Sh SEE ALSO +.Xr compressBound 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateCopy.3 b/doc/zlib/deflateCopy.3 new file mode 100644 index 00000000..f30d6301 --- /dev/null +++ b/doc/zlib/deflateCopy.3 @@ -0,0 +1,66 @@ +.Dd January 15, 2017 +.Dt DEFLATECOPY 3 +.Os +. +.Sh NAME +.Nm deflateCopy +.Nd copy deflate stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn deflateCopy "z_streamp dest" "z_streamp source" +. +.Sh DESCRIPTION +Sets the destination stream +as a complete copy of the source stream. +. +.Pp +This function can be useful when +several compression strategies will be tried, +for example when there are several ways of +pre-processing the input data with a filter. +The streams that will be discarded +should then be freed by calling +.Xr deflateEnd 3 . +Note that +.Fn deflateCopy +duplicates the internal compression state +which can be quite large, +so this strategy is slow +and can consume lots of memory. +. +.Sh RETURN VALUES +.Fn deflateCopy +returns +.Dv Z_OK +if success, +.Dv Z_MEM_ERROR +if there was not enough memory, +.Dv Z_STREAM_ERROR +if the source stream state was inconsistent +.Po +such as +.Fa zalloc +being +.Dv Z_NULL +.Pc . +.Fa msg +is left unchanged +in both source and destination. +. +.Sh SEE ALSO +.Xr deflateInit 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateEnd.3 b/doc/zlib/deflateEnd.3 new file mode 100644 index 00000000..e24259a3 --- /dev/null +++ b/doc/zlib/deflateEnd.3 @@ -0,0 +1,50 @@ +.Dd January 15, 2017 +.Dt DEFLATEEND 3 +.Os +. +.Sh NAME +.Nm deflateEnd +.Nd free deflate stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn deflateEnd "z_streamp strm" +. +.Sh DESCRIPTION +All dynamically allocated data structures +for this stream are freed. +This function discards any unprocessed input +and does not flush any pending output. +. +.Sh RETURN VALUES +.Fn deflateEnd +returns +.Dv Z_OK +if success, +.Dv Z_STREAM_ERROR +if the stream state was inconsistent, +.Dv Z_DATA_ERROR +if the stream was freed prematurely +(some input or output was discarded). +In the error case, +.Fa msg +may be set but then points to a static string +(which must not be deallocated). +. +.Sh SEE ALSO +.Xr deflate 3 , +.Xr deflateInit 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateGetDictionary.3 b/doc/zlib/deflateGetDictionary.3 new file mode 100644 index 00000000..403f6d10 --- /dev/null +++ b/doc/zlib/deflateGetDictionary.3 @@ -0,0 +1,79 @@ +.Dd January 15, 2017 +.Dt DEFLATEGETDICTIONARY 3 +.Os +. +.Sh NAME +.Nm deflateGetDictionary +.Nd deflate sliding dictionary +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fo deflateGetDictionary +.Fa "z_streamp strm" +.Fa "Bytef *dictionary" +.Fa "uInt *dictLength" +.Fc +. +.Sh DESCRIPTION +Returns the sliding dictionary +being maintained by deflate. +.Fa dictLength +is set to the number of bytes in the dictionary, +and that many bytes are copied to +.Fa dictionary . +.Fa dictionary +must have enough space, +where 32768 bytes is always enough. +If +.Fn deflateGetDictionary +is called with +.Fa dictionary +equal to +.Dv Z_NULL , +then only the dictionary length is returned, +and nothing is copied. +Similarly, +if +.Fa dictLength +is +.Dv Z_NULL , +then it is not set. +. +.Pp +.Fn deflateGetDictionary +may return a length less than the window size, +even when more than the window size in input +has been provided. +It may return up to 258 bytes less in that case, +due to how zlib's implementation of deflate +manages the sliding window and lookahead for matches, +where matches can be up to 258 bytes long. +If the application needs the last window-size bytes of input, +then that would need to be saved by the application +outside of zlib. +. +.Sh RETURN VALUES +.Fn deflateGetDictionary +returns +.Dv Z_OK +on success, +or +.Dv Z_STREAM_ERROR +if the stream state is inconsistent. +. +.Sh SEE ALSO +.Xr deflateSetDictionary 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateInit.3 b/doc/zlib/deflateInit.3 new file mode 100644 index 00000000..a893dd91 --- /dev/null +++ b/doc/zlib/deflateInit.3 @@ -0,0 +1,178 @@ +.Dd January 15, 2017 +.Dt DEFLATEINIT 3 +.Os +. +.Sh NAME +.Nm deflateInit +.Nd initialize deflate stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +. +.Ft typedef voidpf +.Fo (*alloc_func) +.Fa "voidpf opaque" +.Fa "uInt items" +.Fa "uInt size" +.Fc +. +.Ft typedef void +.Fo (*free_func) +.Fa "voidpf opaque" +.Fa "voidpf address" +.Fc +. +.Bd -literal +typedef struct z_stream_s { + z_const Bytef *next_in; + uInt avail_in; + uLong total_in; + + Bytef *next_out; + uInt avail_out; + uLong total_out; + + z_const char *msg; + struct internal_state FAR *state; + + alloc_func zalloc; + free_func zfree; + voidpf opaque; + + int data_type; + uLong adler; + uLong reserved; +} z_stream; +.Ed +. +.Pp +.Vt typedef z_stream FAR *z_streamp; +. +.Ft int +.Fn deflateInit "z_streamp strm" "int level" +. +.Sh DESCRIPTION +Initializes the internal stream state for compression. +The fields +.Fa zalloc , +.Fa zfree +and +.Fa opaque +must be initialized before by the caller. +If +.Fa zalloc +and +.Fa zfree +are set to +.Dv Z_NULL , +.Fn deflateInit +updates them to use default allocation functions. +.Fn deflateInit +does not perform any compression: +this will be done by +.Xr deflate 3 . +. +.Pp +The compression +.Fa level +must be +.Dv Z_DEFAULT_COMPRESSION , +or between 0 and 9: +1 gives best speed, +9 gives best compression, +0 gives no compression at all +(the input data is simply copied a block at a time). +.Dv Z_DEFAULT_COMPRESSION +requests a default compromise between speed and compression +(currently equivalent to level 6). +. +.Pp +The fields of +.Vt z_stream +are as follows: +. +.Bl -tag -width "data_type" +.It Fa next_in +next input byte +.It Fa avail_in +number of bytes available at +.Fa next_in +.It Fa total_in +total number of input bytes read so far +.It Fa next_out +next output byte will go here +.It Fa avail_out +remaining free space at +.Fa next_out +.It Fa total_out +total number of bytes output so far +.It Fa msg +last error message, +.Dv NULL +if no error +.It Fa state +not visible by applications +.It Fa zalloc +used to allocate the internal state +.It Fa zfree +used to free the internal state +.It Fa opaque +private data object passed to +.Fa zalloc +and +.Fa zfree +.It data_type +best guess about the data type: +binary or text for +.Xr deflate 3 , +or the decoding state for +.Xr inflate 3 +.It adler +Adler-32 or CRC-32 value of the uncompressed data +.It reserved +reserved for future use +.El +. +.Sh RETURN VALUES +.Fn deflateInit +returns +.Dv Z_OK +if success, +.Dv Z_MEM_ERROR +if there was not enough memory, +.Dv Z_STREAM_ERROR +if +.Fa level +is not a valid compression level, +or +.Dv Z_VERSION_ERROR +if the zlib library version +.Pq Xr zlibVersion 3 +is incompatible with the version assumed by the caller +.Pq Dv ZLIB_VERSION . +.Fa msg +is set to null +if there is no error message. +. +.Sh SEE ALSO +.Xr deflate 3 , +.Xr deflateCopy 3 , +.Xr deflateEnd 3 , +.Xr deflateInit2 3 , +.Xr deflatePrime 3 , +.Xr deflateReset 3 , +.Xr deflateSetDictionary 3 , +.Xr deflateTune 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateInit2.3 b/doc/zlib/deflateInit2.3 new file mode 100644 index 00000000..6a581ef8 --- /dev/null +++ b/doc/zlib/deflateInit2.3 @@ -0,0 +1,227 @@ +.Dd January 15, 2017 +.Dt DEFLATEINIT2 3 +.Os +. +.Sh NAME +.Nm deflateInit2 +.Nd deflate compression options +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fo deflateInit2 +.Fa "z_streamp strm" +.Fa "int level" +.Fa "int method" +.Fa "int windowBits" +.Fa "int memLevel" +.Fa "int strategy" +.Fc +. +.Sh DESCRIPTION +This is another version of +.Xr deflateInit 3 +with more compression options. +The fields +.Fa next_in , +.Fa zalloc , +.Fa zfree +and +.Fa opaque +must be initialized before by the caller. +. +.Pp +The +.Fa method +parameter is the compression method. +It must be +.Dv Z_DEFLATED +in this version of the library. +. +.Pp +The +.Fa windowBits +parameter is the base two logarithm +of the window size +(the size of the history buffer). +It should be in the range 8..15 +for this version of the library. +Larger values of this parameter +result in better compression +at the expense of memory usage. +The default value is 15 if +.Xr deflateInit 3 +is used instead. +. +.Pp +For the current implementation of +.Xr deflate 3 , +a +.Fa windowBits +value of 8 +(a window size of 256 bytes) +is not supported. +As a result, +a request for 8 +will result in 9 +(a 512-byte window). +In that case, +providing 8 to +.Xr inflateInit2 3 +will result in an error +when the zlib header with 9 +is checked against the initialization of +.Xr inflate 3 . +The remedy is to not use 8 with +.Fn deflateInit2 +with this initialization, +or at least in that case use 9 with +.Xr inflateInit2 3 . +. +.Pp +.Fa windowBits +can also be -8..-15 for raw deflate. +In this case, +.Fa -windowBits +determines the window size. +.Xr deflate 3 +will then generate raw deflate data +with no zlib header or trailer, +and will not compute a check value. +. +.Pp +.Fa windowBits +can also be greater than 15 +for optional gzip encoding. +Add 16 to +.Fa windowBits +to write a simple gzip header and trailer +around the compressed data +instead of a zlib wrapper. +The gzip header will have +no file name, +no extra data, +no comment, +no modification time (set to zero), +no header CRC, +and the operating system will be set +to the appropriate value, +if the operating system was determined at compile time. +If a gzip stream is being written, +.Fa strm->adler +is a CRC-32 instead of an Adler-32. +. +.Pp +For raw deflate or gzip encoding, +a request for a 256-byte window +is rejected as invalid, +since only the zlib header provides +a means of transmitting the window size +to the decompressor. +. +.Pp +The +.Fa memLevel +parameter specifies how much memory should be allocated +for the internal compression state. +.Fa memLevel=1 +uses minimum memory +but is slow and reduces compression ratio; +.Fa memLevel=9 +uses maximum memory for optimal speed. +The default value is 8. +See +.In zconf.h +for total memory usage +as a function of +.Fa windowBits +and +.Fa memLevel . +. +.Pp +The +.Fa strategy +parameter is used to tune the compression algorithm. +Use the value +.Dv Z_DEFAULT_STRATEGY +for normal data, +.Dv Z_FILTERED +for data produced by a filter +(or predictor), +.Dv Z_HUFFMAN_ONLY +to force Huffman encoding only +(no string match), +or +.Dv Z_RLE +to limit match distances to one +(run-length encoding). +Filtered data consists mostly of small values +with a somewhat random distribution. +In this case, +the compression algorithm +is tuned to compress them better. +The effect of +.Dv Z_FILTERED +is to force more Huffman coding +and less string matching; +it is somewhat intermediate between +.Dv Z_DEFAULT_STRATEGY +and +.Dv Z_HUFFMAN_ONLY . +.Dv Z_RLE +is designed to be almost as fast as +.Dv Z_HUFFMAN_ONLY , +but give better compression for PNG image data. +The +.Fa strategy +parameter only affects the compression ratio +but not the correctness of the compressed output +even if it is not set appropriately. +.Dv Z_FIXED +prevents the use of dynamic Huffman codes, +allowing for a simpler decoder +for special applications. +. +.Pp +.Fn deflateInit2 +does not perform any compression: +this will be done by +.Xr deflate 3 . +. +.Sh RETURN VALUES +.Fn deflateInit2 +returns +.Dv Z_OK +if success, +.Dv Z_MEM_ERROR +if there was not enough memory, +.Dv Z_STREAM_ERROR +if any parameter is invalid +(such as invalid method), +or +.Dv Z_VERSION_ERROR +if the zlib library version +.Pq Xr zlibVersion 3 +is incompatible with the version assumed by the caller +.Pq Dv ZLIB_VERSION . +.Fa msg +is set to null if there is no error message. +. +.Sh SEE ALSO +.Xr deflate 3 , +.Xr deflateInit 3 , +.Xr deflateParams 3 , +.Xr deflateSetHeader 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateParams.3 b/doc/zlib/deflateParams.3 new file mode 100644 index 00000000..8e770d4e --- /dev/null +++ b/doc/zlib/deflateParams.3 @@ -0,0 +1,123 @@ +.Dd January 15, 2017 +.Dt DEFLATEPARAMS 3 +.Os +. +.Sh NAME +.Nm deflateParams +.Nd update compression level and strategy +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn deflateParams "z_streamp strm" "int level" "int strategy" +. +.Sh DESCRIPTION +Dynamically update the compression level +and compression strategy. +The interpretation of +.Fa level +and +.Fa strategy +is as in +.Xr deflateInit2 3 . +This can be used to switch between compression +and straight copy of the input data, +or to switch to a different kind of input data +requiring a different strategy. +If the compression approach +(which is a function of the level) +or the strategy is changed, +and if any input has been consumed +in a previous +.Xr deflate 3 +call, +then the input available so far is compressed +with the old level and strategy using +.Fn deflate strm Z_BLOCK . +There are three approaches +for the compression levels +0, 1..3, and 4..9 respectively. +The new level and strategy +will take effect at the next call of +.Xr deflate 3 . +. +.Pp +If a +.Fn deflate strm Z_BLOCK +is performed by +.Fn deflateParams , +and it does not have enough output space to complete, +then the parameter change will not take effect. +In this case, +.Fn deflateParams +can be called again +with the same parameters +and more output space +to try again. +. +.Pp +In order to assure a change in the parameters +on the first try, +the deflate stream should be flushed using +.Xr deflate 3 +with +.Dv Z_BLOCK +or other flush request until +.Fa strm.avail_out +is not zero, +before calling +.Fn deflateParams . +Then no more input data +should be provided before the +.Fn deflateParams +call. +If this is done, +the old level and strategy +will be applied +to the data compressed before +.Fn deflateParams , +and the new level and strategy +will be applied +to the data compressed after +.Fn deflateParams . +. +.Sh RETURN VALUES +.Fn deflateParams +returns +.Dv Z_OK +on success, +.Dv Z_STREAM_ERROR +if the source stream state was inconsistent +or if a parameter was invalid, +or +.Dv Z_BUF_ERROR +if there was not enough output space +to complete the compression +of the available input data +before a change in the strategy or approach. +Note that in the case of a +.Dv Z_BUF_ERROR , +the parameters are not changed. +A return value of +.Dv Z_BUF_ERROR +is not fatal, +in which case +.Fn deflateParams +can be retried +with more output space. +. +.Sh SEE ALSO +.Xr deflateInit2 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflatePending.3 b/doc/zlib/deflatePending.3 new file mode 100644 index 00000000..1ce40fc2 --- /dev/null +++ b/doc/zlib/deflatePending.3 @@ -0,0 +1,56 @@ +.Dd January 15, 2017 +.Dt DEFLATEPENDING 3 +.Os +. +.Sh NAME +.Nm deflatePending +.Nd pending deflate output +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn deflatePending "z_streamp strm" "unsigned *pending" "int *bits" +. +.Sh DESCRIPTION +.Fn deflatePending +returns the number of bytes and bits +of output that have been generated, +but not yet provided in the available output. +The bytes not provided would be due to +the available output space having been consumed. +The number of bits of output not provided +are between 0 and 7, +where they await more bits to join them +in order to fill out a full byte. +If +.Fa pending +or +.Fa bits +are +.Dv Z_NULL , +then those values are not set. +. +.Sh RETURN VALUES +.Fn deflatePending +returns +.Dv Z_OK +if success, +or +.Dv Z_STREAM_ERROR +if the source stream state was inconsistent. +. +.Sh SEE ALSO +.Xr deflate 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflatePrime.3 b/doc/zlib/deflatePrime.3 new file mode 100644 index 00000000..639e715a --- /dev/null +++ b/doc/zlib/deflatePrime.3 @@ -0,0 +1,64 @@ +.Dd January 15, 2017 +.Dt DEFLATEPRIME 3 +.Os +. +.Sh NAME +.Nm deflatePrime +.Nd insert bits in deflate stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn deflatePrime "z_streamp strm" "int bits" "int value" +. +.Sh DESCRIPTION +.Fn deflatePrime +inserts bits in the deflate output stream. +The intent is that this function +is used to start off the deflate output +with the bits leftover +from a previous deflate stream +when appending to it. +As such, +this function can only be used for raw deflate, +and must be used before the first +.Xr deflate 3 +call +after a +.Xr deflateInit2 3 +or +.Xr deflateReset 3 . +.Fa bits +must be less than or equal to 16, +and that many of the least significant bits of +.Fa value +will be inserted in the output. +. +.Sh RETURN VALUES +.Fn deflatePrime +returns +.Dv Z_OK +if success, +.Dv Z_BUF_ERROR +if there was not enough room +in the internal buffer +to insert the bits, +or +.Dv Z_STREAM_ERROR +if the source stream state was inconsistent. +. +.Sh SEE ALSO +.Xr deflateInit2 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateReset.3 b/doc/zlib/deflateReset.3 new file mode 100644 index 00000000..7309ac15 --- /dev/null +++ b/doc/zlib/deflateReset.3 @@ -0,0 +1,57 @@ +.Dd January 15, 2017 +.Dt DEFLATERESET 3 +.Os +. +.Sh NAME +.Nm deflateReset +.Nd reset deflate stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn deflateReset "z_streamp strm" +. +.Sh DESCRIPTION +This function is equivalent to +.Xr deflateEnd 3 +followed by +.Xr deflateInit 3 , +but does not free and reallocate +the internal compression state. +The stream will leave the compression level +and any other attributes +that may have been set unchanged. +. +.Sh RETURN VALUES +.Fn deflateReset +returns +.Dv Z_OK +if success, +or +.Dv Z_STREAM_ERROR +if the source stream state was inconsistent +.Po +such as +.Fa zalloc +or +.Fa state +being +.Dv Z_NULL +.Pc . +. +.Sh SEE ALSO +.Xr deflateEnd 3 , +.Xr deflateInit 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateSetDictionary.3 b/doc/zlib/deflateSetDictionary.3 new file mode 100644 index 00000000..c2c9d7c2 --- /dev/null +++ b/doc/zlib/deflateSetDictionary.3 @@ -0,0 +1,142 @@ +.Dd January 15, 2017 +.Dt DEFLATESETDICTIONARY 3 +.Os +. +.Sh NAME +.Nm deflateSetDictionary +.Nd initialize compression dictionary +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fo deflateSetDictionary +.Fa "z_streamp strm" +.Fa "const Bytef *dictionary" +.Fa "uInt dictLength" +.Fc +. +.Sh DESCRIPTION +.Fn deflateSetDictionary +initializes the compression dictionary +from the given byte sequence +without producing any compressed output. +When using the zlib format, +this function must be called immediately after +.Xr deflateInit 3 , +.Xr deflateInit2 3 , +or +.Xr deflateReset 3 , +and before any call of +.Xr deflate 3 . +When doing raw deflate, +this function must be called +either before any call of +.Xr deflate 3 , +or immediately after the completion of a deflate block, +i.e. after all input has been consumed +and all output has been delivered +when using any of the flush options +.Dv Z_BLOCK , +.Dv Z_PARTIAL_FLUSH , +.Dv Z_SYNC_FLUSH , +or +.Dv Z_FULL_FLUSH . +The compressor and decompressor +must use exactly the same dictionary +.Po +see +.Xr inflateSetDictionary 3 +.Pc . +. +.Pp +The dictionary should consist of strings +(byte sequences) +that are likely to be encountered later +in the data to be compressed, +with the most commonly used strings +preferably put towards the end of the dictionary. +Using a dictionary is most useful +when the data to be compressed is short +and can be predicted with good accuracy; +the data can then be compressed better than +with the default empty dictionary. +. +.Pp +Depending on the size of +the compression data structures selected by +.Xr deflateInit 3 +or +.Xr deflateInit2 3 , +a part of the dictionary may in effect be discarded, +for example if the dictionary is larger +than the window size provided in +.Xr deflateInit 3 +or +.Xr deflateInit2 3 . +Thus the strings most likely to be useful +should be put at the end of the dictionary, +not at the front. +In addition, +the current implementation of deflate +will use at most the window size minus 262 bytes +of the provided dictionary. +. +.Pp +Upon return of this function, +.Fa strm->adler +is set to the Adler-32 value +of the dictionary; +the decompressor may later use this value +to determine which dictionary has been used +by the compressor. +(The Adler-32 value applies to the whole dictionary +even if only a subset of the dictionary +is actually used by the compressor.) +If a raw deflate was requested, +then the Adler-32 value is not computed and +.Fa strm->adler +is not set. +. +.Pp +.Fn deflateSetDictionary +does not perform any compression: +this will be done by +.Xr deflate 3 . +. +.Sh RETURN VALUES +.Fn deflateSetDictionary +returns +.Dv Z_OK +if success, +or +.Dv Z_STREAM_ERROR +if a parameter is invalid +.Po +e.g. dictionary being +.Dv Z_NULL +.Pc +or the stream state is inconsistent +.Po +for example if +.Xr deflate 3 +has already been called for this stream +or if not at a block boundary +for raw deflate +.Pc . +. +.Sh SEE ALSO +.Xr deflateGetDictionary 3 , +.Xr inflateSetDictionary 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateSetHeader.3 b/doc/zlib/deflateSetHeader.3 new file mode 100644 index 00000000..6fec645c --- /dev/null +++ b/doc/zlib/deflateSetHeader.3 @@ -0,0 +1,180 @@ +.Dd January 15, 2017 +.Dt DEFLATESETHEADER 3 +.Os +. +.Sh NAME +.Nm deflateSetHeader +.Nd set gzip header +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +. +.Bd -literal +typedef struct gz_header_s { + int text; + uLong time; + int xflags; + int os; + Bytef *extra; + uInt extra_len; + uInt extra_max; + Bytef *name; + uInt name_max; + Bytef *comment; + uInt comm_max; + int hcrc; + int done; +} gz_header; +.Ed +. +.Pp +.Vt typedef gz_header FAR *gz_headerp; +. +.Ft int +.Fn deflateSetHeader "z_streamp strm" "gz_headerp head" +. +.Sh DESCRIPTION +.Fn deflateSetHeader +provides gzip header information +for when a gzip stream +is requested by +.Xr deflateInit2 3 . +.Fn deflateSetHeader +may be called after +.Xr deflateInit2 3 +or +.Xr deflateReset 3 +and before the first call of +.Xr deflate 3 . +The +text, +time, +OS, +extra field, +name, +and comment +information in the provided +.Vt gz_header +structure +are written to the gzip header +.Po +.Fa xflag +is ignored \(em +the extra flags are set +according to the compression level +.Pc . +The caller must assure that, +if not +.Dv Z_NULL , +.Fa name +and +.Fa comment +are terminated with a zero byte, +and that if +.Fa extra +is not +.Dv Z_NULL , +that +.Fa extra_len +bytes are available there. +If +.Fa hcrc +is true, +a gzip header CRC is included. +Note that the current versions +of the command-line version of +.Xr gzip 1 +(up through version 1.3.x) +do not support header CRCs, +and will report that it is a +"multi-part gzip file" +and give up. +. +.Pp +If +.Fn deflateSetHeader +is not used, +the default gzip header has +text false, +the time set to zero, +and OS set to 255, +with no extra, name, or comment fields. +The gzip header is returned +to the default state by +.Xr deflateReset 3 . +. +.Pp +The fields of +.Vt gz_header +are as follows: +. +.Bl -tag -width "extra_len" +.It Fa text +true if compressed data believed to be text +.It Fa time +modification time +.It Fa xflags +extra flags +(not used when writing a gzip file) +.It Fa os +operating system +.It Fa extra +pointer to extra field or +.Dv Z_NULL +if none +.It Fa extra_len +extra field length +.Po +valid if +.Fa extra +!= +.Dv Z_NULL +.Pc +.It Fa extra_max +space at extra +(only when reading header) +.It Fa name +pointer to zero-terminated file name or +.Dv Z_NULL +.It Fa name_max +space at +.Fa name +(only when reading header) +.It Fa comment +pointer to zero-terminated comment or +.Dv Z_NULL +.It Fa comm_max +space at comment +(only when reading header) +.It Fa hcrc +true if there was or will be a header CRC +.It Fa done +true when done reading gzip header +(not used when writing a gzip file) +.El +. +.Sh RETURN VALUES +.Fn deflateSetHeader +returns +.Dv Z_OK +if success, +or +.Dv Z_STREAM_ERROR +if the source stream state was inconsistent. +. +.Sh SEE ALSO +.Xr gzip 1 , +.Xr deflateInit2 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/deflateTune.3 b/doc/zlib/deflateTune.3 new file mode 100644 index 00000000..7269dec0 --- /dev/null +++ b/doc/zlib/deflateTune.3 @@ -0,0 +1,70 @@ +.Dd January 15, 2017 +.Dt DEFLATETUNE 3 +.Os +. +.Sh NAME +.Nm deflateTune +.Nd fine tune compression parameters +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fo deflateTune +.Fa "z_streamp strm" +.Fa "int good_length" +.Fa "int max_lazy" +.Fa "int nice_length" +.Fa "int max_chain" +.Fc +. +.Sh DESCRIPTION +Fine tune deflate's internal compression parameters. +This should only be used +by someone who understands the algorithm +used by zlib's deflate +for searching for the best matching string, +and even then only by the most fanatic optimizer +trying to squeeze out the last compressed bit +for their specific input data. +Read the +.Pa deflate.c +source code for the meaning of the +.Fa max_lazy , +.Fa good_length , +.Fa nice_length , +and +.Fa max_chain +parameters. +. +.Pp +.Fn deflateTune +can be called after +.Xr deflateInit 3 +or +.Xr deflateInit2 3 . +. +.Sh RETURN VALUES +.Fn deflateTune +returns +.Dv Z_OK +on success, +or +.Dv Z_STREAM_ERROR +for an invalid deflate stream. +. +.Sh SEE ALSO +.Xr deflateInit 3 , +.Xr deflateInit2 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzbuffer.3 b/doc/zlib/gzbuffer.3 new file mode 100644 index 00000000..de7c706a --- /dev/null +++ b/doc/zlib/gzbuffer.3 @@ -0,0 +1,59 @@ +.Dd January 15, 2017 +.Dt GZBUFFER 3 +.Os +. +.Sh NAME +.Nm gzbuffer +.Nd set buffer size +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzbuffer "gzFile file" "unsigned size" +. +.Sh DESCRIPTION +Set the internal buffer size +used by this library's functions. +The default buffer size is 8192 bytes. +This function must be called after +.Xr gzopen 3 +or +.Xr gzdopen 3 , +and before any other calls +that read or write the file. +The buffer memory allocation +is always deferred to the first read or write. +Three times that size in buffer space is allocated. +A larger buffer size of, +for example, +64K or 128K bytes +will noticeably increase the speed +of decompression (reading). +. +.Pp +The new buffer size also affects +the maximum length for +.Xr gzprintf 3 . +. +.Sh RETURN VALUES +.Fn gzbuffer +returns 0 on success, +or -1 on failure, +such as being called too late. +. +.Sh SEE ALSO +.Xr gzopen 3 , +.Xr gzprintf 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzclose.3 b/doc/zlib/gzclose.3 new file mode 100644 index 00000000..77eae11e --- /dev/null +++ b/doc/zlib/gzclose.3 @@ -0,0 +1,97 @@ +.Dd January 15, 2017 +.Dt GZCLOSE 3 +.Os +. +.Sh NAME +.Nm gzclose , +.Nm gzclose_r , +.Nm gzclose_w +.Nd close compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzclose "gzFile file" +.Ft int +.Fn gzclose_r "gzFile file" +.Ft int +.Fn gzclose_w "gzFile file" +. +.Sh DESCRIPTION +Flushes all pending output if necessary, +closes the compressed file +and deallocates the (de)compression state. +Note that once +.Fa file +is closed, +you cannot call +.Xr gzerror 3 +with +.Fa file , +since its structures +have been deallocated. +.Fn gzclose +must not be called more than once +on the same file, +just as +.Xr free 3 +must not be called more than once +on the same allocation. +. +.Pp +.Fn gzclose_r +and +.Fn gzclose_w +are the same as +.Fn gzclose , +but +.Fn gzclose_r +is only for use when reading, +and +.Fn gzclose_w +is only for use when writing or appending. +The advantage to using these instead of +.Fn gzclose +is that they avoid linking in +zlib compression or decompression code +that is not used when only reading +or only writing respectively. +If +.Fn gzclose +is used, +then both compression and decompression code +will be included in the application +when linking to a static zlib library. +. +.Sh RETURN VALUES +.Fn gzclose +will return +.Dv Z_STREAM_ERROR +if +.Fa file +is not valid, +.Dv Z_ERRNO +on a file operator error, +.Dv Z_MEM_ERROR +if out of memory, +.Dv Z_BUF_ERROR +if the last read ended in the middle of a gzip stream, +or +.Dv Z_OK +on success. +. +.Sh SEE ALSO +.Xr gzopen 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzdirect.3 b/doc/zlib/gzdirect.3 new file mode 100644 index 00000000..8fa26aae --- /dev/null +++ b/doc/zlib/gzdirect.3 @@ -0,0 +1,85 @@ +.Dd January 15, 2017 +.Dt GZDIRECT 3 +.Os +. +.Sh NAME +.Nm gzdirect +.Nd check direct copy +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzdirect "gzFile file" +. +.Sh DESCRIPTION +Returns true (1) if +.Fa file +is being copied directly while reading, +or false (0) if +.Fa file +is a gzip stream being decompressed. +. +.Pp +If the input file is empty, +.Fn gzdirect +will return true, +since the input does not contain a gzip stream. +. +.Pp +If +.Fn gzdirect +is used immediately after +.Xr gzopen 3 +or +.Xr gzdopen 3 +it will cause buffers to be allocated +to allow reading the file +to determine if it is a gzip file. +Therefore if +.Xr gzbuffer 3 +is used, +it should be called before +.Fn gzdirect . +. +.Pp +When writing, +.Fn gzdirect +returns true (1) +if transparent writing was requested +.Po +.Dq wT +for the +.Xr gzopen 3 +mode +.Pc , +or false (0) otherwise. +.Po +Note: +.Fn gzdirect +is not needed when writing. +Transparent writing +must be explicitly requested, +so the application already knows the answer. +When linking statically, +using +.Fn gzdirect +will include all of the zlib code +for gzip file reading and decompression, +which may not be desired. +.Pc +. +.Sh SEE ALSO +.Xr gzopen 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzeof.3 b/doc/zlib/gzeof.3 new file mode 100644 index 00000000..26c415fe --- /dev/null +++ b/doc/zlib/gzeof.3 @@ -0,0 +1,63 @@ +.Dd January 15, 2017 +.Dt GZEOF 3 +.Os +. +.Sh NAME +.Nm gzeof +.Nd check end-of-file indicator +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzeof "gzFile file" +. +.Sh DESCRIPTION +Returns true (1) +if the end-of-file indicator +has been set while reading, +false (0) otherwise. +Note that the end-of-file indicator +is set only if the read +tried to go past the end of the input, +but came up short. +Therefore, +just like +.Xr feof 3 , +.Fn gzeof +may return false +even if there is no more data to read, +in the event that the last read request +was for the exact number of bytes +remaining in the input file. +This will happen if the input file size +is an exact multiple of the buffer size. +. +.Pp +If +.Fn gzeof +returns true, +then the read functions +will return no more data, +unless the end-of-file indicator +is reset by +.Xr gzclearerr 3 +and the input file +has grown since the previous +end of file was detected. +. +.Sh SEE ALSO +.Xr gzerror 3 , +.Xr gzread 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzerror.3 b/doc/zlib/gzerror.3 new file mode 100644 index 00000000..13dcddd4 --- /dev/null +++ b/doc/zlib/gzerror.3 @@ -0,0 +1,75 @@ +.Dd January 15, 2017 +.Dt GZERROR 3 +.Os +. +.Sh NAME +.Nm gzerror , +.Nm gzclearerr +.Nd check and reset compressed file error +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft const char * +.Fn gzerror "gzFile file" "int *errnum" +.Ft void +.Fn gzclearerr "gzFile file" +. +.Sh DESCRIPTION +.Fn gzerror +returns the error message for the last error +which occured on the given compressed file. +.Fa errnum +is set to the zlib error number. +If an error occurred in the file system +and not in the compression library, +.Fa errnum +is set to +.Dv Z_ERRNO +and the application may consult +.Va errno +to get the exact error code. +. +.Pp +The application must not modify the returned string. +Future calls to this function +may invalidate the previously returned string. +If +.Fa file +is closed, +then the string previously returned by +.Fn gzerror +will no longer be available. +. +.Pp +.Fn gzerror +should be used to distinguish errors from end-of-file +for those functions that do not distinguish those cases +in their return values. +. +.Pp +.Fn gzclearerr +clears the error and end-of-file for +.Fa file . +This is analogous to the +.Xr clearerr 3 +function in stdio. +This is useful for continuing to read a gzip file +that is being written concurrently. +. +.Sh SEE ALSO +.Xr gzeof 3 , +.Xr gzread 3 , +.Xr gzwrite 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzflush.3 b/doc/zlib/gzflush.3 new file mode 100644 index 00000000..b93c03e7 --- /dev/null +++ b/doc/zlib/gzflush.3 @@ -0,0 +1,73 @@ +.Dd January 15, 2017 +.Dt GZFLUSH 3 +.Os +. +.Sh NAME +.Nm gzflush +.Nd flush output to compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzflush "gzFile file" "int flush" +. +.Sh DESCRIPTION +Flushes all pending output +into the compressed file. +The parameter +.Fa flush +is as in the +.Xr deflate 3 +function. +.Fn gzflush +is only permitted when writing. +. +.Pp +If the +.Fa flush +parameter is +.Dv Z_FINISH , +the remaining data is written +and the gzip stream +is completed in the output. +If +.Xr gzwrite 3 +is called again, +a new gzip stream +will be started in the output. +.Xr gzread 3 +is able to read +such concatenated gzip streams. +. +.Pp +.Fn gzflush +should be called only when strictly necessary +because it will degrade compression +if called too often. +. +.Sh RETURN VALUES +The return value +is the zlib error number +.Po +see function +.Xr gzerror 3 +.Pc . +. +.Sh SEE ALSO +.Xr deflate 3 , +.Xr gzerror 3 , +.Xr gzread 3 , +.Xr gzwrite 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzfread.3 b/doc/zlib/gzfread.3 new file mode 100644 index 00000000..66231cc3 --- /dev/null +++ b/doc/zlib/gzfread.3 @@ -0,0 +1,107 @@ +.Dd January 15, 2017 +.Dt GZFREAD 3 +.Os +. +.Sh NAME +.Nm gzfread +.Nd read from compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft z_size_t +.Fn gzfread "voidp buf" "z_size_t size" "z_size_t nitems" "gzFile file" +. +.Sh DESCRIPTION +Read up to +.Fa nitems +of size +.Fa size +from +.Fa file +to +.Fa buf , +otherwise operating as +.Xr gzread 3 +does. +This duplicates the interface of stdio's +.Xr fread 3 , +with +.Vt size_t +request and return types. +If the library defines +.Vt size_t , +then +.Vt z_size_t +is identical to +.Vt size_t . +If not, +then +.Vt z_size_t +is an unsigned integer type +that can contain a pointer. +. +.Pp +In the event that the end of file is reached +and only a partial item is available at the end, +i.e. the remaining uncompressed data length +is not a multiple of +.Fa size , +then the file partial item +is nevertheless read into +.Fa buf +and the end-of-file flag is set. +The length of the partial item read +is not provided, +but could be inferred from the result of +.Xr gztell 3 . +This behavior is the same as the behavior of +.Xr fread 3 +implementations in common libraries, +but it prevents the direct use of +.Fn gzfread +to read a concurrently written file, +reseting and retrying on end-of-file, +when +.Fa size +is not 1. +. +.Sh RETURN VALUES +.Fn gzfread +returns the number of full items read of size +.Fa size , +or zero if the end of the file was reached +and a full item could not be read, +or if there was an error. +.Xr gzerror 3 +must be consulted if zero is returned +in order to determine if there was an error. +If the multiplication of +.Fa size +and +.Fa nitems +overflows, +i.e. the product does not fit in +.Vt z_size_t , +then nothing is read, +zero is returned, +and the error state is set to +.Dv Z_STREAM_ERROR . +. +.Sh SEE ALSO +.Xr gzeof 3 , +.Xr gzerror 3 , +.Xr gzopen 3 , +.Xr gzread 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzfwrite.3 b/doc/zlib/gzfwrite.3 new file mode 100644 index 00000000..38383a33 --- /dev/null +++ b/doc/zlib/gzfwrite.3 @@ -0,0 +1,75 @@ +.Dd January 15, 2017 +.Dt GZFWRITE 3 +.Os +. +.Sh NAME +.Nm gzfwrite +.Nd write to compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft z_size_t +.Fn gzfwrite "voidpc buf" "z_size_t size" "z_size_t nitems" "gzFile file" +. +.Sh DESCRIPTION +.Fn gzfwrite +writes +.Fa nitems +items of size +.Fa size +from +.Fa buf +to +.Fa file , +duplicating the interface of stdio's +.Xr fwrite 3 , +with +.Vt size_t +request and return types. +If the library defines +.Vt size_t , +then +.Vt z_size_t +is identical to +.Vt size_t . +If not, +then +.Vt z_size_t +is an unsigned integer type +that can contain a pointer. +. +.Sh RETURN VALUES +.Fn gzfwrite +returns the number of full items +written of size +.Fa size , +or zero if there was an error. +If the multiplication of +.Fa size +and +.Fa nitems +overflows, +i.e. the product does not fit in a +.Vt z_size_t , +then nothing is written, +zero is returned, +and the error state is set to +.Dv Z_STREAM_ERROR . +. +.Sh SEE ALSO +.Xr gzerror 3 , +.Xr gzopen 3 , +.Xr gzwrite 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzgetc.3 b/doc/zlib/gzgetc.3 new file mode 100644 index 00000000..93a90edd --- /dev/null +++ b/doc/zlib/gzgetc.3 @@ -0,0 +1,55 @@ +.Dd January 15, 2017 +.Dt GZGETC 3 +.Os +. +.Sh NAME +.Nm gzgetc +.Nd get character from compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzgetc "gzFile file" +. +.Sh DESCRIPTION +Reads one byte from the compressed file. +This is implemented as a macro for speed. +As such, +it does not do all of the checking +the other functions do. +I.e.\& +it does not check to see if +.Fa file +is +.Dv NULL , +nor whether the structure +.Fa file +points to has been clobbered or not. +. +.Sh RETURN VALUES +.Fn gzgetc +returns the byte +or -1 in case of +end of file +or error. +. +.Sh SEE ALSO +.Xr gzeof 3 , +.Xr gzerror 3 , +.Xr gzgets 3 , +.Xr gzopen 3 , +.Xr gzread 3 , +.Xr gzungetc 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzgets.3 b/doc/zlib/gzgets.3 new file mode 100644 index 00000000..2a329e9e --- /dev/null +++ b/doc/zlib/gzgets.3 @@ -0,0 +1,67 @@ +.Dd January 15, 2017 +.Dt GZGETS 3 +.Os +. +.Sh NAME +.Nm gzgets +.Nd read line from compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft char * +.Fn gzgets "gzFile file" "char *buf" "int len" +. +.Sh DESCRIPTION +Reads bytes from the compressed file +until +.Fa len-1 +characters are read, +or a newline character +is read and transferred to +.Fa buf , +or an end-of-file condition +is encountered. +If any characters are read or if +.Fa len +== 1, +the string is terminated +with a null character. +If no characters are read +due to an end-of-file or +.Fa len +< 1, +then the buffer is left untouched. +. +.Sh RETURN VALUES +.Fn gzgets +returns +.Fa buf +which is a null-terminated string, +or it returns +.Dv NULL +for end-of-file +or in case of error. +If there was an error, +the contents at +.Fa buf +are indeterminate. +. +.Sh SEE ALSO +.Xr gzeof 3 , +.Xr gzerror 3 , +.Xr gzgetc 3 , +.Xr gzopen 3 , +.Xr gzread 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzoffset.3 b/doc/zlib/gzoffset.3 new file mode 100644 index 00000000..cbb78a77 --- /dev/null +++ b/doc/zlib/gzoffset.3 @@ -0,0 +1,51 @@ +.Dd January 15, 2017 +.Dt GZOFFSET 3 +.Os +. +.Sh NAME +.Nm gzoffset +.Nd offset in compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft z_off_t +.Fn gzoffset "gzFile file" +. +.Sh DESCRIPTION +Returns the current offset +in the file being read or written. +This offset includes +the count of bytes +that precede the gzip stream, +for example when appending +or when using +.Xr gzdopen 3 +for reading. +When reading, +the offset does not include +as yet unused buffered input. +This information can be used +for a progress indicator. +. +.Sh RETURN VALUES +On error, +.Fn gzoffset +returns -1. +. +.Sh SEE ALSO +.Xr gzerror 3 , +.Xr gzopen 3 , +.Xr gzseek 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzopen.3 b/doc/zlib/gzopen.3 new file mode 100644 index 00000000..e3cb4cbd --- /dev/null +++ b/doc/zlib/gzopen.3 @@ -0,0 +1,261 @@ +.Dd January 15, 2017 +.Dt GZOPEN 3 +.Os +. +.Sh NAME +.Nm gzopen , +.Nm gzdopen +.Nd open gzip file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft gzFile +.Fn gzopen "const char *path" "const char *mode" +.Ft gzFile +.Fn gzdopen "int fd" "const char *mode" +. +.Sh DESCRIPTION +Opens a gzip (.gz) file +for reading or writing. +The +.Fa mode +parameter is as in +.Xr fopen 3 +.Po +.Dq rb +or +.Dq wb +.Pc +but can also include a compression level +.Pq Dq wb9 +or a strategy: +.Sq f +for filtered data as in +.Dq wb6f , +.Sq h +for Huffman-only compression as in +.Dq wb1h , +.Sq R +for run-length encoding as in +.Dq wb1R , +or +.Sq F +for fixed code compression as in +.Dq wb9F . +.Po +See the description of +.Xr deflateInit2 3 +for more information about the +.Fa strategy +parameter. +.Pc \& +.Sq T +will request transparent writing or appending +with no compression +and not using the gzip format. +. +.Pp +.Dq a +can be used instead of +.Dq w +to request that the gzip stream +that will be written +be appended to the file. +.Dq + +will result in an error, +since reading and writing +to the same gzip file +is not supported. +The addition of +.Dq x +when writing will create the file exclusively, +which fails if the file already exists. +On systems that support it, +the addition of +.Dq e +when reading or writing +will set the flag to close the file on an +.Xr execve 2 +call. +. +.Pp +These functions, +as well as +.Xr gzip 1 , +will read and decode +a sequence of gzip streams in a file. +The append function of +.Fn gzopen +can be used to create such a file. +.Po +Also see +.Xr gzflush 3 +for another way to do this. +.Pc \& +When appending, +.Fn gzopen +does not test whether the file begins with a gzip stream, +nor does it look for the end of the gzip streams +to begin appending. +.Fn gzopen +will simply append a gzip stream +to the existing file. +. +.Pp +.Fn gzopen +can be used to read a file which is not in gzip format; +in this case +.Xr gzread 3 +will directly read from the file without decompression. +When reading, +this will be detected automatically +by looking for the magic two-byte gzip header. +. +.Pp +.Fn gzdopen +associates at +.Vt gzFile +with the file descriptor +.Fa fd . +File descriptors +are obtained from calls like +.Xr open 2 , +.Xr dup 2 , +.Xr creat 2 , +.Xr pipe 2 +or +.Xr fileno 3 +.Po +if the file has been previously opened with +.Xr fopen 3 +.Pc . +The +.Fa mode +parameter is as in +.Fn gzopen . +. +.Pp +The next call of +.Xr gzclose 3 +on the returned +.Vt gzFile +will also close the file descriptor +.Fa fd , +just like +.Xr fclose 3 . +If you want to keep +.Fa fd +open, +use +.Li "fd = dup(fd_keep); gz = gzdopen(fd, mode)" . +The duplicated descriptor should be saved +to avoid a leak, +since +.Fn gzdopen +does not close +.Fa fd +if it fails. +If you are using +.Xr fileno 3 +to get the file descriptor from a +.Vt FILE * , +then you will have to use +.Xr dup 2 +to avoid double-closing +the file descriptor. +Both +.Xr gzclose 3 +and +.Xr flcose 3 +will close the associated file descriptor, +so they need to have different file descriptors. +. +.Sh RETURN VALUES +.Fn gzopen +and +.Fn gzdopen +return +.Dv NULL +if the file could not be opened, +if there was insufficient memory +to allocate the +.Vt gzFile +state, +or if an invalid +.Fa mode +was specified +.Po +an +.Sq r , +.Sq w , +or +.Sq a +was not provided, +or +.Sq + +was provided +.Pc , +or if +.Fa fd +is -1. +The file descriptor +is not used until the next +gz* read, write, seek, or close operation, +so +.Fn gzdopen +will not detect if +.Fa fd +is invalid +.Po +unless +.Fa fd +is -1 +.Pc . +.Va errno +can be checked +to determine if the reason +.Fn gzopen +failed was that the file +could not be opened. +. +.Sh ERRORS +The +.Fn gzopen +function may fail and set +.Va errno +for any of the errors specified +for the routine +.Xr fopen 3 . +. +.Sh SEE ALSO +.Xr deflateInit2 3 , +.Xr fopen 3 , +.Xr gzbuffer 3 , +.Xr gzclose 3 , +.Xr gzdirect 3 , +.Xr gzeof 3 , +.Xr gzerror 3 , +.Xr gzflush 3 , +.Xr gzgetc 3 , +.Xr gzgets 3 , +.Xr gzoffset 3 , +.Xr gzprintf 3 , +.Xr gzputc 3 , +.Xr gzputs 3 , +.Xr gzread 3 , +.Xr gzseek 3 , +.Xr gzsetparams 3 , +.Xr gzwrite 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzprintf.3 b/doc/zlib/gzprintf.3 new file mode 100644 index 00000000..26961f34 --- /dev/null +++ b/doc/zlib/gzprintf.3 @@ -0,0 +1,71 @@ +.Dd January 15, 2017 +.Dt GZPRINTF 3 +.Os +. +.Sh NAME +.Nm gzprintf +.Nd format output to compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzprintf "gzFile file" "const char *format" "..." +. +.Sh DESCRIPTION +Converts, formats, and writes the arguments +to the compressed file +under control of the format string, +as in +.Xr fprintf 3 . +. +.Sh RETURN VALUES +.Fn gzprintf +returns the number of +uncompressed bytes actually written, +or a negative zlib error code +in case of error. +The number of uncompressed bytes written +is limited to 8191, +or one less than the buffer size given to +.Xr gzbuffer 3 . +The caller should assure that +this limit is not exceeded. +If it is exceeded, +then +.Fn gzprintf +will return an error (0) +with nothing written. +In this case, +there may also be a buffer overflow +with unpredictable consequences, +which is possibly only if zlib +was compiled with the insecure functions +.Xr sprintf 3 +or +.Xr vsprintf 3 +because the secure +.Xr snprintf 3 +or +.Xr vsnprintf 3 +functions +were not available. +This can be determined using +.Xr zlibCompileFlags 3 . +. +.Sh SEE ALSO +.Xr fprintf 3 , +.Xr gzerror 3 , +.Xr gzopen 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzputc.3 b/doc/zlib/gzputc.3 new file mode 100644 index 00000000..161e5631 --- /dev/null +++ b/doc/zlib/gzputc.3 @@ -0,0 +1,43 @@ +.Dd January 15, 2017 +.Dt GZPUTC 3 +.Os +. +.Sh NAME +.Nm gzputc +.Nd write character to compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzputc "gzFile file" "int c" +. +.Sh DESCRIPTION +Writes +.Fa c , +converted to an +.Vt unsigned char , +into the compressed file. +. +.Sh RETURN VALUES +.Fn gzputc +returns the value that was written, +or -1 in case of error. +. +.Sh SEE ALSO +.Xr gzerror 3 , +.Xr gzopen 3 , +.Xr gzputs 3 , +.Xr gzwrite 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzputs.3 b/doc/zlib/gzputs.3 new file mode 100644 index 00000000..f5d1fd84 --- /dev/null +++ b/doc/zlib/gzputs.3 @@ -0,0 +1,41 @@ +.Dd January 15, 2017 +.Dt GZPUTS 3 +.Os +. +.Sh NAME +.Nm gzputs +.Nd write string to compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzputs "gzFile file" "const char *s" +. +.Sh DESCRIPTION +Writes the given null-terminated string +to the compressed file, +excluding the terminating null character. +. +.Sh RETURN VALUES +.Fn gzputs +returns the number of characters written, +or -1 in case of error. +. +.Sh SEE ALSO +.Xr gzerror 3 , +.Xr gzopen 3 , +.Xr gzputc 3 , +.Xr gzwrite 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzread.3 b/doc/zlib/gzread.3 new file mode 100644 index 00000000..84439eaa --- /dev/null +++ b/doc/zlib/gzread.3 @@ -0,0 +1,115 @@ +.Dd January 15, 2017 +.Dt GZREAD 3 +.Os +. +.Sh NAME +.Nm gzread +.Nd read from compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzread "gzFile file" "voidp buf" "unsigned len" +. +.Sh DESCRIPTION +Reads the given number of uncompressed bytes +from the compressed file. +If the input file +is not in gzip format, +.Fn gzread +copies the given number of bytes +into the buffer directly from the file. +. +.Pp +After reaching the end of a gzip stream +in the input, +.Fn gzread +will continue to read, +looking for another gzip stream. +Any number of gzip streams +may be concatenated in the input file, +and will all be decompressed by +.Fn gzread . +If something other than a gzip stream +is encountered after a gzip stream, +that remaining trailing garbage is ignored +(and no error is returned). +. +.Pp +.Fn gzread +can be used to read a gzip file +that is being concurrently written. +Upon reaching the end of the input, +.Fn gzread +will return with the available data. +If the error code returned by +.Xr gzerror 3 +is +.Dv Z_OK +or +.Dv Z_BUF_ERROR , +then +.Xr gzclearerr 3 +can be used +to clear the end of file indicator +in order to permit +.Fn gzread +to be tried again. +.Dv Z_OK +indicates that a gzip stream was completed +on the last +.Fn gzread . +.Dv Z_BUF_ERROR +indicates that the input file +ended in the middle of a gzip stream. +Note that +.Fn gzread +does not return -1 +in the event of an incomplete gzip stream. +This error is deferred until +.Xr gzclose 3 , +which will return +.Dv Z_BUF_ERROR +if the last +.Fn gzread +ended in the middle of a gzip stream. +Alternatively, +.Xr gzerror 3 +can be used before +.Xr gzclose 3 +to detect this case. +. +.Sh RETURN VALUES +.Fn gzread +returns the number of uncompressed bytes actually read, +less than +.Fa len +for end of file, +or -1 for error. +If +.Fa len +is too large to fit in an +.Vt int , +then nothing is read, +-1 is returned, +and the error state is set to +.Dv Z_STREAM_ERROR . +. +.Sh SEE ALSO +.Xr gzeof 3 , +.Xr gzerror 3 , +.Xr gzfread 3 , +.Xr gzopen 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzseek.3 b/doc/zlib/gzseek.3 new file mode 100644 index 00000000..cd85fd4c --- /dev/null +++ b/doc/zlib/gzseek.3 @@ -0,0 +1,108 @@ +.Dd January 15, 2017 +.Dt GZSEEK 3 +.Os +. +.Sh NAME +.Nm gzseek , +.Nm gzrewind , +.Nm gztell +.Nd seek compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft z_off_t +.Fn gzseek "gzFile file" "z_off_t offset" "int whence" +.Ft int +.Fn gzrewind "gzFile file" +.Ft z_off_t +.Fn gztell "gzFile file" +. +.Sh DESCRIPTION +Sets the starting position +for the next +.Xr gzread 3 +or +.Xr gzwrite 3 +on the given compressed file. +The +.Fa offset +represents a number of bytes +in the uncompressed data stream. +The +.Fa whence +parameter +is defined as in +.Xr lseek 2 ; +the value +.Dv SEEK_END +is not supported. +. +.Pp +If the file is opened for reading, +this function is emulated +but can be extremely slow. +If the file is opened for writing, +only forward seeks are supported; +.Fn gzseek +then compresses a sequence of zeroes +up to the new starting position. +. +.Pp +.Fn gzrewind +rewinds the given file. +This function is supported +only for reading. +. +.Pp +.Fn gzrewind file +is equivalent to +.Li (int) Ns Fn gzseek file 0L SEEK_SET . +. +.Pp +.Fn gztell +returns the starting position +for the next +.Xr gzread 3 +or +.Xr gzwrite 3 +on the given compressed file. +This position represents a number of bytes +in the uncompressed data stream, +and is zero when starting, +even if appending or reading +a gzip stream from the middle of a file using +.Xr gzdopen 3 . +. +.Pp +.Fn gztell file +is equivalent to +.Fn gzseek file 0L SEEK_CUR . +. +.Sh RETURN VALUES +.Fn gzseek +returns the resulting offset location +as measured in bytes +from the beginning of the uncompressed stream, +or -1 in case of error, +in particular if the file +is opened for writing +and the new starting position +would be before the current position. +. +.Sh SEE ALSO +.Xr gzerror 3 , +.Xr gzoffset 3 , +.Xr gzopen 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzsetparams.3 b/doc/zlib/gzsetparams.3 new file mode 100644 index 00000000..ff544d23 --- /dev/null +++ b/doc/zlib/gzsetparams.3 @@ -0,0 +1,51 @@ +.Dd January 15, 2017 +.Dt GZSETPARAMS 3 +.Os +. +.Sh NAME +.Nm gzsetparams +.Nd set compression level and strategy +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzsetparams "gzFile file" "int level" "int strategy" +. +.Sh DESCRIPTION +Dynamically update the compression level or strategy. +See the description of +.Xr deflateInit2 3 +for the meaning +of these parameters. +Previously provided data is flushed +before the parameter change. +. +.Sh RETURN VALUES +.Fn gzsetparams +returns +.Dv Z_OK +if success, +.Dv Z_STREAM_ERROR +if the file was not opened for writing, +.Dv Z_ERRNO +if there is an error writing the flushed data, +or +.Dv Z_MEM_ERROR +if there is a memory allocation error. +. +.Sh SEE ALSO +.Xr deflateInit2 3 , +.Xr gzopen 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzungetc.3 b/doc/zlib/gzungetc.3 new file mode 100644 index 00000000..90cdafc7 --- /dev/null +++ b/doc/zlib/gzungetc.3 @@ -0,0 +1,67 @@ +.Dd January 15, 2017 +.Dt GZUNGETC 3 +.Os +. +.Sh NAME +.Nm gzungetc +.Nd un-get character from compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzungetc "int c" "gzFile file" +. +.Sh DESCRIPTION +Push one character back onto the stream +to be read as the first character +on the next read. +At least one character of push-back +is allowed. +.Fn gzungetc +will fail if +.Fa c +is -1, +and may fail if a character +has been pushed +but not read yet. +If +.Fn gzungetc +is used immediately after +.Xr gzopen 3 +or +.Xr gzdopen 3 , +at least the output buffer size +of pushed characters is allowed. +.Po +See +.Xr gzbuffer 3 . +.Pc \& +The pushed character will be discarded +if the stream is repositioned with +.Xr gzseek 3 +or +.Xr gzrewind 3 . +. +.Sh RETURN VALUES +.Fn gzungetc +returns the character pushed, +or -1 on failure. +. +.Sh SEE ALSO +.Xr gzbuffer 3 , +.Xr gzerror 3 , +.Xr gzgetc 3 , +.Xr gzopen 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/gzwrite.3 b/doc/zlib/gzwrite.3 new file mode 100644 index 00000000..606d89f4 --- /dev/null +++ b/doc/zlib/gzwrite.3 @@ -0,0 +1,39 @@ +.Dd January 15, 2017 +.Dt GZWRITE 3 +.Os +. +.Sh NAME +.Nm gzwrite +.Nd write to compressed file +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn gzwrite "gzFile file" "voidpc buf" "unsigned len" +. +.Sh DESCRIPTION +Writes the given number of uncompressed bytes +into the compressed file. +. +.Sh RETURN VALUES +.Fn gzwrite +returns the number of uncompressed bytes written +or 0 in case of error. +. +.Sh SEE ALSO +.Xr gzerror 3 , +.Xr gzfwrite 3 , +.Xr gzopen 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflate.3 b/doc/zlib/inflate.3 new file mode 100644 index 00000000..ca90c270 --- /dev/null +++ b/doc/zlib/inflate.3 @@ -0,0 +1,398 @@ +.Dd January 15, 2017 +.Dt INFLATE 3 +.Os +. +.Sh NAME +.Nm inflate +.Nd deflate decompression +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn inflate "z_streamp strm" "int flush" +. +.Sh DESCRIPTION +.Fn inflate +decompresses as much data as possible, +and stops when the input buffer becomes empty +or the output buffer becomes full. +It may introduce some output latency +(reading input without producing any output) +except when forced to flush. +. +.Pp +The detailed semantics are as follows. +.Fn inflate +performs one or both of the following actions: +. +.Bl -dash +.It +Decompress more input starting at +.Fa next_in +and update +.Fa next_in +and +.Fa avail_in +accordingly. +If not all input can be processed +(because there is not enough room in the output buffer), +then +.Fa next_in +and +.Fa avail_in +are updated accordingly, +and processing will resume at this point +for the next call of +.Fn inflate . +. +.It +Generate more output starting at +.Fa next_out +and update +.Fa next_out +and +.Fa avail_out +accordingly. +.Fn inflate +provides as much output as possible, +until there is no more input data +or no more space in the output buffer +.Po +see below about the +.Fa flush +parameter +.Pc . +.El +. +.Pp +Before the call of +.Fn inflate , +the application should ensure that +at least one of the actions is possible, +by providing more input +and/or consuming more output, +and updating the +.Fa next_* +and +.Fa avail_* +values accordingly. +If the caller of +.Fn inflate +does not provide both available input +and available output space, +it is possible that there will be no progress made. +The application can consume the uncompressed output +when it wants, +for example when the output buffer is full +.Po +.Fa avail_out +== 0 +.Pc , +or after each call of +.Fn inflate . +If +.Fn inflate +returns +.Dv Z_OK +and with zero +.Fa avail)out , +it must be called again after making room +in the output buffer +because there might be more output pending. +. +.Pp +The +.Fa flush +parameter of +.Fn inflate +can be +.Dv Z_NO_FLUSH , +.Dv Z_SYNC_FLUSH , +.Dv Z_FINISH , +.Dv Z_BLOCK , +or +.Dv Z_TREES . +.Dv Z_SYNC_FLUSH +requests that +.Fn inflate +flush as much output as possible +to the output buffer. +.Dv Z_BLOCK +requests that +.Fn inflate +stop if and when it gets to the next deflate block boundary. +When decoding the zlib or gzip format, +this will cause +.Fn inflate +to return immediately after the header +and before the first block. +When doing a raw inflate, +.Fn inflate +will go ahead and process the first block, +and will return when it gets to the end of that block, +or when it runs out of data. +. +.Pp +The +.Dv Z_BLOCK +option assists in appending to +or combining deflate streams. +To assist in this, +on return +.Fn inflate +always sets +.Fa strm->data_type +to the number of unused bits +in the last byte taken from +.Fa strm->next_in , +plus 64 if +.Fn inflate +is currently decoding the last block in the deflate stream, +plus 128 if +.Fn inflate +returned immediately after decoding an end-of-block code +or decoding the complete header up to +just before the first byte of the deflate stream. +The end-of-block will not be indicated +until all of the uncompressed data +from that block has been written to +.Fa strm->next_out . +The number of unused bits may in general be greater than seven, +except when bit 7 of +.Fa data_type +is set, +in which case the number of unused bits +will be less than eight. +.Fa data_type +is set as noted here every time +.Fn inflate +returns for all flush options, +and so can be used to determine +the amount of currently consumed input in bits. +. +.Pp +The +.Dv Z_TREES +option behaves as +.Dv Z_BLOCK +does, +but it also returns +when the end of each deflate block header is reached, +before any actual data in that block is decoded. +This allows the caller to determine +the length of the deflate block header +for later use in random access +within a deflate block. +256 is added to the value of +.Fa strm->data_type +when +.Fn inflate +returns immediately after reaching +the end of the deflate block header. +. +.Pp +.Fn inflate +should normally be called until it returns +.Dv Z_STREAM_END +or an error. +However if all decompression is to be performed +in a single step +.Po +a single call of +.Fn inflate +.Pc , +the parameter +.Fa flush +should be set to +.Dv Z_FINISH . +In this case all pending input is processed +and all pending output is flushed; +.Fa avail_out +must be large enough to hold all of +the uncompressed data for the operation to complete. +(The size of the uncompressed data +may have been saved by the compressor for this purpose.) +The use of +.Dv Z_FINISH +is not required to perform inflation in one step. +However it may be used to inform +.Fn inflate +that a faster approach can be used for the single +.Fn inflate +call. +.Dv Z_FINISH also informs +.Fn inflate +to not maintain a sliding window +if the stream completes, +which reduces +.Fn inflate Ap s +memory footprint. +If the stream does not complete, +either because not all of the stream is provided +or not enough output space is provided, +then a sliding window will be allocated and +.Fn inflate +can be called again to continue the operation as if +.Dv Z_NO_FLUSH +had been used. +. +.Pp +In this implementation, +.Fn inflate +always flushes as much output as possible +to the output buffer, +and always uses the faster approach +on the first call. +So the effects of the +.Fa flush +parameter in this implementation +are on the return value of +.Fn inflate +as noted below, +when +.Fn inflate +returns early when +.Dv Z_BLOCK +or +.Dv Z_TREES +is used, +and when +.Fn inflate +avoids the allocation of memory +for a sliding window when +.Dv Z_FINISH +is used. +. +.Pp +If a preset dictionary is needed after this call +.Po +see +.Xr inflateSetDictionary 3 +.Pc , +.Fn inflate +sets +.Fa strm->adler +to the Adler-32 checksum of the dictionary +chosen by the compressor +and returns +.Dv Z_NEED_DICT ; +otherwise it sets +.Fa strm->adler +to the Adler-32 checksum +of all output produced so far +.Po +that is, +.Fa total_out +bytes +.Pc +and returns +.Dv Z_OK , +.Dv Z_STREAM_END +or an error code +as described in +.Sx RETURN VALUES . +At the end of the stream, +.Fn inflate +checks that its computed Adler-32 checksum +is equal to that saved by the compressor +and returns +.Dv Z_STREAM_END +only if the checksum is correct. +. +.Pp +.Fn inflate +can decompress and check +either zlib-wrapped or gzip-wrapped +deflate data. +The header type is detected automatically, +if requested when initializing with +.Xr inflateInit2 3 . +Any information contained in the gzip header +is not retained unless +.Xr inflateGetHeader 3 +is used. +When processing gzip-wrapped deflate data, +.Fa strm->adler32 +is set to the CRC-32 +of the output produced so far. +The CRC-32 is checked against the gzip trailer, +as is the uncompressed length, +modulo 2^32. +. +.Sh RETURN VALUES +.Fn inflate +returns +.Dv Z_OK +if some progress has been made +(more input processed or more output produced), +.Dv Z_STREAM_END +if the end of the compressed data has been reached +and all uncompressed output has been produced, +.Dv Z_NEED_DICT +if a preset dictionary is needed at this point, +.Dv Z_DATA_ERROR +if the input data was corrupted +.Po +input stream not conforming to the zlib format +or incorrect check value, +in which case +.Fa strm->msg +points to a string with a more specific error +.Pc , +.Dv Z_STREAM_ERROR +if the stream structure was inconsistent +.Po +for example +.Fa next_in +or +.Fa next_out +was +.Dv Z_NULL , +or the state was inadvertently written over +by the application +.Pc , +.Dv Z_MEM_ERROR +if there was not enough memory, +.Dv Z_BUF_ERROR +if no progress was possible +or if there was not enough room +in the output buffer when +.Dv Z_FINISH +is used. +Note that +.Dv Z_BUF_ERROR +is not fatal, +and +.Fn inflate +can be called again with more input +and more output space +to continue decompressing. +If +.Dv Z_DATA_ERROR +is returned, +the application may then call +.Xr inflateSync 3 +to look for a good compression block +if a partial recovery of the data +is to be attempted. +. +.Sh SEE ALSO +.Xr deflate 3 , +.Xr inflateBack 3 , +.Xr inflateEnd 3 , +.Xr inflateInit 3 , +.Xr inflateMark 3 , +.Xr inflateSync 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateBack.3 b/doc/zlib/inflateBack.3 new file mode 100644 index 00000000..59d5f8cb --- /dev/null +++ b/doc/zlib/inflateBack.3 @@ -0,0 +1,285 @@ +.Dd January 15, 2017 +.Dt INFLATEBACK 3 +.Os +. +.Sh NAME +.Nm inflateBack +.Nd inflate call-back interface +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +. +.Ft typedef unsigned +.Fo (*in_func) +.Fa "void FAR *" +.Fa "z_const unsigned char FAR * FAR *" +.Fc +. +.Ft typedef int +.Fo (*out_func) +.Fa "void FAR *" +.Fa "unsigned char FAR *" +.Fa "unsigned" +.Fc +. +.Ft int +.Fo inflateBack +.Fa "z_streamp strm" +.Fa "in_func in" +.Fa "void FAR *in_desc" +.Fa "out_func out" +.Fa "void FAR *out_desc" +.Fc +. +.Sh DESCRIPTION +.Fn inflateBack +does a raw inflate +with a single call +using a call-back interface +for input and output. +This is potentially more efficient than +.Xr inflate 3 +for file I/O applications, +in that it avoids copying between the output +and the sliding window +by simply making the window itself the output buffer. +.Xr inflate 3 +can be faster on modern CPUs +when used with large buffers. +.Fn inflateBack +trusts the application to not change +the output buffer passed by the output function, +at least until +.Fn inflateBack +returns. +. +.Pp +.Xr inflateBackInit 3 +must be called first +to allocate the internal state +and to initialize the state +with the user-provided window buffer. +.Fn inflateBack +may then be used multiple times +to inflate a complete, +raw deflate stream +with each call. +.Xr inflateBackEnd 3 +is then called to free the allocated state. +. +.Pp +A raw deflate stream +is one with no zlib or gzip header or trailer. +This routine would normally be used +in a utility that reads zip or gzip files +and write out uncompressed files. +The utility would decode the header +and process the trailer on its own, +hence this routine expects only +the raw deflate stream to decompress. +This is different from the default behaviour of +.Xr inflate 3 , +which expects a zlib header and trailer +around the deflate stream. +. +.Pp +.Fn inflateBack +uses two subroutines +supplied by the caller +that are then called by +.Fn inflateBack +for input and output. +.Fn inflateBack +calls those routines +until it reads a complete deflate stream +and writes out all of the uncompressed data, +or until it encounters an error. +The function's parameters and return types +are defined above in the +.Vt in_func +and +.Vt out_func +typedefs. +.Fn inflateBack +will call +.Fn in in_desc &buf +which should return +the number of bytes of provided input, +and a pointer to that input in +.Fa buf . +If there is no input available, +.Fn in +must return zero \(em +.Fa buf +is ignored in that case \(em +and +.Fn inflateBack +will return a buffer error. +.Fn inflateBack +will call +.Fn out out_desc buf len +to write the uncompressed data +.Fa buf[0..len-1] . +.Fn out +should return zero on success, +or non-zero on failure. +If +.Fn out +returns non-zero, +.Fn inflateBack +will return with an error. +Neither +.Fn in +nor +.Fn out +are permitted to change +the contents of the window provided to +.Xr inflateBackInit 3 , +which is also the buffer that +.Fn out +uses to write from. +The length written by +.Fn out +will be at most the window size. +Any non-zero amount of input +may be provided by +.Fn in . +. +.Pp +For convenience, +.Fn inflateBack +can be provided input on the first call +by setting +.Fa strm->next_in +and +.Fa strm->avail_in . +If that input is exhausted, +then +.Fn in +will be called. +Therefore +.Fa strm->next_in +must be initialized before calling +.Fn inflateBack . +If +.Fa strm->next_in +is +.Dv Z_NULL , +then +.Fn in +will be called immediately for input. +If +.Fa strm->next_in +is not +.Dv Z_NULL , +then +.Fa strm->avail_in +must also be initialized, +and then if +.Fa strm->avail_in +is not zero, +input will initially be taken from +.Fa "strm->next_in[0 .. strm->avail_in - 1]" . +. +.Pp +The +.Fa in_desc +and +.Fa out_desc +parameters of +.Fn inflateBack +is passed as the first parameter of +.Fn in +and +.Fn out +respectively when they are called. +These descriptors can be optionally used +to pass any information that the caller-supplied +.Fn in +and +.Fn out +functions need to do their job. +. +.Sh RETURN VALUES +On return, +.Fn inflateBack +will set +.Fa strm->next_in +and +.Fa strm->avail_in +to pass back any unused input +that was provided by the last +.Fn in +call. +The return values of +.Fn inflateBack +can be +.Dv Z_STREAM_END +on success, +.Dv Z_BUF_ERROR +if +.Fn in +or +.Fn out +returned an error, +.Dv Z_DATA_ERROR +if there was a format error +in the deflate stream +.Po +in which case +.Fa strm->msg +is set to indicate the nature of the error +.Pc , +or +.Dv Z_STREAM_ERROR +if the stream was not properly initialized. +In the case of +.Dv Z_BUF_ERROR , +an input or output error can be distinguished using +.Fa strm->next_in +which will be +.Dv Z_NULL +only if +.Fn in +returned an error. +If +.Fa strm->next_in +is not +.Dv Z_NULL , +then the +.Dv Z_BUF_ERROR +was due to +.Fn out +returning non-zero. +.Po +.Fn in +will always be called before +.Fn out , +so +.Fa strm->next_in +is assured to be defined if +.Fa out +returns non-zero. +.Pc \& +Note that +.Fn inflateBack +cannot return +.Dv Z_OK . +. +.Sh SEE ALSO +.Xr inflate 3 , +.Xr inflateBackEnd 3 , +.Xr inflateBackInit 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateBackEnd.3 b/doc/zlib/inflateBackEnd.3 new file mode 100644 index 00000000..eeb88636 --- /dev/null +++ b/doc/zlib/inflateBackEnd.3 @@ -0,0 +1,43 @@ +.Dd January 15, 2017 +.Dt INFLATEBACKEND 3 +.Os +. +.Sh NAME +.Nm inflateBackEnd +.Nd free inflateBack stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn inflateBackEnd "z_streamp strm" +. +.Sh DESCRIPTION +All memory allocated by +.Xr inflateBackInit 3 +is freed. +. +.Sh RETURN VALUES +.Fn inflateBackEnd +returns +.Dv Z_OK +on success, +or +.Dv Z_STREAM_ERROR +if the stream state was inconsistent. +. +.Sh SEE ALSO +.Xr inflateBack 3 , +.Xr inflateBackInit 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateBackInit.3 b/doc/zlib/inflateBackInit.3 new file mode 100644 index 00000000..483edda5 --- /dev/null +++ b/doc/zlib/inflateBackInit.3 @@ -0,0 +1,84 @@ +.Dd January 15, 2017 +.Dt INFLATEBACKINIT 3 +.Os +. +.Sh NAME +.Nm inflateBackInit +.Nd initialize inflateBack stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fo inflateBackInit +.Fa "z_streamp strm" +.Fa "int windowBits" +.Fa "unsigned char FAR *window" +.Fc +. +.Sh DESCRIPTION +Initialize the internal stream state +for decompression using +.Xr inflateBack 3 +calls. +The fields +.Fa zalloc , +.Fa zfree +and +.Fa opaque +in +.Fa strm +must be initialized before the call. +If +.Fa zalloc +and +.Fa zfree +are +.Dv Z_NULL , +then the default +library-derived memory allocation routines are used. +.Fa windowBits +is the base two logarithm of the window size, +in the range 8..15. +.Fa window +is a caller supplied buffer of that size. +Except for special applications +where it is assured that deflate +was used with small window sizes, +.Fa windowBits +must be 15 +and a 32K byte +.Fa window +must be supplied +to be able to decompress general deflate streams. +. +.Sh RETURN VALUES +.Fn inflateBackInit +will return +.Dv Z_OK +on success, +.Dv Z_STREAM_ERROR +if any of the parameters are invalid, +.Dv Z_MEM_ERROR +if the internal state could not be allocated, +or +.Dv Z_VERSION_ERROR +if the version of the library +does not match the version of the header file. +. +.Sh SEE ALSO +.Xr inflateBack 3 , +.Xr inflateBackEnd 3 , +.Xr inflateInit 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateCopy.3 b/doc/zlib/inflateCopy.3 new file mode 100644 index 00000000..53b30edf --- /dev/null +++ b/doc/zlib/inflateCopy.3 @@ -0,0 +1,59 @@ +.Dd January 15, 2017 +.Dt INFLATECOPY 3 +.Os +. +.Sh NAME +.Nm inflateCopy +.Nd copy inflate stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn inflateCopy "z_streamp dest" "z_streamp source" +. +.Sh DESCRIPTION +Sets the destination stream +as a complete copy of the source stream. +. +.Pp +This function can be useful +when randomly accessing a large stream. +The first pass through the stream +can periodically record the inflate state, +allowing restarting inflate at those points +when randomly accessing the stream. +. +.Sh RETURN VALUES +.Fn inflateCopy +returns +.Dv Z_OK +if success, +.Dv Z_MEM_ERROR +if there was not enough memory, +.Dv Z_STREAM_ERROR +if the source stream state was inconsistent +.Po +such as +.Fa zalloc +being +.Dv Z_NULL +.Pc . +.Fa msg +is left unchanged +in both source and destination. +. +.Sh SEE ALSO +.Xr inflateInit 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateEnd.3 b/doc/zlib/inflateEnd.3 new file mode 100644 index 00000000..9b18226b --- /dev/null +++ b/doc/zlib/inflateEnd.3 @@ -0,0 +1,44 @@ +.Dd January 15, 2017 +.Dt INFLATEEND 3 +.Os +. +.Sh NAME +.Nm inflateEnd +.Nd free inflate stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn inflateEnd "z_streamp strm" +. +.Sh DESCRIPTION +All dynamically allocated data structures +for this stream are feed. +This function discards any unprocessed input +and does not flush any pending output. +. +.Sh RETURN VALUES +.Fn inflateEnd +returns +.Dv Z_OK +if success, +or +.Dv Z_STREAM_ERROR +if the stream state was inconsistent. +. +.Sh SEE ALSO +.Xr inflate 3 , +.Xr inflateInit 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateGetDictionary.3 b/doc/zlib/inflateGetDictionary.3 new file mode 100644 index 00000000..e70ee736 --- /dev/null +++ b/doc/zlib/inflateGetDictionary.3 @@ -0,0 +1,69 @@ +.Dd January 15, 2017 +.Dt INFLATEGETDICTIONARY 3 +.Os +. +.Sh NAME +.Nm inflateGetDictionary +.Nd inflate sliding dictionary +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fo inflateGetDictionary +.Fa "z_streamp strm" +.Fa "Bytef *dictionary" +.Fa "uInt *dictLength" +.Fc +. +.Sh DESCRIPTION +Returns the sliding dictionary +being maintained by +.Xr inflate 3 . +.Fa dictLength +is set to the number of bytes +in the dictionary, +and that many bytes are copied to +.Fa dictionary . +.Fa dictionary +must have enough space, +where 32768 bytes is always enough. +If +.Fn inflateGetDictionary +is called with +.Fa dictionary +equal to +.Dv Z_NULL , +then only the dictionary length is returned, +and nothing is copied. +Similarly, +if +.Fa dictLength +is +.Dv Z_NULL , +then it is not set. +. +.Sh RETURN VALUES +.Fn inflateGetDictionary +returns +.Dv Z_OK +on success, +or +.Dv Z_STREAM_ERROR +if the stream state is inconsistent. +. +.Sh SEE ALSO +.Xr deflateSetDictionary 3 , +.Xr inflateSetDictionary 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateGetHeader.3 b/doc/zlib/inflateGetHeader.3 new file mode 100644 index 00000000..f77670f2 --- /dev/null +++ b/doc/zlib/inflateGetHeader.3 @@ -0,0 +1,170 @@ +.Dd January 15, 2017 +.Dt INFLATEGETHEADER 3 +.Os +. +.Sh NAME +.Nm inflateGetHeader +.Nd get gzip header +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn inflateGetHeader "z_streamp strm" "gz_headerp head" +. +.Sh DESCRIPTION +.Fn inflateGetHeader +requests that gzip header information +be stored in the provided +.Vt gz_header +structure. +.Fn inflateGetHeader +may be called after +.Xr inflateInit2 3 +or +.Xr inflateReset 3 , +and before the first call of +.Xr inflate 3 . +As +.Xr inflate 3 +processes the gzip stream, +.Fa head->done +is zero until the header is completed, +at which time +.Fa head->done +is set to one. +If a zlib stream is being decoded, +then +.Fa head->done +is set to -1 to indicate that +there will be no gzip header information forthcoming. +Note that +.Dv Z_BLOCK +or +.Dv Z_TREES +can be used to force +.Xr inflate 3 +to return immediately after +header processing is complete +and before any actual data is decompressed. +. +.Pp +The +.Fa text , +.Fa time , +.Fa xflags , +and +.Fa os +fields are filled in with the gzip header contents. +.Fa hcrc +is set to true if there is a header CRC. +.Po +The header CRC was valid if +.Fa done +is set to one. +.Pc \& +If +.Fa extra +is not +.Dv Z_NULL , +then +.Fa extra_max +contains the maximum number of bytes to write to +.Fa extra . +Once +.Fa done +is true, +.Fa extra_len +contains the actual extra field length, +and +.Fa extra +contains the extra field, +or that field truncated if +.Fa extra_max +is less than +.Fa extra_len . +If +.Fa name +is not +.Dv Z_NULL , +then up to +.Fa name_max +characters are written there, +terminated with a zero +unless the length is greater than +.Fa name_max . +If +.Fa comment +is not +.Dv Z_NULL , +then up to +.Fa comm_max +characters are written there, +terminated with a zero +unless the length is greater than +.Fa comm_max . +When any of +.Fa extra , +.Fa name , +or +.Fa comment +are not +.Dv Z_NULL +and the respective field +is not present in the header, +then that field is set to +.Dv Z_NULL +to signal its absence. +This allows the use of +.Xr deflateSetHeader 3 +with the returned structure +to duplicate the header. +However if those fields are set to allocated memory, +then the application will need to +save those pointers elsewhere +so that they can be eventually feed. +. +.Pp +If +.Fn inflateGetHeader +is not used, +then the header information is simply discarded. +The header is always checked for validity, +including the header CRC if present. +.Xr inflateReset 3 +will reset the process to discard the header information. +The application would need to call +.Fn inflateGetHeader +again to retrieve the header from the next gzip stream. +. +.Pp +The +.Vt gz_headerp +type is documented in +.Xr deflateSetHeader 3 . +. +.Sh RETURN VALUES +.Fn inflateGetHeader +returns +.Dv Z_OK +if success, +or +.Dv Z_STREAM_ERROR +if the source stream state was inconsistent. +. +.Sh SEE ALSO +.Xr gzip 1 , +.Xr deflateSetHeader 3 , +.Xr inflateInit2 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateInit.3 b/doc/zlib/inflateInit.3 new file mode 100644 index 00000000..186b058a --- /dev/null +++ b/doc/zlib/inflateInit.3 @@ -0,0 +1,101 @@ +.Dd January 15, 2017 +.Dt INFLATEINIT 3 +.Os +. +.Sh NAME +.Nm inflateInit +.Nd initialize inflate stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn inflateInit "z_streamp strm" +. +.Sh DESCRIPTION +Initializes the internal stream state for decompression. +The fields +.Fa next_in , +.Fa avail_in , +.Fa zalloc , +.Fa zfree +and +.Fa opaque +must be initialized before by the caller. +In the current version of +.Fn inflateInit , +the provided input is not read or consumed. +The allocation of a sliding window +will be deferred to the first call of +.Xr inflate 3 +(if the decompression does not complete on the first call). +If +.Fa zalloc +and +.Fa zfree +are set to +.Dv Z_NULL , +.Fn inflateInit +updates them to use default allocation functions. +. +.Pp +.Fn inflateInit +does not perform any decompression. +Actual decompression will be done by +.Xr inflate 3 . +So +.Fa next_in , +.Fa avail_in , +.Fa next_out +and +.Fa avail_out +are unused and unchanged. +The current implementation of +.Fn inflateInit +does not process any header information \(em +that is deferred until +.Xr inflate 3 +is called. +. +.Pp +The +.Vt z_streamp +type is documented in +.Xr deflateInit 3 . +. +.Sh RETURN VALUES +.Fn inflateInit +returns +.Dv Z_OK +if success, +.Dv Z_MEM_ERROR +if there was not enough memory, +.Dv Z_VERSION_ERROR +if the zlib library version +is incompatible with the version assumed by the caller, +or +.Dv Z_STREAM_ERROR +if the parameters are invalid, +such as a null pointer to the structure. +.Fa msg +is set to null if there is no error message. +. +.Sh SEE ALSO +.Xr inflate 3 , +.Xr inflateBackInit 3 , +.Xr inflateCopy 3 , +.Xr inflateEnd 3 , +.Xr inflateInit2 3 , +.Xr inflateSetDictionary 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateInit2.3 b/doc/zlib/inflateInit2.3 new file mode 100644 index 00000000..a630f12a --- /dev/null +++ b/doc/zlib/inflateInit2.3 @@ -0,0 +1,181 @@ +.Dd January 15, 2017 +.Dt INFLATEINIT2 3 +.Os +. +.Sh NAME +.Nm inflateInit2 +.Nd inflate compression options +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn inflateInit2 "z_streamp strm" "int windowBits" +. +.Sh DESCRIPTION +This is another version of +.Xr inflateInit 3 +with an extra parameter. +The fields +.Fa next_in , +.Fa avail_in , +.Fa zalloc , +.Fa zfree +and +.Fa opaque +must be initialized before by the caller. +. +.Pp +The +.Fa windowBits +parameter is the base two logarithm +of the maximum window size +(the size of the history buffer). +It should be in the range 8..15 +for this version of the library. +The default value is 15 if +.Xr inflateInit 3 +is used instead. +.Fa windowBits +must be greater than or equal to the +.Fa windowBits +value provided to +.Xr deflateInit2 3 +while compressing, +or it must be equal to 15 if +.Xr deflateInit2 3 +was not used. +If a compressed stream with a larger window size +is given as input, +.Xr inflate 3 +will return with the error code +.Dv Z_DATA_ERROR +instead of trying to allocate a larger window. +. +.Pp +.Fa windowBits +can also be zero +to request that +.Xr inflate 3 +use the window size +in the zlib header +of the compressed stream. +. +.Pp +.Fa windowBits +can also be -8..-15 +for raw inflate. +In this case, +.Fa -windowBits +determines the window size. +.Xr inflate 3 +will then process raw deflate data, +not looking for a zlib or gzip header, +not generating a check value, +and not looking for any check values +for comparison at the end of the stream. +This is for use with other formats +that use the deflate compressed data format +such as zip. +Those formats provide their own check values. +If a custom format is developed +using the raw deflate format for compressed data, +it is recommended that a check value +such as an Adler-32 or a CRC-32 +be applied to the uncompressed data +as is done in the zlib, gzip and zip formats. +For most applications, +the zlib format should be used as is. +Note that comments above on the use in +.Xr deflateInit2 3 +applies to the magnitude of +.Fa windowBits . +. +.Pp +.Fa windowBits +can also be greater than 15 +for optional gzip decoding. +Add 32 to +.Fa windowBits +to enable zlib and gzip decoding +with automatic header detection, +or add 16 to decode only the gzip format +.Po +the zlib format will return a +.Dv Z_DATA_ERROR +.Pc . +If a gzip stream is being decoded, +.Fa strm->adler +is a CRC-32 instead of an Adler-32. +Unlike the +.Xr gunzip 1 +utility and +.Xr gzread 3 , +.Xr inflate 3 +will not automatically decode +concatenated gzip streams. +.Xr inflate 3 +will return +.Dv Z_STREAM_END +at the end of the gzip stream. +The state would need to be reset +to continue decoding a subsequent gzip stream. +. +.Pp +.Fn inflateInit2 +does not perform any decompression +apart from possibly reading the zlib header if present: +actual decompression will be done by +.Xr inflate 3 . +.Po +So +.Fa next_in +and +.Fa avail_in +may be modified, +but +.Fa next_out +and +.Fa avail_out +are unused and unchanged. +.Pc \& +The current implementation of +.Fn inflateInit2 +does not process any header information \(em +that is deferred until +.Xr inflate 3 +is called. +. +.Sh RETURN VALUES +.Fn inflateInit2 +returns +.Dv Z_OK +if success, +.Dv Z_MEM_ERROR +if there was not enough memory, +.Dv Z_VERSION_ERROR +if the zlib library version +is incompatible with the version assumed by the caller, +or +.Dv Z_STREAM_ERROR +if the parameters are invalid, +such as a null pointer to the structure. +.Fa msg +is set to null if there is no error message. +. +.Sh SEE ALSO +.Xr deflateInit2 3 , +.Xr inflateInit 3 , +.Xr inflatePrime 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateMark.3 b/doc/zlib/inflateMark.3 new file mode 100644 index 00000000..2d15993d --- /dev/null +++ b/doc/zlib/inflateMark.3 @@ -0,0 +1,88 @@ +.Dd January 15, 2017 +.Dt INFLATEMARK 3 +.Os +. +.Sh NAME +.Nm inflateMark +.Nd mark location for random access +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft long +.Fn inflateMark "z_streamp strm" +. +.Sh DESCRIPTION +This function returns two values, +one in the lower 16 bits of the return value, +and the other in the remaining upper bits, +obtained by shifting the return value down 16 bits. +If the upper value is -1 +and the lower value is zero, +then +.Xr inflate 3 +is currently decoding information outside of a block. +If the upper value is -1 +and the lower value is non-zero, +then +.Xr inflate 3 +is in the middle of a stored block, +with the lower value equaling +the number of bytes from the input remaining to copy. +If the upper value is not -1, +then it is the number of bits +back from the current bit position +in the input of the code +(literal of length/distance pair) +currently being processed. +In that case the lower value +is the number of bytes +already emitted for that code. +. +.Pp +A code is being processed if +.Xr inflate 3 +is waiting for more input to complete +decoding of the code, +or if it has completed decoding +but is waiting for more output space +to write the literal or match data. +. +.Pp +.Fn inflateMark +is used to mark locations in the input data +for random access, +which may be at bit positions, +and to note those cases where +the output of a code may span boundaries +of random access blocks. +The current location in the input stream +can be determined from +.Fa avail_in +and +.Fa data_type +as noted in the description for the +.Dv Z_BLOCK +.Fa flush +parameter for +.Xr inflate 3 . +. +.Sh RETURN VALUES +.Fn inflateMark +returns the value noted above, +or -65536 if the provided source stream state was inconsistent. +. +.Sh SEE ALSO +.Xr inflate 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflatePrime.3 b/doc/zlib/inflatePrime.3 new file mode 100644 index 00000000..c89dc2c5 --- /dev/null +++ b/doc/zlib/inflatePrime.3 @@ -0,0 +1,73 @@ +.Dd January 15, 2017 +.Dt INFLATEPRIME 3 +.Os +. +.Sh NAME +.Nm inflatePrime +.Nd insert bits in inflate stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn inflatePrime "z_streamp strm" "int bits" "int value" +. +.Sh DESCRIPTION +This function inserts bits +in the inflate input stream. +The intent is that this function +is used to start inflating +at a bit position +in the middle of a byte. +The provided bits will be used +before any bytes are used from +.Fa next_in . +This function should only be used with raw inflate, +and should be used before the first +.Xr inflate 3 +call after +.Xr inflateInit2 3 +or +.Xr inflateReset 3 . +.Fa bits +must be less than or equal to 16, +and that many of the least significant bits of +.Fa value +will be inserted in the input. +. +.Pp +If +.Fa bits +is negative, +then the input stream bit buffer is emptied. +Then +.Fn inflatePrime +can be called again +to put bits in the buffer. +This is used to clear out bits leftover +after feeding inflate a block description +prior to feeding inflate codes. +. +.Sh RETURN VALUES +.Fn inflatePrime +returns +.Dv Z_OK +if success, +or +.Dv Z_STREAM_ERROR +if the source stream state was inconsistent. +. +.Sh SEE ALSO +.Xr inflateInit2 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateReset.3 b/doc/zlib/inflateReset.3 new file mode 100644 index 00000000..a8d2e219 --- /dev/null +++ b/doc/zlib/inflateReset.3 @@ -0,0 +1,81 @@ +.Dd January 15, 2017 +.Dt INFLATERESET 3 +.Os +. +.Sh NAME +.Nm inflateReset , +.Nm inflateReset2 +.Nd reset inflate stream +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn inflateReset "z_streamp strm" +.Ft int +.Fn inflateReset2 "z_streamp strm" "int windowBits" +. +.Sh DESCRIPTION +This function is equivalent to +.Xr inflateEnd 3 +followed by +.Xr inflateInit 3 , +but does not free and reallocate +the internal decompression state. +The stream will keep attributes +that may have been set by +.Xr inflateInit2 3 . +. +.Pp +.Fn inflateReset2 +is the same as +.Fn inflateReset , +but it also permits changing +the wrap and window size requests. +The +.Fa windowBits +parameter is interpreted the same as it is for +.Xr inflateInit2 3 . +If the window size is changed, +then the memory allocated for the window is freed, +and the window will be reallocated by +.Xr inflate 3 +if needed. +. +.Sh RETURN VALUES +.Fn inflateReset +and +.Fn inflateReset2 +return +.Dv Z_OK +if success, +or +.Dv Z_STREAM_ERROR +if the source stream state was inconsistent +.Po +such as +.Fa zalloc +or +.Fa state +being +.Dv Z_NULL +.Pc , +or if the +.Fa windowBits +parameter is invalid. +. +.Sh SEE ALSO +.Xr inflateEnd 3 , +.Xr inflateInit 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateSetDictionary.3 b/doc/zlib/inflateSetDictionary.3 new file mode 100644 index 00000000..0e3c60c7 --- /dev/null +++ b/doc/zlib/inflateSetDictionary.3 @@ -0,0 +1,85 @@ +.Dd January 15, 2017 +.Dt INFLATESETDICTIONARY 3 +.Os +. +.Sh NAME +.Nm inflateSetDictionary +.Nd initialize decompression dictionary +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fo inflateSetDictionary +.Fa "z_streamp strm" +.Fa "const Bytef *dictionary" +.Fa "uInt dictLength" +.Fc +. +.Sh DESCRIPTION +Initializes the decompression dictionary +from the given uncompressed byte sequence. +This function must be called +immediately after a call of +.Xr inflate 3 , +if that call returned +.Dv Z_NEED_DICT . +The dictionary chosen by the compressor +can be determined from the Adler-32 value +returned by that call of +.Xr inflate 3 . +The compressor and decompressor +must use exactly the same dictionary +.Po +see +.Xr deflateSetDictionary 3 +.Pc . +For raw inflate, +this function can be called at any time +to set the dictionary. +If the provided dictionary +is smaller than the window +and there is already data in the window, +then the provided dictionary +will amend what's there. +The application must insure that the dictionary +that was used for compression is provided. +. +.Pp +.Fn inflateSetDictionary +does not perform any decompression: +this will be done by subsequent calls of +.Xr inflate 3 . +. +.Sh RETURN VALUES +.Fn inflateSetDictionary +returns +.Dv Z_OK +if success, +.Dv Z_STREAM_ERROR +if a parameter is invalid +.Po +e.g. dictionary being +.Dv Z_NULL +.Pc +or the stream state is inconsistent, +.Dv Z_DATA_ERROR +if the given dictionary +doesn't match the expected one +(incorrect Adler-32 value). +. +.Sh SEE ALSO +.Xr deflateGetDictionary 3 , +.Xr inflateGetDictionary 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/inflateSync.3 b/doc/zlib/inflateSync.3 new file mode 100644 index 00000000..35264ddd --- /dev/null +++ b/doc/zlib/inflateSync.3 @@ -0,0 +1,72 @@ +.Dd January 15, 2017 +.Dt INFLATESYNC 3 +.Os +. +.Sh NAME +.Nm inflateSync +.Nd skip invalid data +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft int +.Fn inflateSync "z_streamp strm" +. +.Sh DESCRIPTION +Skips invalid compressed data +until a possible full flush point +.Po +see +.Xr deflate 3 +for the description of +.Dv Z_FULL_FLUSH +.Pc +can be found, +or until all available input is skipped. +No output is provided. +. +.Pp +.Fn inflateSync +searches for a 00 00 FF FF pattern +in the compressed data. +All full flush points have this pattern, +but not all occurrences of this pattern +are full flush points. +. +.Sh RETURN VALUES +.Fn inflateSync +returns +.Dv Z_OK +if a possible full flush point has been found, +.Dv Z_BUF_ERROR +if no more input was provided, +.Dv Z_DATA_ERROR +if no flush point has been found, +or +.Dv Z_STREAM_ERROR +if the stream structure was inconsistent. +In the success case, +the application may save the current value of +.Fa total_in +which indicates where valid compressed data was found. +In the error case, +the application may repeatedly call +.Fn inflateSync , +providing more input each time, +until success or the end of the input data. +. +.Sh SEE ALSO +.Xr deflate 3 , +.Xr inflate 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/uncompress.3 b/doc/zlib/uncompress.3 new file mode 100644 index 00000000..d951da9b --- /dev/null +++ b/doc/zlib/uncompress.3 @@ -0,0 +1,92 @@ +.Dd January 15, 2017 +.Dt UNCOMPRESS 3 +.Os +. +.Sh NAME +.Nm uncompress , +.Nm uncompress2 +.Nd decompress source buffer into destination buffer +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +. +.Ft int +.Fo uncompress +.Fa "Bytef *dest" +.Fa "uLongf *destLen" +.Fa "const Bytef *source" +.Fa "uLong sourceLen" +.Fc +. +.Ft int +.Fo uncompress2 +.Fa "Bytef *dest" +.Fa "uLongf *destLen" +.Fa "const Bytef *source" +.Fa "uLong *sourceLen" +.Fc +. +.Sh DESCRIPTION +Decompresses the source buffer into the destination buffer. +.Fa sourceLen +is the byte length of the source buffer. +Upon entry, +.Fa destLen +is the total size of the destination buffer, +which must be large enough to hold the entire uncompressed data. +.Po +The size of the uncompressed data +must have been saved previously by the compressor +and transmitted to the decompressor +by some mechanism outside the scope of this compression library. +.Pc \& +Upon exit, +.Fa destLen +is the actual size of the uncompressed data. +. +.Pp +.Fn uncompress2 +is the same as +.Fn uncompress , +except that +.Fa sourceLen +is a pointer, +where the length of the source is +.Fa *sourceLen . +On return, +.Fa *sourceLen +is the number of source bytes consumed. +. +.Sh RETURN VALUES +.Fn uncompress +returns +.Dv Z_OK +if success, +.Dv Z_MEM_ERROR +if there was not enough memory, +.Dv Z_BUF_ERROR +if there was not enough room in the output buffer, +or +.Dv Z_DATA_ERROR +if the input data was corrupted or incomplete. +In the case where there is not enough room, +.Fn uncompress +will fill the output buffer +with the uncompressed data up to that point. +. +.Sh SEE ALSO +.Xr compress 3 , +.Xr inflate 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/zlibCompileFlags.3 b/doc/zlib/zlibCompileFlags.3 new file mode 100644 index 00000000..465195c2 --- /dev/null +++ b/doc/zlib/zlibCompileFlags.3 @@ -0,0 +1,163 @@ +.Dd January 15, 2017 +.Dt ZLIBCOMPILEFLAGS 3 +.Os +. +.Sh NAME +.Nm zlibCompileFlags +.Nd zlib compile-time options +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Ft uLong +.Fn zlibCompileFlags void +. +.Sh DESCRIPTION +Return flags indicating compile-time options. +. +.Ss Type sizes +Two bits each, +00 = 16 bits, +01 = 32 bits, +10 = 64 bits, +11 = other. +. +.Pp +.Bl -tag -width Ds -compact +.It 1.0 +size of +.Vt uInt +.It 3.2 +size of +.Vt uLong +.It 5.4 +size of +.Vt voidpf +(pointer) +.It 7.6 +size of +.Vt z_off_t +.El +. +.Ss Compiler, assembler, and debug options +.Bl -tag -width Ds -compact +.It 8 +.Dv ZLIB_DEBUG +.It 9 +.Dv ASMV +or +.Dv ASMINF +\(em +use ASM code +.It 10 +.Dv ZLIB_WINAPI +\(em +exported functions use the WINAPI calling convention +.It 11 +0 (reserved) +.El +. +.Ss One-time table building +Smaller code, +but not thread-safe if true. +. +.Pp +.Bl -tag -width Ds -compact +.It 12 +.Dv BUILDFIXED +\(em +build static block decoding tables when needed +.It 13 +.Dv DYNAMIC_CRC_TABLE +\(em +build CRC calculation tables when needed +.It 14,15 +0 (reserved) +.El +. +.Ss Library content +Indicates missing functionality. +. +.Pp +.Bl -tag -width Ds -compact +.It 16 +.Dv NO_GZCOMPRESS +\(em +gz* functions cannot compress +(to avoid linking deflate code when not needed) +.It 17 +.Dv NO_GZIP +\(em +.Xr deflate 3 +can't write gzip streams, +and +.Xr inflate 3 +can't detect and decode gzip streams +(to avoid linking crc code) +.It 18-19 +0 (reserved) +.El +. +.Ss Operation variations +Changes in library functionality. +. +.Pp +.Bl -tag -width Ds -compact +.It 20 +.Dv PKZIP_BUG_WORKAROUND +\(em +slightly more permissive +.Xr inflate 3 +.It 21 +.Dv FASTEST +\(em +deflate algorithm with only one, +lowest compression level +.It 22,23 +0 (reserved) +.El +. +.Ss sprintf variant used by gzprintf +Zero is best. +. +.Pp +.Bl -tag -width Ds -compact +.It 24 +0 = vs*, +1 = s* +\(em +1 means limited to 20 arguments after the format +.It 25 +0 = *nprintf, +1 = *printf +\(em +1 means +.Xr gzprintf 3 +not secure! +.It 26 +0 = returns value, +1 = void +\(em +1 means inferred string length returned +.El +. +.Ss Remainder +.Bl -tag -width Ds -compact +.It 27-31 +0 (reserved) +.El +. +.Sh SEE ALSO +.Xr zlibVersion 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/doc/zlib/zlibVersion.3 b/doc/zlib/zlibVersion.3 new file mode 100644 index 00000000..35a9854b --- /dev/null +++ b/doc/zlib/zlibVersion.3 @@ -0,0 +1,45 @@ +.Dd January 15, 2017 +.Dt ZLIBVERSION 3 +.Os +. +.Sh NAME +.Nm zlibVersion +.Nd check zlib version +. +.Sh LIBRARY +.Lb libz +. +.Sh SYNOPSIS +.In zlib.h +.Fd #define ZLIB_VERSION \(dq1.2.11\(dq +.Ft "const char *" +.Fn zlibVersion void +. +.Sh DESCRIPTION +The application can compare +.Fn zlibVersion +and +.Dv ZLIB_VERSION +for consistency. +If the first character differs, +the library code actually used +is not compatible with the +.In zlib.h +header file used by the application. +This check is automatically made by +.Xr deflateInit 3 +and +.Xr inflateInit 3 . +. +.Sh SEE ALSO +.Xr zlibCompileFlags 3 +. +.Sh HISTORY +This manual page was converted from +.In zlib.h +to mdoc format by +.An C. McEnroe Aq Mt june@causal.agency . +. +.Sh AUTHORS +.An Jean-loup Gailly Aq Mt jloup@gzip.org +.An Mark Adler Aq Mt madler@alumni.caltech.edu diff --git a/etc/CodeQWERTY.bundle/Contents/Info.plist b/etc/CodeQWERTY.bundle/Contents/Info.plist new file mode 100644 index 00000000..f78351e8 --- /dev/null +++ b/etc/CodeQWERTY.bundle/Contents/Info.plist @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>CFBundleIdentifier</key> + <string>agency.causal.keyboardlayout.code</string> + <key>CFBundleName</key> + <string>Code QWERTY</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>KLInfo_Code QWERTY</key> + <dict> + <key>TISInputSourceID</key> + <string>agency.causal.keyboardlayout.code.qwerty</string> + <key>TISIntendedLanguage</key> + <string>en-CA</string> + </dict> + </dict> +</plist> diff --git a/etc/CodeQWERTY.bundle/Contents/Resources/CodeQWERTY.keylayout b/etc/CodeQWERTY.bundle/Contents/Resources/CodeQWERTY.keylayout new file mode 100644 index 00000000..393a86dd --- /dev/null +++ b/etc/CodeQWERTY.bundle/Contents/Resources/CodeQWERTY.keylayout @@ -0,0 +1,1178 @@ +<?xml version="1.1" encoding="UTF-8"?> +<!DOCTYPE keyboard PUBLIC "" "file://localhost/System/Library/DTDs/KeyboardLayout.dtd"> +<keyboard group="0" id="5069" maxout="1" name="Code QWERTY"> + <layouts> + <layout first="0" last="17" mapSet="16c" modifiers="f4"/> + <layout first="18" last="18" mapSet="994" modifiers="f4"/> + <layout first="21" last="23" mapSet="994" modifiers="f4"/> + <layout first="30" last="30" mapSet="994" modifiers="f4"/> + <layout first="194" last="194" mapSet="994" modifiers="f4"/> + <layout first="197" last="197" mapSet="994" modifiers="f4"/> + <layout first="200" last="201" mapSet="994" modifiers="f4"/> + <layout first="206" last="207" mapSet="994" modifiers="f4"/> + </layouts> + <modifierMap defaultIndex="7" id="f4"> + <keyMapSelect mapIndex="8"> + <modifier keys="command?"/> + </keyMapSelect> + <keyMapSelect mapIndex="0"> + <modifier keys="anyShift? caps? command"/> + </keyMapSelect> + <keyMapSelect mapIndex="9"> + <modifier keys="anyShift caps?"/> + </keyMapSelect> + <keyMapSelect mapIndex="2"> + <modifier keys="caps"/> + </keyMapSelect> + <keyMapSelect mapIndex="3"> + <modifier keys="anyOption"/> + </keyMapSelect> + <keyMapSelect mapIndex="4"> + <modifier keys="anyShift caps? anyOption command?"/> + </keyMapSelect> + <keyMapSelect mapIndex="5"> + <modifier keys="caps anyOption"/> + </keyMapSelect> + <keyMapSelect mapIndex="6"> + <modifier keys="caps? anyOption command"/> + </keyMapSelect> + <keyMapSelect mapIndex="7"> + <modifier keys="anyShift caps? option? command? control"/> + <modifier keys="shift? caps? anyOption command? control"/> + <modifier keys="caps? anyOption? command? control"/> + </keyMapSelect> + </modifierMap> + <keyMapSet id="16c"> + <keyMap index="0"> + <key action="13" code="0"/> + <key code="1" output="s"/> + <key code="2" output="d"/> + <key code="3" output="f"/> + <key code="4" output="h"/> + <key code="5" output="g"/> + <key code="6" output="z"/> + <key code="7" output="x"/> + <key code="8" output="c"/> + <key code="9" output="v"/> + <key code="10" output="§"/> + <key code="11" output="b"/> + <key code="12" output="q"/> + <key code="13" output="w"/> + <key action="14" code="14"/> + <key code="15" output="r"/> + <key action="19" code="16"/> + <key code="17" output="t"/> + <key code="18" output="1"/> + <key code="19" output="2"/> + <key code="20" output="3"/> + <key code="21" output="4"/> + <key code="22" output="6"/> + <key code="23" output="5"/> + <key code="24" output="="/> + <key code="25" output="9"/> + <key code="26" output="7"/> + <key code="27" output="-"/> + <key code="28" output="8"/> + <key code="29" output="0"/> + <key code="30" output="]"/> + <key action="17" code="31"/> + <key action="18" code="32"/> + <key code="33" output="["/> + <key action="15" code="34"/> + <key code="35" output="p"/> + <key code="36" output="
"/> + <key code="37" output="l"/> + <key code="38" output="j"/> + <key code="39" output="'"/> + <key code="40" output="k"/> + <key code="41" output=";"/> + <key code="42" output="\"/> + <key code="43" output=","/> + <key code="44" output="/"/> + <key action="16" code="45"/> + <key code="46" output="m"/> + <key code="47" output="."/> + <key code="48" output="	"/> + <key action="5" code="49"/> + <key code="50" output="`"/> + <key code="51" output=""/> + <key code="52" output=""/> + <key code="53" output=""/> + <key code="64" output=""/> + <key code="65" output="."/> + <key code="66" output=""/> + <key code="67" output="*"/> + <key code="69" output="+"/> + <key code="70" output=""/> + <key code="71" output=""/> + <key code="72" output=""/> + <key code="75" output="/"/> + <key code="76" output=""/> + <key code="77" output=""/> + <key code="78" output="-"/> + <key code="79" output=""/> + <key code="80" output=""/> + <key code="81" output="="/> + <key code="82" output="0"/> + <key code="83" output="1"/> + <key code="84" output="2"/> + <key code="85" output="3"/> + <key code="86" output="4"/> + <key code="87" output="5"/> + <key code="88" output="6"/> + <key code="89" output="7"/> + <key code="91" output="8"/> + <key code="92" output="9"/> + <key code="96" output=""/> + <key code="97" output=""/> + <key code="98" output=""/> + <key code="99" output=""/> + <key code="100" output=""/> + <key code="101" output=""/> + <key code="102" output=""/> + <key code="103" output=""/> + <key code="104" output=""/> + <key code="105" output=""/> + <key code="106" output=""/> + <key code="107" output=""/> + <key code="108" output=""/> + <key code="109" output=""/> + <key code="110" output=""/> + <key code="111" output=""/> + <key code="112" output=""/> + <key code="113" output=""/> + <key code="114" output=""/> + <key code="115" output=""/> + <key code="116" output=""/> + <key code="117" output=""/> + <key code="118" output=""/> + <key code="119" output=""/> + <key code="120" output=""/> + <key code="121" output=""/> + <key code="122" output=""/> + <key code="123" output=""/> + <key code="124" output=""/> + <key code="125" output=""/> + <key code="126" output=""/> + </keyMap> + <keyMap index="1"> + <key action="6" code="0"/> + <key code="1" output="S"/> + <key code="2" output="D"/> + <key code="3" output="F"/> + <key code="4" output="H"/> + <key code="5" output="G"/> + <key code="6" output="Z"/> + <key code="7" output="X"/> + <key code="8" output="C"/> + <key code="9" output="V"/> + <key code="10" output="±"/> + <key code="11" output="B"/> + <key code="12" output="Q"/> + <key code="13" output="W"/> + <key action="7" code="14"/> + <key code="15" output="R"/> + <key action="12" code="16"/> + <key code="17" output="T"/> + <key code="18" output="!"/> + <key code="19" output="@"/> + <key code="20" output="#"/> + <key code="21" output="$"/> + <key code="22" output="^"/> + <key code="23" output="%"/> + <key code="24" output="+"/> + <key code="25" output="("/> + <key code="26" output="&"/> + <key code="27" output="_"/> + <key code="28" output="*"/> + <key code="29" output=")"/> + <key code="30" output="}"/> + <key action="10" code="31"/> + <key action="11" code="32"/> + <key code="33" output="{"/> + <key action="8" code="34"/> + <key code="35" output="P"/> + <key code="36" output="
"/> + <key code="37" output="L"/> + <key code="38" output="J"/> + <key code="39" output="""/> + <key code="40" output="K"/> + <key code="41" output=":"/> + <key code="42" output="|"/> + <key code="43" output="<"/> + <key code="44" output="?"/> + <key action="9" code="45"/> + <key code="46" output="M"/> + <key code="47" output=">"/> + <key code="48" output="	"/> + <key action="5" code="49"/> + <key code="50" output="~"/> + <key code="51" output=""/> + <key code="52" output=""/> + <key code="53" output=""/> + <key code="64" output=""/> + <key code="65" output="."/> + <key code="66" output="*"/> + <key code="67" output="*"/> + <key code="69" output="+"/> + <key code="70" output="+"/> + <key code="71" output=""/> + <key code="72" output="="/> + <key code="75" output="/"/> + <key code="76" output=""/> + <key code="77" output="/"/> + <key code="78" output="-"/> + <key code="79" output=""/> + <key code="80" output=""/> + <key code="81" output="="/> + <key code="82" output="0"/> + <key code="83" output="1"/> + <key code="84" output="2"/> + <key code="85" output="3"/> + <key code="86" output="4"/> + <key code="87" output="5"/> + <key code="88" output="6"/> + <key code="89" output="7"/> + <key code="91" output="8"/> + <key code="92" output="9"/> + <key code="96" output=""/> + <key code="97" output=""/> + <key code="98" output=""/> + <key code="99" output=""/> + <key code="100" output=""/> + <key code="101" output=""/> + <key code="102" output=""/> + <key code="103" output=""/> + <key code="104" output=""/> + <key code="105" output=""/> + <key code="106" output=""/> + <key code="107" output=""/> + <key code="108" output=""/> + <key code="109" output=""/> + <key code="110" output=""/> + <key code="111" output=""/> + <key code="112" output=""/> + <key code="113" output=""/> + <key code="114" output=""/> + <key code="115" output=""/> + <key code="116" output=""/> + <key code="117" output=""/> + <key code="118" output=""/> + <key code="119" output=""/> + <key code="120" output=""/> + <key code="121" output=""/> + <key code="122" output=""/> + <key code="123" output=""/> + <key code="124" output=""/> + <key code="125" output=""/> + <key code="126" output=""/> + </keyMap> + <keyMap index="2"> + <key action="6" code="0"/> + <key code="1" output="S"/> + <key code="2" output="D"/> + <key code="3" output="F"/> + <key code="4" output="H"/> + <key code="5" output="G"/> + <key code="6" output="Z"/> + <key code="7" output="X"/> + <key code="8" output="C"/> + <key code="9" output="V"/> + <key code="10" output="§"/> + <key code="11" output="B"/> + <key code="12" output="Q"/> + <key code="13" output="W"/> + <key action="7" code="14"/> + <key code="15" output="R"/> + <key action="12" code="16"/> + <key code="17" output="T"/> + <key code="18" output="1"/> + <key code="19" output="2"/> + <key code="20" output="3"/> + <key code="21" output="4"/> + <key code="22" output="6"/> + <key code="23" output="5"/> + <key code="24" output="="/> + <key code="25" output="9"/> + <key code="26" output="7"/> + <key code="27" output="-"/> + <key code="28" output="8"/> + <key code="29" output="0"/> + <key code="30" output="]"/> + <key action="10" code="31"/> + <key action="11" code="32"/> + <key code="33" output="["/> + <key action="8" code="34"/> + <key code="35" output="P"/> + <key code="36" output="
"/> + <key code="37" output="L"/> + <key code="38" output="J"/> + <key code="39" output="'"/> + <key code="40" output="K"/> + <key code="41" output=";"/> + <key code="42" output="\"/> + <key code="43" output=","/> + <key code="44" output="/"/> + <key action="9" code="45"/> + <key code="46" output="M"/> + <key code="47" output="."/> + <key code="48" output="	"/> + <key action="5" code="49"/> + <key code="50" output="`"/> + <key code="51" output=""/> + <key code="52" output=""/> + <key code="53" output=""/> + <key code="64" output=""/> + <key code="65" output="."/> + <key code="66" output=""/> + <key code="67" output="*"/> + <key code="69" output="+"/> + <key code="70" output=""/> + <key code="71" output=""/> + <key code="72" output=""/> + <key code="75" output="/"/> + <key code="76" output=""/> + <key code="77" output=""/> + <key code="78" output="-"/> + <key code="79" output=""/> + <key code="80" output=""/> + <key code="81" output="="/> + <key code="82" output="0"/> + <key code="83" output="1"/> + <key code="84" output="2"/> + <key code="85" output="3"/> + <key code="86" output="4"/> + <key code="87" output="5"/> + <key code="88" output="6"/> + <key code="89" output="7"/> + <key code="91" output="8"/> + <key code="92" output="9"/> + <key code="96" output=""/> + <key code="97" output=""/> + <key code="98" output=""/> + <key code="99" output=""/> + <key code="100" output=""/> + <key code="101" output=""/> + <key code="102" output=""/> + <key code="103" output=""/> + <key code="104" output=""/> + <key code="105" output=""/> + <key code="106" output=""/> + <key code="107" output=""/> + <key code="108" output=""/> + <key code="109" output=""/> + <key code="110" output=""/> + <key code="111" output=""/> + <key code="112" output=""/> + <key code="113" output=""/> + <key code="114" output=""/> + <key code="115" output=""/> + <key code="116" output=""/> + <key code="117" output=""/> + <key code="118" output=""/> + <key code="119" output=""/> + <key code="120" output=""/> + <key code="121" output=""/> + <key code="122" output=""/> + <key code="123" output=""/> + <key code="124" output=""/> + <key code="125" output=""/> + <key code="126" output=""/> + </keyMap> + <keyMap index="3"> + <key code="0" output="å"/> + <key code="1" output="ß"/> + <key code="2" output="∂"/> + <key code="3" output="ƒ"/> + <key code="4" output="˙"/> + <key code="5" output="©"/> + <key code="6" output="Ω"/> + <key code="7" output="≈"/> + <key code="8" output="ç"/> + <key code="9" output="√"/> + <key code="10" output="§"/> + <key code="11" output="∫"/> + <key code="12" output="œ"/> + <key code="13" output="∑"/> + <key action="0" code="14"/> + <key code="15" output="®"/> + <key code="16" output="¥"/> + <key code="17" output="†"/> + <key code="18" output="¡"/> + <key code="19" output="™"/> + <key code="20" output="£"/> + <key code="21" output="¢"/> + <key code="22" output="§"/> + <key code="23" output="∞"/> + <key code="24" output="≠"/> + <key code="25" output="ª"/> + <key code="26" output="¶"/> + <key code="27" output="–"/> + <key code="28" output="•"/> + <key code="29" output="º"/> + <key code="30" output="‘"/> + <key code="31" output="ø"/> + <key action="3" code="32"/> + <key code="33" output="“"/> + <key action="2" code="34"/> + <key code="35" output="π"/> + <key code="36" output="
"/> + <key code="37" output="¬"/> + <key code="38" output="∆"/> + <key code="39" output="æ"/> + <key code="40" output="˚"/> + <key code="41" output="…"/> + <key code="42" output="«"/> + <key code="43" output="≤"/> + <key code="44" output="÷"/> + <key action="4" code="45"/> + <key code="46" output="µ"/> + <key code="47" output="≥"/> + <key code="48" output="	"/> + <key code="49" output=" "/> + <key action="1" code="50"/> + <key code="51" output=""/> + <key code="52" output=""/> + <key code="53" output=""/> + <key code="64" output=""/> + <key code="65" output="."/> + <key code="66" output=""/> + <key code="67" output="*"/> + <key code="69" output="+"/> + <key code="70" output=""/> + <key code="71" output=""/> + <key code="72" output=""/> + <key code="75" output="/"/> + <key code="76" output=""/> + <key code="77" output=""/> + <key code="78" output="-"/> + <key code="79" output=""/> + <key code="80" output=""/> + <key code="81" output="="/> + <key code="82" output="0"/> + <key code="83" output="1"/> + <key code="84" output="2"/> + <key code="85" output="3"/> + <key code="86" output="4"/> + <key code="87" output="5"/> + <key code="88" output="6"/> + <key code="89" output="7"/> + <key code="91" output="8"/> + <key code="92" output="9"/> + <key code="96" output=""/> + <key code="97" output=""/> + <key code="98" output=""/> + <key code="99" output=""/> + <key code="100" output=""/> + <key code="101" output=""/> + <key code="102" output=""/> + <key code="103" output=""/> + <key code="104" output=""/> + <key code="105" output=""/> + <key code="106" output=""/> + <key code="107" output=""/> + <key code="108" output=""/> + <key code="109" output=""/> + <key code="110" output=""/> + <key code="111" output=""/> + <key code="112" output=""/> + <key code="113" output=""/> + <key code="114" output=""/> + <key code="115" output=""/> + <key code="116" output=""/> + <key code="117" output=""/> + <key code="118" output=""/> + <key code="119" output=""/> + <key code="120" output=""/> + <key code="121" output=""/> + <key code="122" output=""/> + <key code="123" output=""/> + <key code="124" output=""/> + <key code="125" output=""/> + <key code="126" output=""/> + </keyMap> + <keyMap index="4"> + <key code="0" output="Å"/> + <key code="1" output="Í"/> + <key code="2" output="Î"/> + <key code="3" output="Ï"/> + <key code="4" output="Ó"/> + <key code="5" output="˝"/> + <key code="6" output="¸"/> + <key code="7" output="˛"/> + <key code="8" output="Ç"/> + <key code="9" output="◊"/> + <key code="10" output="±"/> + <key code="11" output="ı"/> + <key code="12" output="Œ"/> + <key code="13" output="„"/> + <key code="14" output="´"/> + <key code="15" output="‰"/> + <key code="16" output="Á"/> + <key code="17" output="ˇ"/> + <key code="18" output="⁄"/> + <key code="19" output="€"/> + <key code="20" output="‹"/> + <key code="21" output="›"/> + <key code="22" output="fl"/> + <key code="23" output="fi"/> + <key code="24" output="±"/> + <key code="25" output="·"/> + <key code="26" output="‡"/> + <key code="27" output="—"/> + <key code="28" output="°"/> + <key code="29" output="‚"/> + <key code="30" output="’"/> + <key code="31" output="Ø"/> + <key code="32" output="¨"/> + <key code="33" output="”"/> + <key code="34" output="ˆ"/> + <key code="35" output="∏"/> + <key code="36" output="
"/> + <key code="37" output="Ò"/> + <key code="38" output="Ô"/> + <key code="39" output="Æ"/> + <key code="40" output=""/> + <key code="41" output="Ú"/> + <key code="42" output="»"/> + <key code="43" output="¯"/> + <key code="44" output="¿"/> + <key code="45" output="˜"/> + <key code="46" output="Â"/> + <key code="47" output="˘"/> + <key code="48" output="	"/> + <key code="49" output=" "/> + <key code="50" output="`"/> + <key code="51" output=""/> + <key code="52" output=""/> + <key code="53" output=""/> + <key code="64" output=""/> + <key code="65" output="."/> + <key code="66" output="*"/> + <key code="67" output="*"/> + <key code="69" output="+"/> + <key code="70" output="+"/> + <key code="71" output=""/> + <key code="72" output="="/> + <key code="75" output="/"/> + <key code="76" output=""/> + <key code="77" output="/"/> + <key code="78" output="-"/> + <key code="79" output=""/> + <key code="80" output=""/> + <key code="81" output="="/> + <key code="82" output="0"/> + <key code="83" output="1"/> + <key code="84" output="2"/> + <key code="85" output="3"/> + <key code="86" output="4"/> + <key code="87" output="5"/> + <key code="88" output="6"/> + <key code="89" output="7"/> + <key code="91" output="8"/> + <key code="92" output="9"/> + <key code="96" output=""/> + <key code="97" output=""/> + <key code="98" output=""/> + <key code="99" output=""/> + <key code="100" output=""/> + <key code="101" output=""/> + <key code="102" output=""/> + <key code="103" output=""/> + <key code="104" output=""/> + <key code="105" output=""/> + <key code="106" output=""/> + <key code="107" output=""/> + <key code="108" output=""/> + <key code="109" output=""/> + <key code="110" output=""/> + <key code="111" output=""/> + <key code="112" output=""/> + <key code="113" output=""/> + <key code="114" output=""/> + <key code="115" output=""/> + <key code="116" output=""/> + <key code="117" output=""/> + <key code="118" output=""/> + <key code="119" output=""/> + <key code="120" output=""/> + <key code="121" output=""/> + <key code="122" output=""/> + <key code="123" output=""/> + <key code="124" output=""/> + <key code="125" output=""/> + <key code="126" output=""/> + </keyMap> + <keyMap index="5"> + <key code="0" output="Å"/> + <key code="1" output="Í"/> + <key code="2" output="Î"/> + <key code="3" output="Ï"/> + <key code="4" output="Ó"/> + <key code="5" output="©"/> + <key code="6" output="Ω"/> + <key code="7" output="≈"/> + <key code="8" output="Ç"/> + <key code="9" output="√"/> + <key code="10" output="§"/> + <key code="11" output="ı"/> + <key code="12" output="Œ"/> + <key code="13" output="∑"/> + <key code="14" output="´"/> + <key code="15" output="®"/> + <key code="16" output="Á"/> + <key code="17" output="†"/> + <key code="18" output="¡"/> + <key code="19" output="™"/> + <key code="20" output="£"/> + <key code="21" output="¢"/> + <key code="22" output="§"/> + <key code="23" output="∞"/> + <key code="24" output="≠"/> + <key code="25" output="ª"/> + <key code="26" output="¶"/> + <key code="27" output="–"/> + <key code="28" output="•"/> + <key code="29" output="º"/> + <key code="30" output="‘"/> + <key code="31" output="Ø"/> + <key code="32" output="¨"/> + <key code="33" output="“"/> + <key code="34" output="ˆ"/> + <key code="35" output="∏"/> + <key code="36" output="
"/> + <key code="37" output="Ò"/> + <key code="38" output="Ô"/> + <key code="39" output="Æ"/> + <key code="40" output="˚"/> + <key code="41" output="…"/> + <key code="42" output="«"/> + <key code="43" output="≤"/> + <key code="44" output="÷"/> + <key code="45" output="˜"/> + <key code="46" output="Â"/> + <key code="47" output="≥"/> + <key code="48" output="	"/> + <key code="49" output=" "/> + <key code="50" output="`"/> + <key code="51" output=""/> + <key code="52" output=""/> + <key code="53" output=""/> + <key code="64" output=""/> + <key code="65" output="."/> + <key code="66" output=""/> + <key code="67" output="*"/> + <key code="69" output="+"/> + <key code="70" output=""/> + <key code="71" output=""/> + <key code="72" output=""/> + <key code="75" output="/"/> + <key code="76" output=""/> + <key code="77" output=""/> + <key code="78" output="-"/> + <key code="79" output=""/> + <key code="80" output=""/> + <key code="81" output="="/> + <key code="82" output="0"/> + <key code="83" output="1"/> + <key code="84" output="2"/> + <key code="85" output="3"/> + <key code="86" output="4"/> + <key code="87" output="5"/> + <key code="88" output="6"/> + <key code="89" output="7"/> + <key code="91" output="8"/> + <key code="92" output="9"/> + <key code="96" output=""/> + <key code="97" output=""/> + <key code="98" output=""/> + <key code="99" output=""/> + <key code="100" output=""/> + <key code="101" output=""/> + <key code="102" output=""/> + <key code="103" output=""/> + <key code="104" output=""/> + <key code="105" output=""/> + <key code="106" output=""/> + <key code="107" output=""/> + <key code="108" output=""/> + <key code="109" output=""/> + <key code="110" output=""/> + <key code="111" output=""/> + <key code="112" output=""/> + <key code="113" output=""/> + <key code="114" output=""/> + <key code="115" output=""/> + <key code="116" output=""/> + <key code="117" output=""/> + <key code="118" output=""/> + <key code="119" output=""/> + <key code="120" output=""/> + <key code="121" output=""/> + <key code="122" output=""/> + <key code="123" output=""/> + <key code="124" output=""/> + <key code="125" output=""/> + <key code="126" output=""/> + </keyMap> + <keyMap index="6"> + <key code="0" output="å"/> + <key code="1" output="ß"/> + <key code="2" output="∂"/> + <key code="3" output="ƒ"/> + <key code="4" output="˙"/> + <key code="5" output="©"/> + <key code="6" output="Ω"/> + <key code="7" output="≈"/> + <key code="8" output="ç"/> + <key code="9" output="√"/> + <key code="10" output="§"/> + <key code="11" output="∫"/> + <key code="12" output="œ"/> + <key code="13" output="∑"/> + <key code="14" output="´"/> + <key code="15" output="®"/> + <key code="16" output="¥"/> + <key code="17" output="†"/> + <key code="18" output="¡"/> + <key code="19" output="™"/> + <key code="20" output="£"/> + <key code="21" output="¢"/> + <key code="22" output="§"/> + <key code="23" output="∞"/> + <key code="24" output="≠"/> + <key code="25" output="ª"/> + <key code="26" output="¶"/> + <key code="27" output="–"/> + <key code="28" output="•"/> + <key code="29" output="º"/> + <key code="30" output="‘"/> + <key code="31" output="ø"/> + <key code="32" output="¨"/> + <key code="33" output="“"/> + <key code="34" output="^"/> + <key code="35" output="π"/> + <key code="36" output="
"/> + <key code="37" output="¬"/> + <key code="38" output="∆"/> + <key code="39" output="æ"/> + <key code="40" output="˚"/> + <key code="41" output="…"/> + <key code="42" output="«"/> + <key code="43" output="≤"/> + <key code="44" output="÷"/> + <key code="45" output="~"/> + <key code="46" output="µ"/> + <key code="47" output="≥"/> + <key code="48" output="	"/> + <key code="49" output=" "/> + <key code="50" output="`"/> + <key code="51" output=""/> + <key code="52" output=""/> + <key code="53" output=""/> + <key code="64" output=""/> + <key code="65" output="."/> + <key code="66" output=""/> + <key code="67" output="*"/> + <key code="69" output="+"/> + <key code="70" output=""/> + <key code="71" output=""/> + <key code="72" output=""/> + <key code="75" output="/"/> + <key code="76" output=""/> + <key code="77" output=""/> + <key code="78" output="-"/> + <key code="79" output=""/> + <key code="80" output=""/> + <key code="81" output="="/> + <key code="82" output="0"/> + <key code="83" output="1"/> + <key code="84" output="2"/> + <key code="85" output="3"/> + <key code="86" output="4"/> + <key code="87" output="5"/> + <key code="88" output="6"/> + <key code="89" output="7"/> + <key code="91" output="8"/> + <key code="92" output="9"/> + <key code="96" output=""/> + <key code="97" output=""/> + <key code="98" output=""/> + <key code="99" output=""/> + <key code="100" output=""/> + <key code="101" output=""/> + <key code="102" output=""/> + <key code="103" output=""/> + <key code="104" output=""/> + <key code="105" output=""/> + <key code="106" output=""/> + <key code="107" output=""/> + <key code="108" output=""/> + <key code="109" output=""/> + <key code="110" output=""/> + <key code="111" output=""/> + <key code="112" output=""/> + <key code="113" output=""/> + <key code="114" output=""/> + <key code="115" output=""/> + <key code="116" output=""/> + <key code="117" output=""/> + <key code="118" output=""/> + <key code="119" output=""/> + <key code="120" output=""/> + <key code="121" output=""/> + <key code="122" output=""/> + <key code="123" output=""/> + <key code="124" output=""/> + <key code="125" output=""/> + <key code="126" output=""/> + </keyMap> + <keyMap index="7"> + <key code="0" output=""/> + <key code="1" output=""/> + <key code="2" output=""/> + <key code="3" output=""/> + <key code="4" output=""/> + <key code="5" output=""/> + <key code="6" output=""/> + <key code="7" output=""/> + <key code="8" output=""/> + <key code="9" output=""/> + <key code="10" output="0"/> + <key code="11" output=""/> + <key code="12" output=""/> + <key code="13" output=""/> + <key code="14" output=""/> + <key code="15" output=""/> + <key code="16" output=""/> + <key code="17" output=""/> + <key code="18" output="1"/> + <key code="19" output="2"/> + <key code="20" output="3"/> + <key code="21" output="4"/> + <key code="22" output="6"/> + <key code="23" output="5"/> + <key code="24" output="="/> + <key code="25" output="9"/> + <key code="26" output="7"/> + <key code="27" output=""/> + <key code="28" output="8"/> + <key code="29" output="0"/> + <key code="30" output=""/> + <key code="31" output=""/> + <key code="32" output=""/> + <key code="33" output=""/> + <key code="34" output="	"/> + <key code="35" output=""/> + <key code="36" output="
"/> + <key code="37" output=""/> + <key code="38" output="
"/> + <key code="39" output="'"/> + <key code="40" output=""/> + <key code="41" output=";"/> + <key code="42" output=""/> + <key code="43" output=","/> + <key code="44" output="/"/> + <key code="45" output=""/> + <key code="46" output="
"/> + <key code="47" output="."/> + <key code="48" output="	"/> + <key action="5" code="49"/> + <key code="50" output="`"/> + <key code="51" output=""/> + <key code="52" output=""/> + <key code="53" output=""/> + <key code="64" output=""/> + <key code="65" output="."/> + <key code="66" output=""/> + <key code="67" output="*"/> + <key code="69" output="+"/> + <key code="70" output=""/> + <key code="71" output=""/> + <key code="72" output=""/> + <key code="75" output="/"/> + <key code="76" output=""/> + <key code="77" output=""/> + <key code="78" output="-"/> + <key code="79" output=""/> + <key code="80" output=""/> + <key code="81" output="="/> + <key code="82" output="0"/> + <key code="83" output="1"/> + <key code="84" output="2"/> + <key code="85" output="3"/> + <key code="86" output="4"/> + <key code="87" output="5"/> + <key code="88" output="6"/> + <key code="89" output="7"/> + <key code="91" output="8"/> + <key code="92" output="9"/> + <key code="96" output=""/> + <key code="97" output=""/> + <key code="98" output=""/> + <key code="99" output=""/> + <key code="100" output=""/> + <key code="101" output=""/> + <key code="102" output=""/> + <key code="103" output=""/> + <key code="104" output=""/> + <key code="105" output=""/> + <key code="106" output=""/> + <key code="107" output=""/> + <key code="108" output=""/> + <key code="109" output=""/> + <key code="110" output=""/> + <key code="111" output=""/> + <key code="112" output=""/> + <key code="113" output=""/> + <key code="114" output=""/> + <key code="115" output=""/> + <key code="116" output=""/> + <key code="117" output=""/> + <key code="118" output=""/> + <key code="119" output=""/> + <key code="120" output=""/> + <key code="121" output=""/> + <key code="122" output=""/> + <key code="123" output=""/> + <key code="124" output=""/> + <key code="125" output=""/> + <key code="126" output=""/> + </keyMap> + <keyMap index="8" baseMapSet="16c" baseIndex="0"> + <key code="18" output="!"/> + <key code="19" output="@"/> + <key code="20" output="#"/> + <key code="21" output="$"/> + <key code="22" output="^"/> + <key code="23" output="%"/> + <key code="25" output="("/> + <key code="26" output="&"/> + <key code="27" output="_"/> + <key code="28" output="*"/> + <key code="29" output=")"/> + <key code="30" output="}"/> + <key code="33" output="{"/> + <key code="42" output="|"/> + </keyMap> + <keyMap index="9" baseMapSet="16c" baseIndex="1"> + <key code="18" output="1"/> + <key code="19" output="2"/> + <key code="20" output="3"/> + <key code="21" output="4"/> + <key code="22" output="6"/> + <key code="23" output="5"/> + <key code="25" output="9"/> + <key code="26" output="7"/> + <key code="27" output="-"/> + <key code="28" output="8"/> + <key code="29" output="0"/> + <key code="30" output="]"/> + <key code="33" output="["/> + <key code="42" output="\"/> + </keyMap> + </keyMapSet> + <keyMapSet id="994"> + <keyMap baseIndex="0" baseMapSet="16c" index="0"> + <key code="24" output="^"/> + <key code="30" output="["/> + <key code="33" output="@"/> + <key code="39" output=":"/> + <key code="42" output="]"/> + <key code="93" output="¥"/> + <key code="94" output="_"/> + <key code="95" output=","/> + <key action="5" code="102"/> + <key action="5" code="104"/> + </keyMap> + <keyMap baseIndex="1" baseMapSet="16c" index="1"> + <key code="19" output="""/> + <key code="22" output="&"/> + <key code="24" output="~"/> + <key code="25" output=")"/> + <key code="26" output="'"/> + <key code="27" output="="/> + <key code="28" output="("/> + <key code="29" output="0"/> + <key code="30" output="{"/> + <key code="33" output="`"/> + <key code="39" output="*"/> + <key code="41" output="+"/> + <key code="42" output="}"/> + <key code="93" output="|"/> + <key code="94" output="_"/> + <key code="95" output=","/> + <key action="5" code="102"/> + <key action="5" code="104"/> + </keyMap> + <keyMap baseIndex="2" baseMapSet="16c" index="2"> + <key code="24" output="^"/> + <key code="30" output="["/> + <key code="33" output="@"/> + <key code="39" output=":"/> + <key code="42" output="]"/> + <key code="93" output="¥"/> + <key code="94" output="_"/> + <key code="95" output=","/> + <key action="5" code="102"/> + <key action="5" code="104"/> + </keyMap> + <keyMap baseIndex="3" baseMapSet="16c" index="3"> + <key code="93" output="\"/> + <key action="1" code="94"/> + <key code="95" output=","/> + <key action="5" code="102"/> + <key action="5" code="104"/> + </keyMap> + <keyMap baseIndex="4" baseMapSet="16c" index="4"> + <key code="93" output="|"/> + <key code="94" output="`"/> + <key code="95" output=","/> + <key action="5" code="102"/> + <key action="5" code="104"/> + </keyMap> + <keyMap baseIndex="5" baseMapSet="16c" index="5"> + <key code="93" output="\"/> + <key code="94" output="`"/> + <key code="95" output=","/> + <key action="5" code="102"/> + <key action="5" code="104"/> + </keyMap> + <keyMap baseIndex="6" baseMapSet="16c" index="6"> + <key code="93" output="\"/> + <key code="94" output="_"/> + <key code="95" output=","/> + <key action="5" code="102"/> + <key action="5" code="104"/> + </keyMap> + <keyMap baseIndex="7" baseMapSet="16c" index="7"> + <key code="93" output="|"/> + <key code="94" output="_"/> + <key code="95" output=","/> + <key action="5" code="102"/> + <key action="5" code="104"/> + </keyMap> + </keyMapSet> + <actions> + <action id="0"> + <when next="s1" state="none"/> + </action> + <action id="1"> + <when next="s2" state="none"/> + </action> + <action id="10"> + <when output="O" state="none"/> + <when output="Ó" state="s1"/> + <when output="Ò" state="s2"/> + <when output="Ô" state="s3"/> + <when output="Ö" state="s4"/> + <when output="Õ" state="s5"/> + </action> + <action id="11"> + <when output="U" state="none"/> + <when output="Ú" state="s1"/> + <when output="Ù" state="s2"/> + <when output="Û" state="s3"/> + <when output="Ü" state="s4"/> + </action> + <action id="12"> + <when output="Y" state="none"/> + <when output="Ÿ" state="s4"/> + </action> + <action id="13"> + <when output="a" state="none"/> + <when output="á" state="s1"/> + <when output="à" state="s2"/> + <when output="â" state="s3"/> + <when output="ä" state="s4"/> + <when output="ã" state="s5"/> + </action> + <action id="14"> + <when output="e" state="none"/> + <when output="é" state="s1"/> + <when output="è" state="s2"/> + <when output="ê" state="s3"/> + <when output="ë" state="s4"/> + </action> + <action id="15"> + <when output="i" state="none"/> + <when output="í" state="s1"/> + <when output="ì" state="s2"/> + <when output="î" state="s3"/> + <when output="ï" state="s4"/> + </action> + <action id="16"> + <when output="n" state="none"/> + <when output="ñ" state="s5"/> + </action> + <action id="17"> + <when output="o" state="none"/> + <when output="ó" state="s1"/> + <when output="ò" state="s2"/> + <when output="ô" state="s3"/> + <when output="ö" state="s4"/> + <when output="õ" state="s5"/> + </action> + <action id="18"> + <when output="u" state="none"/> + <when output="ú" state="s1"/> + <when output="ù" state="s2"/> + <when output="û" state="s3"/> + <when output="ü" state="s4"/> + </action> + <action id="19"> + <when output="y" state="none"/> + <when output="ÿ" state="s4"/> + </action> + <action id="2"> + <when next="s3" state="none"/> + </action> + <action id="3"> + <when next="s4" state="none"/> + </action> + <action id="4"> + <when next="s5" state="none"/> + </action> + <action id="5"> + <when output=" " state="none"/> + <when output="´" state="s1"/> + <when output="`" state="s2"/> + <when output="ˆ" state="s3"/> + <when output="¨" state="s4"/> + <when output="˜" state="s5"/> + </action> + <action id="6"> + <when output="A" state="none"/> + <when output="Á" state="s1"/> + <when output="À" state="s2"/> + <when output="Â" state="s3"/> + <when output="Ä" state="s4"/> + <when output="Ã" state="s5"/> + </action> + <action id="7"> + <when output="E" state="none"/> + <when output="É" state="s1"/> + <when output="È" state="s2"/> + <when output="Ê" state="s3"/> + <when output="Ë" state="s4"/> + </action> + <action id="8"> + <when output="I" state="none"/> + <when output="Í" state="s1"/> + <when output="Ì" state="s2"/> + <when output="Î" state="s3"/> + <when output="Ï" state="s4"/> + </action> + <action id="9"> + <when output="N" state="none"/> + <when output="Ñ" state="s5"/> + </action> + </actions> + <terminators> + <when output="´" state="s1"/> + <when output="`" state="s2"/> + <when output="ˆ" state="s3"/> + <when output="¨" state="s4"/> + <when output="˜" state="s5"/> + </terminators> +</keyboard> diff --git a/etc/Dark.terminal b/etc/Dark.terminal new file mode 100644 index 00000000..75184f05 --- /dev/null +++ b/etc/Dark.terminal @@ -0,0 +1,1652 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>ANSIBlackColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECswLjA4NjQxNDgyMTQ1IDAuMDgyNjczMjQ0MTggMC4wNjE3 + NjQxODgxMSAxTxApMC4wNjg1NjU0ODc4NiAwLjA2NjU1MDExMzI2IDAuMDUzMTg5MzQ0 + NwAQAYACgAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50 + clJHQiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAA + AAAAAPbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAU + YmtwdAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1u + ZAAAAlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAA + A/gAAAAUbWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwA + AAgMYlRSQwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1Q + YWNrYXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAA + AAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAA + AAAAAAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABja + WFlaIAAAAAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMu + Y2gAAAAAAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2 + LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVD + IDYxOTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlv + biBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25k + aXRpb24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3 + AAAAAAATpP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf + 521lYXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1 + cnYAAAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4A + YwBoAG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDg + AOUA6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwB + gwGLAZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJU + Al0CZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oD + ZgNyA34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSo + BLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicG + NwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4 + CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsK + EQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxc + DHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4P + CQ8lD0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHo + EgcSJhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIV + NBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihiv + GNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHsc + oxzMHPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDE + IPAhHCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTgl + aCWXJccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1 + KmgqmyrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ov + kS/HL/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUT + NU01hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87 + LTtrO6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFq + QaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVI + S0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9J + T5NP3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW + 91dEV5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69 + Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhn + PWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/R + cCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5 + KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKS + gvSDV4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOM + yo0xjZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cK + l3WX4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobai + JqKWowajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1E + rbiuLa6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5 + SrnCuju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVL + xcjGRsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7S + P9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p + 36/gNuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77Ibt + Ee2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn + +3f8B/yY/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9y + U3BhY2WiIyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEA + GgAkACkAMgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANUBAQEDAQUBBwEOARMBGQEb + AR0BHw1rDXANew2EDZENlA2hDaoNrw23AAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAA + AAAADbo= + </data> + <key>ANSIBlueColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjIzNzg3NzEzMDUgMC4zODQ0ODYzMTc2IDAuNDAxODE3 + NTYwMiAxTxAnMC4xODUxNzgwMTE3IDAuMzEyNjgzODIwNyAwLjMyNzM1MzE0OTcAEAGA + AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig + WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2 + 1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA + AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU + AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA + FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU + UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy + ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA + EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA + AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA + AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA + AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg + RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2 + Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g + SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u + IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA + E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz + AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA + AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt + AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA + 8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS + AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC + cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+ + A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE + 0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ + BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I + MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 + ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M + pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B + D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS + RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4 + FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ + IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1 + HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh + SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH + JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq + zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+ + MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1 + wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq + O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C + MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX + SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q + J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS + V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff + s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp + aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw + 4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn + ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD + uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y + jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY + TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG + o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u + oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7 + urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG + w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE + 08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg + veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o + 7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8 + mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl + oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp + ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN + Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21 + </data> + <key>ANSIBrightBlackColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjI5ODc1MjYwNTkgMC4yNzU0NDMxMzY3IDAuMjA4Mjg5 + OTIxMyAxTxAnMC4yMzI1NTg4MTY3IDAuMjEzOTgzMDU4OSAwLjE1NzM3MjYyMzcAEAGA + AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig + WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2 + 1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA + AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU + AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA + FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU + UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy + ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA + EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA + AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA + AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA + AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg + RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2 + Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g + SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u + IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA + E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz + AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA + AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt + AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA + 8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS + AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC + cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+ + A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE + 0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ + BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I + MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 + ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M + pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B + D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS + RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4 + FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ + IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1 + HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh + SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH + JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq + zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+ + MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1 + wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq + O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C + MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX + SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q + J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS + V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff + s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp + aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw + 4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn + ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD + uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y + jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY + TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG + o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u + oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7 + urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG + w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE + 08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg + veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o + 7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8 + mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl + oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp + ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN + Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21 + </data> + <key>ANSIBrightBlueColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjI5OTI5MDg5NTUgMC40ODI3MTYzODE1IDAuNDk2MTAy + ODA5OSAxTxAnMC4yMzg5NDMwNzAyIDAuNDA5NTA4Mzc3MyAwLjQyMDQxODk3NzcAEAGA + AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig + WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2 + 1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA + AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU + AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA + FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU + UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy + ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA + EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA + AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA + AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA + AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg + RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2 + Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g + SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u + IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA + E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz + AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA + AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt + AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA + 8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS + AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC + cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+ + A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE + 0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ + BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I + MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 + ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M + pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B + D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS + RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4 + FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ + IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1 + HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh + SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH + JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq + zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+ + MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1 + wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq + O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C + MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX + SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q + J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS + V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff + s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp + aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw + 4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn + ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD + uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y + jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY + TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG + o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u + oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7 + urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG + w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE + 08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg + veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o + 7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8 + mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl + oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp + ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN + Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21 + </data> + <key>ANSIBrightCyanColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjQxODI5MzY1NDkgMC41OTkyNjI5NTI4IDAuNDIxMTYz + NDY5NiAxTxAnMC4zNDk1MDUyMTU5IDAuNTM3MzI0Nzg2MiAwLjM0NjU0MzYzOTkAEAGA + AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig + WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2 + 1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA + AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU + AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA + FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU + UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy + ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA + EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA + AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA + AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA + AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg + RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2 + Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g + SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u + IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA + E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz + AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA + AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt + AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA + 8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS + AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC + cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+ + A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE + 0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ + BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I + MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 + ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M + pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B + D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS + RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4 + FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ + IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1 + HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh + SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH + JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq + zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+ + MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1 + wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq + O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C + MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX + SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q + J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS + V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff + s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp + aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw + 4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn + ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD + uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y + jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY + TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG + o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u + oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7 + urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG + w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE + 08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg + veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o + 7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8 + mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl + oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp + ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN + Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21 + </data> + <key>ANSIBrightGreenColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjU1NjU1MTQ1NjUgMC42MDA4OTg1NjM5IDAuMTE2MjQx + OTY5MiAxTxAoMC40ODUyMTc3NTAxIDAuNTQxMTE2ODkzMyAwLjA5MjAwNzkyMDE1ABAB + gAKABdMYGREaGxxUTlNJRFVOU0lDQxAHgAOABE8RDEgAAAxITGlubwIQAABtbnRyUkdC + IFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA + 9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0 + AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAAC + VAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAA + ABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxi + VFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2th + cmQgQ29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAA + ABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAA + AAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVog + AAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAA + AAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4x + IERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5 + NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGlu + IElFQzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlv + biBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAA + ABOk/gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVh + cwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAA + AAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgA + bQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDr + APAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsB + kgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn + AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3ID + fgOKA5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTE + BNME4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgG + WQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgf + CDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicK + PQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyO + DKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUP + QQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxIm + EkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYV + eBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6 + GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc + 9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEc + IUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZcl + xyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqb + Ks8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv + /jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWH + NcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7 + qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHu + QjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI + 10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d + UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RX + klfgWC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19h + X7NgBWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn + 6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CG + cOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl5 + 53pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INX + g7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGN + mI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfg + mEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopaj + BqN2o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4t + rqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6 + O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZG + xsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHT + RNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A2 + 4L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7Zzu + KO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH + /Jj9Kf26/kv+3P9t///SHyAhIlokY2xhc3NuYW1lWCRjbGFzc2VzXE5TQ29sb3JTcGFj + ZaIjJFxOU0NvbG9yU3BhY2VYTlNPYmplY3TSHyAmJ1dOU0NvbG9yoiYkAAgAEQAaACQA + KQAyADcASQBMAFEAUwBaAGAAawB4AH4AiwCgAKcA0gD9AP8BAQEDAQoBDwEVARcBGQEb + DWcNbA13DYANjQ2QDZ0Npg2rDbMAAAAAAAACAQAAAAAAAAAoAAAAAAAAAAAAAAAAAAAN + tg== + </data> + <key>ANSIBrightMagentaColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjYwMDY0NDIzMDggMC4zNTgyNDcxNjA5IDAuNDE4NDg3 + MzEwNCAxTxAnMC41MjUyNzc3OTM0IDAuMjc3OTIxMzc4NiAwLjM0MzkxMTY3NzYAEAGA + AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig + WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2 + 1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA + AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU + AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA + FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU + UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy + ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA + EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA + AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA + AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA + AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg + RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2 + Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g + SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u + IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA + E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz + AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA + AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt + AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA + 8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS + AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC + cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+ + A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE + 0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ + BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I + MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 + ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M + pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B + D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS + RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4 + FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ + IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1 + HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh + SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH + JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq + zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+ + MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1 + wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq + O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C + MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX + SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q + J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS + V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff + s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp + aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw + 4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn + ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD + uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y + jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY + TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG + o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u + oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7 + urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG + w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE + 08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg + veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o + 7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8 + mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl + oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp + ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN + Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21 + </data> + <key>ANSIBrightRedColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECkwLjgwMDg5NjQ2NTggMC4xOTc5Nzc1NDI5IDAuMDc4OTg3 + MTU4ODMgMU8QKDAuNzQ2NjI0MTEyMSAwLjExOTYyMTgwNTggMC4wNjg5MzI5ODc3NQAQ + AYACgAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50clJH + QiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAA + APbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtw + dAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAA + AlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gA + AAAUbWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgM + YlRSQwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNr + YXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAA + AAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAA + AAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFla + IAAAAAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gA + AAAAAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIu + MSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVDIDYx + OTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBp + biBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRp + b24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAA + AAATpP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521l + YXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYA + AAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBo + AG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA + 6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGL + AZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0C + ZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNy + A34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYE + xATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZI + BlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsI + HwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQon + Cj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUM + jgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8l + D0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcS + JhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVW + FXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY + +hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzM + HPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAh + HCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWX + Jccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1Kmgq + myrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/H + L/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01 + hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtr + O6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB + 7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iR + SNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP + 3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dE + V5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9f + YV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeT + Z+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtw + hnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJ + eed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSD + V4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0x + jZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX + 4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKW + owajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1Erbiu + La6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnC + uju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjG + RsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB + 00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/g + NuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c + 7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8 + B/yY/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3Bh + Y2WiIyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEAGgAk + ACkAMgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANMA/gEAAQIBBAELARABFgEYARoB + HA1oDW0NeA2BDY4NkQ2eDacNrA20AAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAA + Dbc= + </data> + <key>ANSIBrightWhiteColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjgwMTk4NTU2MTggMC43MzU3NzAwNDY3IDAuNTU1MDUz + NTMyMSAxTxAnMC43NTY0MTcxNTUzIDAuNjg1MjI5MjQxOCAwLjQ4MjY3MjM5MzMAEAGA + AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig + WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2 + 1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA + AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU + AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA + FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU + UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy + ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA + EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA + AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA + AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA + AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg + RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2 + Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g + SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u + IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA + E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz + AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA + AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt + AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA + 8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS + AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC + cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+ + A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE + 0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ + BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I + MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 + ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M + pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B + D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS + RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4 + FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ + IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1 + HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh + SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH + JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq + zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+ + MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1 + wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq + O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C + MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX + SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q + J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS + V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff + s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp + aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw + 4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn + ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD + uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y + jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY + TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG + o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u + oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7 + urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG + w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE + 08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg + veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o + 7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8 + mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl + oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp + ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN + Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21 + </data> + <key>ANSIBrightYellowColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjgwMTIwNzQyMzIgMC41ODM2NjYxNDU4IDAuMTU3Mjcw + OTk3OCAxTxAnMC43NTIwNzE5NzY3IDAuNTE1MzE4NzUxMyAwLjEyMjE5NTczNTYAEAGA + AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig + WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2 + 1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA + AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU + AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA + FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU + UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy + ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA + EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA + AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA + AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA + AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg + RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2 + Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g + SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u + IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA + E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz + AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA + AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt + AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA + 8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS + AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC + cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+ + A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE + 0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ + BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I + MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 + ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M + pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B + D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS + RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4 + FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ + IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1 + HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh + SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH + JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq + zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+ + MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1 + wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq + O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C + MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX + SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q + J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS + V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff + s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp + aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw + 4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn + ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD + uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y + jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY + TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG + o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u + oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7 + urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG + w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE + 08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg + veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o + 7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8 + mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl + oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp + ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN + Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21 + </data> + <key>ANSICyanColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECcwLjMzMzEyNjMwNjUgMC40NzcwOTI5ODEzIDAuMzMyMDk3 + ODg4IDFPECcwLjI2ODExMDA5NjUgMC40MDc4NTk0NDQ2IDAuMjYyOTM4MDIyNgAQAYAC + gAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50clJHQiBY + WVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAAAPbW + AAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtwdAAA + AgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAAAlQA + AABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gAAAAU + bWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgMYlRS + QwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNrYXJk + IENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAS + c1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAAAAAA + AAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAA + AAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAA + AAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIuMSBE + ZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVDIDYxOTY2 + LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJ + RUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24g + aW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAAAAAT + pP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521lYXMA + AAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYAAAAA + AAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBoAG0A + cgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA6wDw + APYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGLAZIB + mgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJx + AnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNyA34D + igOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYExATT + BOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZIBlkG + agZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsIHwgy + CEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQonCj0K + VApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUMjgyn + DMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8lD0EP + Xg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcSJhJF + EmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVWFXgV + mxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY+hkg + GUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzMHPUd + Hh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAhHCFI + IXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl + 9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1KmgqmyrP + KwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/HL/4w + NTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXC + Nf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtrO6o7 + 6DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB7kIw + QnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iRSNdJ + HUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP3VAn + UHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dEV5JX + 4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9fYV+z + YAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeTZ+lo + P2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtwhnDg + cTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJeed6 + RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6 + hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0xjZiN + /45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX4JhM + mLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKWowaj + dqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1ErbiuLa6h + rxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnCuju6 + tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjGRsbD + x0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TT + xtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/gNuC9 + 4UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c7iju + tO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8B/yY + /Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3BhY2Wi + IyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEAGgAkACkA + MgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANEA+wD9AP8BAQEIAQ0BEwEVARcBGQ1l + DWoNdQ1+DYsNjg2bDaQNqQ2xAAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAADbQ= + </data> + <key>ANSIGreenColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECkwLjQ0NjkyMDM5NDkgMC40NzgzODYyMjMzIDAuMDkzNTM1 + OTM3MzcgMU8QKDAuMzcyNzA2MDU1NiAwLjQxMDYwNDExOTMgMC4wNzU3MTQ2MzI4NwAQ + AYACgAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50clJH + QiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAA + APbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtw + dAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAA + AlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gA + AAAUbWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgM + YlRSQwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNr + YXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAA + AAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAA + AAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFla + IAAAAAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gA + AAAAAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIu + MSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVDIDYx + OTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBp + biBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRp + b24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAA + AAATpP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521l + YXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYA + AAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBo + AG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA + 6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGL + AZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0C + ZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNy + A34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYE + xATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZI + BlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsI + HwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQon + Cj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUM + jgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8l + D0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcS + JhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVW + FXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY + +hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzM + HPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAh + HCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWX + Jccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1Kmgq + myrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/H + L/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01 + hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtr + O6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB + 7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iR + SNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP + 3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dE + V5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9f + YV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeT + Z+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtw + hnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJ + eed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSD + V4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0x + jZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX + 4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKW + owajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1Erbiu + La6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnC + uju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjG + RsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB + 00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/g + NuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c + 7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8 + B/yY/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3Bh + Y2WiIyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEAGgAk + ACkAMgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANMA/gEAAQIBBAELARABFgEYARoB + HA1oDW0NeA2BDY4NkQ2eDacNrA20AAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAA + Dbc= + </data> + <key>ANSIMagentaColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjQ3ODEyOTQ0NjUgMC4yODgyOTk1NjA1IDAuMzMyMzUx + ODMzNiAxTxAnMC4zOTg3NjkxNDAyIDAuMjE3NjQ0NzUxMSAwLjI2MzEzMjE4NDcAEAGA + AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig + WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2 + 1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA + AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU + AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA + FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU + UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy + ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA + EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA + AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA + AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA + AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg + RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2 + Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g + SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u + IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA + E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz + AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA + AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt + AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA + 8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS + AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC + cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+ + A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE + 0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ + BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I + MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 + ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M + pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B + D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS + RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4 + FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ + IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1 + HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh + SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH + JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq + zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+ + MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1 + wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq + O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C + MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX + SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q + J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS + V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff + s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp + aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw + 4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn + ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD + uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y + jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY + TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG + o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u + oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7 + urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG + w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE + 08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg + veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o + 7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8 + mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl + oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp + ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN + Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21 + </data> + <key>ANSIRedColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECkwLjYzODQyMzk3OTMgMC4xNTYyMTk5NTkzIDAuMDYzNTUz + ODE3NTcgMU8QKTAuNTYzOTM3NzgzMiAwLjA5NTk2MjQ3OTcxIDAuMDU3NzQ0NjQ0NTgA + EAGAAoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJS + R0IgWFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAA + AAD21gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJr + cHQAAAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQA + AAJUAAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4 + AAAAFG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAI + DGJUUkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFj + a2FyZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAA + AAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAA + AAAAAAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZ + WiAAAAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNo + AAAAAAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0y + LjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2 + MTk2Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAA + AAAAAAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24g + aW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0 + aW9uIGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAA + AAAAE6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dt + ZWFzAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2 + AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMA + aABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl + AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMB + iwGSAZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJd + AmcCcQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YD + cgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2 + BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcG + SAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgL + CB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEK + Jwo9ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1 + DI4MpwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkP + JQ9BD14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIH + EiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQV + VhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjV + GPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMc + zBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDw + IRwhSCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgl + lyXHJfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpo + KpsqzysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Ev + xy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVN + NYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07 + azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs + Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtI + kUjXSR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+T + T91QJ1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdX + RFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8P + X2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1n + k2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XAr + cIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5 + iXnnekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0 + g1eDuoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqN + MY2Yjf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1 + l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiai + lqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24 + ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5 + wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XI + xkbGw8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/S + wdNE08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v + 4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHt + nO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3 + /Af8mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNw + YWNloiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoA + JAApADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDTAP8BAQEDAQUBDAERARcBGQEb + AR0NaQ1uDXkNgg2PDZINnw2oDa0NtQAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAA + AA24 + </data> + <key>ANSIWhiteColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjYwMTQyMTM1NjIgMC41NTQ3NzkxNzE5IDAuNDIwMzc2 + ODM3MyAxTxAmMC41MzAyMTU0NDIyIDAuNDg0NTIxOTI1NCAwLjM0NjA2NTQwMgAQAYAC + gAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50clJHQiBY + WVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAAAPbW + AAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtwdAAA + AgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAAAlQA + AABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gAAAAU + bWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgMYlRS + QwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNrYXJk + IENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAS + c1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAAAAAA + AAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAA + AAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAA + AAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIuMSBE + ZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVDIDYxOTY2 + LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJ + RUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24g + aW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAAAAAT + pP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521lYXMA + AAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYAAAAA + AAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBoAG0A + cgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA6wDw + APYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGLAZIB + mgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJx + AnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNyA34D + igOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYExATT + BOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZIBlkG + agZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsIHwgy + CEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQonCj0K + VApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUMjgyn + DMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8lD0EP + Xg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcSJhJF + EmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVWFXgV + mxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY+hkg + GUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzMHPUd + Hh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAhHCFI + IXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl + 9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1KmgqmyrP + KwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/HL/4w + NTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXC + Nf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtrO6o7 + 6DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB7kIw + QnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iRSNdJ + HUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP3VAn + UHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dEV5JX + 4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9fYV+z + YAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeTZ+lo + P2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtwhnDg + cTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJeed6 + RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6 + hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0xjZiN + /45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX4JhM + mLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKWowaj + dqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1ErbiuLa6h + rxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnCuju6 + tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjGRsbD + x0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TT + xtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/gNuC9 + 4UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c7iju + tO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8B/yY + /Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3BhY2Wi + IyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEAGgAkACkA + MgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANIA+wD9AP8BAQEIAQ0BEwEVARcBGQ1l + DWoNdQ1+DYsNjg2bDaQNqQ2xAAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAADbQ= + </data> + <key>ANSIYellowColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjYzOTQxMzA1ODggMC40NjU0MDE4MjgzIDAuMTI1ODc5 + NzM0OCAxTxAoMC41Njg4Njk1OTA4IDAuMzkyMjI2OTY0MiAwLjA5ODMyNTU2NTQ2ABAB + gAKABdMYGREaGxxUTlNJRFVOU0lDQxAHgAOABE8RDEgAAAxITGlubwIQAABtbnRyUkdC + IFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA + 9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0 + AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAAC + VAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAA + ABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxi + VFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2th + cmQgQ29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAA + ABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAA + AAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVog + AAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAA + AAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4x + IERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5 + NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGlu + IElFQzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlv + biBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAA + ABOk/gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVh + cwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAA + AAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgA + bQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDr + APAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsB + kgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn + AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3ID + fgOKA5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTE + BNME4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgG + WQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgf + CDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicK + PQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyO + DKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUP + QQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxIm + EkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYV + eBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6 + GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc + 9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEc + IUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZcl + xyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqb + Ks8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv + /jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWH + NcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7 + qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHu + QjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI + 10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d + UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RX + klfgWC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19h + X7NgBWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn + 6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CG + cOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl5 + 53pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INX + g7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGN + mI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfg + mEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopaj + BqN2o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4t + rqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6 + O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZG + xsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHT + RNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A2 + 4L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7Zzu + KO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH + /Jj9Kf26/kv+3P9t///SHyAhIlokY2xhc3NuYW1lWCRjbGFzc2VzXE5TQ29sb3JTcGFj + ZaIjJFxOU0NvbG9yU3BhY2VYTlNPYmplY3TSHyAmJ1dOU0NvbG9yoiYkAAgAEQAaACQA + KQAyADcASQBMAFEAUwBaAGAAawB4AH4AiwCgAKcA0gD9AP8BAQEDAQoBDwEVARcBGQEb + DWcNbA13DYANjQ2QDZ0Npg2rDbMAAAAAAAACAQAAAAAAAAAoAAAAAAAAAAAAAAAAAAAN + tg== + </data> + <key>BackgroundColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECswLjA3ODU3MTg5MzI3IDAuMDc0ODI3NTQ0MzkgMC4wNTM5 + MTM1MTEzNCAxTxApMC4wNjM1NDA2MzAwNCAwLjA2MTU1OTQwODkgMC4wNDg0NzkxODQ1 + MQAQAYACgAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50 + clJHQiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAA + AAAAAPbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAU + YmtwdAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1u + ZAAAAlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAA + A/gAAAAUbWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwA + AAgMYlRSQwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1Q + YWNrYXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAA + AAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAA + AAAAAAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABja + WFlaIAAAAAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMu + Y2gAAAAAAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2 + LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVD + IDYxOTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlv + biBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25k + aXRpb24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3 + AAAAAAATpP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf + 521lYXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1 + cnYAAAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4A + YwBoAG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDg + AOUA6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwB + gwGLAZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJU + Al0CZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oD + ZgNyA34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSo + BLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicG + NwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4 + CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsK + EQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxc + DHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4P + CQ8lD0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHo + EgcSJhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIV + NBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihiv + GNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHsc + oxzMHPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDE + IPAhHCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTgl + aCWXJccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1 + KmgqmyrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ov + kS/HL/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUT + NU01hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87 + LTtrO6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFq + QaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVI + S0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9J + T5NP3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW + 91dEV5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69 + Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhn + PWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/R + cCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5 + KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKS + gvSDV4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOM + yo0xjZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cK + l3WX4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobai + JqKWowajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1E + rbiuLa6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5 + SrnCuju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVL + xcjGRsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7S + P9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p + 36/gNuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77Ibt + Ee2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn + +3f8B/yY/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9y + U3BhY2WiIyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEA + GgAkACkAMgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANUBAQEDAQUBBwEOARMBGQEb + AR0BHw1rDXANew2EDZENlA2hDaoNrw23AAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAA + AAAADbo= + </data> + <key>Bell</key> + <false/> + <key>BellBounceCritical</key> + <false/> + <key>CursorColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjQ3OTUyNTg2NDEgMC40NDQ2OTg0ODI4IDAuMzMxMzI2 + MjQ2MyAxTxAnMC40MDMyNjEwNjU1IDAuMzcxNzA4MDk1MSAwLjI2MjQ2MDc2ODIAEAGA + AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig + WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2 + 1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA + AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU + AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA + FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU + UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy + ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA + EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA + AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA + AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA + AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg + RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2 + Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g + SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u + IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA + E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz + AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA + AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt + AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA + 8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS + AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC + cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+ + A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE + 0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ + BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I + MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 + ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M + pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B + D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS + RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4 + FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ + IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1 + HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh + SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH + JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq + zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+ + MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1 + wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq + O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C + MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX + SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q + J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS + V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff + s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp + aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw + 4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn + ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD + uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y + jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY + TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG + o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u + oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7 + urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG + w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE + 08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg + veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o + 7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8 + mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl + oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp + ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN + Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21 + </data> + <key>DisableANSIColor</key> + <false/> + <key>Font</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGkCwwVFlUkbnVsbNQNDg8QERIT + FFZOU1NpemVYTlNmRmxhZ3NWTlNOYW1lViRjbGFzcyNAJgAAAAAAABAQgAKAA1ZHb01v + bm/SFxgZGlokY2xhc3NuYW1lWCRjbGFzc2VzVk5TRm9udKIZG1hOU09iamVjdAgRGiQp + MjdJTFFTWF5nbnd+hY6QkpSboKu0u74AAAAAAAABAQAAAAAAAAAcAAAAAAAAAAAAAAAA + AAAAxw== + </data> + <key>FontAntialias</key> + <true/> + <key>FontHeightSpacing</key> + <integer>1</integer> + <key>FontWidthSpacing</key> + <integer>1</integer> + <key>ProfileCurrentVersion</key> + <real>2.0699999999999998</real> + <key>SelectionColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECkwLjYzNzg1MDc2MTQgMC4yNTM4NjgzNzEyIDAuMDYwOTk5 + MzExNTEgMU8QKDAuNTY0MDcwNzAxNiAwLjE4NDY3ODM2MDggMC4wNTY1MzkxODUzNQAQ + AYACgAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50clJH + QiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAA + APbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtw + dAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAA + AlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gA + AAAUbWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgM + YlRSQwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNr + YXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAA + AAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAA + AAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFla + IAAAAAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gA + AAAAAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIu + MSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVDIDYx + OTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBp + biBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRp + b24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAA + AAATpP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521l + YXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYA + AAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBo + AG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA + 6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGL + AZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0C + ZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNy + A34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYE + xATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZI + BlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsI + HwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQon + Cj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUM + jgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8l + D0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcS + JhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVW + FXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY + +hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzM + HPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAh + HCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWX + Jccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1Kmgq + myrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/H + L/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01 + hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtr + O6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB + 7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iR + SNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP + 3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dE + V5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9f + YV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeT + Z+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtw + hnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJ + eed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSD + V4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0x + jZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX + 4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKW + owajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1Erbiu + La6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnC + uju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjG + RsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB + 00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/g + NuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c + 7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8 + B/yY/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3Bh + Y2WiIyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEAGgAk + ACkAMgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANMA/gEAAQIBBAELARABFgEYARoB + HA1oDW0NeA2BDY4NkQ2eDacNrA20AAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAA + Dbc= + </data> + <key>ShowActiveProcessInTabTitle</key> + <false/> + <key>ShowActiveProcessInTitle</key> + <false/> + <key>ShowActivityIndicatorInTab</key> + <false/> + <key>ShowCommandKeyInTitle</key> + <false/> + <key>ShowComponentsWhenTabHasCustomTitle</key> + <true/> + <key>ShowDimensionsInTitle</key> + <false/> + <key>ShowRepresentedURLInTabTitle</key> + <false/> + <key>ShowRepresentedURLInTitle</key> + <false/> + <key>ShowRepresentedURLPathInTitle</key> + <false/> + <key>ShowShellCommandInTitle</key> + <false/> + <key>ShowTTYNameInTitle</key> + <false/> + <key>ShowWindowSettingsNameInTitle</key> + <false/> + <key>TerminalType</key> + <string>xterm</string> + <key>TextBoldColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECgwLjgwMTk4NTU2MTggMC43MzU3NzAwNDY3IDAuNTU1MDUz + NTMyMSAxTxAnMC43NTY0MTcxNTUzIDAuNjg1MjI5MjQxOCAwLjQ4MjY3MjM5MzMAEAGA + AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig + WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2 + 1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA + AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU + AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA + FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU + UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy + ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA + EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA + AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA + AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA + AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg + RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2 + Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g + SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u + IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA + E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz + AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA + AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt + AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA + 8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS + AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC + cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+ + A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE + 0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ + BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I + MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9 + ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M + pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B + D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS + RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4 + FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ + IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1 + HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh + SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH + JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq + zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+ + MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1 + wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq + O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C + MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX + SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q + J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS + V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff + s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp + aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw + 4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn + ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD + uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y + jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY + TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG + o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u + oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7 + urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG + w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE + 08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg + veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o + 7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8 + mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl + oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp + ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN + Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21 + </data> + <key>TextColor</key> + <data> + YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS + AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR + EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s + b3JTcGFjZVYkY2xhc3NPECYwLjcxOTQwMjEzNDQgMC42NjA5ODgxNTIgMC41MDEzMjYy + NjMgMU8QJzAuNjYwODU1MjkzMyAwLjYwMDE1MjY3MTMgMC40MjY4MTE1NzU5ABABgAKA + BdMYGREaGxxUTlNJRFVOU0lDQxAHgAOABE8RDEgAAAxITGlubwIQAABtbnRyUkdCIFhZ + WiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA9tYA + AQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0AAAC + BAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAACVAAA + AHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAAABRt + ZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxiVFJD + AAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQg + Q29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAABJz + UkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAA + AAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAA + AAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAA + AAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERl + ZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5NjYt + Mi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElF + QzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBp + biBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAAABOk + /gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAA + AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAA + AAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQBy + AHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDrAPAA + 9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsBkgGa + AaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJnAnEC + egKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOK + A5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME + 4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZq + BnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDII + RghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpU + CmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcM + wAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e + D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUS + ZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWb + Fb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZ + RRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0e + HUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUgh + dSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3 + JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8r + Ais2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1 + MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1 + /TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvo + PCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHuQjBC + ckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI10kd + SWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQ + cVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfg + WC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7Ng + BWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/ + aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBx + OnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl553pG + eqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INXg7qE + HYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/ + jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyY + uJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2 + o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGv + Fq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1 + uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPH + Qce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG + 1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3h + ROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO60 + 70DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9 + Kf26/kv+3P9t///SHyAhIlokY2xhc3NuYW1lWCRjbGFzc2VzXE5TQ29sb3JTcGFjZaIj + JFxOU0NvbG9yU3BhY2VYTlNPYmplY3TSHyAmJ1dOU0NvbG9yoiYkAAgAEQAaACQAKQAy + ADcASQBMAFEAUwBaAGAAawB4AH4AiwCgAKcA0AD6APwA/gEAAQcBDAESARQBFgEYDWQN + aQ10DX0Nig2NDZoNow2oDbAAAAAAAAACAQAAAAAAAAAoAAAAAAAAAAAAAAAAAAANsw== + </data> + <key>UseBoldFonts</key> + <false/> + <key>UseBrightBold</key> + <true/> + <key>VisualBell</key> + <false/> + <key>VisualBellOnlyWhenMuted</key> + <false/> + <key>WindowTitle</key> + <string>Terminal</string> + <key>name</key> + <string>Dark</string> + <key>noWarnProcesses</key> + <array> + <dict> + <key>ProcessName</key> + <string>screen</string> + </dict> + <dict> + <key>ProcessName</key> + <string>tmux</string> + </dict> + <dict> + <key>ProcessName</key> + <string>atch</string> + </dict> + </array> + <key>rowCount</key> + <integer>25</integer> + <key>shellExitAction</key> + <integer>1</integer> + <key>type</key> + <string>Window Settings</string> + <key>useOptionAsMetaKey</key> + <false/> +</dict> +</plist> diff --git a/etc/Go-Mono-Bold-Italic.ttf b/etc/Go-Mono-Bold-Italic.ttf new file mode 100644 index 00000000..c138a9e1 --- /dev/null +++ b/etc/Go-Mono-Bold-Italic.ttf Binary files differdiff --git a/etc/Go-Mono-Bold.ttf b/etc/Go-Mono-Bold.ttf new file mode 100644 index 00000000..551da07f --- /dev/null +++ b/etc/Go-Mono-Bold.ttf Binary files differdiff --git a/etc/Go-Mono-Italic.ttf b/etc/Go-Mono-Italic.ttf new file mode 100644 index 00000000..22d4390e --- /dev/null +++ b/etc/Go-Mono-Italic.ttf Binary files differdiff --git a/etc/Go-Mono.ttf b/etc/Go-Mono.ttf new file mode 100644 index 00000000..71e30123 --- /dev/null +++ b/etc/Go-Mono.ttf Binary files differdiff --git a/etc/README.Go-Mono b/etc/README.Go-Mono new file mode 100644 index 00000000..7043c362 --- /dev/null +++ b/etc/README.Go-Mono @@ -0,0 +1,36 @@ +These fonts were created by the Bigelow & Holmes foundry specifically for the +Go project. See https://blog.golang.org/go-fonts for details. + +They are licensed under the same open source license as the rest of the Go +project's software: + +Copyright (c) 2016 Bigelow & Holmes Inc.. All rights reserved. + +Distribution of this font is governed by the following license. If you do not +agree to this license, including the disclaimer, do not distribute or modify +this font. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Google Inc. nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/etc/code.map b/etc/code.map new file mode 100644 index 00000000..a3749b8d --- /dev/null +++ b/etc/code.map @@ -0,0 +1,20 @@ +include "/usr/share/kbd/keymaps/i386/qwerty/us.map.gz" + +keycode 2 = exclam one +keycode 3 = at two +keycode 4 = numbersign three +keycode 5 = dollar four +keycode 6 = percent five +keycode 7 = asciicircum six +keycode 8 = ampersand seven +keycode 9 = asterisk eight +keycode 10 = parenleft nine +keycode 11 = parenright zero +keycode 12 = underscore minus +keycode 26 = braceleft bracketleft +keycode 27 = braceright bracketright +keycode 43 = bar backslash +keycode 58 = Escape + +keycode 100 = Compose +keycode 125 = Escape diff --git a/etc/psf/.gitignore b/etc/psf/.gitignore new file mode 100644 index 00000000..446e6b46 --- /dev/null +++ b/etc/psf/.gitignore @@ -0,0 +1,2 @@ +*.png +*.psfu diff --git a/etc/psf/Makefile b/etc/psf/Makefile new file mode 100644 index 00000000..e92178cd --- /dev/null +++ b/etc/psf/Makefile @@ -0,0 +1,24 @@ +TABLE = default.u + +FONTS += sans6x8.psfu +FONTS += sans6x10.psfu +FONTS += sans6x12.psfu + +PNGS = $(FONTS:psfu=png) + +all: $(FONTS) + +png: $(PNGS) + +$(FONTS): $(TABLE) + +.SUFFIXES: .psf .psfu .png + +.psf.psfu: + psfaddtable $< $(TABLE) $@ + +.psf.png: + psf2png $< > $@ + +clean: + rm -f $(FONTS) $(PNGS) diff --git a/etc/psf/default.u b/etc/psf/default.u new file mode 100644 index 00000000..790ad92b --- /dev/null +++ b/etc/psf/default.u @@ -0,0 +1,259 @@ +# +# Character table extracted from font default8x16.psfu +# +0x000 U+2008 +0x001 U+263a +0x002 U+263b +0x003 U+2665 +0x004 U+2666 +0x005 U+2663 +0x006 U+2660 +0x007 U+2022 +0x008 U+25d8 +0x009 U+25cb +0x00a U+25d9 +0x00b U+2642 +0x00c U+2640 +0x00d U+266a +0x00e U+266b U+266c +0x00f U+263c +0x010 U+25b6 U+25ba +0x011 U+25c0 U+25c4 +0x012 U+2195 +0x013 U+203c +0x014 U+00b6 +0x015 U+00a7 +0x016 U+25ac +0x017 U+21a8 +0x018 U+2191 +0x019 U+2193 +0x01a U+2192 +0x01b U+2190 +0x01c U+221f U+2319 +0x01d U+2194 +0x01e U+25b2 +0x01f U+25bc +0x020 U+0020 U+00a0 U+2000 U+2001 U+2002 U+2003 U+2004 U+2005 U+2006 U+2007 U+2008 U+2009 U+200a U+202f +0x021 U+0021 +0x022 U+0022 +0x023 U+0023 +0x024 U+0024 +0x025 U+0025 +0x026 U+0026 +0x027 U+0027 +0x028 U+0028 +0x029 U+0029 +0x02a U+002a +0x02b U+002b +0x02c U+002c +0x02d U+002d +0x02e U+002e +0x02f U+002f +0x030 U+0030 +0x031 U+0031 +0x032 U+0032 +0x033 U+0033 +0x034 U+0034 +0x035 U+0035 +0x036 U+0036 +0x037 U+0037 +0x038 U+0038 +0x039 U+0039 +0x03a U+003a +0x03b U+003b +0x03c U+003c +0x03d U+003d +0x03e U+003e +0x03f U+003f +0x040 U+0040 +0x041 U+0041 +0x042 U+0042 +0x043 U+0043 +0x044 U+0044 +0x045 U+0045 +0x046 U+0046 +0x047 U+0047 +0x048 U+0048 +0x049 U+0049 +0x04a U+004a +0x04b U+004b +0x04c U+004c +0x04d U+004d +0x04e U+004e +0x04f U+004f +0x050 U+0050 +0x051 U+0051 +0x052 U+0052 +0x053 U+0053 +0x054 U+0054 +0x055 U+0055 +0x056 U+0056 +0x057 U+0057 +0x058 U+0058 +0x059 U+0059 +0x05a U+005a +0x05b U+005b +0x05c U+005c +0x05d U+005d +0x05e U+005e +0x05f U+005f +0x060 U+0060 +0x061 U+0061 +0x062 U+0062 +0x063 U+0063 +0x064 U+0064 +0x065 U+0065 +0x066 U+0066 +0x067 U+0067 +0x068 U+0068 +0x069 U+0069 +0x06a U+006a +0x06b U+006b +0x06c U+006c +0x06d U+006d +0x06e U+006e +0x06f U+006f +0x070 U+0070 +0x071 U+0071 +0x072 U+0072 +0x073 U+0073 +0x074 U+0074 +0x075 U+0075 +0x076 U+0076 +0x077 U+0077 +0x078 U+0078 +0x079 U+0079 +0x07a U+007a +0x07b U+007b +0x07c U+007c +0x07d U+007d +0x07e U+007e +0x07f U+2302 +0x080 U+00c7 +0x081 U+00fc +0x082 U+00e9 +0x083 U+00e2 +0x084 U+00e4 +0x085 U+00e0 +0x086 U+00e5 +0x087 U+00e7 +0x088 U+00ea +0x089 U+00eb +0x08a U+00e8 +0x08b U+00ef +0x08c U+00ee +0x08d U+00ec +0x08e U+00c4 +0x08f U+00c5 U+212b +0x090 U+00c9 +0x091 U+00e6 +0x092 U+00c6 +0x093 U+00f4 +0x094 U+00f6 +0x095 U+00f2 +0x096 U+00fb +0x097 U+00f9 +0x098 U+00ff +0x099 U+00d6 +0x09a U+00dc +0x09b U+00a2 +0x09c U+00a3 +0x09d U+00a5 +0x09e U+20a7 +0x09f U+0192 +0x0a0 U+00e1 +0x0a1 U+00ed +0x0a2 U+00f3 +0x0a3 U+00fa +0x0a4 U+00f1 +0x0a5 U+00d1 +0x0a6 U+00aa +0x0a7 U+00ba +0x0a8 U+00bf +0x0a9 U+2310 +0x0aa U+00ac +0x0ab U+00bd +0x0ac U+00bc +0x0ad U+00a1 +0x0ae U+00ab +0x0af U+00bb +0x0b0 U+2591 +0x0b1 U+2592 +0x0b2 U+2593 +0x0b3 U+2502 +0x0b4 U+2524 +0x0b5 U+2561 +0x0b6 U+2562 +0x0b7 U+2556 +0x0b8 U+2555 +0x0b9 U+2563 +0x0ba U+2551 +0x0bb U+2557 +0x0bc U+255d +0x0bd U+255c +0x0be U+255b +0x0bf U+2510 +0x0c0 U+2514 +0x0c1 U+2534 +0x0c2 U+252c +0x0c3 U+251c +0x0c4 U+2500 +0x0c5 U+253c +0x0c6 U+255e +0x0c7 U+255f +0x0c8 U+255a +0x0c9 U+2554 +0x0ca U+2569 +0x0cb U+2566 +0x0cc U+2560 +0x0cd U+2550 +0x0ce U+256c +0x0cf U+2567 +0x0d0 U+2568 +0x0d1 U+2564 +0x0d2 U+2565 +0x0d3 U+2559 +0x0d4 U+2558 +0x0d5 U+2552 +0x0d6 U+2553 +0x0d7 U+256b +0x0d8 U+256a +0x0d9 U+2518 +0x0da U+250c +0x0db U+2588 +0x0dc U+2584 +0x0dd U+258c +0x0de U+2590 +0x0df U+2580 +0x0e0 U+03b1 +0x0e1 U+00df U+03b2 +0x0e2 U+0393 +0x0e3 U+03c0 +0x0e4 U+03a3 +0x0e5 U+03c3 +0x0e6 U+00b5 U+03bc +0x0e7 U+03c4 +0x0e8 U+03a6 +0x0e9 U+0398 +0x0ea U+03a9 U+2126 +0x0eb U+03b4 +0x0ec U+221e +0x0ed U+03c6 U+2205 U+2300 +0x0ee U+03b5 U+2208 +0x0ef U+2229 +0x0f0 U+2261 +0x0f1 U+00b1 +0x0f2 U+2265 +0x0f3 U+2264 +0x0f4 U+2320 +0x0f5 U+2321 +0x0f6 U+00f7 +0x0f7 U+2248 +0x0f8 U+00b0 +0x0f9 U+2219 U+22c5 +0x0fa U+00b7 +0x0fb U+221a +0x0fc U+207f +0x0fd U+00b2 +0x0fe U+220e U+25a0 +0x0ff U+00a0 diff --git a/etc/psf/sans6x10.psf b/etc/psf/sans6x10.psf new file mode 100644 index 00000000..09bb1af6 --- /dev/null +++ b/etc/psf/sans6x10.psf Binary files differdiff --git a/etc/psf/sans6x12.psf b/etc/psf/sans6x12.psf new file mode 100644 index 00000000..75c1fd49 --- /dev/null +++ b/etc/psf/sans6x12.psf Binary files differdiff --git a/etc/psf/sans6x8.psf b/etc/psf/sans6x8.psf new file mode 100644 index 00000000..fef671d8 --- /dev/null +++ b/etc/psf/sans6x8.psf Binary files differdiff --git a/etc/wsconsctl.conf b/etc/wsconsctl.conf new file mode 100644 index 00000000..05f29b6a --- /dev/null +++ b/etc/wsconsctl.conf @@ -0,0 +1,26 @@ +display.brightness=50% +keyboard.backlight=0% + +mouse1.tp.tapping=1 +mouse1.tp.scaling=0.2 +mouse1.reverse_scrolling=1 + +keyboard1.repeat.del1=200 +keyboard1.repeat.deln=50 + +keyboard1.map+='keycode 30 = exclam 1' +keyboard1.map+='keycode 31 = at 2' +keyboard1.map+='keycode 32 = numbersign 3' +keyboard1.map+='keycode 33 = dollar 4' +keyboard1.map+='keycode 34 = percent 5' +keyboard1.map+='keycode 35 = asciicircum 6' +keyboard1.map+='keycode 36 = ampersand 7' +keyboard1.map+='keycode 37 = asterisk 8' +keyboard1.map+='keycode 38 = parenleft 9' +keyboard1.map+='keycode 39 = parenright 0' +keyboard1.map+='keycode 45 = underscore minus' +keyboard1.map+='keycode 47 = braceleft bracketleft' +keyboard1.map+='keycode 48 = braceright bracketright' +keyboard1.map+='keycode 49 = bar backslash' +keyboard1.map+='keycode 50 = bar backslash' +keyboard1.map+='keycode 57 = Escape' diff --git a/gpl.c b/gpl.c new file mode 100644 index 00000000..604089c3 --- /dev/null +++ b/gpl.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2021 C. McEnroe <june@causal.agency> + * + * 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/>. + */ + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> diff --git a/home/.config/X/modmap b/home/.config/X/modmap new file mode 100644 index 00000000..b0b1ea79 --- /dev/null +++ b/home/.config/X/modmap @@ -0,0 +1,16 @@ +clear Lock +keysym Caps_Lock = Escape +keysym 1 = exclam 1 +keysym 2 = at 2 +keysym 3 = numbersign 3 +keysym 4 = dollar 4 +keysym 5 = percent 5 +keysym 6 = asciicircum 6 +keysym 7 = ampersand 7 +keysym 8 = asterisk 8 +keysym 9 = parenleft 9 +keysym 0 = parenright 0 +keysym minus = underscore minus +keysym bracketleft = braceleft bracketleft +keysym bracketright = braceright bracketright +keysym backslash = bar backslash diff --git a/home/.config/X/resources b/home/.config/X/resources new file mode 100644 index 00000000..44b8e791 --- /dev/null +++ b/home/.config/X/resources @@ -0,0 +1,49 @@ +Xft.dpi: 144 +Xft.antialias: true +Xft.hinting: false + +Xcursor.size: 64 +Xcursor.theme: dmz-aa + +XLock.usefirst: false +XLock.echokeys: true + +*Background: rgb:14/13/0E +*Foreground: rgb:B7/A9/80 +*BorderColor: rgb:99/8D/6B + +XTerm*utf8: true +XTerm*metaSendsEscape: true +XTerm*alternateScroll: true +XTerm*allowMouseOps: false +XTerm*bellIsUrgent: true +XTerm*charClass: 33:48,36-47:48,58-59:48,61:48,63-64:48,95:48,126:48 + +XTerm*VT100*translations: #override \n\ + Super <Key>C: copy-selection(CLIPBOARD) \n\ + Super <Key>V: insert-selection(CLIPBOARD) + +XTerm*faceName: Go Mono:size=11 +XTerm*internalBorder: 6 +XTerm*colorBDMode: true +XTerm*scrollBar: false +XTerm*pointerMode: 2 + +XTerm*color0: rgb:16/15/10 +XTerm*color1: rgb:A3/28/10 +XTerm*color2: rgb:72/7A/18 +XTerm*color3: rgb:A3/77/20 +XTerm*color4: rgb:3D/62/66 +XTerm*color5: rgb:7A/49/55 +XTerm*color6: rgb:55/7A/55 +XTerm*color7: rgb:99/8D/6B +XTerm*color8: rgb:4C/46/35 +XTerm*color9: rgb:CC/32/14 +XTerm*color10: rgb:8E/99/1E +XTerm*color11: rgb:CC/95/28 +XTerm*color12: rgb:4C/7B/7F +XTerm*color13: rgb:99/5B/6B +XTerm*color14: rgb:6B/99/6B +XTerm*color15: rgb:CC/BC/8E +XTerm*colorBD: rgb:CC/BC/8E +XTerm*cursorColor: rgb:7A/71/55 diff --git a/home/.config/cwm/cwmrc b/home/.config/cwm/cwmrc new file mode 100644 index 00000000..d72ec163 --- /dev/null +++ b/home/.config/cwm/cwmrc @@ -0,0 +1,87 @@ +sticky yes +snapdist 10 +moveamount 10 + +ignore clock +autogroup 0 clock,XTerm +gap 38 0 0 0 + +unbind-key all +bind-key 4-n terminal +bind-key 4-t "firefox -new-tab about:blank" +bind-key 4-Delete lock +bind-key 4-Down window-lower +bind-key 4-Up window-raise +bind-key 4-slash menu-window +bind-key 4-Tab group-cycle +bind-key 4S-Tab group-rcycle +bind-key 4-grave window-cycle +bind-key 4S-grave window-rcycle +bind-key 4-w window-close +bind-key 4-exclam group-only-1 +bind-key 4-at group-only-2 +bind-key 4-numbersign group-only-3 +bind-key 4-dollar group-only-4 +bind-key 4-percent group-only-5 +bind-key 4-asciicircum group-only-6 +bind-key 4-ampersand group-only-7 +bind-key 4-asterisk group-only-8 +bind-key 4-parenleft group-only-9 +bind-key 4S-exclam window-movetogroup-1 +bind-key 4S-at window-movetogroup-2 +bind-key 4S-numbersign window-movetogroup-3 +bind-key 4S-dollar window-movetogroup-4 +bind-key 4S-percent window-movetogroup-5 +bind-key 4S-asciicircum window-movetogroup-6 +bind-key 4S-ampersand window-movetogroup-7 +bind-key 4S-asterisk window-movetogroup-8 +bind-key 4S-parenleft window-movetogroup-9 +bind-key 4-f window-fullscreen +bind-key 4-m window-maximize +bind-key 4-equal window-vmaximize +bind-key 4S-equal window-hmaximize +bind-key 4-underscore window-vtile +bind-key 4S-underscore window-htile +bind-key 4-h window-move-left-big +bind-key 4-j window-move-down-big +bind-key 4-k window-move-up-big +bind-key 4-l window-move-right-big +bind-key 4S-h window-move-left +bind-key 4S-j window-move-down +bind-key 4S-k window-move-up +bind-key 4S-l window-move-right +bind-key 4S-y window-snap-up-left +bind-key 4S-u window-snap-up-right +bind-key 4S-b window-snap-down-left +bind-key 4S-n window-snap-down-right +bind-key 4M-h window-resize-left +bind-key 4M-j window-resize-down +bind-key 4M-k window-resize-up +bind-key 4M-l window-resize-right +bind-key 4MS-h window-resize-left-big +bind-key 4MS-j window-resize-down-big +bind-key 4MS-k window-resize-up-big +bind-key 4MS-l window-resize-right-big +bind-key 4-space menu-exec +bind-key 4S-r restart +bind-key 4S-q quit + +bind-key F1 "xbacklight -steps 1 -5" +bind-key F2 "xbacklight -steps 1 +5" +bind-key F10 "sndioctl output.mute=!" +bind-key F11 "sndioctl output.level=-0.05" +bind-key F12 "sndioctl output.level=+0.05" + +unbind-mouse all +bind-mouse 4-1 window-move +bind-mouse 4S-1 window-resize + +fontname "Go Mono:size=11" +borderwidth 2 +color inactiveborder rgb:4C/46/35 +color activeborder rgb:99/8D/6B +color urgencyborder rgb:A3/77/20 +color menubg rgb:14/13/0E +color menufg rgb:B7/A9/80 +color font rgb:B7/A9/80 +color selfont rgb:14/13/0E diff --git a/home/.config/git/config b/home/.config/git/config new file mode 100644 index 00000000..ada63e41 --- /dev/null +++ b/home/.config/git/config @@ -0,0 +1,24 @@ +[user] + name = C. McEnroe + email = june@causal.agency + +[commit] + verbose = true + +[diff] + colorMoved = default + +[merge] + conflictStyle = diff3 + +[pull] + rebase = true + +[rebase] + autosquash = true + +[pretty] + log = %Cred%h %Creset%s%C(yellow)%d %Cgreen(%ar) %Cblue<%aN> + +[include] + path = ./private diff --git a/home/.config/git/ignore b/home/.config/git/ignore new file mode 100644 index 00000000..fea54519 --- /dev/null +++ b/home/.config/git/ignore @@ -0,0 +1,2 @@ +*.DS_store +*.dSYM/ diff --git a/home/.config/htop/htoprc b/home/.config/htop/htoprc new file mode 100644 index 00000000..cdda268d --- /dev/null +++ b/home/.config/htop/htoprc @@ -0,0 +1,29 @@ +# Beware! This file is rewritten by htop when settings are changed in the interface. +# The parser is also very primitive, and not human-friendly. +fields=0 48 39 2 46 49 1 +sort_key=47 +sort_direction=1 +hide_threads=0 +hide_kernel_threads=1 +hide_userland_threads=1 +shadow_other_users=0 +show_thread_names=0 +show_program_path=1 +highlight_base_name=1 +highlight_megabytes=1 +highlight_threads=1 +tree_view=1 +header_margin=0 +detailed_cpu_time=0 +cpu_count_from_zero=0 +show_cpu_usage=1 +show_cpu_frequency=0 +update_process_names=0 +account_guest_in_cpu_meter=0 +color_scheme=0 +enable_mouse=0 +delay=15 +left_meters=AllCPUs2 +left_meter_modes=1 +right_meters=Memory Swap +right_meter_modes=1 1 diff --git a/home/.editrc b/home/.editrc new file mode 100644 index 00000000..cf779a7d --- /dev/null +++ b/home/.editrc @@ -0,0 +1 @@ +bind -v diff --git a/home/.gdbinit b/home/.gdbinit new file mode 100644 index 00000000..9422460c --- /dev/null +++ b/home/.gdbinit @@ -0,0 +1 @@ +set disassembly-flavor intel diff --git a/home/.hushlogin b/home/.hushlogin new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/home/.hushlogin diff --git a/home/.inputrc b/home/.inputrc new file mode 100644 index 00000000..b2cc9d61 --- /dev/null +++ b/home/.inputrc @@ -0,0 +1 @@ +set editing-mode vi diff --git a/home/.lldbinit b/home/.lldbinit new file mode 100644 index 00000000..73f3e676 --- /dev/null +++ b/home/.lldbinit @@ -0,0 +1 @@ +settings set target.x86-disassembly-flavor intel diff --git a/home/.local/bin/aes b/home/.local/bin/aes new file mode 100755 index 00000000..32b52637 --- /dev/null +++ b/home/.local/bin/aes @@ -0,0 +1,7 @@ +#!/bin/sh +set -eu + +enwiden() { + exec tr ' -~' ' !-~' +} +[ $# -gt 0 ] && echo "$*" | enwiden || enwiden diff --git a/home/.local/bin/clock b/home/.local/bin/clock new file mode 100755 index 00000000..ef8cd6d8 --- /dev/null +++ b/home/.local/bin/clock @@ -0,0 +1,17 @@ +#!/bin/sh +set -eu + +tput civis +sleep=$(( 60 - $(date +'%S' | sed 's/^0//') )) +while :; do + if [ $(apm -a) -eq 1 ]; then + printf '%3s%%' "$(apm -l)" + else + test $(apm -b) -eq 2 && tput setaf 1 bold + printf '%3.3sm' "$(apm -m)" + tput sgr0 + fi + printf ' %s\r' "$(date +'%a %H:%M')" + sleep $sleep + sleep=60 +done diff --git a/home/.local/bin/def b/home/.local/bin/def new file mode 100755 index 00000000..6a1681d3 --- /dev/null +++ b/home/.local/bin/def @@ -0,0 +1,47 @@ +#!/bin/sh +set -eu + +macro=$1 +headers=' +assert.h +complex.h +ctype.h +errno.h +fenv.h +float.h +inttypes.h +iso646.h +limits.h +locale.h +math.h +setjmp.h +signal.h +stdalign.h +stdarg.h +stdatomic.h +stdbool.h +stddef.h +stdint.h +stdio.h +stdlib.h +stdnoreturn.h +string.h +tgmath.h +threads.h +time.h +uchar.h +wchar.h +wctype.h +' + +for header in $headers; do + defined=$( + echo "$macro" \ + | cc -E -x c -include "$header" - \ + 2> /dev/null \ + | tail -n 1 + ) + [ $? -ne 0 -o "$defined" = "$macro" ] && continue + echo "#include <${header}>" + echo "$defined" +done diff --git a/home/.local/bin/deg b/home/.local/bin/deg new file mode 100755 index 00000000..216029ed --- /dev/null +++ b/home/.local/bin/deg @@ -0,0 +1,6 @@ +#!/bin/sh +set -eu +cat <<EOF +${1}°F = $(dc -e "1k $(echo "$1" | sed 's/^-/_/') 32-1.8/p")°C +${1}°C = $(dc -e "1k $(echo "$1" | sed 's/^-/_/') 1.8*32+p")°F +EOF diff --git a/home/.local/bin/git-password b/home/.local/bin/git-password new file mode 100755 index 00000000..41351e38 --- /dev/null +++ b/home/.local/bin/git-password @@ -0,0 +1,7 @@ +#!/bin/sh +set -eu + +url=$1 +echo "url=${url}" \ + | git credential fill \ + | sed -En 's/^password=(.*)/\1/p' diff --git a/home/.local/bin/mdate b/home/.local/bin/mdate new file mode 100755 index 00000000..daff50dc --- /dev/null +++ b/home/.local/bin/mdate @@ -0,0 +1,2 @@ +#!/bin/sh +exec date +'.Dd %B %e, %Y' diff --git a/home/.local/bin/nasd b/home/.local/bin/nasd new file mode 100755 index 00000000..d64b2c3a --- /dev/null +++ b/home/.local/bin/nasd @@ -0,0 +1,9 @@ +#!/bin/sh +set -eu + +dir=$(mktemp -d) +echo 'bits 64' > "${dir}/input" +cat >> "${dir}/input" +nasm -o "${dir}/output" "${dir}/input" || true +ndisasm -b 64 "${dir}/output" || true +rm -r "$dir" diff --git a/home/.local/bin/notify-send b/home/.local/bin/notify-send new file mode 100755 index 00000000..5630440d --- /dev/null +++ b/home/.local/bin/notify-send @@ -0,0 +1,9 @@ +#!/usr/bin/osascript + +on run argv + if count of argv is 2 then + display notification (item 2 of argv) with title (item 1 of argv) + else + display notification (item 1 of argv) + end if +end run diff --git a/home/.local/bin/np b/home/.local/bin/np new file mode 100755 index 00000000..b0eb2326 --- /dev/null +++ b/home/.local/bin/np @@ -0,0 +1,7 @@ +#!/usr/bin/osascript + +tell application "Music" + tell current track + get "/me is listening to " & artist & " — " & name + end tell +end tell diff --git a/home/.local/bin/open b/home/.local/bin/open new file mode 100755 index 00000000..9439f07d --- /dev/null +++ b/home/.local/bin/open @@ -0,0 +1,19 @@ +#!/bin/sh +set -eu + +if [ -n "${SSH_CLIENT:-}" ]; then + exec pbd -o "$@" +fi + +case "$1" in + (*.gif|*.jpeg|*.jpg|*.png) + curl -LSs "$1" | imv - + ;; + (https://youtu.be/*|https://www.youtube.com/watch*|https://twitch.tv/*) + ulimit -c 0 # mpv segfaults on exit every time on OpenBSD... + exec mpv "$1" >/dev/null 2>&1 + ;; + (*) + exec firefox -new-tab "$1" >/dev/null 2>&1 + ;; +esac diff --git a/home/.local/bin/pbcopy b/home/.local/bin/pbcopy new file mode 100755 index 00000000..a804f836 --- /dev/null +++ b/home/.local/bin/pbcopy @@ -0,0 +1,11 @@ +#!/bin/sh +set -eu + +if [ -n "${SSH_CLIENT:-}" ]; then + exec pbd -c +elif [ -n "${DISPLAY:-}" ]; then + exec xsel -bi +else + echo "${0}: don't know what to do" >&2 + exit 1 +fi diff --git a/home/.local/bin/pbpaste b/home/.local/bin/pbpaste new file mode 100755 index 00000000..2924f01e --- /dev/null +++ b/home/.local/bin/pbpaste @@ -0,0 +1,11 @@ +#!/bin/sh +set -eu + +if [ -n "${SSH_CLIENT:-}" ]; then + exec pbd -p +elif [ -n "${DISPLAY:-}" ]; then + exec xsel -bo +else + echo "${0}: don't know what to do" >&2 + exit 1 +fi diff --git a/home/.local/bin/versions b/home/.local/bin/versions new file mode 100755 index 00000000..25e5ff72 --- /dev/null +++ b/home/.local/bin/versions @@ -0,0 +1,9 @@ +#!/bin/sh +set -u + +for repo in ~/src/git/*; do + version=$(git -C "${repo}" describe --dirty 2>/dev/null) + if [ $? -eq 0 ]; then + echo "${repo##*/}-${version#v}" + fi +done | sort -nr -t '-' -k 3 | column -t -s '-' diff --git a/home/.profile b/home/.profile new file mode 100644 index 00000000..f4e4169e --- /dev/null +++ b/home/.profile @@ -0,0 +1,23 @@ +_PATH=$PATH PATH= +path() { [ -d "$1" ] && PATH="${PATH}${PATH:+:}${1}"; } +for prefix in '' /usr/local /opt/local /usr ~/.local; do + path "${prefix}/sbin" + path "${prefix}/bin" +done +path /usr/X11R6/bin +path /usr/games +export MANPATH=:~/.local/share/man + +export EDITOR=vi +type nvi >/dev/null && EDITOR=nvi +export EXINIT='set ai iclower sm sw=4 ts=4 para=BlBdPpIt sect=ShSs | map gg 1G' +export PAGER=less +export LESS=FRXix4 +export CLICOLOR=1 +export MANSECT=2:3:1:8:6:5:7:4:9 +export NETHACKOPTIONS='pickup_types:$!?+/=, color, DECgraphics' + +[ -e /usr/share/mk/sys.mk ] || export CFLAGS=-O +[ -d /usr/home ] && cd + +export ENV=~/.shrc diff --git a/home/.shrc b/home/.shrc new file mode 100644 index 00000000..4b0e6c61 --- /dev/null +++ b/home/.shrc @@ -0,0 +1,63 @@ +set -o noclobber -o nounset -o vi + +CDPATH=:~ + +alias vi=$EDITOR +alias ls='LC_COLLATE=C ls -p' +alias ll='ls -hl' +alias ff='find . -type f -name' +alias bc='bc -l' +alias ag='ag --pager=$PAGER' +alias gs='git status --short --branch || ls' gd='git diff' +alias gsh='git show' gl='git log --graph --pretty=log' +alias gco='git checkout' gb='git branch' gm='git merge' gst='git stash' +alias ga='git add' gmv='git mv' grm='git rm' +alias gc='git commit' gca='gc --amend' gt='git tag' +alias gp='git push' gu='git pull' gf='git fetch' gr='git rebase' +alias rand='openssl rand -base64 33' +alias private='eval "$(gpg -d ~/.private)"' +type doas >/dev/null && alias sudo=doas + +man() { + test $# -ne 1 && { command man "$@"; return $?; } + (IFS=: + for sect in $MANSECT; do + command man -w $sect "$1" >/dev/null 2>&1 && exec man $sect "$1" + done + exec man "$1") +} + +cd() { + local path + if [ $# -eq 0 ]; then + command cd + elif [ "${1%%:*}" != "$1" ]; then + path=${1#*:} + [ -n "${path}" ] || path=${PWD#${HOME}/} + SSH_CD=$path ssh -o SendEnv=SSH_CD "${1%%:*}" + elif [ -e "$1" -a ! -d "$1" ]; then + command cd "${1%/*}" && $EDITOR "${1##*/}" + else + command cd "$@" + fi +} +if [ -n "${SSH_CD:-}" ]; then + cd "${SSH_CD}" + unset SSH_CD +fi + +export LESS_TERMCAP_us=$(tput sitm) +export LESS_TERMCAP_ue=$(tput ritm) + +hostname=$(hostname -s) +rprompt() { + local pwd + pwd=${PWD#${HOME}} + [ "${pwd}" != "${PWD}" ] && pwd="~${pwd}" + [ "${TERM%-*}" = 'xterm' ] \ + && printf '\33]0;%s\a' "${SSH_CLIENT:+${hostname}:}${pwd##*/}" >&2 + printf '%s' "${SSH_CLIENT:+${hostname}:}${pwd}" +} +PS1=' +$ ' +RPS1='${?#0} $(rprompt)' diff --git a/home/.ssh/config b/home/.ssh/config new file mode 100644 index 00000000..3fc6a8db --- /dev/null +++ b/home/.ssh/config @@ -0,0 +1,18 @@ +IgnoreUnknown Include +Include config_private + +HashKnownHosts yes + +SendEnv LANG LC_* + +Host monday beastie puffy toaster tux progynova + HostName %h.local + ForwardAgent yes + RemoteForward 7062 127.0.0.1:7062 + +Host june july + HostName %h.nyc3.do.causal.agency + Port 2222 + +Host git.causal.agency temp.causal.agency + Port 2222 diff --git a/home/.xsession b/home/.xsession new file mode 100644 index 00000000..b6ebd8a1 --- /dev/null +++ b/home/.xsession @@ -0,0 +1,12 @@ +. ~/.profile +export LC_CTYPE=en_US.UTF-8 + +xset r rate 175 m 5/4 0 +xmodmap ~/.config/X/modmap +xrdb -load ~/.config/X/resources +xsetroot -bitmap /usr/X11R6/include/X11/bitmaps/xsnow \ + -bg rgb:14/13/0E -fg rgb:7A/49/55 + +sctd 3600 & +xterm -name clock -geometry 14x1-0+0 -sl 0 -e clock & +exec cwm -c ~/.config/cwm/cwmrc diff --git a/install.sh b/install.sh new file mode 100644 index 00000000..9e18c6bf --- /dev/null +++ b/install.sh @@ -0,0 +1,54 @@ +#!/bin/sh +set -eu + +X= +while getopts 'X' opt; do + case "$opt" in + (X) X=1;; + (?) exit 1;; + esac +done + +packages='curl htop sl the_silver_searcher tree' + +FreeBSD() { + sudo pkg install ddate $packages +} + +OpenBSD() { + doas pkg_add $packages + if test $X; then + doas pkg_add firefox go-fonts imv scrot sct w3m-- xcursor-dmz xsel + fi +} + +Linux() { + sudo pacman -Sy --needed bc ctags gdb openssh vi $packages +} + +installMacPorts() { + xcode-select --install + xcodebuild -license + dir=MacPorts-2.6.3 + tar=${dir}.tar.bz2 + curl -O "https://distfiles.macports.org/MacPorts/${tar}" + tar -x -f $tar + (cd $dir && ./configure) + make -C $dir + sudo make -C $dir install + rm -fr $tar $dir +} + +Darwin() { + [ -d /opt/local ] || installMacPorts + sudo /opt/local/bin/port selfupdate + sudo /opt/local/bin/port -N install git mandoc nvi pkgconfig $packages + sudo mkdir -p /opt/local/etc/select/man + printf 'bin/man\nshare/man/man1/man.1\nshare/man/man1/man.1.gz\n' \ + | sudo tee /opt/local/etc/select/man/base >/dev/null + printf '/usr/bin/man\n/usr/share/man/man1/man.1\n-\n' \ + | sudo tee /opt/local/etc/select/man/system >/dev/null + sudo port select --set man system +} + +$(uname) diff --git a/link.sh b/link.sh new file mode 100644 index 00000000..6763f2e0 --- /dev/null +++ b/link.sh @@ -0,0 +1,23 @@ +#!/bin/sh +set -eu + +die() { + echo "$*" + exit 1 +} + +if [ $# -eq 1 ]; then + link=$1 + file="${PWD}/home/${link#${HOME}/}" + [ ! -f "$file" ] || die "${file} exists" + mkdir -p "${file%/*}" + mv "$link" "$file" +fi + +find home -type f | while read -r find; do + file="${PWD}/${find}" + link="${HOME}/${find#home/}" + mkdir -p "${link%/*}" + [ \( -f "$link" -a -L "$link" \) -o ! -f "$link" ] || die "${link} exists" + ln -fs "$file" "$link" +done diff --git a/port/caesar/.gitignore b/port/caesar/.gitignore new file mode 100644 index 00000000..e2c3034b --- /dev/null +++ b/port/caesar/.gitignore @@ -0,0 +1,2 @@ +caesar +rot13 diff --git a/port/caesar/Makefile b/port/caesar/Makefile new file mode 100644 index 00000000..01205b16 --- /dev/null +++ b/port/caesar/Makefile @@ -0,0 +1,19 @@ +PREFIX = ~/.local +MANDIR = ${PREFIX}/share/man + +LDLIBS = -lm + +all: caesar rot13 + +clean: + rm -f caesar rot13 + +install: caesar rot13 caesar.6 + install -d ${PREFIX}/bin ${MANDIR}/man6 + install caesar rot13 ${PREFIX}/bin + install -m 644 caesar.6 ${MANDIR}/man6/caesar.6 + install -m 644 caesar.6 ${MANDIR}/man6/rot13.6 + +uninstall: + rm -f ${PREFIX}/bin/caesar ${PREFIX}/bin/rot13 + rm -f ${MANDIR}/man6/caesar.6 ${MANDIR}/man6/rot13.6 diff --git a/port/caesar/caesar.6 b/port/caesar/caesar.6 new file mode 100644 index 00000000..4c4bbfb4 --- /dev/null +++ b/port/caesar/caesar.6 @@ -0,0 +1,73 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)caesar.6 8.2 (Berkeley) 11/16/93 +.\" $FreeBSD: releng/11.2/usr.bin/caesar/caesar.6 216239 2010-12-06 19:12:51Z uqs $ +.\" +.Dd November 16, 1993 +.Dt CAESAR 6 +.Os +.Sh NAME +.Nm caesar , rot13 +.Nd decrypt caesar ciphers +.Sh SYNOPSIS +.Nm +.Op Ar rotation +.Nm rot13 +.Sh DESCRIPTION +The +.Nm +utility attempts to decrypt caesar ciphers using English letter frequency +statistics. +.Nm Caesar +reads from the standard input and writes to the standard output. +.Pp +The optional numerical argument +.Ar rotation +may be used to specify a specific rotation value. +If invoked as +.Nm rot13 , +a rotation value of 13 will be used. +.Pp +The frequency (from most common to least) of English letters is as follows: +.Bd -ragged -offset indent +ETAONRISHDLFCMUGPYWBVKXJQZ +.Ed +.Pp +Their frequencies as a percentage are as follows: +.Bd -ragged -offset indent +E(13), T(10.5), A(8.1), O(7.9), N(7.1), R(6.8), I(6.3), S(6.1), H(5.2), +D(3.8), L(3.4), F(2.9), C(2.7), M(2.5), U(2.4), G(2), +P(1.9), Y(1.9), +W(1.5), B(1.4), V(.9), K(.4), X(.15), J(.13), Q(.11), Z(.07). +.Ed +.Pp +Rotated postings to +.Tn USENET +and some of the databases used by the +.Xr fortune 6 +program are rotated by 13 characters. diff --git a/port/caesar/caesar.c b/port/caesar/caesar.c new file mode 100644 index 00000000..cd6cd579 --- /dev/null +++ b/port/caesar/caesar.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Adams. + * + * Authors: + * Stan King, John Eldridge, based on algorithm suggested by + * Bob Morris + * 29-Sep-82 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static const char sccsid[] = "@(#)caesar.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +//__FBSDID("$FreeBSD: releng/11.2/usr.bin/caesar/caesar.c 241846 2012-10-22 03:06:53Z eadler $"); + +#include <errno.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> + +#define LINELENGTH 2048 +#define ROTATE(ch, perm) \ + isascii(ch) ? ( \ + isupper(ch) ? ('A' + (ch - 'A' + perm) % 26) : \ + islower(ch) ? ('a' + (ch - 'a' + perm) % 26) : ch) : ch + +/* + * letter frequencies (taken from some unix(tm) documentation) + * (unix is a trademark of Bell Laboratories) + */ +static double stdf[26] = { + 7.97, 1.35, 3.61, 4.78, 12.37, 2.01, 1.46, 4.49, 6.39, 0.04, + 0.42, 3.81, 2.69, 5.92, 6.96, 2.91, 0.08, 6.63, 8.77, 9.68, + 2.62, 0.81, 1.88, 0.23, 2.07, 0.06, +}; + +static void printit(char *); + +int +main(int argc, char **argv) +{ + int ch, dot, i, nread, winnerdot = 0; + char *inbuf; + int obs[26], try, winner; + + if (argc > 1) + printit(argv[1]); + + if (!(inbuf = malloc((size_t)LINELENGTH))) { + (void)fprintf(stderr, "caesar: out of memory.\n"); + exit(1); + } + + /* adjust frequency table to weight low probs REAL low */ + for (i = 0; i < 26; ++i) + stdf[i] = log(stdf[i]) + log(26.0 / 100.0); + + /* zero out observation table */ + bzero(obs, 26 * sizeof(int)); + + if ((nread = read(STDIN_FILENO, inbuf, (size_t)LINELENGTH)) < 0) { + (void)fprintf(stderr, "caesar: %s\n", strerror(errno)); + exit(1); + } + for (i = nread; i--;) { + ch = (unsigned char) inbuf[i]; + if (isascii(ch)) { + if (islower(ch)) + ++obs[ch - 'a']; + else if (isupper(ch)) + ++obs[ch - 'A']; + } + } + + /* + * now "dot" the freqs with the observed letter freqs + * and keep track of best fit + */ + for (try = winner = 0; try < 26; ++try) { /* += 13) { */ + dot = 0; + for (i = 0; i < 26; i++) + dot += obs[i] * stdf[(i + try) % 26]; + /* initialize winning score */ + if (try == 0) + winnerdot = dot; + if (dot > winnerdot) { + /* got a new winner! */ + winner = try; + winnerdot = dot; + } + } + + for (;;) { + for (i = 0; i < nread; ++i) { + ch = (unsigned char) inbuf[i]; + putchar(ROTATE(ch, winner)); + } + if (nread < LINELENGTH) + break; + if ((nread = read(STDIN_FILENO, inbuf, (size_t)LINELENGTH)) < 0) { + (void)fprintf(stderr, "caesar: %s\n", strerror(errno)); + exit(1); + } + } + exit(0); +} + +static void +printit(char *arg) +{ + int ch, rot; + + if ((rot = atoi(arg)) < 0) { + (void)fprintf(stderr, "caesar: bad rotation value.\n"); + exit(1); + } + while ((ch = getchar()) != EOF) + putchar(ROTATE(ch, rot)); + exit(0); +} diff --git a/port/caesar/rot13.sh b/port/caesar/rot13.sh new file mode 100644 index 00000000..8ce4b94e --- /dev/null +++ b/port/caesar/rot13.sh @@ -0,0 +1,33 @@ +#!/bin/sh - +# +# Copyright (c) 1992, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)rot13.sh 8.1 (Berkeley) 5/31/93 +# $FreeBSD: releng/11.2/usr.bin/caesar/rot13.sh 278616 2015-02-12 05:35:00Z cperciva $ + +exec caesar 13 "$@" diff --git a/port/cgram/.gitignore b/port/cgram/.gitignore new file mode 100644 index 00000000..d4f2ec10 --- /dev/null +++ b/port/cgram/.gitignore @@ -0,0 +1 @@ +cgram diff --git a/port/cgram/Makefile b/port/cgram/Makefile new file mode 100644 index 00000000..02f11eec --- /dev/null +++ b/port/cgram/Makefile @@ -0,0 +1,17 @@ +PREFIX = ~/.local +MANDIR = ${PREFIX}/share/man + +LDLIBS = -lcurses + +cgram: + +clean: + rm -f cgram + +install: cgram cgram.6 + install -d ${PREFIX}/bin ${MANDIR}/man6 + install cgram ${PREFIX}/bin + install -m 644 cgram.6 ${MANDIR}/man6 + +uninstall: + rm -f ${PREFIX}/bin/cgram ${MANDIR}/man6/cgram.6 diff --git a/port/cgram/cgram.6 b/port/cgram/cgram.6 new file mode 100644 index 00000000..9f315804 --- /dev/null +++ b/port/cgram/cgram.6 @@ -0,0 +1,65 @@ +.\" $NetBSD: cgram.6,v 1.2 2013/08/04 07:55:09 wiz Exp $ +.\" +.\" Copyright (c) 2004, 2013 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by David A. Holland. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd August 3, 2013 +.Dt CGRAM 6 +.Os +.Sh NAME +.Nm cgram +.Nd solve Sunday-paper cryptograms +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +is a curses-based widget for solving Sunday-paper-type cryptograms +based on substitution ciphers. +A random cleartext is chosen using +.Xr fortune 6 +and a random substitution key is generated. +.Pp +The ciphertext is displayed. +Typing a letter changes the key so that the letter under the cursor +maps to the newly typed letter, and updates the display accordingly. +Use Emacs-type cursor commands to move around. +Enter a tilde +.Pq ~ +to quit. +Press asterisk +.Pq * +to enter an easier mode where correct letters are displayed in +boldface. +.Sh SEE ALSO +.Xr caesar 6 +.Sh HISTORY +.Nm +was written circa 2004. +It was imported into +.Nx +in 2013 and first appeared in +.Nx 7.0 . diff --git a/port/cgram/cgram.c b/port/cgram/cgram.c new file mode 100644 index 00000000..76ea55fb --- /dev/null +++ b/port/cgram/cgram.c @@ -0,0 +1,344 @@ +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <time.h> +#include <err.h> +#include <assert.h> +#include <curses.h> +#include "pathnames.h" + +//////////////////////////////////////////////////////////// + +static char *xstrdup(const char *s) { + char *ret; + + ret = malloc(strlen(s) + 1); + if (ret == NULL) { + errx(1, "Out of memory"); + } + strcpy(ret, s); + return ret; +} + +//////////////////////////////////////////////////////////// + +struct stringarray { + char **v; + int num; +}; + +static void stringarray_init(struct stringarray *a) { + a->v = NULL; + a->num = 0; +} + +static void stringarray_cleanup(struct stringarray *a) { + free(a->v); +} + +static void stringarray_add(struct stringarray *a, const char *s) { + a->v = realloc(a->v, (a->num + 1) * sizeof(a->v[0])); + if (a->v == NULL) { + errx(1, "Out of memory"); + } + a->v[a->num] = xstrdup(s); + a->num++; +} + +//////////////////////////////////////////////////////////// + +static struct stringarray lines; +static struct stringarray sollines; +static bool hinting; +static int scrolldown; +static unsigned curx; +static int cury; + +static void readquote(void) { + FILE *f = popen(_PATH_FORTUNE, "r"); + if (!f) { + err(1, "%s", _PATH_FORTUNE); + } + + char buf[128], buf2[8*sizeof(buf)]; + while (fgets(buf, sizeof(buf), f)) { + char *s = strrchr(buf, '\n'); + assert(s); + assert(strlen(s)==1); + *s = 0; + + int i,j; + for (i=j=0; buf[i]; i++) { + if (buf[i]=='\t') { + buf2[j++] = ' '; + while (j%8) buf2[j++] = ' '; + } + else if (buf[i]=='\b') { + if (j>0) j--; + } + else { + buf2[j++] = buf[i]; + } + } + buf2[j] = 0; + + stringarray_add(&lines, buf2); + stringarray_add(&sollines, buf2); + } + + pclose(f); +} + +static void encode(void) { + int used[26]; + for (int i=0; i<26; i++) used[i] = 0; + + int key[26]; + int keypos=0; + while (keypos < 26) { + int c = random()%26; + if (used[c]) continue; + key[keypos++] = c; + used[c] = 1; + } + + for (int y=0; y<lines.num; y++) { + for (unsigned x=0; lines.v[y][x]; x++) { + if (islower((unsigned char)lines.v[y][x])) { + int q = lines.v[y][x]-'a'; + lines.v[y][x] = 'a'+key[q]; + } + if (isupper((unsigned char)lines.v[y][x])) { + int q = lines.v[y][x]-'A'; + lines.v[y][x] = 'A'+key[q]; + } + } + } +} + +static int substitute(int ch) { + assert(cury>=0 && cury<lines.num); + if (curx >= strlen(lines.v[cury])) { + beep(); + return -1; + } + + int och = lines.v[cury][curx]; + if (!isalpha((unsigned char)och)) { + beep(); + return -1; + } + + int loch = tolower((unsigned char)och); + int uoch = toupper((unsigned char)och); + int lch = tolower((unsigned char)ch); + int uch = toupper((unsigned char)ch); + + for (int y=0; y<lines.num; y++) { + for (unsigned x=0; lines.v[y][x]; x++) { + if (lines.v[y][x]==loch) { + lines.v[y][x] = lch; + } + else if (lines.v[y][x]==uoch) { + lines.v[y][x] = uch; + } + else if (lines.v[y][x]==lch) { + lines.v[y][x] = loch; + } + else if (lines.v[y][x]==uch) { + lines.v[y][x] = uoch; + } + } + } + return 0; +} + +//////////////////////////////////////////////////////////// + +static void redraw(void) { + erase(); + bool won = true; + for (int i=0; i<LINES-1; i++) { + move(i, 0); + int ln = i+scrolldown; + if (ln < lines.num) { + for (unsigned j=0; lines.v[i][j]; j++) { + int ch = lines.v[i][j]; + if (ch != sollines.v[i][j] && isalpha((unsigned char)ch)) { + won = false; + } + bool bold=false; + if (hinting && ch==sollines.v[i][j] && + isalpha((unsigned char)ch)) { + bold = true; + attron(A_BOLD); + } + addch(lines.v[i][j]); + if (bold) { + attroff(A_BOLD); + } + } + } + clrtoeol(); + } + + move(LINES-1, 0); + if (won) { + addstr("*solved* "); + } + addstr("~ to quit, * to cheat, ^pnfb to move"); + + move(LINES-1, 0); + + move(cury-scrolldown, curx); + + refresh(); +} + +static void opencurses(void) { + initscr(); + cbreak(); + noecho(); +} + +static void closecurses(void) { + endwin(); +} + +//////////////////////////////////////////////////////////// + +static void loop(void) { + bool done=false; + while (!done) { + redraw(); + int ch = getch(); + switch (ch) { + case 1: /* ^A */ + curx=0; + break; + case 2: /* ^B */ + if (curx > 0) { + curx--; + } + else if (cury > 0) { + cury--; + curx = strlen(lines.v[cury]); + } + break; + case 5: /* ^E */ + curx = strlen(lines.v[cury]); + break; + case 6: /* ^F */ + if (curx < strlen(lines.v[cury])) { + curx++; + } + else if (cury < lines.num - 1) { + cury++; + curx = 0; + } + break; + case 12: /* ^L */ + clear(); + break; + case 14: /* ^N */ + if (cury < lines.num-1) { + cury++; + } + if (curx > strlen(lines.v[cury])) { + curx = strlen(lines.v[cury]); + } + if (scrolldown < cury - (LINES-2)) { + scrolldown = cury - (LINES-2); + } + break; + case 16: /* ^P */ + if (cury > 0) { + cury--; + } + if (curx > strlen(lines.v[cury])) { + curx = strlen(lines.v[cury]); + } + if (scrolldown > cury) { + scrolldown = cury; + } + break; + case '*': + hinting = !hinting; + break; + case '~': + done = true; + break; + default: + if (isalpha(ch)) { + if (!substitute(ch)) { + if (curx < strlen(lines.v[cury])) { + curx++; + } + if (curx==strlen(lines.v[cury]) && cury < lines.num-1) { + curx=0; + cury++; + } + } + } + else if (curx < strlen(lines.v[cury]) && ch==lines.v[cury][curx]) { + curx++; + if (curx==strlen(lines.v[cury]) && cury < lines.num-1) { + curx=0; + cury++; + } + } + else { + beep(); + } + break; + } + } +} + +//////////////////////////////////////////////////////////// + +int main(void) { + stringarray_init(&lines); + stringarray_init(&sollines); + srandom(time(NULL)); + readquote(); + encode(); + opencurses(); + + loop(); + + closecurses(); + stringarray_cleanup(&sollines); + stringarray_cleanup(&lines); + return 0; +} diff --git a/port/cgram/pathnames.h b/port/cgram/pathnames.h new file mode 100644 index 00000000..40db1eed --- /dev/null +++ b/port/cgram/pathnames.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David A. Holland. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define _PATH_FORTUNE "fortune" diff --git a/port/ddate/.gitignore b/port/ddate/.gitignore new file mode 100644 index 00000000..eaa8a5fd --- /dev/null +++ b/port/ddate/.gitignore @@ -0,0 +1 @@ +ddate diff --git a/port/ddate/Makefile b/port/ddate/Makefile new file mode 100644 index 00000000..c1bc4553 --- /dev/null +++ b/port/ddate/Makefile @@ -0,0 +1,15 @@ +PREFIX ?= ~/.local +MANDIR ?= ${PREFIX}/share/man + +ddate: + +clean: + rm -f ddate + +install: ddate ddate.1 + install -d ${PREFIX}/bin ${MANDIR}/man1 + install ddate ${PREFIX}/bin + install -m 644 ddate.1 ${MANDIR}/man1 + +uninstall: + rm -f ${PREFIX}/bin/ddate ${MANDIR}/man1/ddate.1 diff --git a/port/ddate/ddate.1 b/port/ddate/ddate.1 new file mode 100644 index 00000000..c340578f --- /dev/null +++ b/port/ddate/ddate.1 @@ -0,0 +1,117 @@ +.\" All Rites Reversed. This file is in the PUBLIC DOMAIN. +.\" Kallisti. +.TH DDATE 1 "Bureaucracy 3161" "ddate" "Emperor Norton User Command" +.SH NAME +ddate \- convert Gregorian dates to Discordian dates +.SH SYNOPSIS +.B ddate +.RI [ \fB+\fPformat] +.RI [ date ] +.SH DESCRIPTION +.B ddate +prints the date in Discordian date format. +.PP +If called with no arguments, +.B ddate +will get the current system date, convert this to the Discordian +date format and print this on the standard output. Alternatively, a +Gregorian date may be specified on the command line, in the form of a numerical +day, month and year. +.PP +If a format string is specified, the Discordian date will be printed in +a format specified by the string. This mechanism works similarly to the +format string mechanism of +.B date(1), +only almost completely differently. The fields are: +.IP %A +Full name of the day of the week (i.e., Sweetmorn) +.IP %a +Abbreviated name of the day of the week (i.e., SM) +.IP %B +Full name of the season (i.e., Chaos) +.IP %b +Abbreviated name of the season (i.e., Chs) +.IP %d +Cardinal number of day in season (i.e., 23) +.IP %e +Ordinal number of day in season (i.e., 23rd) +.IP %H +Name of current Holyday, if any +.IP %N +Magic code to prevent rest of format from being printed unless today is +a Holyday. +.IP %n +Newline +.IP %t +Tab +.IP %X +Number of days remaining until X-Day. (Not valid if the SubGenius options +are not compiled in.) +.IP %Y +The year of our Lady Discord (i.e., 3182) +.IP %{ +.IP %} +Used to enclose the part of the string which is to be replaced with the +words "St. Tib's Day" if the current day is St. Tib's Day. +.IP %\. +Try it and see. +.bp +.SH EXAMPLES +.nf +% ddate +.br +Sweetmorn, Bureaucracy 42, 3161 YOLD +.PP +% ddate +'Today is %{%A, the %e of %B%}, %Y. %N%nCelebrate %H' +.br +Today is Sweetmorn, the 42nd of Bureaucracy, 3161. +.PP +% ddate +"It's %{%A, the %e of %B%}, %Y. %N%nCelebrate %H" 26 9 1995 +.br +It's Prickle-Prickle, the 50th of Bureaucracy, 3161. +.br +Celebrate Bureflux +.PP +% ddate +"Today's %{%A, the %e of %B%}, %Y. %N%nCelebrate %H" 29 2 1996 +.br +Today's St. Tib's Day, 3162. +.br + +.SH BUGS + +.B ddate(1) +will produce undefined behavior if asked to produce the date for St. Tib's +day and its format string does not contain the St. Tib's Day delimiters +%{ and %}. + +.SH NOTE + +After `X-Day' passed without incident, the Church of the SubGenius +declared that it had got the year upside down - X-Day is actually in 8661 AD +rather than 1998 AD. Thus, the True X-Day is Cfn 40, 9827. + +.SH AUTHOR +.nh +Original program by Druel the Chaotic aka Jeremy Johnson (mpython@gnu.ai.mit.edu) +.br +Major rewrite by Lee H:. O:. Smith, KYTP, aka Andrew Bulhak (acb@dev.null.org) +.br +Gregorian B.C.E. dates fixed by Chaplain Nyan the Wiser, aka Dan Dart (ntw@dandart.co.uk) +.br +Five tons of flax. + +.SH DISTRIBUTION POLICY + +Public domain. All rites reversed. + +.SH SEE ALSO + +date(1), +.br +http://www.subgenius.com/ +.br +Malaclypse the Younger, +.I "Principia Discordia, Or How I Found Goddess And What I Did To Her When I Found Her" + +.SH AVAILABILITY +The ddate command is available from https://github.com/bo0ts/ddate. diff --git a/port/ddate/ddate.c b/port/ddate/ddate.c new file mode 100644 index 00000000..c0a6bf37 --- /dev/null +++ b/port/ddate/ddate.c @@ -0,0 +1,399 @@ +/* $ DVCS ID: $jer|,523/lhos,KYTP!41023161\b"?" <<= DO NOT DELETE! */ + +/* ddate.c .. converts boring normal dates to fun Discordian Date -><- + written the 65th day of The Aftermath in the Year of Our Lady of + Discord 3157 by Druel the Chaotic aka Jeremy Johnson aka + mpython@gnu.ai.mit.edu + 28 Sever St Apt #3 + Worcester MA 01609 + + and I'm not responsible if this program messes anything up (except your + mind, I'm responsible for that) + + (k) YOLD 3161 and all time before and after. + Reprint, reuse, and recycle what you wish. + This program is in the public domain. Distribute freely. Or not. + + Majorly hacked, extended and bogotified/debogotified on + Sweetmorn, Bureaucracy 42, 3161 YOLD, by Lee H:. O:. Smith, KYTP, + aka Andrew Bulhak, aka acb@dev.null.org + + Slightly hackled and crackled by a sweet firey stove on + Boomtime, the 53rd day of Bureaucracy in the YOLD 3179, + by Chaplain Nyan the Wiser, aka Dan Dart, aka ntw@dandart.co.uk + + and I'm not responsible if this program messes anything up (except your + mind, I'm responsible for that) (and that goes for me as well --lhos) + + Version history: + Bureflux 3161: First release of enhanced ddate with format strings + 59 Bcy, 3161: PRAISE_BOB and KILL_BOB options split, other minor + changes. + 53 Bcy, 3179: Fixed gregorian date conversions less than YOLD 1167 + + 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL> + - added Native Language Support + + 2000-03-17 Burt Holzman <holzman+ddate@gmail.com> + - added range checks for dates + + 2014-06-07 William Woodruff <william@tuffbizz.com> + - removed gettext dependent locale code + + 15th of Confusion, 3180: + - call out adherents of the wrong fruit + + FIVE TONS OF FLAX +*/ + +/* configuration options VVVVV READ THIS!!! */ + +/* If you wish ddate(1) to print the date in the same format as Druel's + * original ddate when called in immediate mode, define OLD_IMMEDIATE_FMT + */ + +#define OLD_IMMEDIATE_FMT + +/* If you wish to use the US format for aneristic dates (m-d-y), as opposed to + * the Commonwealth format, define US_FORMAT. + */ + +/* #define US_FORMAT */ + +/* If you are ideologically, theologically or otherwise opposed to the + * Church of the SubGenius and do not wish your copy of ddate(1) to contain + * code for counting down to X-Day, undefine KILL_BOB */ + +#define KILL_BOB 13013 + +/* If you wish ddate(1) to contain SubGenius slogans, define PRAISE_BOB */ + +/*#define PRAISE_BOB 13013*/ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <stdio.h> + + +// work around includes and defines from formerly c.h +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) +#endif + +/* &a[0] degrades to a pointer: a different type from an array */ +# define __must_be_array(a) \ + BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(__typeof__(a), __typeof__(&a[0]))) + +#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) + +/* work around hacks for standalone package */ +#define PACKAGE "ddate" +#define PACKAGE_STRING "Stand Alone" + +#ifndef __GNUC__ +#define inline /* foo */ +#endif + +#ifdef KILL_BOB +int xday_countdown(int yday, int year); +#endif + + +/* string constants */ + +char *day_long[5] = { + "Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange" +}; + +char *day_short[5] = {"SM","BT","PD","PP","SO"}; + +char *season_long[5] = { + "Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath" +}; + +char *season_short[5] = {"Chs", "Dsc", "Cfn", "Bcy", "Afm"}; + +char *holyday[5][2] = { + { "Mungday", "Chaoflux" }, + { "Mojoday", "Discoflux" }, + { "Syaday", "Confuflux" }, + { "Zaraday", "Bureflux" }, + { "Maladay", "Afflux" } +}; + +struct disc_time { + int season; /* 0-4 */ + int day; /* 0-72 */ + int yday; /* 0-365 */ + int year; /* 3066- */ +}; + +char *excl[] = { + "Hail Eris!", "All Hail Discordia!", "Kallisti!", "Fnord.", "Or not.", + "Wibble.", "Pzat!", "P'tang!", "Frink!", +#ifdef PRAISE_BOB + "Slack!", "Praise \"Bob\"!", "Or kill me.", +#endif /* PRAISE_BOB */ + /* randomness, from the Net and other places. Feel free to add (after + checking with the relevant authorities, of course). */ + "Grudnuk demand sustenance!", "Keep the Lasagna flying!", + "You are what you see.", + "Or is it?", "This statement is false.", + "Lies and slander, sire!", "Hee hee hee!", +#if defined(linux) || defined (__linux__) || defined (__linux) + "Hail Eris, Hack Linux!", +#elif defined(__APPLE__) + "This Fruit is not the True Fruit of Discord.", +#endif + "" +}; + +char default_fmt[] = "%{%A, %B %d%}, %Y YOLD"; +char *default_immediate_fmt= +#ifdef OLD_IMMEDIATE_FMT +"Today is %{%A, the %e day of %B%} in the YOLD %Y%N%nCelebrate %H" +#else +default_fmt +#endif +; + +#define DY(y) (y+1166) + +static inline char *ending(int i) { + return i/10==1?"th":(i%10==1?"st":(i%10==2?"nd":(i%10==3?"rd":"th"))); +} + +static inline int leapp(int i) { + return (!(DY(i)%4))&&((DY(i)%100)||(!(DY(i)%400))); +} + +/* select a random string */ +static inline char *sel(char **strings, int num) { + return(strings[random()%num]); +} + +void print(struct disc_time,char **); /* old */ +void format(char *buf, const char* fmt, struct disc_time dt); +/* read a fortune file */ +int load_fortunes(char *fn, char *delim, char** result); + +struct disc_time convert(int,int); +struct disc_time makeday(int,int,int); + +int +main (int argc, char *argv[]) { + time_t t; + struct tm *eris; + int bob,raw; + struct disc_time hastur; + char schwa[23*17], *fnord=0; + int pi; + char *progname, *p; + + progname = argv[0]; + if ((p = strrchr(progname, '/')) != NULL) + progname = p+1; + + srandom(time(NULL)); + /* do args here */ + for(pi=1; pi<argc; pi++) { + switch(argv[pi][0]) { + case '+': fnord=argv[pi]+1; break; + case '-': + switch(argv[pi][1]) { + case 'V': + printf(("%s (%s)\n"), progname, PACKAGE_STRING); + default: goto usage; + } + default: goto thud; + } + } + + thud: + if (argc-pi==3){ + int moe=atoi(argv[pi]), larry=atoi(argv[pi+1]), curly=atoi(argv[pi+2]); + hastur=makeday( +#ifdef US_FORMAT + moe,larry, +#else + larry,moe, +#endif + curly); + if (hastur.season == -1) { + printf("Invalid date -- out of range\n"); + return -1; + } + fnord=fnord?fnord:default_fmt; + } else if (argc!=pi) { + usage: + fprintf(stderr,("usage: %s [+format] [day month year]\n"), argv[0]); + exit(1); + } else { + t= time(NULL); + eris=localtime(&t); + bob=eris->tm_yday; /* days since Jan 1. */ + raw=eris->tm_year; /* years since 1980 */ + hastur=convert(bob,raw); + fnord=fnord?fnord:default_immediate_fmt; + } + format(schwa, fnord, hastur); + printf("%s\n", schwa); + + return 0; +} + +void format(char *buf, const char* fmt, struct disc_time dt) +{ + int tib_start=-1, tib_end=0; + int i, fmtlen=strlen(fmt); + char *bufptr=buf; + +/* fprintf(stderr, "format(%p, \"%s\", dt)\n", buf, fmt);*/ + + /* first, find extents of St. Tib's Day area, if defined */ + for(i=0; i<fmtlen; i++) { + if(fmt[i]=='%') { + switch(fmt[i+1]) { + case 'A': + case 'a': + case 'd': + case 'e': + if(tib_start>0) tib_end=i+1; + else tib_start=i; + break; + case '{': tib_start=i; break; + case '}': tib_end=i+1; break; + } + } + } + + /* now do the formatting */ + buf[0]=0; + + for(i=0; i<fmtlen; i++) { + if((i==tib_start) && (dt.day==-1)) { + /* handle St. Tib's Day */ + strcpy(bufptr, ("St. Tib's Day")); + bufptr += strlen(bufptr); + i=tib_end; + } else { + if(fmt[i]=='%') { + char *wibble=0, snarf[23]; + switch(fmt[++i]) { + case 'A': wibble=day_long[dt.yday%5]; break; + case 'a': wibble=day_short[dt.yday%5]; break; + case 'B': wibble=season_long[dt.season]; break; + case 'b': wibble=season_short[dt.season]; break; + case 'd': sprintf(snarf, "%d", dt.day+1); wibble=snarf; break; + case 'e': sprintf(snarf, "%d%s", dt.day+1, ending(dt.day+1)); + wibble=snarf; break; + case 'H': if(dt.day==4||dt.day==49) + wibble=holyday[dt.season][dt.day==49]; break; + case 'N': if(dt.day!=4&&dt.day!=49) goto eschaton; break; + case 'n': *(bufptr++)='\n'; break; + case 't': *(bufptr++)='\t'; break; + + case 'Y': sprintf(snarf, "%d", dt.year); wibble=snarf; break; + case '.': wibble=sel(excl, ARRAY_SIZE(excl)); + break; +#ifdef KILL_BOB + case 'X': sprintf(snarf, "%d", + xday_countdown(dt.yday, dt.year)); + wibble = snarf; break; +#endif /* KILL_BOB */ + } + if(wibble) { +/* fprintf(stderr, "wibble = (%s)\n", wibble);*/ + strcpy(bufptr, wibble); bufptr+=strlen(wibble); + } + } else { + *(bufptr++) = fmt[i]; + } + } + } + eschaton: + *(bufptr)=0; +} + +struct disc_time makeday(int imonth,int iday,int iyear) /*i for input */ +{ + struct disc_time funkychickens; + + int cal[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; + int dayspast=0; + + memset(&funkychickens,0,sizeof(funkychickens)); + /* basic range checks */ + if (imonth < 1 || imonth > 12 || iyear == 0) { + funkychickens.season = -1; + return funkychickens; + } + if (iday < 1 || iday > cal[imonth-1]) { + if (!(imonth == 2 && iday == 29 && iyear%4 == 0 && + (iyear%100 != 0 || iyear%400 == 0))) { + funkychickens.season = -1; + return funkychickens; + } + } + + imonth--; + /* note: gregorian year 0 doesn't exist so + * add one if user specifies a year less than 0 */ + funkychickens.year= iyear+1166 + ((0 > iyear)?1:0); + while(imonth>0) { dayspast+=cal[--imonth]; } + funkychickens.day=dayspast+iday-1; + funkychickens.season=0; + if((funkychickens.year%4)==2) { + if (funkychickens.day==59 && iday==29) funkychickens.day=-1; + } + funkychickens.yday=funkychickens.day; +/* note: EQUAL SIGN...hopefully that fixes it */ + while(funkychickens.day>=73) { + funkychickens.season++; + funkychickens.day-=73; + } + return funkychickens; +} + +struct disc_time convert(int nday, int nyear) +{ struct disc_time funkychickens; + + funkychickens.year = nyear+3066; + funkychickens.day=nday; + funkychickens.season=0; + if ((funkychickens.year%4)==2) + {if (funkychickens.day==59) + funkychickens.day=-1; + else if (funkychickens.day >59) + funkychickens.day-=1; + } + funkychickens.yday=funkychickens.day; + while (funkychickens.day>=73) + { funkychickens.season++; + funkychickens.day-=73; + } + return funkychickens; + + } + +#ifdef KILL_BOB + +/* Code for counting down to X-Day, X-Day being Cfn 40, 3164 + * + * After `X-Day' passed without incident, the CoSG declared that it had + * got the year upside down --- X-Day is actually in 8661 AD rather than + * 1998 AD. + * + * Thus, the True X-Day is Cfn 40, 9827. + * + */ + +int xday_countdown(int yday, int year) { + int r=(185-yday)+(((yday<59)&&(leapp(year)))?1:0); + while(year<9827) r+=(leapp(++year)?366:365); + while(year>9827) r-=(leapp(year--)?366:365); + return r; +} + +#endif diff --git a/port/file2c/.gitignore b/port/file2c/.gitignore new file mode 100644 index 00000000..aafb358f --- /dev/null +++ b/port/file2c/.gitignore @@ -0,0 +1 @@ +file2c diff --git a/port/file2c/Makefile b/port/file2c/Makefile new file mode 100644 index 00000000..09f6b5d0 --- /dev/null +++ b/port/file2c/Makefile @@ -0,0 +1,15 @@ +PREFIX = ~/.local +MANDIR = ${PREFIX}/share/man + +file2c: + +clean: + rm -f file2c + +install: file2c file2c.1 + install -d ${PREFIX}/bin ${MANDIR}/man1 + install file2c ${PREFIX}/bin + install -m 644 file2c.1 ${MANDIR}/man1 + +uninstall: + rm -f ${PREFIX}/bin/file2c ${MANDIR}/man1/file2c.1 diff --git a/port/file2c/file2c.1 b/port/file2c/file2c.1 new file mode 100644 index 00000000..fe1fe5e7 --- /dev/null +++ b/port/file2c/file2c.1 @@ -0,0 +1,75 @@ +.\"---------------------------------------------------------------------------- +.\" "THE BEER-WARE LICENSE" (Revision 42): +.\" <phk@FreeBSD.org> wrote this file. As long as you retain this notice, you +.\" can do whatever you want with this file. If we meet some day, and you think +.\" this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp +.\" --------------------------------------------------------------------------- +.\" +.\" $FreeBSD: releng/11.2/usr.bin/file2c/file2c.1 173197 2007-10-30 17:49:00Z ru $ +.\" +.Dd March 22, 2007 +.Dt FILE2C 1 +.Os +.Sh NAME +.Nm file2c +.Nd convert file to c-source +.Sh SYNOPSIS +.Nm +.Op Fl sx +.Op Fl n Ar count +.Op Ar prefix Op Ar suffix +.Sh DESCRIPTION +The +.Nm +utility reads a file from stdin and writes it to stdout, converting each +byte to its decimal or hexadecimal representation on the fly. +The byte values are separated by a comma. +This also means that the last byte value is not followed by a comma. +By default the byte values are printed in decimal, but when the +.Fl x +option is given, the values will be printed in hexadecimal. +When +.Fl s +option is given, each line is printed with a leading tab and each comma is +followed by a space except for the last one on the line. +.Pp +If more than 70 characters are printed on the same line, that line is +ended and the output continues on the next line. +With the +.Fl n +option this can be made to happen after the specified number of +byte values have been printed. +The length of the line will not be considered anymore. +To have all the byte values printed on the same line, give the +.Fl n +option a negative number. +.Pp +A prefix and suffix strings can be printed before and after the byte values +(resp.) +If a suffix is to be printed, a prefix must also be specified. +The first non-option word is the prefix, which may optionally be followed +by a word that is to be used as the suffix. +.Pp +This program is typically used to embed binary files into C source files. +The prefix is used to define an array type and the suffix is used to end +the C statement. +The +.Fl n , s +and +.Fl x +options are useful when the binary data represents a bitmap and the output +needs to remain readable and/or editable. +Fonts, for example, are a good example of this. +.Sh EXAMPLES +The command: +.Bd -literal -offset indent +date | file2c 'const char date[] = {' ',0};' +.Ed +.Pp +will produce: +.Bd -literal -offset indent +const char date[] = { +83,97,116,32,74,97,110,32,50,56,32,49,54,58,50,56,58,48,53, +32,80,83,84,32,49,57,57,53,10 +,0}; +.Ed diff --git a/port/file2c/file2c.c b/port/file2c/file2c.c new file mode 100644 index 00000000..cff7f602 --- /dev/null +++ b/port/file2c/file2c.c @@ -0,0 +1,92 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + */ + +#include <sys/cdefs.h> +//__FBSDID("$FreeBSD: releng/11.2/usr.bin/file2c/file2c.c 200462 2009-12-13 03:14:06Z delphij $"); + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static void +usage(void) +{ + + fprintf(stderr, "usage: %s [-sx] [-n count] [prefix [suffix]]\n", + "file2c"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int c, count, linepos, maxcount, pretty, radix; + + maxcount = 0; + pretty = 0; + radix = 10; + while ((c = getopt(argc, argv, "n:sx")) != -1) { + switch (c) { + case 'n': /* Max. number of bytes per line. */ + maxcount = strtol(optarg, NULL, 10); + break; + case 's': /* Be more style(9) comliant. */ + pretty = 1; + break; + case 'x': /* Print hexadecimal numbers. */ + radix = 16; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc > 0) + printf("%s\n", argv[0]); + count = linepos = 0; + while((c = getchar()) != EOF) { + if (count) { + putchar(','); + linepos++; + } + if ((maxcount == 0 && linepos > 70) || + (maxcount > 0 && count >= maxcount)) { + putchar('\n'); + count = linepos = 0; + } + if (pretty) { + if (count) { + putchar(' '); + linepos++; + } else { + putchar('\t'); + linepos += 8; + } + } + switch (radix) { + case 10: + linepos += printf("%d", c); + break; + case 16: + linepos += printf("0x%02x", c); + break; + default: + abort(); + } + count++; + } + putchar('\n'); + if (argc > 1) + printf("%s\n", argv[1]); + return (0); +} diff --git a/port/wcwidth/.gitignore b/port/wcwidth/.gitignore new file mode 100644 index 00000000..132e8098 --- /dev/null +++ b/port/wcwidth/.gitignore @@ -0,0 +1,3 @@ +*.o +libwcwidth.dylib +wcfix diff --git a/port/wcwidth/COPYRIGHT b/port/wcwidth/COPYRIGHT new file mode 100644 index 00000000..e6472371 --- /dev/null +++ b/port/wcwidth/COPYRIGHT @@ -0,0 +1,190 @@ +musl as a whole is licensed under the following standard MIT license: + +---------------------------------------------------------------------- +Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------- + +Authors/contributors include: + +A. Wilcox +Ada Worcester +Alex Dowad +Alex Suykov +Alexander Monakov +Andre McCurdy +Andrew Kelley +Anthony G. Basile +Aric Belsito +Arvid Picciani +Bartosz Brachaczek +Benjamin Peterson +Bobby Bingham +Boris Brezillon +Brent Cook +Chris Spiegel +Clément Vasseur +Daniel Micay +Daniel Sabogal +Daurnimator +David Carlier +David Edelsohn +Denys Vlasenko +Dmitry Ivanov +Dmitry V. Levin +Drew DeVault +Emil Renner Berthing +Fangrui Song +Felix Fietkau +Felix Janda +Gianluca Anzolin +Hauke Mehrtens +He X +Hiltjo Posthuma +Isaac Dunham +Jaydeep Patil +Jens Gustedt +Jeremy Huntwork +Jo-Philipp Wich +Joakim Sindholt +John Spencer +Julien Ramseier +Justin Cormack +Kaarle Ritvanen +Khem Raj +Kylie McClain +Leah Neukirchen +Luca Barbato +Luka Perkov +M Farkas-Dyck (Strake) +Mahesh Bodapati +Markus Wichmann +Masanori Ogino +Michael Clark +Michael Forney +Mikhail Kremnyov +Natanael Copa +Nicholas J. Kain +orc +Pascal Cuoq +Patrick Oppenlander +Petr Hosek +Petr Skocik +Pierre Carrier +Reini Urban +Rich Felker +Richard Pennington +Ryan Fairfax +Samuel Holland +Segev Finer +Shiz +sin +Solar Designer +Stefan Kristiansson +Stefan O'Rear +Szabolcs Nagy +Timo Teräs +Trutz Behn +Valentin Ochs +Will Dietz +William Haddon +William Pitcock + +Portions of this software are derived from third-party works licensed +under terms compatible with the above MIT license: + +The TRE regular expression implementation (src/regex/reg* and +src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed +under a 2-clause BSD license (license text in the source files). The +included version has been heavily modified by Rich Felker in 2012, in +the interests of size, simplicity, and namespace cleanliness. + +Much of the math library code (src/math/* and src/complex/*) is +Copyright © 1993,2004 Sun Microsystems or +Copyright © 2003-2011 David Schultz or +Copyright © 2003-2009 Steven G. Kargl or +Copyright © 2003-2009 Bruce D. Evans or +Copyright © 2008 Stephen L. Moshier or +Copyright © 2017-2018 Arm Limited +and labelled as such in comments in the individual source files. All +have been licensed under extremely permissive terms. + +The ARM memcpy code (src/string/arm/memcpy_el.S) is Copyright © 2008 +The Android Open Source Project and is licensed under a two-clause BSD +license. It was taken from Bionic libc, used on Android. + +The implementation of DES for crypt (src/crypt/crypt_des.c) is +Copyright © 1994 David Burren. It is licensed under a BSD license. + +The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was +originally written by Solar Designer and placed into the public +domain. The code also comes with a fallback permissive license for use +in jurisdictions that may not recognize the public domain. + +The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 +Valentin Ochs and is licensed under an MIT-style license. + +The x86_64 port was written by Nicholas J. Kain and is licensed under +the standard MIT terms. + +The mips and microblaze ports were originally written by Richard +Pennington for use in the ellcc project. The original code was adapted +by Rich Felker for build system and code conventions during upstream +integration. It is licensed under the standard MIT terms. + +The mips64 port was contributed by Imagination Technologies and is +licensed under the standard MIT terms. + +The powerpc port was also originally written by Richard Pennington, +and later supplemented and integrated by John Spencer. It is licensed +under the standard MIT terms. + +All other files which have no copyright comments are original works +produced specifically for use as part of this library, written either +by Rich Felker, the main author of the library, or by one or more +contibutors listed above. Details on authorship of individual files +can be found in the git version control history of the project. The +omission of copyright and license comments in each file is in the +interest of source tree size. + +In addition, permission is hereby granted for all public header files +(include/* and arch/*/bits/*) and crt files intended to be linked into +applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit +the copyright notice and permission notice otherwise required by the +license, and to use these files without any requirement of +attribution. These files include substantial contributions from: + +Bobby Bingham +John Spencer +Nicholas J. Kain +Rich Felker +Richard Pennington +Stefan Kristiansson +Szabolcs Nagy + +all of whom have explicitly granted such permission. + +This file previously contained text expressing a belief that most of +the files covered by the above exception were sufficiently trivial not +to be subject to copyright, resulting in confusion over whether it +negated the permissions granted in the license. In the spirit of +permissive licensing, and of not having licensing issues being an +obstacle to adoption, that text has been removed. diff --git a/port/wcwidth/Makefile b/port/wcwidth/Makefile new file mode 100644 index 00000000..50faa653 --- /dev/null +++ b/port/wcwidth/Makefile @@ -0,0 +1,27 @@ +PREFIX ?= ~/.local + +OBJS = wcwidth.o wcswidth.o + +all: libwcwidth.dylib wcfix + +libwcwidth.dylib: ${OBJS} + ${CC} -dynamiclib ${LDFLAGS} ${OBJS} -o $@ + +wcwidth.o: nonspacing.h wide.h + +.SUFFIXES: .in + +.in: + sed 's|%%PREFIX%%|${PREFIX}|g' $< > $@ + chmod a+x $@ + +clean: + rm -f libwcwidth.dylib wcfix ${OBJS} + +install: libwcwidth.dylib wcfix + install -d ${PREFIX}/lib ${PREFIX}/bin + install -m 644 libwcwidth.dylib ${PREFIX}/lib + install wcfix ${PREFIX}/bin + +uninstall: + rm -f ${PREFIX}/lib/libwcwidth.dylib ${PREFIX}/bin/wcfix diff --git a/port/wcwidth/nonspacing.h b/port/wcwidth/nonspacing.h new file mode 100644 index 00000000..5d05a3d1 --- /dev/null +++ b/port/wcwidth/nonspacing.h @@ -0,0 +1,89 @@ +16,16,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,16,16,32,16,16,16,33,34,35, +36,37,38,39,16,16,40,16,16,16,16,16,16,16,16,16,16,16,41,42,16,16,43,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,44,16,45,46,47,48,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,49,16,16,50, +51,16,52,53,54,16,16,16,16,16,16,55,16,16,56,16,57,58,59,60,61,62,63,64,65,66, +67,68,16,69,70,71,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,72,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,73,74,16,16,16,75,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,76,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,77,78,16,16,16,16,16,16,16,79,16,16,16,16,16,80,81,82,16,16,16,16,16,83, +84,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,248,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,254,255,255,255,255,191,182,0,0,0,0,0,0,0,63,0,255,23,0,0,0,0,0,248,255, +255,0,0,1,0,0,0,0,0,0,0,0,0,0,0,192,191,159,61,0,0,0,128,2,0,0,0,255,255,255, +7,0,0,0,0,0,0,0,0,0,0,192,255,1,0,0,0,0,0,0,248,15,32,0,0,192,251,239,62,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,255,255,255,255, +255,7,0,0,0,0,0,0,20,254,33,254,0,12,0,0,0,2,0,0,0,0,0,0,16,30,32,0,0,12,0,0, +64,6,0,0,0,0,0,0,16,134,57,2,0,0,0,35,0,6,0,0,0,0,0,0,16,190,33,0,0,12,0,0, +252,2,0,0,0,0,0,0,144,30,32,64,0,12,0,0,0,4,0,0,0,0,0,0,0,1,32,0,0,0,0,0,0,17, +0,0,0,0,0,0,192,193,61,96,0,12,0,0,0,2,0,0,0,0,0,0,144,64,48,0,0,12,0,0,0,3,0, +0,0,0,0,0,24,30,32,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,4,92,0,0,0,0,0,0,0,0,0,0,0, +242,7,128,127,0,0,0,0,0,0,0,0,0,0,0,0,242,31,0,63,0,0,0,0,0,0,0,0,0,3,0,0,160, +2,0,0,0,0,0,0,254,127,223,224,255,254,255,255,255,31,64,0,0,0,0,0,0,0,0,0,0,0, +0,224,253,102,0,0,0,195,1,0,30,0,100,32,0,32,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0, +0,0,28,0,0,0,12,0,0,0,12,0,0,0,0,0,0,0,176,63,64,254,15,32,0,0,0,0,0,120,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,1,4,14,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,9,0,0,0,0,0,0,64,127, +229,31,248,159,0,0,0,0,0,0,255,127,0,0,0,0,0,0,0,0,15,0,0,0,0,0,208,23,4,0,0, +0,0,248,15,0,3,0,0,0,60,59,0,0,0,0,0,0,64,163,3,0,0,0,0,0,0,240,207,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,253,33,16,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255, +251,0,248,0,0,0,124,0,0,0,0,0,0,223,255,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255, +255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0, +0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,128,247,63,0,0,0,192,0,0,0,0,0,0,0,0,0,0,3,0,68,8,0,0,96,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,255,255,3,128,0,0,0,0,192,63,0,0,128,255,3,0, +0,0,0,0,7,0,0,0,0,0,200,51,0,0,0,0,32,0,0,0,0,0,0,0,0,126,102,0,8,16,0,0,0,0, +0,16,0,0,0,0,0,0,157,193,2,0,0,0,0,48,64, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,0,0,0,0,0,64, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,255,255,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,110,240,0,0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0, +0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,192,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,255, +127,0,0,0,0,0,0,128,3,0,0,0,0,0,120,38,0,32,0,0,0,0,0,0,7,0,0,0,128,239,31,0, +0,0,0,0,0,0,8,0,3,0,0,0,0,0,192,127,0,30,0,0,0,0,0,0,0,0,0,0,0,128,211,64,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,248,7,0,0,3,0,0,0,0,0,0,24,1,0,0,0,192, +31,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,92,0,0,64,0,0,0,0,0, +0,0,0,0,0,248,133,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,60,176,1,0,0,48,0,0,0, +0,0,0,0,0,0,0,248,167,1,0,0,0,0,0,0,0,0,0,0,0,0,40,191,0,0,0,0,0,0,0,0,0,0,0, +0,224,188,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +128,255,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,12,1,0,0,0,254,7,0,0,0,0,248,121,128,0, +126,14,0,0,0,0,0,252,127,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,191,0,0,0, +0,0,0,0,0,0,0,252,255,255,252,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,180,191,0, +0,0,0,0,0,0,0,0,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,24, +0,0,0,0,0,0,0,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,0,0,0,0,0,127,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0, +0,128,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,15, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,248,255,231,15,0,0,0,60,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,255,255,255,255,255,255,127,248,255,255,255,255,255,31,32,0,16,0,0,248, +254,255,0,0,0,0,0,0,0,0,0, +0,127,255,255,249,219,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,240,7,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0, diff --git a/port/wcwidth/wcfix.in b/port/wcwidth/wcfix.in new file mode 100644 index 00000000..832c83d6 --- /dev/null +++ b/port/wcwidth/wcfix.in @@ -0,0 +1,7 @@ +#!/bin/sh +set -eu + +export DYLD_FORCE_FLAT_NAMESPACE=1 +export DYLD_INSERT_LIBRARIES=%%PREFIX%%/lib/libwcwidth.dylib + +exec "$@" diff --git a/port/wcwidth/wcswidth.c b/port/wcwidth/wcswidth.c new file mode 100644 index 00000000..5c8a5a4d --- /dev/null +++ b/port/wcwidth/wcswidth.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +int wcswidth(const wchar_t *wcs, size_t n) +{ + int l=0, k=0; + for (; n-- && *wcs && (k = wcwidth(*wcs)) >= 0; l+=k, wcs++); + return (k < 0) ? k : l; +} diff --git a/port/wcwidth/wcwidth.c b/port/wcwidth/wcwidth.c new file mode 100644 index 00000000..36256a53 --- /dev/null +++ b/port/wcwidth/wcwidth.c @@ -0,0 +1,29 @@ +#include <wchar.h> + +static const unsigned char table[] = { +#include "nonspacing.h" +}; + +static const unsigned char wtable[] = { +#include "wide.h" +}; + +int wcwidth(wchar_t wc) +{ + if (wc < 0xffU) + return (wc+1 & 0x7f) >= 0x21 ? 1 : wc ? -1 : 0; + if ((wc & 0xfffeffffU) < 0xfffe) { + if ((table[table[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1) + return 0; + if ((wtable[wtable[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1) + return 2; + return 1; + } + if ((wc & 0xfffe) == 0xfffe) + return -1; + if (wc-0x20000U < 0x20000) + return 2; + if (wc == 0xe0001 || wc-0xe0020U < 0x5f || wc-0xe0100U < 0xef) + return 0; + return 1; +} diff --git a/port/wcwidth/wide.h b/port/wcwidth/wide.h new file mode 100644 index 00000000..e403c9a5 --- /dev/null +++ b/port/wcwidth/wide.h @@ -0,0 +1,65 @@ +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,18,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,19,16,20,21,22,16,16,16,23,16,16,24,25,26,27,28,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,29, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,30,16,16,16,16,31,16,16,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,16,16,16,33, +34,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,35,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,36,17,17,37,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,38,39,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,40,41,42,43,44,45,46,47,16,48,49,16,16,16,16, +16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,6,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,48,0,0,0,0,0,0,255,15,0,0,0,0,128,0,0,8, +0,2,12,0,96,48,64,16,0,0,4,44,36,32,12,0,0,0,1,0,0,0,80,184,0,0,0,0,0,0,0,224, +0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,251,255,255,255,255,255,255,255, +255,255,255,15,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,63,0,0,0,255,15,255,255,255,255, +255,255,255,127,254,255,255,255,255,255,255,255,255,255,127,254,255,255,255, +255,255,255,255,255,255,255,255,255,224,255,255,255,255,255,254,255,255,255, +255,255,255,255,255,255,255,127,255,255,255,255,255,7,255,255,255,255,15,0, +255,255,255,255,255,127,255,255,255,255,255,0,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0, +0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,31,255,255,255,255,255,255,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255, +255,255,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,15,0,0,0,0,0,0,0,0,0,0,0,0,0,255,3,0,0,255,255,255,255,247,255,127,15,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,255,255,255,255,255,255,255, +255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,7,0,255,255,255,127,0,0,0,0,0, +0,7,0,240,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255, +15,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,64,254,7,0,0,0,0,0,0,0,0,0,0,0,0,7,0,255,255,255, +255,255,15,255,1,3,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255, +1,224,191,255,255,255,255,255,255,255,255,223,255,255,15,0,255,255,255,255, +255,135,15,0,255,255,17,255,255,255,255,255,255,255,255,127,253,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +159,255,255,255,255,255,255,255,63,0,120,255,255,255,0,0,4,0,0,96,0,16,0,0,0, +0,0,0,0,0,0,0,248,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255, +255,255,255,255,255,255,63,16,39,0,0,24,240,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,15,0, +0,0,224,255,255,255,255,255,255,255,255,255,255,255,255,123,252,255,255,255, +255,231,199,255,255,255,231,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,15,7,7,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0, diff --git a/prune.sh b/prune.sh new file mode 100644 index 00000000..fabe865f --- /dev/null +++ b/prune.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -eu + +find -L ~/.config ~/.local -type l -lname "${PWD}/*" | while read -r link; do + echo "$link" + rm "$link" +done diff --git a/txt/.notemap b/txt/.notemap new file mode 100644 index 00000000..acfe44ab --- /dev/null +++ b/txt/.notemap @@ -0,0 +1,3 @@ +5347bbff-b124-432a-a67e-5d90fa34d0d4 books.txt +9efa92ef-a8c0-4fa6-8a2a-13315992162b music.txt +2d3c58f2-a193-4ff5-b79f-fd430f80eb64 shows.txt diff --git a/txt/books.txt b/txt/books.txt new file mode 100644 index 00000000..44b3ad66 --- /dev/null +++ b/txt/books.txt @@ -0,0 +1,129 @@ +[ 2021 ] + + 12. ★★☆ Margaret Atwood, The Year of the Flood + 11. ★★☆ Charlie Jane Anders, All the Birds in the Sky + 10. ★☆☆ Margaret Atwood, The Penelopiad + 9. ★★★ Alix E. Harrow, The Once and Future Witches + 8. ★★☆ Mary Robinette Kowal, The Relentless Moon + 7. ★☆☆ Connie Willis, Bellwether + 6. ★★☆ Carmen Maria Machado, Her Body and Other Parties + 5. ★★☆ N. K. Jemisin, The City We Became + 4. ★★★ Martha Wells, Network Effect + 3. ★★☆ Nnedi Okorafor, Binti + 2. ★★☆ Kai Cheng Thom, I Hope We Choose Love + 1. ★★☆ Tade Thompson, The Rosewater Redemption + +[ 2020 ] + + 19. ★★★ N. K. Jemisin, The Awakened Kingdom + 18. ★★☆ N. K. Jemisin, The Kingdom of Gods + 17. ★★☆ N. K. Jemisin, The Broken Kingdoms + 16. ★★☆ Ann Leckie, Ancillary Justice + 15. ★★☆ Madeline Miller, The Song of Achilles + 14. ★★☆ N. K. Jemisin, The Hundred Thousand Kingdoms + 13. ★★☆ Annalee Newitz, Autonomous + 12. ★☆☆ Tade Thompson, The Rosewater Insurrection + 11. ★★☆ Alix E. Harrow, The Ten Thousand Doors of January + 10. ★★☆ N. K. Jemisin, The Stone Sky + 9. ★★★ Kai Cheng Thom, Fierce Femmes and Notorious Liars + 8. ★☆☆ Amal El-Mohtar & Max Gladstone, This Is How You Lose the Time War + 7. ★★★ N. K. Jemisin, The Obelisk Gate + 6. ★★★ Becky Chambers, To Be Taught, If Fortunate + 5. ★★★ Annalee Newitz, The Future of Another Timeline + 4. ★★☆ Rivers Solomon, The Deep + 3. ★★☆ ed. Hope Nicholson, Love Beyond Body, Space & Time + 2. ★★☆ Tade Thompson, Rosewater + 1. ★★★ Meg Elison, The Book of Flora + +[ 2019 ] + + 42. ★★★ Meg Elison, The Book of Etta + 41. ★★☆ Martha Wells, Exit Strategy + 40. ★★☆ Martha Wells, Artificial Condition + 39. ★★☆ N. K. Jemisin, The Fifth Season + 38. ★★☆ Martha Wells, Rogue Protocol + 37. ★★☆ Yoon Ha Lee, Ninefox Gambit + 36. ★★★ Rebecca Makkai, The Great Believers + 35. ★★☆ Meg Elison, The Book of the Unnamed Midwife + 34. ★★☆ Martha Wells, All Systems Red + 33. ★★☆ Nnedi Okorafor, The Book of Phoenix + 32. ★★☆ JY Yang, The Red Threads of Fortune + 31. ★★☆ JY Yang, The Black Tides of Heaven + 30. ★☆☆ Rebecca Roanhorse, Trail of Lightning + 29. ★☆☆ Jo Walton, The Just City + 28. ★★★ Arkady Martine, A Memory Called Empire + 27. ★★☆ Mary Robinette Kowal, The Fated Sky + 26. ★★★ Becky Chambers, Record of a Spaceborn Few + 25. ★★☆ Mary Robinette Kowal, The Calculating Stars + 24. ★★☆ Octavia E. Butler, Imago + 23. ★★☆ Octavia E. Butler, Kindred + 22. ★★☆ Octavia E. Butler, Adulthood Rites + 21. ★★★ Octavia E. Butler, Wild Seed + 20. ★★☆ Octavia E. Butler, Parable of the Talents + 19. ★★☆ Jeff VanderMeer, Acceptance + 18. ★★★ Becky Chambers, A Closed and Common Orbit + 17. ★★☆ Octavia E. Butler, Dawn + 16. ★★☆ Jeff VanderMeer, Authority + 15. ★★☆ Octavia E. Butler, Parable of the Sower + 14. ★★☆ C. A. Higgins, Lightless + 13. ★☆☆ Alfred Bester, The Demolished Man + 12. ★★★ Karin Tidbeck, Amatka + 11. ★☆☆ Catherynne M. Valente, Space Opera + 10. ★★☆ Rivers Solomon, An Unkindness of Ghosts + 9. ★★★ Becky Chambers, The Long Way to a Small Angry Planet + 8. ★★☆ Emily St. John Mandel, Station Eleven + 7. ★★★ Douglas Adams, Dirk Gently's Holistic Detective Agency + 6. ★★★ Octavia E. Butler, Fledgling + 5. ★★★ Jeff VanderMeer, Annihilation + 4. ★★☆ Alfred Bester, The Stars My Destination + 3. ★☆☆ Samuel R. Delany, The Einstein Intersection + 2. ★★★ Octavia E. Butler, Mind of My Mind + 1. ★☆☆ Robert A. Heinlein, Waldo & Magic, Inc. + +[ 2018 ] + + 25. ★★★ Douglas Adams, The Long Dark Tea-Time of the Soul + 24. ★★☆ Khaled Hosseini, A Thousand Splendid Suns + 23. ★★☆ Nathan Altice, I Am Error + 22. ★★★ Ruth Ozeki, A Tale for the Time Being + 21. ★☆☆ Arthur C. Clarke, Earthlight + 20. ★★★ Terry Pratchett & Neil Gaiman, Good Omens + 19. ★☆☆ Robert A. Heinlein, Starship Troopers + 18. ★★☆ Liu Cixin, The Three-Body Problem + 17. ★★☆ Robert A. Heinlein, Revolt in 2100 + 16. ★★☆ ed. Robert A. Heinlein, Tomorrow, the Stars + 15. ★★☆ Robert A. Heinlein, Assignment in Eternity + 14. ★☆☆ Robert A. Heinlein, The Rolling Stones + 13. ★★☆ Robert A. Heinlein, Starman Jones + 12. ★☆☆ Robert A. Heinlein, Space Cadet + 11. ★☆☆ Robert A. Heinlein, Farmer in the Sky + 10. ★★☆ Robert A. Heinlein, Between Planets + 9. ★★☆ Robert A. Heinlein, Red Planet + 8. ★☆☆ Robert A. Heinlein, Rocket Ship Galileo + 7. ★☆☆ Robert A. Heinlein, Sixth Column + 6. ★☆☆ Robert A. Heinlein, Beyond This Horizon + 5. ★★☆ Robert A. Heinlein, Orphans of the Sky + 4. ★★☆ Tracy Kidder, The Soul of a New Machine + 3. ★★★ James Alan Gardner, Ascending + 2. ★★★ Anne Frank, The Diary of a Young Girl + 1. ★★★ Harper Lee, Go Set a Watchman + +[ 2017 ] + + 1. ★★★ James Alan Gardner, Expendable + +[ ... ] + + • ★★★ Harper Lee, To Kill a Mockingbird + • ★★★ Douglas Adams, The Hitchhiker's Guide to the Galaxy + • ★★★ Douglas Adams, The Restaurant at the End of the Universe + • ★★☆ Douglas Adams, Life, the Universe and Everything + • ★★★ Douglas Adams, So Long, and Thanks for All the Fish + • ★★☆ Douglas Adams, Mostly Harmless + • ★☆☆ Eoin Colfer, And Another Thing... + • ★★★ Margaret Atwood, The Handmaid's Tale + • ★★☆ Kazuo Ishiguro, Never Let Me Go + • ★★★ Philip Pullman, The Goldan Compass + • ★★★ Philip Pullman, The Subtle Knife + • ★★★ Philip Pullman, The Amber Spyglass + • ★★★ William Goldman, The Princess Bride diff --git a/txt/music.txt b/txt/music.txt new file mode 100644 index 00000000..cd3705c4 --- /dev/null +++ b/txt/music.txt @@ -0,0 +1,285 @@ +Feist — I Feel It All +<https://youtu.be/g-1Gb2rxzlk> + +The Blow — "Come On Petunia" +<https://youtu.be/MO1HSfzK1Ns> + +Black Country, New Road — Sunglasses +<https://youtu.be/UAZhzi9cpkc> + +Mitski, Xiu Xiu — Between the Breaths +<https://youtu.be/HnhTkFl5Imw> + +Black Dresses — CREEP U +<https://youtu.be/w9RSZmltcVI> + +BACKXWASH — DONT COME TO THE WOODS +<https://youtu.be/7ZcJsWyGbOw> + +William Bonney — See Ya Later +<https://youtu.be/-uCUlvUlr9s> + +Four Tet — Hands +<https://youtu.be/ojZoQbRt1tc> + +Buffy Sainte-Marie — Darling Don't Cry + +LINGUA IGNOTA — DO YOU DOUBT ME TRAITOR +<https://youtu.be/M1ZweG__q-w> + +Lizzo — Truth Hurts +<https://youtu.be/P00HMxdsVZI> + +Hobo Johnson and The Lovemakers — Tiny Desk Concert +<https://youtu.be/A8a2EosJIbM> + +Poppy — Concrete +<https://youtu.be/WwoGhpYdebQ> + +Kim Petras — Do Me +<https://youtu.be/LShK0Yhd964> + +Kim Petras — Heart to Break +<https://youtu.be/5CPeHQHAQyo> + +Anomie — Avorter n'est pas tuer +<https://youtu.be/W1iGXRDeZf4> + +Zhaoze — Birds Contending +<https://zhaoze.bandcamp.com/album/birds-contending> + +Colin Stetson — Reborn +<https://youtu.be/MVnSFj6XQZY> + +Holly Herndon — Frontier +<https://youtu.be/rvNqNgHAEys> + +Bleachers — Tiny Desk Concert +<https://youtu.be/QCtkkX2f18M> + +Girlpool — Tiny Desk Concert +<https://youtu.be/VNM8Tg9pvDU> + +Daughters — You Won't Get What You Want +<https://daughters.bandcamp.com/album/you-wont-get-what-you-want> + +La Dispute — FULTON STREET I +<https://ladispute.bandcamp.com/track/fulton-street-i> + +KASHIWA Daisuke — Stella +<https://youtu.be/ei7cdynwRMA> + +Jeff Wayne — The Eve of the War +<https://youtu.be/6YwFvmnbj3E> + +Julia Holter — I Shall Love 2 +<https://youtu.be/k5uwPaCvbhA> + +Chromatics — Running Up That Hill +<https://youtu.be/Mgv88ZLi6LY> + +Low — Double Negative +<https://lowtheband.bandcamp.com/album/double-negative> + +Rival Consoles — Helios +<https://youtu.be/T8n-XC_2a-k> + +Tiffany — I Think We're Alone Now +<https://youtu.be/w6Q3mHyzn78> + +Pulp — Common People +<https://youtu.be/yuTMWgOduFM> + +XTC — Making Plans for Nigel +<https://youtu.be/gZjZBCZWxpg> + +New Order — Temptation +<https://youtu.be/xxDv_RTdLQo> + +Street Sects — In for a World of Hurt +<https://streetsects.bandcamp.com/track/in-for-a-world-of-hurt> + +New Order — Temptation +<https://youtu.be/xxDv_RTdLQo> + +Blondie — Heart of Glass +<https://youtu.be/fWPhhlKHM80> + +Suuns — Watch You, Watch Me +<https://suuns.bandcamp.com/track/watch-you-watch-me> + +Neckbeard Deathcamp — White Nationalism is for Basement Dwelling Losers +<https://neckbearddeathcamp.bandcamp.com/album/white-nationalism-is-for-basement-dwelling-losers> + +rook — shed blood +<https://rooksfeather.bandcamp.com/album/shed-blood> + +SOPHIE — OIL OF EVERY PEARL'S UN-INSIDES +<http://smarturl.it/SOPHIEALBUM> + +Rosetta — Utopioid +<https://theanaesthete.bandcamp.com/album/utopioid> + +Desire — Under Your Spell +<https://youtu.be/9K7rmxjk5RQ> + +Broken Social Scene — Anthems for a Seventeen-Year Old Girl +<https://youtu.be/DDqNL0js0iU> + +Shinsei Kamattechan — Yuugure no tori +<https://youtu.be/sUW4dDWiz-A> + +Petite Meller — The Flute +<https://youtu.be/BLwgeV7dXOI> + +King Gizzard and the Lizard Wizard — Flying Microtonal Banana +<https://youtu.be/D0BsgJxw208> + +rook&nomie — DAYDREAM +<https://youtu.be/00TdaTffFeY> + +Spiritualized — Ladies and Gentlemen We Are Floating in Space +<https://youtu.be/p47V3w4m1yg> + +Street Sects — End Position +<https://streetsects.bandcamp.com/album/end-position-2> + +Converge — The Dusk in Us +<https://convergecult.bandcamp.com/album/the-dusk-in-us> + +St. Vincent — MASSEDUCTION + +Godspeed You! Black Emperor — Undoing a Luciferian Towers +<https://godspeedyoublackemperor.bandcamp.com/track/undoing-a-luciferian-towers> + +Florist — Tiny Desk Concert +<https://youtu.be/WbyyxIZ02Zs> + +CHVRCHES — Tiny Desk Concert +<https://youtu.be/haunJARHPm4> + +Aurora — Tiny Desk Concert +<https://youtu.be/evBgLWQwAFA> + +Joy Division — Atmosphere +<https://youtu.be/1EdUjlawLJM> + +Underworld — Born Slippy +<https://youtu.be/iTFrCbQGyvM> + +Converge — I Can Tell You About Pain +<https://convergecult.bandcamp.com/album/i-can-tell-you-about-pain> + +CocoRosie — Lost Girls +<https://youtu.be/aRa-SlftLQo> + +FAUVE — Blizzard +<https://youtu.be/HMpmedi_pH4> + +FAUVE — Nuits Fauves +<https://youtu.be/cwaAppsy5yo> + +Sarin — House of Leaves +<https://sarin.bandcamp.com/track/house-of-leaves-split-w-guiltfeeder> + +Chromatics — Shadow +<https://youtu.be/IGUboLZx3Tk> + +Arcade Fire — Creature Comfort +<https://youtu.be/xzwicesJQ7E> + +Arcade Fire — Everything Now +<https://youtu.be/zC30BYR3CUk> + +Ed Schrader's Music Beat — Sermon + +Jessica Moss — Pools of Light + +Death Grips — Steroids (Crouching Tiger Hidden Gabber Megamix) +<https://youtu.be/JUTKTk60aGk> + +Saltland — A Common Truth + +Colin Stetson — All This I Do for Glory +<https://colinstetson.bandcamp.com/album/all-this-i-do-for-glory> + +Xiu Xiu — Forget +<https://youtu.be/ywRzfwA75pY> + +Arca — Arca + +Joni Void — Dissociation (Kyla's Song) +<http://cstrecords.com/cst125/> + +Do Make Say Think — Bound and Boundless +<http://cstrecords.com/cst120/> + +The Body Lovers / The Body Haters + +Unwound — Leaves Turn Inside You + +Xiu Xiu — Fabulous Muscles + +Xiu Xiu — A Promise + +Saltland — Light of Mercy +<http://cstrecords.com/saltland-releases-new-single-light-of-mercy/> + +Jessica Moss — Glaciers I (Pt I) +<http://cstrecords.com/cst124/> + +BNNY RBBT — Big World +<http://www.bnnyrbbt.fans> + +Woman is the Earth — Depths +<https://womanistheearth.bandcamp.com/album/depths> + +Saltland — I Only Wish This For You +<http://cstrecords.com/cst123/> + +Sun Kil Moon — Benji +<https://youtu.be/UtndQzCUEY4> + +Neil Cicierega — Mouth Moods +<http://www.neilcic.com/mouthmoods/> + +Those Who Walk Away — "First Degraded Rhythm" + "First Partially Recollected Conversation" +<http://cstrecords.com/cst122/> + +Dan Smith — Some Tunes +<https://thedancemyth.bandcamp.com/album/some-tunes> + +Avec le soleil sortant de sa bouche — Pas pire pop, I Love You So Much + +Arcade Fire — I Give You Power +<https://youtu.be/f6jma9VQEls> + +Xiu Xiu — Jenny GoGo +<https://youtu.be/WMT6MsA3ut8> + +Kero Kero Bonito — Fish Bowl +<https://youtu.be/FY-CjOJCjJE> + +Avec le soleil sortant de sa bouche — Alizé et Margaret D. Midi moins le quart. Sur la plage, un palmier ensanglanté +<http://cstrecords.com/cst121/> + +G.L.O.S.S. — Trans Day of Revenge +<https://girlslivingoutsidesocietysshit.bandcamp.com/releases> + +Joy Division — Love Will Tear Us Apart +<https://youtu.be/zuuObGsB0No> + +Xiu Xiu — Honeysuckle +<https://youtu.be/hYKGR8Er4vM> + +Xiu Xiu — Ceremony +<https://youtu.be/95ms8A2XJY0> + +Xiu Xiu — I Luv the Valley Oh +<https://youtu.be/dztURk0_DOg> + +Porter Robinson & Madeon — Shelter +<https://youtu.be/fzQ6gRAEoy0> + +Julien Baker — Tiny Desk Concert +<https://youtu.be/tADWPTqR_4A> diff --git a/txt/plan.7 b/txt/plan.7 new file mode 100644 index 00000000..f861d395 --- /dev/null +++ b/txt/plan.7 @@ -0,0 +1,26 @@ +.Dd March 10, 2021 +.Dt PLAN 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm plan +.Nd possible future projects +. +.Sh DESCRIPTION +. +.Ss feed reader for IMAP +.Bl -bullet -compact +.It +why use a separate reader +when you already have a mail client? +.It +feeds go into IMAP mailboxes +.It +feed entries turn into internet messages +.It +read/unread state like normal mail +.It +reply or forward feed entries as email +.It +all state in IMAP, easy to cron +.El diff --git a/txt/shows.txt b/txt/shows.txt new file mode 100644 index 00000000..62e6a071 --- /dev/null +++ b/txt/shows.txt @@ -0,0 +1,32 @@ +2020-01-23 (La Sala Rossa) Secondsight, BIG|BRAVE +2019-12-10 (Casa del Popolo) meth, Street Sects +2019-06-22 (Casa del Popolo) Persons Unknown, Palissade, Police des Moeurs, Blu Anxiety +2019-06-22 (Bar Le Ritz) Cloakroom, Pelican +2019-06-21 (La Vitrola) Leash Aggression, CPU Rave, Vitex, Pinocchio +2019-06-20 (Casa del Popolo) Beep Test, Liar//Lier, Wetware +2019-06-18 (Casa del Popolo) Nomadic War Machine; it foot, it ears; Wendy Eisenberg +2019-06-17 (La Sala Rossa) SaskPWR, Lubomyr Melnyk, Architek Percussion (Nicole Lizée) +2019-06-08 (La Sotterenea) Eliza Kavtion, Beep Test, Dri Hiev +2019-05-03 (Turbo Haüs) Issfenn +2019-04-21 (Bar Le Ritz PDB) Claud, Hatchie, Girlpool +2018-11-14 (Turbo Haus) Juss, Street Sects +2018-07-26 (Theatre Fairmount) Uniform, Deafheaven +2018-06-14 (Casa del Popolo) Markus Floats, Mark Lowe, Carodiaro, Eliza Kavtion +2018-04-26 (Bar Le Ritz PDB) Strega, Show of Bedlam, Rosetta +2018-04-13 (La Vitrola) Eliza Kavtion, Lungbutter, Wrekmeister Harmonies +2018-03-08 (Brasserie Beaubien) Clayborne, Nightwitches, Aseethe, Vile Creature +2018-02-08 (Bar Le Ritz PDB) KGD, Efrim Manuel Menuck +2017-09-30 (La Sala Rossa) Truster, Lungbutter, Big Brave +2017-09-19 (La Vitrola) Saccharine, Xiu Xiu +2017-06-01 (Casa del Popolo) Genevieve Heistek, Saltland +2017-05-27 (l’Escogriffe) Sough, Ikaray, C H R I S T +2017-05-14 (Bar Le Ritz PDB) Mutter’d, Aim Low, Wrekmeister Harmonies +2016-09-25 (La Sala Rossa) Cloud Rat, Wolves in the Throne Room +2016-09-21 (Theatre Paradoxe) Godspeed You! Black Emperor +2016-04-08 (La Sala Rossa) Caro Diaro, Wreckage With Stick, Ought +2015-08-13 (La Vitrola) Gutser, Chemical Way, Saul Hittner, In the Name of Havoc +2015-06-10 (La Sala Rossa) Ryan Sawyer, Colin Stetson & Sarah Neufeld +2015-06-09 (La Sala Rossa) The Mile End Ladies String Auxiliary, Christof Migone +2015-06-08 (Casa del Popolo) C H R I S T, Jessica Moss, Big Brave +2014-01-19 (Metropolis) Godspeed You! Black Emperor +2014-01-18 (l’Olympia) Elf Power, Neutral Milk Hotel diff --git a/txt/trouble-at-jinx-hotel.txt b/txt/trouble-at-jinx-hotel.txt new file mode 100644 index 00000000..4db28dd6 --- /dev/null +++ b/txt/trouble-at-jinx-hotel.txt @@ -0,0 +1,236 @@ +mollasses + +...à l'hôtel jinx... + +thierry amar; flüffy erskine; norsola johnson; kate lawrence; +jennifer ménard; mike moya; sam shalabi; scott lregrande chernoff... +chris brokaw; bruce cawdron; david michael curry; lisa gamble; efrim +menuck; michel meunier; js truchy; thalia zedek... à l'hôtel jinx. + +lucky 13: siren's song. la la la, amerika. Saint Christopher's +blues. sign of judgment. Lynn Canyon wedding song. coda. You Can't +Win. trouble in mind. las niñas. Miss Peach's Pawnshop. buffaloed +at Wounded Knee/the weeping winds. no love lost. songs from the +basement. + +trouble at jinx hotel. these cursed recordings began in late winter +2003 at the hotel2tango in montréal. it was a bitter sunday in +march. the world was on the brink of war in old mesopotamia, and +we were all of us sick with the influenza. headaches. fevers. wrecked +guts and water on the lungs. coughing like crazy. everyone with a +base case of the shakes. the hotel wass cold and its ghosts were +unhappy. electricity was being weird. lights flickered and all the +microphones crackled with live current. little blue sparks dancing +through the studio rooms. amplifiers were picking up strange radio +frequencies and the piano, which couldn't hold a tune to save its +own life, was behaving like a cantankerous old man. our cats and +dogs were nervous and excited, a-jitter the way animals get whenever +catastrope looms. we started with a 16-track tape machine, three +spools of 1/4" analog audio tape, and a lucky thirteen little songs +hanging in our heads. but when the 16-track busted and broke down +a few days later, we moved to 2" tape on a 24-track machine and +began anew with our fingers crossed. then the 24-track showed the +first symptoms of its own terrible illness. so we did what we could +with digital audio tape while our trusty friend and technician, +garfield lamb, nursed the equipment back to some semblance of health. +then we transfered the digital recordings to 2" tape and went to +work again... waiting to see what the gods would do next. they +toyed with us for a while before unleashing their full fury: the +24-track, seizing mid-song one evening, ate a reel of tape and sent +it spewing around the studio in little pieces, a scene as cruel as +confetti at a funeral. garfield somehow managed to splice the tape +together again. but our nerves were now in tatters too, scattered +all over the hotel floor. when we finally finished tracking this +music in the summer of 2003, the cats and dogs didn't look any less +worried than when we'd begun. of course, they sensed the disaster +we would soon see for ourselves: as we started mixing the very next +night, a faulty lamp at the studio short-circuited and the tape +machine caught on fire. a freak electrical accident. there was an +asterisk of light; then a dragon-tail of smoke coiling across the +room; then there was a darkness; and then the 24-track quietly died. +the animals, cowering in corners, whined, *why didn't you fucking +fools jump ship at the first sign of the storm*? that's when we +drifted away from the jinxed hotel... floating until we found safe +harbour in a studio we could borrow for a few days - one whose own +resident dogs, zoro and ciska, greeted us with idiot grins and the +contented looks of drunken sailors on calm seas. mixing was finished +in a kind of delirious distress at MixArt in notre-dame-de-grace, +montréal, autumn 2003. these lucky thirteen. dieu merci. slgc. *new +year's eve* 2003. + +siren's song. instrumental. + +la la la, amerika. amerika is crawling with cops tonight as we put +our fists through the windows of the world i know you're broken and +you're down at the mouth but you're pouting like a punished little +girl when darkness calls night falls oh mother where are the children +you've orphaned all over the Earth we're frightened and sickened +and poverty-stricken and waiting for you to send word when darkness +calls the sky falls we're tired mother and we're poor we're wretched +at your teeming shore we're homeless and tossed by the storm looking +for the light at your golden door when darkness calls night falls. + +flüffy: marching drum, percussion & choir; norsola: cello & voice; +kate: violin; jennifer: voice; moya: Crumar organ; dmc: viola & +choir; gamble: choir; efrim: choir; js: upright bass; thalia: voice; +scott: acoustic guitars & vocals; scott recorded the air-raid siren +and bombs on the nightly news when baghdad was attacked by the +united states and great britain in march 2003; words & music by +scott chernoff, as stolen from the inscription on the statue of +liberty: *give me your tired your poor your huddled masses yearning +to breathe the wretched refuse of your teeming shore send these the +homeless tempest-tossed to me i lift my lamp beside the golden +door*. + +Saint Christopher's blues. the locusts are in the jimson Shit Creek +is on the rise and you're out wrecking eternity this town looks +like a prison in September's crimson sky when i should be loving +you perfectly you're such a pretty little mess with your Saint +Christopher blues and that doomed little look in your eye the weight +of the world rests all upon your tiny shoes but June why are you +so terrified give me the locust and the flood i'm a Fool for Love +but i'm kneeling in Shit Creek tonight. + +thierry: upright & electric bass; flüffy: marching drum; kate: +violin; jennifer: voice; moya: piano & Crumar organ; bruce: drums; +gramble: scrap metal; scott: electric guitar & vocals. words & music +by scott chernoff. + +sign of judgment. yes sign of judgment yes sign of judgment yes +sign of judgment time ain't long i don't like old Satan none of his +tempting charms cheats you at your Jesus now and roll you in his +arms yes sign of judgment yes sign of judgment yes sign of judgment +time ain't long i don't like old Satan nothing he say or do tell +one lie to hurt us all and two to make it true yes sign of judgment +yes sign of judgment yes sign of judgment time ain't long. + +norsola: cello; jennifer: voice; efrim: electric guitar & harmonium; +js: upright bass; thalia: voice; scott: acoustic guitar & vocals. +words & music by kid prince moore (traditional). + +Lynn Canyon wedding song. well i never cared very much for the west +except for you there who i loved the best now please take me back +east i wrote your name in silver and green where Love and Hate Play +with Fate and History now please take me back east oh please take +me back east we came to the Canyon across the calm and the wild +from Mountain to Mountain and Island to aisle i came for Abel you +came for Cain and we danced on the tables between the cradle and +the grave and i never cared very much for the west but i will be +there in my Sunday best if you will wear your red wedding dress +then please take me back east oh please take me back east. + +flüffy: saw; jennifer: voice; moya: piano & Crumar organ; dmc: viola +& saw; js: upright bass & voice; scott: acoustic guitar & vocals. +words & music by scott chernoff. + +coda. instrumental. + +flüffy: metal heating duct; michel: banjo; dog & voice drone recorded +by julian evans on video tape at Lynn Canyon 2002; mixed by scott +& harris newman in montréal 2003. + +You Can't Win. you with your railroad bones and you with your +golden-spiked skin you with the wind in your clothes and your hat +like a house caving in oh man You Can't Win oh man You Can't Win +oh man You Can't Win when the Angel of Light is at the window with +the Sanctimonious Kid when you're down and out from Sacramento on +the New York City skids oh man You Can't Win oh man You Can't Win +oh man You Can't Win when the East River is groaning all below the +Williamsburg Bridge when airplanes are exploding in the skyline's +falling limbs oh man You Can't Win oh man You Can't Win oh man You +Can't Win when the Swans are at your throat with their busted burning +wings when they squeal and scream and moan at every last ship that +comes in oh man You Can't Win oh man You Can't Win oh man You Can't +Win. + +flüffy: chor; norsola: choir; jennifer: voice; moya: electric guitars +& Crumar organ; sam: oud; bruce: drums & marimba; dmc: choir; +gramble: scrap metal & choir; efrim: electric guitar & choir; js: +choir; thalia: choir; scott: electric & acoustic guitars, vocals. +words & music by scott chernoff, on the road from sacramento to +nyc. september 11 2001, while reading jack black's 1926 hobo memoir +*You Can't Win* and listening to the music of michael gira.. + +trouble in mind. trouble in mind i'm blue but i won't be blue all +the way you know that sun is gonna shine on me someday i'm gonna +lay my head on some lonesome railroad line and let that big +motherfucking engine nullify my mind trouble in mind i'm blue but +i won't be blue all the way you know that sun is gonna shine on me +someday. + +jennifer: voice; sam: electric guitars; chris: electric guitar; +scott: acoustic guitar & vocals. words & music traditional, as +learned from a 1960s recording by sam lightnin' hopkins. + +las niñas. instrumental. + +andrew mayrs recorded his baby daughter illimani crying while banging +on a piano in kitsilano, vancouver 2003; screaming children recorded +by scott at rue roy, montréal 2003. mixed by scott & harris newman +in montréal 2003. + +Miss Peach's Pawnshop. i bought this cross on the island of St. +Thomas because i wanted jesus on my chest i got this tattoo in New +Orleans and this one in the Orient for my honeymoon and i got this +scar somewhere far far away i was drunk as hell and i tried to walk +home and my hands were broken and i found this prayer in the back +of a magazine i know it's ripped but it came like this it came like +this i bought this song at Miss Peach's Pawn with this old overcoat +and a gold-plated wristwatch and i found this prayer in the back +of a magazine i know it's ripped but it came like this it came like +this it came like this it came like this. + +norsola: cello; kate: violin; jennifer: voice; moya: electric guitar, +piano & Crumar organ; dmc: viola; js: upright bass; scott: acoustic +guitar & vocals. words by hilary peach & scott chernoff; music by +scott chernoff. this is a bastard adaptation of peach's poem *Tattoo* +from her album, *Poems Only Dogs Can Hear*. + +buffaloed at Wounded Knee/the weeping winds. instrumental. (for +l.p. & a.i.m. and a pox on mean-spirited sons of bitches everywhere). + +flüffy: saws; dmc: saws. + +no love lost. good-bye old friend it's time to go this is the end +of all that we know when you worship in the house of burden i worship +in the house of blame as you learn to let your spirit burn i yearn +to be a flame to be a flame and there's no love no love lost as +there is the righteous there is the wrong and there is the night +which is betrayed by the dawn and there's no love no love lost as +there is the sinner there is the god and there is the singer profaned +by the song and there's no love no love lost there's no love no +love lost. + +thierry: electric bass; flüffy: bowed piano & saws; jennifer: voice; +moya: piano; michel: banjo; thalia: voice; scott: acoustic guitar, +bowed piano & vocals. words & music by scott chernoff. + +songs from the basement. there's a song for hatred and a song for +love a song for betrayal and a song for trust there's a song for +what's fated a song for dumb luck a song for what's sacred and one +for what's fucked there's a song which is prayed a song which is +cussed these songs from the basement which play unto us. + +norsola: cello; kate: violin; jennifer: voice; sam: electric guitars; +bruce: marimba & cymbals; dmc: viola; efrim: harmonium; garfield +lamb: knife; scott: acoustic guitar & vocals. words & music by scott +chernoff. mixed by howard bilerman at the hotel... ...after the +storm had all blown by... + +recorded by howard bilerman, efrim & scott at mom & pop sounds in +the hotel2tango, mile end, montréal 2003. except where noted, +everything was mixed by howard bilerman & molasses at MixArt in +notre-dame-de-grace, montréal 2003. mastered by harris newman in +little italy, montréal 2003 & 2004. all songs c&p SOCAN 2004, except +*sign of judgment* & *trouble in mind* which are in the public +domain. hilary peach's poem *Tattoo*, which inspired *Miss Peach's +Pawnshop*, is c&p hilary peach 2003. all music arranged by molasses, +design and hand-lettering by slgc ink. in tokyo. photography by dmc +& norsola. layout by slgc ink. with assistance by sean o'hara. all +love to those who helped: sohara & gary at alien8 recordings; howard +bilerman; cada del popolo; don & ian at constellation; eric craven; +julian evans; michael feuerstack; aidan girt; lea grahovac; taras +grescoe; garfield lamb; tim mahony; andrew mayrs, vally mendoza & +illimani mendoza-mayrs; boris & nicolas at MixArt; harris newman, +*who is never wrong*; hilary peach; sugar & smokey; and all the +cars and dogs... xoxo... diff --git a/txt/tweets.txt b/txt/tweets.txt new file mode 100644 index 00000000..240351ec --- /dev/null +++ b/txt/tweets.txt @@ -0,0 +1,32 @@ +triangle forum +https://twitter.com/g0m/status/963890156477603841 + +damn ya ass fat what's ya pronouns? +https://twitter.com/msbigmilk/status/1291218055939448833 + +afab +https://twitter.com/ErisGael/status/1257524779046907904 + +chaos emeralds +https://twitter.com/ErisGael/status/1190147736244490240 + +might as well salivate +https://twitter.com/prawn_meat/status/857444039833944064 + +notifications +https://twitter.com/WTMMP/status/1259694226595610629 + +all robot & computers +https://twitter.com/cryptcrier/status/1238339418160680960 + +the wet world +https://twitter.com/TragicAllyHere/status/1137187876507140098 + +a purchase? +https://twitter.com/pant_leg/status/1027693563604230144 + +influencer +https://twitter.com/witchpuppy/status/974690828257054721 + +-Wint-conversion +https://twitter.com/jckarter/status/967802665080995840 diff --git a/www/causal.agency/.gitignore b/www/causal.agency/.gitignore new file mode 100644 index 00000000..7935a3c1 --- /dev/null +++ b/www/causal.agency/.gitignore @@ -0,0 +1,3 @@ +*.html +scheme.css +scheme.png diff --git a/www/causal.agency/Makefile b/www/causal.agency/Makefile new file mode 100644 index 00000000..95f52120 --- /dev/null +++ b/www/causal.agency/Makefile @@ -0,0 +1,20 @@ +WEBROOT = /usr/local/www/causal.agency + +FILES = index.html style.css scheme.css scheme.png + +all: ${FILES} + +index.html: index.7 + mandoc -T html -O style=style.css index.7 > index.html + +scheme.css: + scheme -st > scheme.css + +scheme.png: + scheme -g > scheme.png + +install: ${FILES} + install -C -m 644 ${FILES} ${WEBROOT} + +clean: + rm -f index.html scheme.css scheme.png diff --git a/www/causal.agency/index.7 b/www/causal.agency/index.7 new file mode 100644 index 00000000..489b0b53 --- /dev/null +++ b/www/causal.agency/index.7 @@ -0,0 +1,70 @@ +.Dd May 19, 2021 +.Dt CAUSAL.AGENCY 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm june +.Nd computer enthusiast!! +. +.Sh SYNOPSIS +.Nm mail +.Mt june@causal.agency +.Nm +in +.Li #ascii.town +on tilde.chat +. +.Sh DESCRIPTION +I make mostly IRC software in C. +I like +.Fx +and +.Ox +but also the GPL. +I want to learn to talk to strangers +and seek out as much magic as I can. +. +.Pp +.Lk https://git.causal.agency code +\(em +.Lk https://text.causal.agency words +.Pq Lk gopher://text.causal.agency gopher +\(em +.Lk /list/ mailist +\(em +.Lk https://liberapay.com/june/ liberapay +. +.Pp +These are some things I've done: +.Bl -tag -width Ds +.It Lk https://git.causal.agency/pounce/about pounce +a multi-client-first IRC bouncer +.It Lk https://git.causal.agency/catgirl/about catgirl +a cosy IRC client +.It Lk https://git.causal.agency/litterbox/about litterbox +a full-text search IRC logger +.It Lk https://git.causal.agency/scooper/about scooper +a web interface for litterbox +.It Lk https://git.causal.agency/catsit/about catsit +a process supervisor +.It Lk https://git.causal.agency/imbox/about "imbox & git-fetch-email" +a tool to pull patches out of IMAP +.It Lk https://git.causal.agency/bubger/about bubger +a mailing list archive generator for IMAP +.It Lk https://git.causal.agency/notemap/about notemap +a tool to mirror text files to IMAP notes +.It Lk https://ascii.town/explore.html torus@ascii.town +a collaborative ASCII art project +.It Lk ssh://play@ascii.town play@ascii.town +a 2048 and snake game over +.Xr ssh 1 +.It Lk https://git.causal.agency/cards/about cards +a +.Pa CARDS.DLL +loader for SDL +.It Lk scheme.png scheme +an earthy terminal colour scheme +.El +. +.Sh SEE ALSO +.Lk /bin/ bin diff --git a/www/causal.agency/style.css b/www/causal.agency/style.css new file mode 100644 index 00000000..368d8da1 --- /dev/null +++ b/www/causal.agency/style.css @@ -0,0 +1,23 @@ +@import url("scheme.css"); + +table.head, table.foot { width: 100%; } +td.head-rtitle, td.foot-os { text-align: right; } +td.head-vol { text-align: center; } +div.Pp { margin: 1ex 0ex; } +div.Nd, div.Bf, div.Op { display: inline; } +span.Pa, span.Ad { font-style: italic; } +span.Ms { font-weight: bold; } +dl.Bl-diag > dt { font-weight: bold; } +code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn, +code.Cd { font-weight: bold; font-family: inherit; } + +html { font-family: monospace; line-height: 1.25em; } +body { max-width: 80ch; margin: 1em auto; padding: 0 1ch; } +table { border-collapse: collapse; } +table.Nm code.Nm { padding-right: 1ch; } +table.foot { margin-top: 1em; } + +html { background-color: var(--ansi16); color: var(--ansi17); } +a { color: var(--ansi4); } +a:visited { color: var(--ansi5); } +a.permalink { color: var(--ansi3); text-decoration: none; } diff --git a/www/git.causal.agency/.gitignore b/www/git.causal.agency/.gitignore new file mode 100644 index 00000000..a0ae074c --- /dev/null +++ b/www/git.causal.agency/.gitignore @@ -0,0 +1,6 @@ +about-filter +hilex +htagml +mtags +owner-filter +source-filter diff --git a/www/git.causal.agency/Makefile b/www/git.causal.agency/Makefile new file mode 100644 index 00000000..638c21e7 --- /dev/null +++ b/www/git.causal.agency/Makefile @@ -0,0 +1,20 @@ +ETC = /usr/local/etc +WWW = /usr/local/www/cgit +LIBEXEC = /usr/local/libexec + +BIN = ../../bin +BINS = about-filter source-filter owner-filter hilex htagml mtags + +all: ${BINS} + +install: cgitrc custom.css ${BINS} + install -m 644 cgitrc ${ETC} + install -m 644 custom.css ${WWW} + install ${BINS} ${LIBEXEC} + +hilex htagml mtags:: + ${MAKE} -C ${BIN} $@ + ln -f ${BIN}/$@ $@ + +clean: + rm -f ${BINS} diff --git a/www/git.causal.agency/about-filter.sh b/www/git.causal.agency/about-filter.sh new file mode 100644 index 00000000..2ff645e2 --- /dev/null +++ b/www/git.causal.agency/about-filter.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +options=fragment,man=%N.%S,includes=../tree/%I + +case "$1" in + (README.[1-9]) + exec /usr/bin/mandoc -T html -O $options + ;; + (*.[1-9]) + exec /usr/bin/mandoc -T html -O $options,toc + ;; + (*) + exec /usr/local/libexec/hilex -l text -f html -o pre + ;; +esac diff --git a/www/git.causal.agency/cgit/.gitignore b/www/git.causal.agency/cgit/.gitignore new file mode 100644 index 00000000..bca98fcf --- /dev/null +++ b/www/git.causal.agency/cgit/.gitignore @@ -0,0 +1,13 @@ +# Files I don't care to see in git-status/commit +/cgit +/git +cgit.conf +CGIT-CFLAGS +VERSION +cgitrc.5 +cgitrc.5.fo +cgitrc.5.html +cgitrc.5.pdf +cgitrc.5.xml +*.o +*.d diff --git a/www/git.causal.agency/cgit/.mailmap b/www/git.causal.agency/cgit/.mailmap new file mode 100644 index 00000000..03b54796 --- /dev/null +++ b/www/git.causal.agency/cgit/.mailmap @@ -0,0 +1,10 @@ +Florian Pritz <bluewind@xinu.at> <bluewind@xssn.at> +Harley Laue <losinggeneration@gmail.com> <losinggeneration@aim.com> +John Keeping <john@keeping.me.uk> <john@metanate.com> +Lars Hjemli <hjemli@gmail.com> <larsh@hal-2004.(none)> +Lars Hjemli <hjemli@gmail.com> <larsh@hatman.(none)> +Lars Hjemli <hjemli@gmail.com> <larsh@slackbox.hjemli.net> +Lars Hjemli <hjemli@gmail.com> <larsh@slaptop.hjemli.net> +Lukas Fleischer <lfleischer@lfos.de> <cgit@cryptocrack.de> +Lukas Fleischer <lfleischer@lfos.de> <info@cryptocrack.de> +Stefan Bühler <source@stbuehler.de> <lighttpd@stbuehler.de> diff --git a/www/git.causal.agency/cgit/AUTHORS b/www/git.causal.agency/cgit/AUTHORS new file mode 100644 index 00000000..031de338 --- /dev/null +++ b/www/git.causal.agency/cgit/AUTHORS @@ -0,0 +1,13 @@ +Maintainer: + Jason A. Donenfeld <Jason@zx2c4.com> + +Contributors: + Jason A. Donenfeld <Jason@zx2c4.com> + Lukas Fleischer <cgit@cryptocrack.de> + Johan Herland <johan@herland.net> + Lars Hjemli <hjemli@gmail.com> + Ferry Huberts <ferry.huberts@pelagic.nl> + John Keeping <john@keeping.me.uk> + +Previous Maintainer: + Lars Hjemli <hjemli@gmail.com> diff --git a/www/git.causal.agency/cgit/COPYING b/www/git.causal.agency/cgit/COPYING new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/www/git.causal.agency/cgit/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This 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. diff --git a/www/git.causal.agency/cgit/Makefile b/www/git.causal.agency/cgit/Makefile new file mode 100644 index 00000000..bfaa4ee6 --- /dev/null +++ b/www/git.causal.agency/cgit/Makefile @@ -0,0 +1,170 @@ +all:: + +CGIT_VERSION = causal agency +CGIT_SCRIPT_NAME = cgit.cgi +CGIT_SCRIPT_PATH = /var/www/htdocs/cgit +CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH) +CGIT_CONFIG = /etc/cgitrc +CACHE_ROOT = /var/cache/cgit +prefix = /usr/local +libdir = $(prefix)/lib +filterdir = $(libdir)/cgit/filters +docdir = $(prefix)/share/doc/cgit +htmldir = $(docdir) +pdfdir = $(docdir) +mandir = $(prefix)/share/man +SHA1_HEADER = <openssl/sha.h> +GIT_VER = 2.32.0 +GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.gz +INSTALL = install +COPYTREE = cp -r +MAN5_TXT = $(wildcard *.5.txt) +MAN_TXT = $(MAN5_TXT) +DOC_MAN5 = $(patsubst %.txt,%,$(MAN5_TXT)) +DOC_HTML = $(patsubst %.txt,%.html,$(MAN_TXT)) +DOC_PDF = $(patsubst %.txt,%.pdf,$(MAN_TXT)) + +ASCIIDOC = asciidoc +ASCIIDOC_EXTRA = +ASCIIDOC_HTML = xhtml11 +ASCIIDOC_COMMON = $(ASCIIDOC) $(ASCIIDOC_EXTRA) +TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML) + +# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.) +# do not support the 'size specifiers' introduced by C99, namely ll, hh, +# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t). +# some C compilers supported these specifiers prior to C99 as an extension. +# +# Define HAVE_LINUX_SENDFILE to use sendfile() + +#-include config.mak + +-include git/config.mak.uname +# +# Let the user override the above settings. +# +-include cgit.conf + +export CGIT_VERSION CGIT_SCRIPT_NAME CGIT_SCRIPT_PATH CGIT_DATA_PATH CGIT_CONFIG CACHE_ROOT + +# +# Define a way to invoke make in subdirs quietly, shamelessly ripped +# from git.git +# +QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir +QUIET_SUBDIR1 = + +ifneq ($(findstring w,$(MAKEFLAGS)),w) +PRINT_DIR = --no-print-directory +else # "make -w" +NO_SUBDIR = : +endif + +ifndef V + QUIET_SUBDIR0 = +@subdir= + QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ + $(MAKE) $(PRINT_DIR) -C $$subdir + QUIET_TAGS = @echo ' ' TAGS $@; + export V +endif + +.SUFFIXES: + +all:: cgit + +cgit: + $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk ../cgit $(EXTRA_GIT_TARGETS) NO_CURL=1 + +sparse: + $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk NO_CURL=1 cgit-sparse + +test: + @$(MAKE) --no-print-directory cgit EXTRA_GIT_TARGETS=all + $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all + +install: all + $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_SCRIPT_PATH) + $(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) + $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH) + $(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css + $(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png + $(INSTALL) -m 0644 favicon.ico $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico + $(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt + $(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir) + $(COPYTREE) filters/* $(DESTDIR)$(filterdir) + +install-doc: install-man install-html install-pdf + +install-man: doc-man + $(INSTALL) -m 0755 -d $(DESTDIR)$(mandir)/man5 + $(INSTALL) -m 0644 $(DOC_MAN5) $(DESTDIR)$(mandir)/man5 + +install-html: doc-html + $(INSTALL) -m 0755 -d $(DESTDIR)$(htmldir) + $(INSTALL) -m 0644 $(DOC_HTML) $(DESTDIR)$(htmldir) + +install-pdf: doc-pdf + $(INSTALL) -m 0755 -d $(DESTDIR)$(pdfdir) + $(INSTALL) -m 0644 $(DOC_PDF) $(DESTDIR)$(pdfdir) + +uninstall: + rm -f $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) + rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css + rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png + rm -f $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico + +uninstall-doc: uninstall-man uninstall-html uninstall-pdf + +uninstall-man: + @for i in $(DOC_MAN5); do \ + rm -fv $(DESTDIR)$(mandir)/man5/$$i; \ + done + +uninstall-html: + @for i in $(DOC_HTML); do \ + rm -fv $(DESTDIR)$(htmldir)/$$i; \ + done + +uninstall-pdf: + @for i in $(DOC_PDF); do \ + rm -fv $(DESTDIR)$(pdfdir)/$$i; \ + done + +doc: doc-man doc-html doc-pdf +doc-man: doc-man5 +doc-man5: $(DOC_MAN5) +doc-html: $(DOC_HTML) +doc-pdf: $(DOC_PDF) + +%.5 : %.5.txt + a2x -f manpage $< + +$(DOC_HTML): %.html : %.txt + $(TXT_TO_HTML) -o $@+ $< && \ + mv $@+ $@ + +$(DOC_PDF): %.pdf : %.txt + a2x -f pdf cgitrc.5.txt + +clean: clean-doc + $(RM) cgit VERSION CGIT-CFLAGS *.o tags + $(RM) -r .deps + +cleanall: clean + $(MAKE) -C git clean + +clean-doc: + $(RM) cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo + +get-git: + curl -L $(GIT_URL) | tar -xzf - && rm -rf git && mv git-$(GIT_VER) git + +tags: + $(QUIET_TAGS)find . -name '*.[ch]' | xargs ctags + +.PHONY: all cgit git get-git +.PHONY: clean clean-doc cleanall +.PHONY: doc doc-html doc-man doc-pdf +.PHONY: install install-doc install-html install-man install-pdf +.PHONY: tags test +.PHONY: uninstall uninstall-doc uninstall-html uninstall-man uninstall-pdf diff --git a/www/git.causal.agency/cgit/README b/www/git.causal.agency/cgit/README new file mode 100644 index 00000000..371cf21f --- /dev/null +++ b/www/git.causal.agency/cgit/README @@ -0,0 +1,86 @@ +cgit - CGI for Git +================== + +This is an attempt to create a fast web interface for the Git SCM, using a +built-in cache to decrease server I/O pressure. + +Installation +------------ + +Building cgit involves building a proper version of Git. How to do this +depends on how you obtained the cgit sources: + +a) If you're working in a cloned cgit repository, you first need to +initialize and update the Git submodule: + + $ git submodule init # register the Git submodule in .git/config + $ $EDITOR .git/config # if you want to specify a different url for git + $ git submodule update # clone/fetch and checkout correct git version + +b) If you're building from a cgit tarball, you can download a proper git +version like this: + + $ make get-git + +When either a) or b) has been performed, you can build and install cgit like +this: + + $ make + $ sudo make install + +This will install `cgit.cgi` and `cgit.css` into `/var/www/htdocs/cgit`. You +can configure this location (and a few other things) by providing a `cgit.conf` +file (see the Makefile for details). + + +Dependencies +------------ + +* libzip +* libcrypto (OpenSSL) +* libssl (OpenSSL) + +Apache configuration +-------------------- + +A new `Directory` section must probably be added for cgit, possibly something +like this: + + <Directory "/var/www/htdocs/cgit/"> + AllowOverride None + Options +ExecCGI + Order allow,deny + Allow from all + </Directory> + + +Runtime configuration +--------------------- + +The file `/etc/cgitrc` is read by cgit before handling a request. In addition +to runtime parameters, this file may also contain a list of repositories +displayed by cgit (see `cgitrc.5.txt` for further details). + +The cache +--------- + +When cgit is invoked it looks for a cache file matching the request and +returns it to the client. If no such cache file exists (or if it has expired), +the content for the request is written into the proper cache file before the +file is returned. + +If the cache file has expired but cgit is unable to obtain a lock for it, the +stale cache file is returned to the client. This is done to favour page +throughput over page freshness. + +The generated content contains the complete response to the client, including +the HTTP headers `Modified` and `Expires`. + +Online presence +--------------- + +* The cgit homepage is hosted by cgit at <https://git.zx2c4.com/cgit/about/> + +* Patches, bug reports, discussions and support should go to the cgit + mailing list: <cgit@lists.zx2c4.com>. To sign up, visit + <https://lists.zx2c4.com/mailman/listinfo/cgit> diff --git a/www/git.causal.agency/cgit/cache.c b/www/git.causal.agency/cgit/cache.c new file mode 100644 index 00000000..578b73b0 --- /dev/null +++ b/www/git.causal.agency/cgit/cache.c @@ -0,0 +1,475 @@ +/* cache.c: cache management + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + * + * + * The cache is just a directory structure where each file is a cache slot, + * and each filename is based on the hash of some key (e.g. the cgit url). + * Each file contains the full key followed by the cached content for that + * key. + * + */ + +#include "cgit.h" +#include "cache.h" +#include "html.h" +#ifdef HAVE_LINUX_SENDFILE +#include <sys/sendfile.h> +#endif + +#define CACHE_BUFSIZE (1024 * 4) + +struct cache_slot { + const char *key; + size_t keylen; + int ttl; + cache_fill_fn fn; + int cache_fd; + int lock_fd; + int stdout_fd; + const char *cache_name; + const char *lock_name; + int match; + struct stat cache_st; + int bufsize; + char buf[CACHE_BUFSIZE]; +}; + +/* Open an existing cache slot and fill the cache buffer with + * (part of) the content of the cache file. Return 0 on success + * and errno otherwise. + */ +static int open_slot(struct cache_slot *slot) +{ + char *bufz; + ssize_t bufkeylen = -1; + + slot->cache_fd = open(slot->cache_name, O_RDONLY); + if (slot->cache_fd == -1) + return errno; + + if (fstat(slot->cache_fd, &slot->cache_st)) + return errno; + + slot->bufsize = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); + if (slot->bufsize < 0) + return errno; + + bufz = memchr(slot->buf, 0, slot->bufsize); + if (bufz) + bufkeylen = bufz - slot->buf; + + if (slot->key) + slot->match = bufkeylen == slot->keylen && + !memcmp(slot->key, slot->buf, bufkeylen + 1); + + return 0; +} + +/* Close the active cache slot */ +static int close_slot(struct cache_slot *slot) +{ + int err = 0; + if (slot->cache_fd > 0) { + if (close(slot->cache_fd)) + err = errno; + else + slot->cache_fd = -1; + } + return err; +} + +/* Print the content of the active cache slot (but skip the key). */ +static int print_slot(struct cache_slot *slot) +{ +#ifdef HAVE_LINUX_SENDFILE + off_t start_off; + int ret; + + start_off = slot->keylen + 1; + + do { + ret = sendfile(STDOUT_FILENO, slot->cache_fd, &start_off, + slot->cache_st.st_size - start_off); + if (ret < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + return errno; + } + return 0; + } while (1); +#else + ssize_t i, j; + + i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET); + if (i != slot->keylen + 1) + return errno; + + do { + i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); + if (i > 0) + j = xwrite(STDOUT_FILENO, slot->buf, i); + } while (i > 0 && j == i); + + if (i < 0 || j != i) + return errno; + else + return 0; +#endif +} + +/* Check if the slot has expired */ +static int is_expired(struct cache_slot *slot) +{ + if (slot->ttl < 0) + return 0; + else + return slot->cache_st.st_mtime + slot->ttl * 60 < time(NULL); +} + +/* Check if the slot has been modified since we opened it. + * NB: If stat() fails, we pretend the file is modified. + */ +static int is_modified(struct cache_slot *slot) +{ + struct stat st; + + if (stat(slot->cache_name, &st)) + return 1; + return (st.st_ino != slot->cache_st.st_ino || + st.st_mtime != slot->cache_st.st_mtime || + st.st_size != slot->cache_st.st_size); +} + +/* Close an open lockfile */ +static int close_lock(struct cache_slot *slot) +{ + int err = 0; + if (slot->lock_fd > 0) { + if (close(slot->lock_fd)) + err = errno; + else + slot->lock_fd = -1; + } + return err; +} + +/* Create a lockfile used to store the generated content for a cache + * slot, and write the slot key + \0 into it. + * Returns 0 on success and errno otherwise. + */ +static int lock_slot(struct cache_slot *slot) +{ + struct flock lock = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 0, + }; + + slot->lock_fd = open(slot->lock_name, O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR); + if (slot->lock_fd == -1) + return errno; + if (fcntl(slot->lock_fd, F_SETLK, &lock) < 0) { + int saved_errno = errno; + close(slot->lock_fd); + slot->lock_fd = -1; + return saved_errno; + } + if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0) + return errno; + return 0; +} + +/* Release the current lockfile. If `replace_old_slot` is set the + * lockfile replaces the old cache slot, otherwise the lockfile is + * just deleted. + */ +static int unlock_slot(struct cache_slot *slot, int replace_old_slot) +{ + int err; + + if (replace_old_slot) + err = rename(slot->lock_name, slot->cache_name); + else + err = unlink(slot->lock_name); + + /* Restore stdout and close the temporary FD. */ + if (slot->stdout_fd >= 0) { + dup2(slot->stdout_fd, STDOUT_FILENO); + close(slot->stdout_fd); + slot->stdout_fd = -1; + } + + if (err) + return errno; + + return 0; +} + +/* Generate the content for the current cache slot by redirecting + * stdout to the lock-fd and invoking the callback function + */ +static int fill_slot(struct cache_slot *slot) +{ + /* Preserve stdout */ + slot->stdout_fd = dup(STDOUT_FILENO); + if (slot->stdout_fd == -1) + return errno; + + /* Redirect stdout to lockfile */ + if (dup2(slot->lock_fd, STDOUT_FILENO) == -1) + return errno; + + /* Generate cache content */ + slot->fn(); + + /* Make sure any buffered data is flushed to the file */ + if (fflush(stdout)) + return errno; + + /* update stat info */ + if (fstat(slot->lock_fd, &slot->cache_st)) + return errno; + + return 0; +} + +/* Crude implementation of 32-bit FNV-1 hash algorithm, + * see http://www.isthe.com/chongo/tech/comp/fnv/ for details + * about the magic numbers. + */ +#define FNV_OFFSET 0x811c9dc5 +#define FNV_PRIME 0x01000193 + +unsigned long hash_str(const char *str) +{ + unsigned long h = FNV_OFFSET; + unsigned char *s = (unsigned char *)str; + + if (!s) + return h; + + while (*s) { + h *= FNV_PRIME; + h ^= *s++; + } + return h; +} + +static int process_slot(struct cache_slot *slot) +{ + int err; + + /* + * Make sure any buffered data is flushed before we redirect, + * do sendfile(2) or write(2) + */ + if (fflush(stdout)) + return errno; + + err = open_slot(slot); + if (!err && slot->match) { + if (is_expired(slot)) { + if (!lock_slot(slot)) { + /* If the cachefile has been replaced between + * `open_slot` and `lock_slot`, we'll just + * serve the stale content from the original + * cachefile. This way we avoid pruning the + * newly generated slot. The same code-path + * is chosen if fill_slot() fails for some + * reason. + * + * TODO? check if the new slot contains the + * same key as the old one, since we would + * prefer to serve the newest content. + * This will require us to open yet another + * file-descriptor and read and compare the + * key from the new file, so for now we're + * lazy and just ignore the new file. + */ + if (is_modified(slot) || fill_slot(slot)) { + unlock_slot(slot, 0); + close_lock(slot); + } else { + close_slot(slot); + unlock_slot(slot, 1); + slot->cache_fd = slot->lock_fd; + } + } + } + if ((err = print_slot(slot)) != 0) { + cache_log("[cgit] error printing cache %s: %s (%d)\n", + slot->cache_name, + strerror(err), + err); + } + close_slot(slot); + return err; + } + + /* If the cache slot does not exist (or its key doesn't match the + * current key), lets try to create a new cache slot for this + * request. If this fails (for whatever reason), lets just generate + * the content without caching it and fool the caller to believe + * everything worked out (but print a warning on stdout). + */ + + close_slot(slot); + if ((err = lock_slot(slot)) != 0) { + cache_log("[cgit] Unable to lock slot %s: %s (%d)\n", + slot->lock_name, strerror(err), err); + slot->fn(); + return 0; + } + + if ((err = fill_slot(slot)) != 0) { + cache_log("[cgit] Unable to fill slot %s: %s (%d)\n", + slot->lock_name, strerror(err), err); + unlock_slot(slot, 0); + close_lock(slot); + slot->fn(); + return 0; + } + // We've got a valid cache slot in the lock file, which + // is about to replace the old cache slot. But if we + // release the lockfile and then try to open the new cache + // slot, we might get a race condition with a concurrent + // writer for the same cache slot (with a different key). + // Lets avoid such a race by just printing the content of + // the lock file. + slot->cache_fd = slot->lock_fd; + unlock_slot(slot, 1); + if ((err = print_slot(slot)) != 0) { + cache_log("[cgit] error printing cache %s: %s (%d)\n", + slot->cache_name, + strerror(err), + err); + } + close_slot(slot); + return err; +} + +/* Print cached content to stdout, generate the content if necessary. */ +int cache_process(int size, const char *path, const char *key, int ttl, + cache_fill_fn fn) +{ + unsigned long hash; + int i; + struct strbuf filename = STRBUF_INIT; + struct strbuf lockname = STRBUF_INIT; + struct cache_slot slot; + int result; + + /* If the cache is disabled, just generate the content */ + if (size <= 0 || ttl == 0) { + fn(); + return 0; + } + + /* Verify input, calculate filenames */ + if (!path) { + cache_log("[cgit] Cache path not specified, caching is disabled\n"); + fn(); + return 0; + } + if (!key) + key = ""; + hash = hash_str(key) % size; + strbuf_addstr(&filename, path); + strbuf_ensure_end(&filename, '/'); + for (i = 0; i < 8; i++) { + strbuf_addf(&filename, "%x", (unsigned char)(hash & 0xf)); + hash >>= 4; + } + strbuf_addbuf(&lockname, &filename); + strbuf_addstr(&lockname, ".lock"); + slot.fn = fn; + slot.ttl = ttl; + slot.stdout_fd = -1; + slot.cache_name = filename.buf; + slot.lock_name = lockname.buf; + slot.key = key; + slot.keylen = strlen(key); + result = process_slot(&slot); + + strbuf_release(&filename); + strbuf_release(&lockname); + return result; +} + +/* Return a strftime formatted date/time + * NB: the result from this function is to shared memory + */ +static char *sprintftime(const char *format, time_t time) +{ + static char buf[64]; + struct tm tm; + + if (!time) + return NULL; + gmtime_r(&time, &tm); + strftime(buf, sizeof(buf)-1, format, &tm); + return buf; +} + +int cache_ls(const char *path) +{ + DIR *dir; + struct dirent *ent; + int err = 0; + struct cache_slot slot = { NULL }; + struct strbuf fullname = STRBUF_INIT; + size_t prefixlen; + + if (!path) { + cache_log("[cgit] cache path not specified\n"); + return -1; + } + dir = opendir(path); + if (!dir) { + err = errno; + cache_log("[cgit] unable to open path %s: %s (%d)\n", + path, strerror(err), err); + return err; + } + strbuf_addstr(&fullname, path); + strbuf_ensure_end(&fullname, '/'); + prefixlen = fullname.len; + while ((ent = readdir(dir)) != NULL) { + if (strlen(ent->d_name) != 8) + continue; + strbuf_setlen(&fullname, prefixlen); + strbuf_addstr(&fullname, ent->d_name); + slot.cache_name = fullname.buf; + if ((err = open_slot(&slot)) != 0) { + cache_log("[cgit] unable to open path %s: %s (%d)\n", + fullname.buf, strerror(err), err); + continue; + } + htmlf("%s %s %10"PRIuMAX" %s\n", + fullname.buf, + sprintftime("%Y-%m-%d %H:%M:%S", + slot.cache_st.st_mtime), + (uintmax_t)slot.cache_st.st_size, + slot.buf); + close_slot(&slot); + } + closedir(dir); + strbuf_release(&fullname); + return 0; +} + +/* Print a message to stdout */ +void cache_log(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + diff --git a/www/git.causal.agency/cgit/cache.h b/www/git.causal.agency/cgit/cache.h new file mode 100644 index 00000000..470da4fc --- /dev/null +++ b/www/git.causal.agency/cgit/cache.h @@ -0,0 +1,37 @@ +/* + * Since git has it's own cache.h which we include, + * lets test on CGIT_CACHE_H to avoid confusion + */ + +#ifndef CGIT_CACHE_H +#define CGIT_CACHE_H + +typedef void (*cache_fill_fn)(void); + + +/* Print cached content to stdout, generate the content if necessary. + * + * Parameters + * size max number of cache files + * path directory used to store cache files + * key the key used to lookup cache files + * ttl max cache time in seconds for this key + * fn content generator function for this key + * + * Return value + * 0 indicates success, everything else is an error + */ +extern int cache_process(int size, const char *path, const char *key, int ttl, + cache_fill_fn fn); + + +/* List info about all cache entries on stdout */ +extern int cache_ls(const char *path); + +/* Print a message to stdout */ +__attribute__((format (printf,1,2))) +extern void cache_log(const char *format, ...); + +extern unsigned long hash_str(const char *str); + +#endif /* CGIT_CACHE_H */ diff --git a/www/git.causal.agency/cgit/cgit.c b/www/git.causal.agency/cgit/cgit.c new file mode 100644 index 00000000..a86970de --- /dev/null +++ b/www/git.causal.agency/cgit/cgit.c @@ -0,0 +1,1105 @@ +/* cgit.c: cgi for the git scm + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "cache.h" +#include "cmd.h" +#include "configfile.h" +#include "html.h" +#include "ui-shared.h" +#include "ui-stats.h" +#include "ui-blob.h" +#include "ui-summary.h" +#include "scan-tree.h" + +const char *cgit_version = CGIT_VERSION; + +__attribute__((constructor)) +static void constructor_environment() +{ + /* Do not look in /etc/ for gitconfig and gitattributes. */ + setenv("GIT_CONFIG_NOSYSTEM", "1", 1); + setenv("GIT_ATTR_NOSYSTEM", "1", 1); + unsetenv("HOME"); + unsetenv("XDG_CONFIG_HOME"); +} + +static void add_mimetype(const char *name, const char *value) +{ + struct string_list_item *item; + + item = string_list_insert(&ctx.cfg.mimetypes, name); + item->util = xstrdup(value); +} + +static void process_cached_repolist(const char *path); + +static void repo_config(struct cgit_repo *repo, const char *name, const char *value) +{ + const char *path; + struct string_list_item *item; + + if (!strcmp(name, "name")) + repo->name = xstrdup(value); + else if (!strcmp(name, "clone-url")) + repo->clone_url = xstrdup(value); + else if (!strcmp(name, "desc")) + repo->desc = xstrdup(value); + else if (!strcmp(name, "owner")) + repo->owner = xstrdup(value); + else if (!strcmp(name, "homepage")) + repo->homepage = xstrdup(value); + else if (!strcmp(name, "defbranch")) + repo->defbranch = xstrdup(value); + else if (!strcmp(name, "extra-head-content")) + repo->extra_head_content = xstrdup(value); + else if (!strcmp(name, "snapshots")) + repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); + else if (!strcmp(name, "enable-blame")) + repo->enable_blame = atoi(value); + else if (!strcmp(name, "enable-commit-graph")) + repo->enable_commit_graph = atoi(value); + else if (!strcmp(name, "enable-log-filecount")) + repo->enable_log_filecount = atoi(value); + else if (!strcmp(name, "enable-log-linecount")) + repo->enable_log_linecount = atoi(value); + else if (!strcmp(name, "enable-remote-branches")) + repo->enable_remote_branches = atoi(value); + else if (!strcmp(name, "enable-subject-links")) + repo->enable_subject_links = atoi(value); + else if (!strcmp(name, "enable-html-serving")) + repo->enable_html_serving = atoi(value); + else if (!strcmp(name, "branch-sort")) { + if (!strcmp(value, "age")) + repo->branch_sort = 1; + if (!strcmp(value, "name")) + repo->branch_sort = 0; + } else if (!strcmp(name, "commit-sort")) { + if (!strcmp(value, "date")) + repo->commit_sort = 1; + if (!strcmp(value, "topo")) + repo->commit_sort = 2; + } else if (!strcmp(name, "max-stats")) + repo->max_stats = cgit_find_stats_period(value, NULL); + else if (!strcmp(name, "module-link")) + repo->module_link= xstrdup(value); + else if (skip_prefix(name, "module-link.", &path)) { + item = string_list_append(&repo->submodules, xstrdup(path)); + item->util = xstrdup(value); + } else if (!strcmp(name, "section")) + repo->section = xstrdup(value); + else if (!strcmp(name, "snapshot-prefix")) + repo->snapshot_prefix = xstrdup(value); + else if (!strcmp(name, "readme") && value != NULL) { + if (repo->readme.items == ctx.cfg.readme.items) + memset(&repo->readme, 0, sizeof(repo->readme)); + string_list_append(&repo->readme, xstrdup(value)); + } else if (!strcmp(name, "logo") && value != NULL) + repo->logo = xstrdup(value); + else if (!strcmp(name, "logo-link") && value != NULL) + repo->logo_link = xstrdup(value); + else if (!strcmp(name, "hide")) + repo->hide = atoi(value); + else if (!strcmp(name, "ignore")) + repo->ignore = atoi(value); + else if (ctx.cfg.enable_filter_overrides) { + if (!strcmp(name, "about-filter")) + repo->about_filter = cgit_new_filter(value, ABOUT); + else if (!strcmp(name, "commit-filter")) + repo->commit_filter = cgit_new_filter(value, COMMIT); + else if (!strcmp(name, "source-filter")) + repo->source_filter = cgit_new_filter(value, SOURCE); + else if (!strcmp(name, "email-filter")) + repo->email_filter = cgit_new_filter(value, EMAIL); + else if (!strcmp(name, "owner-filter")) + repo->owner_filter = cgit_new_filter(value, OWNER); + } +} + +static void config_cb(const char *name, const char *value) +{ + const char *arg; + + if (!strcmp(name, "section")) + ctx.cfg.section = xstrdup(value); + else if (!strcmp(name, "repo.url")) + ctx.repo = cgit_add_repo(value); + else if (ctx.repo && !strcmp(name, "repo.path")) + ctx.repo->path = trim_end(value, '/'); + else if (ctx.repo && skip_prefix(name, "repo.", &arg)) + repo_config(ctx.repo, arg, value); + else if (!strcmp(name, "readme")) + string_list_append(&ctx.cfg.readme, xstrdup(value)); + else if (!strcmp(name, "root-title")) + ctx.cfg.root_title = xstrdup(value); + else if (!strcmp(name, "root-desc")) + ctx.cfg.root_desc = xstrdup(value); + else if (!strcmp(name, "root-readme")) + ctx.cfg.root_readme = xstrdup(value); + else if (!strcmp(name, "css")) + ctx.cfg.css = xstrdup(value); + else if (!strcmp(name, "favicon")) + ctx.cfg.favicon = xstrdup(value); + else if (!strcmp(name, "footer")) + ctx.cfg.footer = xstrdup(value); + else if (!strcmp(name, "head-include")) + ctx.cfg.head_include = xstrdup(value); + else if (!strcmp(name, "header")) + ctx.cfg.header = xstrdup(value); + else if (!strcmp(name, "logo")) + ctx.cfg.logo = xstrdup(value); + else if (!strcmp(name, "logo-link")) + ctx.cfg.logo_link = xstrdup(value); + else if (!strcmp(name, "module-link")) + ctx.cfg.module_link = xstrdup(value); + else if (!strcmp(name, "strict-export")) + ctx.cfg.strict_export = xstrdup(value); + else if (!strcmp(name, "virtual-root")) + ctx.cfg.virtual_root = ensure_end(value, '/'); + else if (!strcmp(name, "noplainemail")) + ctx.cfg.noplainemail = atoi(value); + else if (!strcmp(name, "noheader")) + ctx.cfg.noheader = atoi(value); + else if (!strcmp(name, "snapshots")) + ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); + else if (!strcmp(name, "enable-filter-overrides")) + ctx.cfg.enable_filter_overrides = atoi(value); + else if (!strcmp(name, "enable-follow-links")) + ctx.cfg.enable_follow_links = atoi(value); + else if (!strcmp(name, "enable-http-clone")) + ctx.cfg.enable_http_clone = atoi(value); + else if (!strcmp(name, "enable-index-links")) + ctx.cfg.enable_index_links = atoi(value); + else if (!strcmp(name, "enable-index-owner")) + ctx.cfg.enable_index_owner = atoi(value); + else if (!strcmp(name, "enable-blame")) + ctx.cfg.enable_blame = atoi(value); + else if (!strcmp(name, "enable-commit-graph")) + ctx.cfg.enable_commit_graph = atoi(value); + else if (!strcmp(name, "enable-log-filecount")) + ctx.cfg.enable_log_filecount = atoi(value); + else if (!strcmp(name, "enable-log-linecount")) + ctx.cfg.enable_log_linecount = atoi(value); + else if (!strcmp(name, "enable-remote-branches")) + ctx.cfg.enable_remote_branches = atoi(value); + else if (!strcmp(name, "enable-subject-links")) + ctx.cfg.enable_subject_links = atoi(value); + else if (!strcmp(name, "enable-html-serving")) + ctx.cfg.enable_html_serving = atoi(value); + else if (!strcmp(name, "enable-tree-linenumbers")) + ctx.cfg.enable_tree_linenumbers = atoi(value); + else if (!strcmp(name, "enable-git-config")) + ctx.cfg.enable_git_config = atoi(value); + else if (!strcmp(name, "max-stats")) + ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); + else if (!strcmp(name, "cache-size")) + ctx.cfg.cache_size = atoi(value); + else if (!strcmp(name, "cache-root")) + ctx.cfg.cache_root = xstrdup(expand_macros(value)); + else if (!strcmp(name, "cache-root-ttl")) + ctx.cfg.cache_root_ttl = atoi(value); + else if (!strcmp(name, "cache-repo-ttl")) + ctx.cfg.cache_repo_ttl = atoi(value); + else if (!strcmp(name, "cache-scanrc-ttl")) + ctx.cfg.cache_scanrc_ttl = atoi(value); + else if (!strcmp(name, "cache-static-ttl")) + ctx.cfg.cache_static_ttl = atoi(value); + else if (!strcmp(name, "cache-dynamic-ttl")) + ctx.cfg.cache_dynamic_ttl = atoi(value); + else if (!strcmp(name, "cache-about-ttl")) + ctx.cfg.cache_about_ttl = atoi(value); + else if (!strcmp(name, "cache-snapshot-ttl")) + ctx.cfg.cache_snapshot_ttl = atoi(value); + else if (!strcmp(name, "case-sensitive-sort")) + ctx.cfg.case_sensitive_sort = atoi(value); + else if (!strcmp(name, "about-filter")) + ctx.cfg.about_filter = cgit_new_filter(value, ABOUT); + else if (!strcmp(name, "commit-filter")) + ctx.cfg.commit_filter = cgit_new_filter(value, COMMIT); + else if (!strcmp(name, "email-filter")) + ctx.cfg.email_filter = cgit_new_filter(value, EMAIL); + else if (!strcmp(name, "owner-filter")) + ctx.cfg.owner_filter = cgit_new_filter(value, OWNER); + else if (!strcmp(name, "auth-filter")) + ctx.cfg.auth_filter = cgit_new_filter(value, AUTH); + else if (!strcmp(name, "embedded")) + ctx.cfg.embedded = atoi(value); + else if (!strcmp(name, "max-atom-items")) + ctx.cfg.max_atom_items = atoi(value); + else if (!strcmp(name, "max-message-length")) + ctx.cfg.max_msg_len = atoi(value); + else if (!strcmp(name, "max-repodesc-length")) + ctx.cfg.max_repodesc_len = atoi(value); + else if (!strcmp(name, "max-blob-size")) + ctx.cfg.max_blob_size = atoi(value); + else if (!strcmp(name, "max-repo-count")) + ctx.cfg.max_repo_count = atoi(value); + else if (!strcmp(name, "max-commit-count")) + ctx.cfg.max_commit_count = atoi(value); + else if (!strcmp(name, "project-list")) + ctx.cfg.project_list = xstrdup(expand_macros(value)); + else if (!strcmp(name, "scan-path")) + if (ctx.cfg.cache_size) + process_cached_repolist(expand_macros(value)); + else if (ctx.cfg.project_list) + scan_projects(expand_macros(value), + ctx.cfg.project_list, repo_config); + else + scan_tree(expand_macros(value), repo_config); + else if (!strcmp(name, "scan-hidden-path")) + ctx.cfg.scan_hidden_path = atoi(value); + else if (!strcmp(name, "section-from-path")) + ctx.cfg.section_from_path = atoi(value); + else if (!strcmp(name, "repository-sort")) + ctx.cfg.repository_sort = xstrdup(value); + else if (!strcmp(name, "section-sort")) + ctx.cfg.section_sort = atoi(value); + else if (!strcmp(name, "source-filter")) + ctx.cfg.source_filter = cgit_new_filter(value, SOURCE); + else if (!strcmp(name, "summary-log")) + ctx.cfg.summary_log = atoi(value); + else if (!strcmp(name, "summary-branches")) + ctx.cfg.summary_branches = atoi(value); + else if (!strcmp(name, "summary-tags")) + ctx.cfg.summary_tags = atoi(value); + else if (!strcmp(name, "side-by-side-diffs")) + ctx.cfg.difftype = atoi(value) ? DIFF_SSDIFF : DIFF_UNIFIED; + else if (!strcmp(name, "agefile")) + ctx.cfg.agefile = xstrdup(value); + else if (!strcmp(name, "mimetype-file")) + ctx.cfg.mimetype_file = xstrdup(value); + else if (!strcmp(name, "renamelimit")) + ctx.cfg.renamelimit = atoi(value); + else if (!strcmp(name, "remove-suffix")) + ctx.cfg.remove_suffix = atoi(value); + else if (!strcmp(name, "robots")) + ctx.cfg.robots = xstrdup(value); + else if (!strcmp(name, "clone-prefix")) + ctx.cfg.clone_prefix = xstrdup(value); + else if (!strcmp(name, "clone-url")) + ctx.cfg.clone_url = xstrdup(value); + else if (!strcmp(name, "local-time")) + ctx.cfg.local_time = atoi(value); + else if (!strcmp(name, "commit-sort")) { + if (!strcmp(value, "date")) + ctx.cfg.commit_sort = 1; + if (!strcmp(value, "topo")) + ctx.cfg.commit_sort = 2; + } else if (!strcmp(name, "branch-sort")) { + if (!strcmp(value, "age")) + ctx.cfg.branch_sort = 1; + if (!strcmp(value, "name")) + ctx.cfg.branch_sort = 0; + } else if (skip_prefix(name, "mimetype.", &arg)) + add_mimetype(arg, value); + else if (!strcmp(name, "include")) + parse_configfile(expand_macros(value), config_cb); +} + +static void querystring_cb(const char *name, const char *value) +{ + if (!value) + value = ""; + + if (!strcmp(name,"r")) { + ctx.qry.repo = xstrdup(value); + ctx.repo = cgit_get_repoinfo(value); + } else if (!strcmp(name, "p")) { + ctx.qry.page = xstrdup(value); + } else if (!strcmp(name, "url")) { + if (*value == '/') + value++; + ctx.qry.url = xstrdup(value); + cgit_parse_url(value); + } else if (!strcmp(name, "qt")) { + ctx.qry.grep = xstrdup(value); + } else if (!strcmp(name, "q")) { + ctx.qry.search = xstrdup(value); + } else if (!strcmp(name, "h")) { + ctx.qry.head = xstrdup(value); + ctx.qry.has_symref = 1; + } else if (!strcmp(name, "id")) { + ctx.qry.oid = xstrdup(value); + ctx.qry.has_oid = 1; + } else if (!strcmp(name, "id2")) { + ctx.qry.oid2 = xstrdup(value); + ctx.qry.has_oid = 1; + } else if (!strcmp(name, "ofs")) { + ctx.qry.ofs = atoi(value); + } else if (!strcmp(name, "path")) { + ctx.qry.path = trim_end(value, '/'); + } else if (!strcmp(name, "name")) { + ctx.qry.name = xstrdup(value); + } else if (!strcmp(name, "s")) { + ctx.qry.sort = xstrdup(value); + } else if (!strcmp(name, "showmsg")) { + ctx.qry.showmsg = atoi(value); + } else if (!strcmp(name, "period")) { + ctx.qry.period = xstrdup(value); + } else if (!strcmp(name, "dt")) { + ctx.qry.difftype = atoi(value); + ctx.qry.has_difftype = 1; + } else if (!strcmp(name, "ss")) { + /* No longer generated, but there may be links out there. */ + ctx.qry.difftype = atoi(value) ? DIFF_SSDIFF : DIFF_UNIFIED; + ctx.qry.has_difftype = 1; + } else if (!strcmp(name, "all")) { + ctx.qry.show_all = atoi(value); + } else if (!strcmp(name, "context")) { + ctx.qry.context = atoi(value); + } else if (!strcmp(name, "ignorews")) { + ctx.qry.ignorews = atoi(value); + } else if (!strcmp(name, "follow")) { + ctx.qry.follow = atoi(value); + } +} + +static void prepare_context(void) +{ + memset(&ctx, 0, sizeof(ctx)); + ctx.cfg.agefile = "info/web/last-modified"; + ctx.cfg.cache_size = 0; + ctx.cfg.cache_max_create_time = 5; + ctx.cfg.cache_root = CGIT_CACHE_ROOT; + ctx.cfg.cache_about_ttl = 15; + ctx.cfg.cache_snapshot_ttl = 5; + ctx.cfg.cache_repo_ttl = 5; + ctx.cfg.cache_root_ttl = 5; + ctx.cfg.cache_scanrc_ttl = 15; + ctx.cfg.cache_dynamic_ttl = 5; + ctx.cfg.cache_static_ttl = -1; + ctx.cfg.case_sensitive_sort = 1; + ctx.cfg.branch_sort = 0; + ctx.cfg.commit_sort = 0; + ctx.cfg.css = "/cgit.css"; + ctx.cfg.logo = "/cgit.png"; + ctx.cfg.favicon = "/favicon.ico"; + ctx.cfg.local_time = 0; + ctx.cfg.enable_http_clone = 1; + ctx.cfg.enable_index_owner = 1; + ctx.cfg.enable_tree_linenumbers = 1; + ctx.cfg.enable_git_config = 0; + ctx.cfg.max_repo_count = 50; + ctx.cfg.max_commit_count = 50; + ctx.cfg.max_lock_attempts = 5; + ctx.cfg.max_msg_len = 80; + ctx.cfg.max_repodesc_len = 80; + ctx.cfg.max_blob_size = 0; + ctx.cfg.max_stats = 0; + ctx.cfg.project_list = NULL; + ctx.cfg.renamelimit = -1; + ctx.cfg.remove_suffix = 0; + ctx.cfg.robots = "index, nofollow"; + ctx.cfg.root_title = "Git repository browser"; + ctx.cfg.root_desc = "a fast webinterface for the git dscm"; + ctx.cfg.scan_hidden_path = 0; + ctx.cfg.script_name = CGIT_SCRIPT_NAME; + ctx.cfg.section = ""; + ctx.cfg.repository_sort = "name"; + ctx.cfg.section_sort = 1; + ctx.cfg.summary_branches = 10; + ctx.cfg.summary_log = 10; + ctx.cfg.summary_tags = 10; + ctx.cfg.max_atom_items = 10; + ctx.cfg.difftype = DIFF_UNIFIED; + ctx.env.cgit_config = getenv("CGIT_CONFIG"); + ctx.env.http_host = getenv("HTTP_HOST"); + ctx.env.https = getenv("HTTPS"); + ctx.env.no_http = getenv("NO_HTTP"); + ctx.env.path_info = getenv("PATH_INFO"); + ctx.env.query_string = getenv("QUERY_STRING"); + ctx.env.request_method = getenv("REQUEST_METHOD"); + ctx.env.script_name = getenv("SCRIPT_NAME"); + ctx.env.server_name = getenv("SERVER_NAME"); + ctx.env.server_port = getenv("SERVER_PORT"); + ctx.env.http_cookie = getenv("HTTP_COOKIE"); + ctx.env.http_referer = getenv("HTTP_REFERER"); + ctx.env.content_length = getenv("CONTENT_LENGTH") ? strtoul(getenv("CONTENT_LENGTH"), NULL, 10) : 0; + ctx.env.authenticated = 0; + ctx.page.mimetype = "text/html"; + ctx.page.charset = PAGE_ENCODING; + ctx.page.filename = NULL; + ctx.page.size = 0; + ctx.page.modified = time(NULL); + ctx.page.expires = ctx.page.modified; + ctx.page.etag = NULL; + string_list_init(&ctx.cfg.mimetypes, 1); + if (ctx.env.script_name) + ctx.cfg.script_name = xstrdup(ctx.env.script_name); + if (ctx.env.query_string) + ctx.qry.raw = xstrdup(ctx.env.query_string); + if (!ctx.env.cgit_config) + ctx.env.cgit_config = CGIT_CONFIG; +} + +struct refmatch { + char *req_ref; + char *first_ref; + int match; +}; + +static int find_current_ref(const char *refname, const struct object_id *oid, + int flags, void *cb_data) +{ + struct refmatch *info; + + info = (struct refmatch *)cb_data; + if (!strcmp(refname, info->req_ref)) + info->match = 1; + if (!info->first_ref) + info->first_ref = xstrdup(refname); + return info->match; +} + +static void free_refmatch_inner(struct refmatch *info) +{ + if (info->first_ref) + free(info->first_ref); +} + +static char *find_default_branch(struct cgit_repo *repo) +{ + struct refmatch info; + char *ref; + + info.req_ref = repo->defbranch; + info.first_ref = NULL; + info.match = 0; + for_each_branch_ref(find_current_ref, &info); + if (info.match) + ref = info.req_ref; + else + ref = info.first_ref; + if (ref) + ref = xstrdup(ref); + free_refmatch_inner(&info); + + return ref; +} + +static char *guess_defbranch(void) +{ + const char *ref, *refname; + struct object_id oid; + + ref = resolve_ref_unsafe("HEAD", 0, &oid, NULL); + if (!ref || !skip_prefix(ref, "refs/heads/", &refname)) + return "master"; + return xstrdup(refname); +} + +/* The caller must free filename and ref after calling this. */ +static inline void parse_readme(const char *readme, char **filename, char **ref, struct cgit_repo *repo) +{ + const char *colon; + + *filename = NULL; + *ref = NULL; + + if (!readme || !readme[0]) + return; + + /* Check if the readme is tracked in the git repo. */ + colon = strchr(readme, ':'); + if (colon && strlen(colon) > 1) { + /* If it starts with a colon, we want to use + * the default branch */ + if (colon == readme && repo->defbranch) + *ref = xstrdup(repo->defbranch); + else + *ref = xstrndup(readme, colon - readme); + readme = colon + 1; + } + + /* Prepend repo path to relative readme path unless tracked. */ + if (!(*ref) && readme[0] != '/') + *filename = fmtalloc("%s/%s", repo->path, readme); + else + *filename = xstrdup(readme); +} +static void choose_readme(struct cgit_repo *repo) +{ + int found; + char *filename, *ref; + struct string_list_item *entry; + + if (!repo->readme.nr) + return; + + found = 0; + for_each_string_list_item(entry, &repo->readme) { + parse_readme(entry->string, &filename, &ref, repo); + if (!filename) { + free(filename); + free(ref); + continue; + } + if (ref) { + if (cgit_ref_path_exists(filename, ref, 1)) { + found = 1; + break; + } + } + else if (!access(filename, R_OK)) { + found = 1; + break; + } + free(filename); + free(ref); + } + repo->readme.strdup_strings = 1; + string_list_clear(&repo->readme, 0); + repo->readme.strdup_strings = 0; + if (found) + string_list_append(&repo->readme, filename)->util = ref; +} + +static void print_no_repo_clone_urls(const char *url) +{ + html("<tr><td><a rel='vcs-git' href='"); + html_url_path(url); + html("' title='"); + html_attr(ctx.repo->name); + html(" Git repository'>"); + html_txt(url); + html("</a></td></tr>\n"); +} + +static void prepare_repo_env(int *nongit) +{ + /* The path to the git repository. */ + setenv("GIT_DIR", ctx.repo->path, 1); + + /* Setup the git directory and initialize the notes system. Both of these + * load local configuration from the git repository, so we do them both while + * the HOME variables are unset. */ + setup_git_directory_gently(nongit); + load_display_notes(NULL); +} + +static int prepare_repo_cmd(int nongit) +{ + struct object_id oid; + int rc; + + if (nongit) { + const char *name = ctx.repo->name; + rc = errno; + ctx.page.title = fmtalloc("%s - %s", ctx.cfg.root_title, + "config error"); + ctx.repo = NULL; + cgit_print_http_headers(); + cgit_print_docstart(); + cgit_print_pageheader(); + cgit_print_error("Failed to open %s: %s", name, + rc ? strerror(rc) : "Not a valid git repository"); + cgit_print_docend(); + return 1; + } + ctx.page.title = fmtalloc("%s - %s", ctx.repo->name, ctx.repo->desc); + + if (!ctx.repo->defbranch) + ctx.repo->defbranch = guess_defbranch(); + + if (!ctx.qry.head) { + ctx.qry.nohead = 1; + ctx.qry.head = find_default_branch(ctx.repo); + } + + if (!ctx.qry.head) { + cgit_print_http_headers(); + cgit_print_docstart(); + cgit_print_pageheader(); + cgit_print_error("Repository seems to be empty"); + if (!strcmp(ctx.qry.page, "summary")) { + html("<table class='list'><tr class='nohover'><td> </td></tr><tr class='nohover'><th class='left'>Clone</th></tr>\n"); + cgit_prepare_repo_env(ctx.repo); + cgit_add_clone_urls(print_no_repo_clone_urls); + html("</table>\n"); + } + cgit_print_docend(); + return 1; + } + + if (get_oid(ctx.qry.head, &oid)) { + char *old_head = ctx.qry.head; + ctx.qry.head = xstrdup(ctx.repo->defbranch); + cgit_print_error_page(404, "Not found", + "Invalid branch: %s", old_head); + free(old_head); + return 1; + } + string_list_sort(&ctx.repo->submodules); + cgit_prepare_repo_env(ctx.repo); + choose_readme(ctx.repo); + return 0; +} + +static inline void open_auth_filter(const char *function) +{ + cgit_open_filter(ctx.cfg.auth_filter, function, + ctx.env.http_cookie ? ctx.env.http_cookie : "", + ctx.env.request_method ? ctx.env.request_method : "", + ctx.env.query_string ? ctx.env.query_string : "", + ctx.env.http_referer ? ctx.env.http_referer : "", + ctx.env.path_info ? ctx.env.path_info : "", + ctx.env.http_host ? ctx.env.http_host : "", + ctx.env.https ? ctx.env.https : "", + ctx.qry.repo ? ctx.qry.repo : "", + ctx.qry.page ? ctx.qry.page : "", + cgit_currentfullurl(), + cgit_loginurl()); +} + +/* We intentionally keep this rather small, instead of looping and + * feeding it to the filter a couple bytes at a time. This way, the + * filter itself does not need to handle any denial of service or + * buffer bloat issues. If this winds up being too small, people + * will complain on the mailing list, and we'll increase it as needed. */ +#define MAX_AUTHENTICATION_POST_BYTES 4096 +/* The filter is expected to spit out "Status: " and all headers. */ +static inline void authenticate_post(void) +{ + char buffer[MAX_AUTHENTICATION_POST_BYTES]; + ssize_t len; + + open_auth_filter("authenticate-post"); + len = ctx.env.content_length; + if (len > MAX_AUTHENTICATION_POST_BYTES) + len = MAX_AUTHENTICATION_POST_BYTES; + if ((len = read(STDIN_FILENO, buffer, len)) < 0) + die_errno("Could not read POST from stdin"); + if (fwrite(buffer, 1, len, stdout) < len) + die_errno("Could not write POST to stdout"); + cgit_close_filter(ctx.cfg.auth_filter); + exit(0); +} + +static inline void authenticate_cookie(void) +{ + /* If we don't have an auth_filter, consider all cookies valid, and thus return early. */ + if (!ctx.cfg.auth_filter) { + ctx.env.authenticated = 1; + return; + } + + /* If we're having something POST'd to /login, we're authenticating POST, + * instead of the cookie, so call authenticate_post and bail out early. + * This pattern here should match /?p=login with POST. */ + if (ctx.env.request_method && ctx.qry.page && !ctx.repo && \ + !strcmp(ctx.env.request_method, "POST") && !strcmp(ctx.qry.page, "login")) { + authenticate_post(); + return; + } + + /* If we've made it this far, we're authenticating the cookie for real, so do that. */ + open_auth_filter("authenticate-cookie"); + ctx.env.authenticated = cgit_close_filter(ctx.cfg.auth_filter); +} + +static void process_request(void) +{ + struct cgit_cmd *cmd; + int nongit = 0; + + /* If we're not yet authenticated, no matter what page we're on, + * display the authentication body from the auth_filter. This should + * never be cached. */ + if (!ctx.env.authenticated) { + ctx.page.title = "Authentication Required"; + cgit_print_http_headers(); + cgit_print_docstart(); + cgit_print_pageheader(); + open_auth_filter("body"); + cgit_close_filter(ctx.cfg.auth_filter); + cgit_print_docend(); + return; + } + + if (ctx.repo) + prepare_repo_env(&nongit); + + cmd = cgit_get_cmd(); + if (!cmd) { + ctx.page.title = "cgit error"; + cgit_print_error_page(404, "Not found", "Invalid request"); + return; + } + + if (!ctx.cfg.enable_http_clone && cmd->is_clone) { + ctx.page.title = "cgit error"; + cgit_print_error_page(404, "Not found", "Invalid request"); + return; + } + + if (cmd->want_repo && !ctx.repo) { + cgit_print_error_page(400, "Bad request", + "No repository selected"); + return; + } + + /* If cmd->want_vpath is set, assume ctx.qry.path contains a "virtual" + * in-project path limit to be made available at ctx.qry.vpath. + * Otherwise, no path limit is in effect (ctx.qry.vpath = NULL). + */ + ctx.qry.vpath = cmd->want_vpath ? ctx.qry.path : NULL; + + if (ctx.repo && prepare_repo_cmd(nongit)) + return; + + cmd->fn(); +} + +static int cmp_repos(const void *a, const void *b) +{ + const struct cgit_repo *ra = a, *rb = b; + return strcmp(ra->url, rb->url); +} + +static char *build_snapshot_setting(int bitmap) +{ + const struct cgit_snapshot_format *f; + struct strbuf result = STRBUF_INIT; + + for (f = cgit_snapshot_formats; f->suffix; f++) { + if (cgit_snapshot_format_bit(f) & bitmap) { + if (result.len) + strbuf_addch(&result, ' '); + strbuf_addstr(&result, f->suffix); + } + } + return strbuf_detach(&result, NULL); +} + +static char *get_first_line(char *txt) +{ + char *t = xstrdup(txt); + char *p = strchr(t, '\n'); + if (p) + *p = '\0'; + return t; +} + +static void print_repo(FILE *f, struct cgit_repo *repo) +{ + struct string_list_item *item; + fprintf(f, "repo.url=%s\n", repo->url); + fprintf(f, "repo.name=%s\n", repo->name); + fprintf(f, "repo.path=%s\n", repo->path); + if (repo->owner) + fprintf(f, "repo.owner=%s\n", repo->owner); + if (repo->desc) { + char *tmp = get_first_line(repo->desc); + fprintf(f, "repo.desc=%s\n", tmp); + free(tmp); + } + for_each_string_list_item(item, &repo->readme) { + if (item->util) + fprintf(f, "repo.readme=%s:%s\n", (char *)item->util, item->string); + else + fprintf(f, "repo.readme=%s\n", item->string); + } + if (repo->defbranch) + fprintf(f, "repo.defbranch=%s\n", repo->defbranch); + if (repo->extra_head_content) + fprintf(f, "repo.extra-head-content=%s\n", repo->extra_head_content); + if (repo->module_link) + fprintf(f, "repo.module-link=%s\n", repo->module_link); + if (repo->section) + fprintf(f, "repo.section=%s\n", repo->section); + if (repo->homepage) + fprintf(f, "repo.homepage=%s\n", repo->homepage); + if (repo->clone_url) + fprintf(f, "repo.clone-url=%s\n", repo->clone_url); + fprintf(f, "repo.enable-blame=%d\n", + repo->enable_blame); + fprintf(f, "repo.enable-commit-graph=%d\n", + repo->enable_commit_graph); + fprintf(f, "repo.enable-log-filecount=%d\n", + repo->enable_log_filecount); + fprintf(f, "repo.enable-log-linecount=%d\n", + repo->enable_log_linecount); + if (repo->about_filter && repo->about_filter != ctx.cfg.about_filter) + cgit_fprintf_filter(repo->about_filter, f, "repo.about-filter="); + if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter) + cgit_fprintf_filter(repo->commit_filter, f, "repo.commit-filter="); + if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter) + cgit_fprintf_filter(repo->source_filter, f, "repo.source-filter="); + if (repo->email_filter && repo->email_filter != ctx.cfg.email_filter) + cgit_fprintf_filter(repo->email_filter, f, "repo.email-filter="); + if (repo->owner_filter && repo->owner_filter != ctx.cfg.owner_filter) + cgit_fprintf_filter(repo->owner_filter, f, "repo.owner-filter="); + if (repo->snapshots != ctx.cfg.snapshots) { + char *tmp = build_snapshot_setting(repo->snapshots); + fprintf(f, "repo.snapshots=%s\n", tmp ? tmp : ""); + free(tmp); + } + if (repo->snapshot_prefix) + fprintf(f, "repo.snapshot-prefix=%s\n", repo->snapshot_prefix); + if (repo->max_stats != ctx.cfg.max_stats) + fprintf(f, "repo.max-stats=%s\n", + cgit_find_stats_periodname(repo->max_stats)); + if (repo->logo) + fprintf(f, "repo.logo=%s\n", repo->logo); + if (repo->logo_link) + fprintf(f, "repo.logo-link=%s\n", repo->logo_link); + fprintf(f, "repo.enable-remote-branches=%d\n", repo->enable_remote_branches); + fprintf(f, "repo.enable-subject-links=%d\n", repo->enable_subject_links); + fprintf(f, "repo.enable-html-serving=%d\n", repo->enable_html_serving); + if (repo->branch_sort == 1) + fprintf(f, "repo.branch-sort=age\n"); + if (repo->commit_sort) { + if (repo->commit_sort == 1) + fprintf(f, "repo.commit-sort=date\n"); + else if (repo->commit_sort == 2) + fprintf(f, "repo.commit-sort=topo\n"); + } + fprintf(f, "repo.hide=%d\n", repo->hide); + fprintf(f, "repo.ignore=%d\n", repo->ignore); + fprintf(f, "\n"); +} + +static void print_repolist(FILE *f, struct cgit_repolist *list, int start) +{ + int i; + + for (i = start; i < list->count; i++) + print_repo(f, &list->repos[i]); +} + +/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc' + * and return 0 on success. + */ +static int generate_cached_repolist(const char *path, const char *cached_rc) +{ + struct strbuf locked_rc = STRBUF_INIT; + int result = 0; + int idx; + FILE *f; + + strbuf_addf(&locked_rc, "%s.lock", cached_rc); + f = fopen(locked_rc.buf, "wx"); + if (!f) { + /* Inform about the error unless the lockfile already existed, + * since that only means we've got concurrent requests. + */ + result = errno; + if (result != EEXIST) + fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", + locked_rc.buf, strerror(result), result); + goto out; + } + idx = cgit_repolist.count; + if (ctx.cfg.project_list) + scan_projects(path, ctx.cfg.project_list, repo_config); + else + scan_tree(path, repo_config); + print_repolist(f, &cgit_repolist, idx); + if (rename(locked_rc.buf, cached_rc)) + fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", + locked_rc.buf, cached_rc, strerror(errno), errno); + fclose(f); +out: + strbuf_release(&locked_rc); + return result; +} + +static void process_cached_repolist(const char *path) +{ + struct stat st; + struct strbuf cached_rc = STRBUF_INIT; + time_t age; + unsigned long hash; + + hash = hash_str(path); + if (ctx.cfg.project_list) + hash += hash_str(ctx.cfg.project_list); + strbuf_addf(&cached_rc, "%s/rc-%8lx", ctx.cfg.cache_root, hash); + + if (stat(cached_rc.buf, &st)) { + /* Nothing is cached, we need to scan without forking. And + * if we fail to generate a cached repolist, we need to + * invoke scan_tree manually. + */ + if (generate_cached_repolist(path, cached_rc.buf)) { + if (ctx.cfg.project_list) + scan_projects(path, ctx.cfg.project_list, + repo_config); + else + scan_tree(path, repo_config); + } + goto out; + } + + parse_configfile(cached_rc.buf, config_cb); + + /* If the cached configfile hasn't expired, lets exit now */ + age = time(NULL) - st.st_mtime; + if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) + goto out; + + /* The cached repolist has been parsed, but it was old. So lets + * rescan the specified path and generate a new cached repolist + * in a child-process to avoid latency for the current request. + */ + if (fork()) + goto out; + + exit(generate_cached_repolist(path, cached_rc.buf)); +out: + strbuf_release(&cached_rc); +} + +static void cgit_parse_args(int argc, const char **argv) +{ + int i; + const char *arg; + int scan = 0; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--version")) { + printf("CGit %s | https://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION); +#ifndef HAVE_LINUX_SENDFILE + printf("[-] "); +#else + printf("[+] "); +#endif + printf("Linux sendfile() usage\n"); + + exit(0); + } + if (skip_prefix(argv[i], "--cache=", &arg)) { + ctx.cfg.cache_root = xstrdup(arg); + } else if (!strcmp(argv[i], "--nohttp")) { + ctx.env.no_http = "1"; + } else if (skip_prefix(argv[i], "--query=", &arg)) { + ctx.qry.raw = xstrdup(arg); + } else if (skip_prefix(argv[i], "--repo=", &arg)) { + ctx.qry.repo = xstrdup(arg); + } else if (skip_prefix(argv[i], "--page=", &arg)) { + ctx.qry.page = xstrdup(arg); + } else if (skip_prefix(argv[i], "--head=", &arg)) { + ctx.qry.head = xstrdup(arg); + ctx.qry.has_symref = 1; + } else if (skip_prefix(argv[i], "--oid=", &arg)) { + ctx.qry.oid = xstrdup(arg); + ctx.qry.has_oid = 1; + } else if (skip_prefix(argv[i], "--ofs=", &arg)) { + ctx.qry.ofs = atoi(arg); + } else if (skip_prefix(argv[i], "--scan-tree=", &arg) || + skip_prefix(argv[i], "--scan-path=", &arg)) { + /* + * HACK: The global snapshot bit mask defines the set + * of allowed snapshot formats, but the config file + * hasn't been parsed yet so the mask is currently 0. + * By setting all bits high before scanning we make + * sure that any in-repo cgitrc snapshot setting is + * respected by scan_tree(). + * + * NOTE: We assume that there aren't more than 8 + * different snapshot formats supported by cgit... + */ + ctx.cfg.snapshots = 0xFF; + scan++; + scan_tree(arg, repo_config); + } + } + if (scan) { + qsort(cgit_repolist.repos, cgit_repolist.count, + sizeof(struct cgit_repo), cmp_repos); + print_repolist(stdout, &cgit_repolist, 0); + exit(0); + } +} + +static int calc_ttl(void) +{ + if (!ctx.repo) + return ctx.cfg.cache_root_ttl; + + if (!ctx.qry.page) + return ctx.cfg.cache_repo_ttl; + + if (!strcmp(ctx.qry.page, "about")) + return ctx.cfg.cache_about_ttl; + + if (!strcmp(ctx.qry.page, "snapshot")) + return ctx.cfg.cache_snapshot_ttl; + + if (ctx.qry.has_oid) + return ctx.cfg.cache_static_ttl; + + if (ctx.qry.has_symref) + return ctx.cfg.cache_dynamic_ttl; + + return ctx.cfg.cache_repo_ttl; +} + +int cmd_main(int argc, const char **argv) +{ + const char *path; + int err, ttl; + + atexit(cgit_cleanup_filters); + + prepare_context(); + cgit_repolist.length = 0; + cgit_repolist.count = 0; + cgit_repolist.repos = NULL; + + cgit_parse_args(argc, argv); + parse_configfile(expand_macros(ctx.env.cgit_config), config_cb); + ctx.repo = NULL; + http_parse_querystring(ctx.qry.raw, querystring_cb); + + /* If virtual-root isn't specified in cgitrc, lets pretend + * that virtual-root equals SCRIPT_NAME, minus any possibly + * trailing slashes. + */ + if (!ctx.cfg.virtual_root && ctx.cfg.script_name) + ctx.cfg.virtual_root = ensure_end(ctx.cfg.script_name, '/'); + + /* If no url parameter is specified on the querystring, lets + * use PATH_INFO as url. This allows cgit to work with virtual + * urls without the need for rewriterules in the webserver (as + * long as PATH_INFO is included in the cache lookup key). + */ + path = ctx.env.path_info; + if (!ctx.qry.url && path) { + if (path[0] == '/') + path++; + ctx.qry.url = xstrdup(path); + if (ctx.qry.raw) { + char *newqry = fmtalloc("%s?%s", path, ctx.qry.raw); + free(ctx.qry.raw); + ctx.qry.raw = newqry; + } else + ctx.qry.raw = xstrdup(ctx.qry.url); + cgit_parse_url(ctx.qry.url); + } + + /* Before we go any further, we set ctx.env.authenticated by checking to see + * if the supplied cookie is valid. All cookies are valid if there is no + * auth_filter. If there is an auth_filter, the filter decides. */ + authenticate_cookie(); + + ttl = calc_ttl(); + if (ttl < 0) + ctx.page.expires += 10 * 365 * 24 * 60 * 60; /* 10 years */ + else + ctx.page.expires += ttl * 60; + if (!ctx.env.authenticated || (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD"))) + ctx.cfg.cache_size = 0; + err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root, + ctx.qry.raw, ttl, process_request); + cgit_cleanup_filters(); + if (err) + cgit_print_error("Error processing page: %s (%d)", + strerror(err), err); + return err; +} diff --git a/www/git.causal.agency/cgit/cgit.css b/www/git.causal.agency/cgit/cgit.css new file mode 100644 index 00000000..f3dbb7a9 --- /dev/null +++ b/www/git.causal.agency/cgit/cgit.css @@ -0,0 +1,877 @@ +div#cgit { + padding: 0em; + margin: 0em; + font-family: sans-serif; + font-size: 10pt; + color: #333; + background: white; + padding: 4px; +} + +div#cgit a { + color: blue; + text-decoration: none; +} + +div#cgit a:hover { + text-decoration: underline; +} + +div#cgit table { + border-collapse: collapse; +} + +div#cgit table#header { + width: 100%; + margin-bottom: 1em; +} + +div#cgit table#header td.logo { + width: 96px; + vertical-align: top; +} + +div#cgit table#header td.main { + font-size: 250%; + padding-left: 10px; + white-space: nowrap; +} + +div#cgit table#header td.main a { + color: #000; +} + +div#cgit table#header td.form { + text-align: right; + vertical-align: bottom; + padding-right: 1em; + padding-bottom: 2px; + white-space: nowrap; +} + +div#cgit table#header td.form form, +div#cgit table#header td.form input, +div#cgit table#header td.form select { + font-size: 90%; +} + +div#cgit table#header td.sub { + color: #777; + border-top: solid 1px #ccc; + padding-left: 10px; +} + +div#cgit table.tabs { + border-bottom: solid 3px #ccc; + border-collapse: collapse; + margin-top: 2em; + margin-bottom: 0px; + width: 100%; +} + +div#cgit table.tabs td { + padding: 0px 1em; + vertical-align: bottom; +} + +div#cgit table.tabs td a { + padding: 2px 0.25em; + color: #777; + font-size: 110%; +} + +div#cgit table.tabs td a.active { + color: #000; + background-color: #ccc; +} + +div#cgit table.tabs a[href^="http://"]:after, div#cgit table.tabs a[href^="https://"]:after { + content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhcJDQY+gm2TAAAAHWlUWHRDb21tZW50AAAAAABDcmVhdGVkIHdpdGggR0lNUGQuZQcAAABbSURBVAhbY2BABs4MU4CwhYHBh2Erww4wrGFQZHjI8B8IgUIscJWyDHcggltQhI4zGDCcRwhChPggHIggP1QoAVmQkSETrGoHsiAEsACtBYN0oDAMbgU6EBcAAL2eHUt4XUU4AAAAAElFTkSuQmCC); + opacity: 0.5; + margin: 0 0 0 5px; +} + +div#cgit table.tabs td.form { + text-align: right; +} + +div#cgit table.tabs td.form form { + padding-bottom: 2px; + font-size: 90%; + white-space: nowrap; +} + +div#cgit table.tabs td.form input, +div#cgit table.tabs td.form select { + font-size: 90%; +} + +div#cgit div.path { + margin: 0px; + padding: 5px 2em 2px 2em; + color: #000; + background-color: #eee; +} + +div#cgit div.content { + margin: 0px; + padding: 2em; + border-bottom: solid 3px #ccc; +} + + +div#cgit table.list { + width: 100%; + border: none; + border-collapse: collapse; +} + +div#cgit table.list tr { + background: white; +} + +div#cgit table.list tr.logheader { + background: #eee; +} + +div#cgit table.list tr:nth-child(even) { + background: #f7f7f7; +} + +div#cgit table.list tr:nth-child(odd) { + background: white; +} + +div#cgit table.list tr:hover { + background: #eee; +} + +div#cgit table.list tr.nohover { + background: white; +} + +div#cgit table.list tr.nohover:hover { + background: white; +} + +div#cgit table.list tr.nohover-highlight:hover:nth-child(even) { + background: #f7f7f7; +} + +div#cgit table.list tr.nohover-highlight:hover:nth-child(odd) { + background: white; +} + +div#cgit table.list th { + font-weight: bold; + /* color: #888; + border-top: dashed 1px #888; + border-bottom: dashed 1px #888; + */ + padding: 0.1em 0.5em 0.05em 0.5em; + vertical-align: baseline; +} + +div#cgit table.list td { + border: none; + padding: 0.1em 0.5em 0.1em 0.5em; +} + +div#cgit table.list td.commitgraph { + font-family: monospace; + white-space: pre; +} + +div#cgit table.list td.commitgraph .column1 { + color: #a00; +} + +div#cgit table.list td.commitgraph .column2 { + color: #0a0; +} + +div#cgit table.list td.commitgraph .column3 { + color: #aa0; +} + +div#cgit table.list td.commitgraph .column4 { + color: #00a; +} + +div#cgit table.list td.commitgraph .column5 { + color: #a0a; +} + +div#cgit table.list td.commitgraph .column6 { + color: #0aa; +} + +div#cgit table.list td.logsubject { + font-family: monospace; + font-weight: bold; +} + +div#cgit table.list td.logmsg { + font-family: monospace; + white-space: pre; + padding: 0 0.5em; +} + +div#cgit table.list td a { + color: black; +} + +div#cgit table.list td a.ls-dir { + font-weight: bold; + color: #00f; +} + +div#cgit table.list td a:hover { + color: #00f; +} + +div#cgit img { + border: none; +} + +div#cgit input#switch-btn { + margin: 2px 0px 0px 0px; +} + +div#cgit td#sidebar input.txt { + width: 100%; + margin: 2px 0px 0px 0px; +} + +div#cgit table#grid { + margin: 0px; +} + +div#cgit td#content { + vertical-align: top; + padding: 1em 2em 1em 1em; + border: none; +} + +div#cgit div#summary { + vertical-align: top; + margin-bottom: 1em; +} + +div#cgit table#downloads { + float: right; + border-collapse: collapse; + border: solid 1px #777; + margin-left: 0.5em; + margin-bottom: 0.5em; +} + +div#cgit table#downloads th { + background-color: #ccc; +} + +div#cgit div#blob { + border: solid 1px black; +} + +div#cgit div.error { + color: red; + font-weight: bold; + margin: 1em 2em; +} + +div#cgit a.ls-blob, div#cgit a.ls-dir, div#cgit .ls-mod { + font-family: monospace; +} + +div#cgit td.ls-size { + text-align: right; + font-family: monospace; + width: 10em; +} + +div#cgit td.ls-mode { + font-family: monospace; + width: 10em; +} + +div#cgit table.blob { + margin-top: 0.5em; + border-top: solid 1px black; +} + +div#cgit table.blob td.hashes, +div#cgit table.blob td.lines { + margin: 0; padding: 0 0 0 0.5em; + vertical-align: top; + color: black; +} + +div#cgit table.blob td.linenumbers { + margin: 0; padding: 0 0.5em 0 0.5em; + vertical-align: top; + text-align: right; + border-right: 1px solid gray; +} + +div#cgit table.blob pre { + padding: 0; margin: 0; +} + +div#cgit table.blob td.linenumbers a, +div#cgit table.ssdiff td.lineno a { + color: gray; + text-align: right; + text-decoration: none; +} + +div#cgit table.blob td.linenumbers a:hover, +div#cgit table.ssdiff td.lineno a:hover { + color: black; +} + +div#cgit table.blame td.hashes, +div#cgit table.blame td.lines, +div#cgit table.blame td.linenumbers { + padding: 0; +} + +div#cgit table.blame td.hashes div.alt, +div#cgit table.blame td.lines div.alt { + padding: 0 0.5em 0 0.5em; +} + +div#cgit table.blame td.linenumbers div.alt { + padding: 0 0.5em 0 0; +} + +div#cgit table.blame div.alt:nth-child(even) { + background: #eee; +} + +div#cgit table.blame div.alt:nth-child(odd) { + background: white; +} + +div#cgit table.blame td.lines > div { + position: relative; +} + +div#cgit table.blame td.lines > div > pre { + padding: 0 0 0 0.5em; + position: absolute; + top: 0; +} + +div#cgit table.bin-blob { + margin-top: 0.5em; + border: solid 1px black; +} + +div#cgit table.bin-blob th { + font-family: monospace; + white-space: pre; + border: solid 1px #777; + padding: 0.5em 1em; +} + +div#cgit table.bin-blob td { + font-family: monospace; + white-space: pre; + border-left: solid 1px #777; + padding: 0em 1em; +} + +div#cgit table.nowrap td { + white-space: nowrap; +} + +div#cgit table.commit-info { + border-collapse: collapse; + margin-top: 1.5em; +} + +div#cgit div.cgit-panel { + float: right; + margin-top: 1.5em; +} + +div#cgit div.cgit-panel table { + border-collapse: collapse; + border: solid 1px #aaa; + background-color: #eee; +} + +div#cgit div.cgit-panel th { + text-align: center; +} + +div#cgit div.cgit-panel td { + padding: 0.25em 0.5em; +} + +div#cgit div.cgit-panel td.label { + padding-right: 0.5em; +} + +div#cgit div.cgit-panel td.ctrl { + padding-left: 0.5em; +} + +div#cgit table.commit-info th { + text-align: left; + font-weight: normal; + padding: 0.1em 1em 0.1em 0.1em; + vertical-align: top; +} + +div#cgit table.commit-info td { + font-weight: normal; + padding: 0.1em 1em 0.1em 0.1em; +} + +div#cgit div.commit-subject { + font-weight: bold; + font-size: 125%; + margin: 1.5em 0em 0.5em 0em; + padding: 0em; +} + +div#cgit div.notes-header { + font-weight: bold; + padding-top: 1.5em; +} + +div#cgit div.notes { + white-space: pre; + font-family: monospace; + border: solid 1px #ee9; + background-color: #ffd; + padding: 0.3em 2em 0.3em 1em; + float: left; +} + +div#cgit div.notes-footer { + clear: left; +} + +div#cgit div.diffstat-header { + font-weight: bold; + padding-top: 1.5em; +} + +div#cgit table.diffstat { + border-collapse: collapse; + border: solid 1px #aaa; + background-color: #eee; +} + +div#cgit table.diffstat th { + font-weight: normal; + text-align: left; + text-decoration: underline; + padding: 0.1em 1em 0.1em 0.1em; + font-size: 100%; +} + +div#cgit table.diffstat td { + padding: 0.2em 0.2em 0.1em 0.1em; + font-size: 100%; + border: none; +} + +div#cgit table.diffstat td.mode { + white-space: nowrap; +} + +div#cgit table.diffstat td span.modechange { + padding-left: 1em; + color: red; +} + +div#cgit table.diffstat td.add a { + color: green; +} + +div#cgit table.diffstat td.del a { + color: red; +} + +div#cgit table.diffstat td.upd a { + color: blue; +} + +div#cgit table.diffstat td.graph { + width: 500px; + vertical-align: middle; +} + +div#cgit table.diffstat td.graph table { + border: none; +} + +div#cgit table.diffstat td.graph td { + padding: 0px; + border: 0px; + height: 7pt; +} + +div#cgit table.diffstat td.graph td.add { + background-color: #5c5; +} + +div#cgit table.diffstat td.graph td.rem { + background-color: #c55; +} + +div#cgit div.diffstat-summary { + color: #888; + padding-top: 0.5em; +} + +div#cgit table.diff { + width: 100%; +} + +div#cgit table.diff td span.head { + font-weight: bold; + color: black; +} + +div#cgit table.diff td span.hunk { + color: #009; +} + +div#cgit table.diff td span.add { + color: green; +} + +div#cgit table.diff td span.del { + color: red; +} + +div#cgit .oid { + font-family: monospace; + font-size: 90%; +} + +div#cgit .left { + text-align: left; +} + +div#cgit .right { + text-align: right; +} + +div#cgit table.list td.reposection { + font-style: italic; + color: #888; +} + +div#cgit a.button { + font-size: 80%; +} + +div#cgit a.primary { + font-size: 100%; +} + +div#cgit a.secondary { + font-size: 90%; +} + +div#cgit td.toplevel-repo { + +} + +div#cgit table.list td.sublevel-repo { + padding-left: 1.5em; +} + +div#cgit ul.pager { + list-style-type: none; + text-align: center; + margin: 1em 0em 0em 0em; + padding: 0; +} + +div#cgit ul.pager li { + display: inline-block; + margin: 0.25em 0.5em; +} + +div#cgit ul.pager a { + color: #777; +} + +div#cgit ul.pager .current { + font-weight: bold; +} + +div#cgit span.age-mins { + font-weight: bold; + color: #080; +} + +div#cgit span.age-hours { + color: #080; +} + +div#cgit span.age-days { + color: #040; +} + +div#cgit span.age-weeks { + color: #444; +} + +div#cgit span.age-months { + color: #888; +} + +div#cgit span.age-years { + color: #bbb; +} + +div#cgit span.insertions { + color: #080; +} + +div#cgit span.deletions { + color: #800; +} + +div#cgit div.footer { + margin-top: 0.5em; + text-align: center; + font-size: 80%; + color: #ccc; +} + +div#cgit div.footer a { + color: #ccc; + text-decoration: none; +} + +div#cgit div.footer a:hover { + text-decoration: underline; +} + +div#cgit a.branch-deco { + color: #000; + padding: 0px 0.25em; + background-color: #88ff88; + border: solid 1px #007700; +} + +div#cgit a.tag-deco { + color: #000; + padding: 0px 0.25em; + background-color: #ffff88; + border: solid 1px #777700; +} + +div#cgit a.tag-annotated-deco { + color: #000; + padding: 0px 0.25em; + background-color: #ffcc88; + border: solid 1px #777700; +} + +div#cgit a.remote-deco { + color: #000; + padding: 0px 0.25em; + background-color: #ccccff; + border: solid 1px #000077; +} + +div#cgit a.deco { + color: #000; + padding: 0px 0.25em; + background-color: #ff8888; + border: solid 1px #770000; +} + +div#cgit div.commit-subject a.branch-deco, +div#cgit div.commit-subject a.tag-deco, +div#cgit div.commit-subject a.tag-annotated-deco, +div#cgit div.commit-subject a.remote-deco, +div#cgit div.commit-subject a.deco { + font-size: 75%; +} + +div#cgit table.stats { + border: solid 1px black; + border-collapse: collapse; +} + +div#cgit table.stats th { + text-align: left; + padding: 1px 0.5em; + background-color: #eee; + border: solid 1px black; +} + +div#cgit table.stats td { + text-align: right; + padding: 1px 0.5em; + border: solid 1px black; +} + +div#cgit table.stats td.total { + font-weight: bold; + text-align: left; +} + +div#cgit table.stats td.sum { + color: #c00; + font-weight: bold; +/* background-color: #eee; */ +} + +div#cgit table.stats td.left { + text-align: left; +} + +div#cgit table.vgraph { + border-collapse: separate; + border: solid 1px black; + height: 200px; +} + +div#cgit table.vgraph th { + background-color: #eee; + font-weight: bold; + border: solid 1px white; + padding: 1px 0.5em; +} + +div#cgit table.vgraph td { + vertical-align: bottom; + padding: 0px 10px; +} + +div#cgit table.vgraph div.bar { + background-color: #eee; +} + +div#cgit table.hgraph { + border: solid 1px black; + width: 800px; +} + +div#cgit table.hgraph th { + background-color: #eee; + font-weight: bold; + border: solid 1px black; + padding: 1px 0.5em; +} + +div#cgit table.hgraph td { + vertical-align: middle; + padding: 2px 2px; +} + +div#cgit table.hgraph div.bar { + background-color: #eee; + height: 1em; +} + +div#cgit table.ssdiff { + width: 100%; +} + +div#cgit table.ssdiff td { + font-size: 75%; + font-family: monospace; + white-space: pre; + padding: 1px 4px 1px 4px; + border-left: solid 1px #aaa; + border-right: solid 1px #aaa; +} + +div#cgit table.ssdiff td.add { + color: black; + background: #cfc; + min-width: 50%; +} + +div#cgit table.ssdiff td.add_dark { + color: black; + background: #aca; + min-width: 50%; +} + +div#cgit table.ssdiff span.add { + background: #cfc; + font-weight: bold; +} + +div#cgit table.ssdiff td.del { + color: black; + background: #fcc; + min-width: 50%; +} + +div#cgit table.ssdiff td.del_dark { + color: black; + background: #caa; + min-width: 50%; +} + +div#cgit table.ssdiff span.del { + background: #fcc; + font-weight: bold; +} + +div#cgit table.ssdiff td.changed { + color: black; + background: #ffc; + min-width: 50%; +} + +div#cgit table.ssdiff td.changed_dark { + color: black; + background: #cca; + min-width: 50%; +} + +div#cgit table.ssdiff td.lineno { + color: black; + background: #eee; + text-align: right; + width: 3em; + min-width: 3em; +} + +div#cgit table.ssdiff td.hunk { + color: black; + background: #ccf; + border-top: solid 1px #aaa; + border-bottom: solid 1px #aaa; +} + +div#cgit table.ssdiff td.head { + border-top: solid 1px #aaa; + border-bottom: solid 1px #aaa; +} + +div#cgit table.ssdiff td.head div.head { + font-weight: bold; + color: black; +} + +div#cgit table.ssdiff td.foot { + border-top: solid 1px #aaa; + border-left: none; + border-right: none; + border-bottom: none; +} + +div#cgit table.ssdiff td.space { + border: none; +} + +div#cgit table.ssdiff td.space div { + min-height: 3em; +} diff --git a/www/git.causal.agency/cgit/cgit.h b/www/git.causal.agency/cgit/cgit.h new file mode 100644 index 00000000..72fcd849 --- /dev/null +++ b/www/git.causal.agency/cgit/cgit.h @@ -0,0 +1,397 @@ +#ifndef CGIT_H +#define CGIT_H + + +#include <git-compat-util.h> +#include <stdbool.h> + +#include <cache.h> +#include <grep.h> +#include <object.h> +#include <object-store.h> +#include <tree.h> +#include <commit.h> +#include <tag.h> +#include <diff.h> +#include <diffcore.h> +#include <strvec.h> +#include <refs.h> +#include <revision.h> +#include <log-tree.h> +#include <archive.h> +#include <string-list.h> +#include <xdiff-interface.h> +#include <xdiff/xdiff.h> +#include <utf8.h> +#include <notes.h> +#include <graph.h> + +/* Add isgraph(x) to Git's sane ctype support (see git-compat-util.h) */ +#undef isgraph +#define isgraph(x) (isprint((x)) && !isspace((x))) + + +/* + * Limits used for relative dates + */ +#define TM_MIN 60 +#define TM_HOUR (TM_MIN * 60) +#define TM_DAY (TM_HOUR * 24) +#define TM_WEEK (TM_DAY * 7) +#define TM_YEAR (TM_DAY * 365) +#define TM_MONTH (TM_YEAR / 12.0) + + +/* + * Default encoding + */ +#define PAGE_ENCODING "UTF-8" + +#define BIT(x) (1U << (x)) + +typedef void (*configfn)(const char *name, const char *value); +typedef void (*filepair_fn)(struct diff_filepair *pair); +typedef void (*linediff_fn)(char *line, int len); + +typedef enum { + DIFF_UNIFIED, DIFF_SSDIFF, DIFF_STATONLY +} diff_type; + +typedef enum { + ABOUT, COMMIT, SOURCE, EMAIL, AUTH, OWNER +} filter_type; + +struct cgit_filter { + int (*open)(struct cgit_filter *, va_list ap); + int (*close)(struct cgit_filter *); + void (*fprintf)(struct cgit_filter *, FILE *, const char *prefix); + void (*cleanup)(struct cgit_filter *); + int argument_count; +}; + +struct cgit_exec_filter { + struct cgit_filter base; + char *cmd; + char **argv; + int old_stdout; + int pid; +}; + +struct cgit_repo { + char *url; + char *name; + char *path; + char *desc; + char *extra_head_content; + char *owner; + char *homepage; + char *defbranch; + char *module_link; + struct string_list readme; + char *section; + char *clone_url; + char *logo; + char *logo_link; + char *snapshot_prefix; + int snapshots; + int enable_blame; + int enable_commit_graph; + int enable_log_filecount; + int enable_log_linecount; + int enable_remote_branches; + int enable_subject_links; + int enable_html_serving; + int max_stats; + int branch_sort; + int commit_sort; + time_t mtime; + struct cgit_filter *about_filter; + struct cgit_filter *commit_filter; + struct cgit_filter *source_filter; + struct cgit_filter *email_filter; + struct cgit_filter *owner_filter; + struct string_list submodules; + int hide; + int ignore; +}; + +typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name, + const char *value); + +struct cgit_repolist { + int length; + int count; + struct cgit_repo *repos; +}; + +struct commitinfo { + struct commit *commit; + char *author; + char *author_email; + unsigned long author_date; + int author_tz; + char *committer; + char *committer_email; + unsigned long committer_date; + int committer_tz; + char *subject; + char *msg; + char *msg_encoding; +}; + +struct taginfo { + char *tagger; + char *tagger_email; + unsigned long tagger_date; + int tagger_tz; + char *msg; +}; + +struct refinfo { + const char *refname; + struct object *object; + union { + struct taginfo *tag; + struct commitinfo *commit; + }; +}; + +struct reflist { + struct refinfo **refs; + int alloc; + int count; +}; + +struct cgit_query { + int has_symref; + int has_oid; + int has_difftype; + char *raw; + char *repo; + char *page; + char *search; + char *grep; + char *head; + char *oid; + char *oid2; + char *path; + char *name; + char *url; + char *period; + int ofs; + int nohead; + char *sort; + int showmsg; + diff_type difftype; + int show_all; + int context; + int ignorews; + int follow; + char *vpath; +}; + +struct cgit_config { + char *agefile; + char *cache_root; + char *clone_prefix; + char *clone_url; + char *css; + char *favicon; + char *footer; + char *head_include; + char *header; + char *logo; + char *logo_link; + char *mimetype_file; + char *module_link; + char *project_list; + struct string_list readme; + char *robots; + char *root_title; + char *root_desc; + char *root_readme; + char *script_name; + char *section; + char *repository_sort; + char *virtual_root; /* Always ends with '/'. */ + char *strict_export; + int cache_size; + int cache_dynamic_ttl; + int cache_max_create_time; + int cache_repo_ttl; + int cache_root_ttl; + int cache_scanrc_ttl; + int cache_static_ttl; + int cache_about_ttl; + int cache_snapshot_ttl; + int case_sensitive_sort; + int embedded; + int enable_filter_overrides; + int enable_follow_links; + int enable_http_clone; + int enable_index_links; + int enable_index_owner; + int enable_blame; + int enable_commit_graph; + int enable_log_filecount; + int enable_log_linecount; + int enable_remote_branches; + int enable_subject_links; + int enable_html_serving; + int enable_tree_linenumbers; + int enable_git_config; + int local_time; + int max_atom_items; + int max_repo_count; + int max_commit_count; + int max_lock_attempts; + int max_msg_len; + int max_repodesc_len; + int max_blob_size; + int max_stats; + int noplainemail; + int noheader; + int renamelimit; + int remove_suffix; + int scan_hidden_path; + int section_from_path; + int snapshots; + int section_sort; + int summary_branches; + int summary_log; + int summary_tags; + diff_type difftype; + int branch_sort; + int commit_sort; + struct string_list mimetypes; + struct cgit_filter *about_filter; + struct cgit_filter *commit_filter; + struct cgit_filter *source_filter; + struct cgit_filter *email_filter; + struct cgit_filter *owner_filter; + struct cgit_filter *auth_filter; +}; + +struct cgit_page { + time_t modified; + time_t expires; + size_t size; + const char *mimetype; + const char *charset; + const char *filename; + const char *etag; + const char *title; + int status; + const char *statusmsg; +}; + +struct cgit_environment { + const char *cgit_config; + const char *http_host; + const char *https; + const char *no_http; + const char *path_info; + const char *query_string; + const char *request_method; + const char *script_name; + const char *server_name; + const char *server_port; + const char *http_cookie; + const char *http_referer; + unsigned int content_length; + int authenticated; +}; + +struct cgit_context { + struct cgit_environment env; + struct cgit_query qry; + struct cgit_config cfg; + struct cgit_repo *repo; + struct cgit_page page; +}; + +typedef int (*write_archive_fn_t)(const char *, const char *); + +struct cgit_snapshot_format { + const char *suffix; + const char *mimetype; + write_archive_fn_t write_func; +}; + +extern const char *cgit_version; + +extern struct cgit_repolist cgit_repolist; +extern struct cgit_context ctx; +extern const struct cgit_snapshot_format cgit_snapshot_formats[]; + +extern char *cgit_default_repo_desc; +extern struct cgit_repo *cgit_add_repo(const char *url); +extern struct cgit_repo *cgit_get_repoinfo(const char *url); +extern void cgit_repo_config_cb(const char *name, const char *value); + +extern int chk_zero(int result, char *msg); +extern int chk_positive(int result, char *msg); +extern int chk_non_negative(int result, char *msg); + +extern char *trim_end(const char *str, char c); +extern char *ensure_end(const char *str, char c); + +extern void strbuf_ensure_end(struct strbuf *sb, char c); + +extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); +extern void cgit_free_reflist_inner(struct reflist *list); +extern int cgit_refs_cb(const char *refname, const struct object_id *oid, + int flags, void *cb_data); + +extern void cgit_free_commitinfo(struct commitinfo *info); +extern void cgit_free_taginfo(struct taginfo *info); + +void cgit_diff_tree_cb(struct diff_queue_struct *q, + struct diff_options *options, void *data); + +extern int cgit_diff_files(const struct object_id *old_oid, + const struct object_id *new_oid, + unsigned long *old_size, unsigned long *new_size, + int *binary, int context, int ignorews, + linediff_fn fn); + +extern void cgit_diff_tree(const struct object_id *old_oid, + const struct object_id *new_oid, + filepair_fn fn, const char *prefix, int ignorews); + +extern void cgit_diff_commit(struct commit *commit, filepair_fn fn, + const char *prefix); + +__attribute__((format (printf,1,2))) +extern char *fmt(const char *format,...); + +__attribute__((format (printf,1,2))) +extern char *fmtalloc(const char *format,...); + +extern struct commitinfo *cgit_parse_commit(struct commit *commit); +extern struct taginfo *cgit_parse_tag(struct tag *tag); +extern void cgit_parse_url(const char *url); + +extern const char *cgit_repobasename(const char *reponame); + +extern int cgit_parse_snapshots_mask(const char *str); +extern const struct object_id *cgit_snapshot_get_sig(const char *ref, + const struct cgit_snapshot_format *f); +extern const unsigned cgit_snapshot_format_bit(const struct cgit_snapshot_format *f); + +extern int cgit_open_filter(struct cgit_filter *filter, ...); +extern int cgit_close_filter(struct cgit_filter *filter); +extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix); +extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv); +extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype); +extern void cgit_cleanup_filters(void); + +extern void cgit_prepare_repo_env(struct cgit_repo * repo); + +extern int readfile(const char *path, char **buf, size_t *size); + +extern char *expand_macros(const char *txt); + +extern char *get_mimetype_for_filename(const char *filename); + +#endif /* CGIT_H */ diff --git a/www/git.causal.agency/cgit/cgit.mk b/www/git.causal.agency/cgit/cgit.mk new file mode 100644 index 00000000..5b9ed5be --- /dev/null +++ b/www/git.causal.agency/cgit/cgit.mk @@ -0,0 +1,114 @@ +# This Makefile is run in the "git" directory in order to re-use Git's +# build variables and operating system detection. Hence all files in +# CGit's directory must be prefixed with "../". +include Makefile + +CGIT_PREFIX = ../ + +-include $(CGIT_PREFIX)cgit.conf + +# The CGIT_* variables are inherited when this file is called from the +# main Makefile - they are defined there. + +$(CGIT_PREFIX)VERSION: force-version + @cd $(CGIT_PREFIX) && '$(SHELL_PATH_SQ)' ./gen-version.sh "$(CGIT_VERSION)" +-include $(CGIT_PREFIX)VERSION +.PHONY: force-version + +# CGIT_CFLAGS is a separate variable so that we can track it separately +# and avoid rebuilding all of Git when these variables change. +CGIT_CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' +CGIT_CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' +CGIT_CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' + +PKG_CONFIG ?= pkg-config + +ifdef NO_C99_FORMAT + CFLAGS += -DNO_C99_FORMAT +endif + +# Add -ldl to linker flags on systems that commonly use GNU libc. +ifneq (,$(filter $(uname_S),Linux GNU GNU/kFreeBSD)) + CGIT_LIBS += -ldl +endif + +# glibc 2.1+ offers sendfile which the most common C library on Linux +ifeq ($(uname_S),Linux) + HAVE_LINUX_SENDFILE = YesPlease +endif + +ifdef HAVE_LINUX_SENDFILE + CGIT_CFLAGS += -DHAVE_LINUX_SENDFILE +endif + +CGIT_OBJ_NAMES += cgit.o +CGIT_OBJ_NAMES += cache.o +CGIT_OBJ_NAMES += cmd.o +CGIT_OBJ_NAMES += configfile.o +CGIT_OBJ_NAMES += filter.o +CGIT_OBJ_NAMES += html.o +CGIT_OBJ_NAMES += parsing.o +CGIT_OBJ_NAMES += scan-tree.o +CGIT_OBJ_NAMES += shared.o +CGIT_OBJ_NAMES += ui-atom.o +CGIT_OBJ_NAMES += ui-blame.o +CGIT_OBJ_NAMES += ui-blob.o +CGIT_OBJ_NAMES += ui-clone.o +CGIT_OBJ_NAMES += ui-commit.o +CGIT_OBJ_NAMES += ui-diff.o +CGIT_OBJ_NAMES += ui-log.o +CGIT_OBJ_NAMES += ui-patch.o +CGIT_OBJ_NAMES += ui-plain.o +CGIT_OBJ_NAMES += ui-refs.o +CGIT_OBJ_NAMES += ui-repolist.o +CGIT_OBJ_NAMES += ui-shared.o +CGIT_OBJ_NAMES += ui-snapshot.o +CGIT_OBJ_NAMES += ui-ssdiff.o +CGIT_OBJ_NAMES += ui-stats.o +CGIT_OBJ_NAMES += ui-summary.o +CGIT_OBJ_NAMES += ui-tag.o +CGIT_OBJ_NAMES += ui-tree.o + +CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES)) + +# Only cgit.c reference CGIT_VERSION so we only rebuild its objects when the +# version changes. +CGIT_VERSION_OBJS := $(addprefix $(CGIT_PREFIX),cgit.o cgit.sp) +$(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION +$(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \ + -DCGIT_VERSION='"$(CGIT_VERSION)"' + +# Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not +# handled by that and we must handle them ourselves. +cgit_dep_files := $(foreach f,$(CGIT_OBJS),$(dir $f).depend/$(notdir $f).d) +cgit_dep_files_present := $(wildcard $(cgit_dep_files)) +ifneq ($(cgit_dep_files_present),) +include $(cgit_dep_files_present) +endif + +ifeq ($(wildcard $(CGIT_PREFIX).depend),) +missing_dep_dirs += $(CGIT_PREFIX).depend +endif + +$(CGIT_PREFIX).depend: + @mkdir -p $@ + +$(CGIT_PREFIX)CGIT-CFLAGS: FORCE + @FLAGS='$(subst ','\'',$(CGIT_CFLAGS))'; \ + if test x"$$FLAGS" != x"`cat ../CGIT-CFLAGS 2>/dev/null`" ; then \ + echo 1>&2 " * new CGit build flags"; \ + echo "$$FLAGS" >$(CGIT_PREFIX)CGIT-CFLAGS; \ + fi + +$(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs) + $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $< + +$(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS) + +CGIT_SP_OBJS := $(patsubst %.o,%.sp,$(CGIT_OBJS)) + +$(CGIT_SP_OBJS): %.sp: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS FORCE + $(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $(SPARSE_FLAGS) $< + +cgit-sparse: $(CGIT_SP_OBJS) diff --git a/www/git.causal.agency/cgit/cgit.png b/www/git.causal.agency/cgit/cgit.png new file mode 100644 index 00000000..425528ee --- /dev/null +++ b/www/git.causal.agency/cgit/cgit.png Binary files differdiff --git a/www/git.causal.agency/cgit/cgitrc.5.txt b/www/git.causal.agency/cgit/cgitrc.5.txt new file mode 100644 index 00000000..8d663952 --- /dev/null +++ b/www/git.causal.agency/cgit/cgitrc.5.txt @@ -0,0 +1,977 @@ +:man source: cgit +:man manual: cgit + +CGITRC(5) +======== + + +NAME +---- +cgitrc - runtime configuration for cgit + + +SYNOPSIS +-------- +Cgitrc contains all runtime settings for cgit, including the list of git +repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank +lines, and lines starting with '#', are ignored. + + +LOCATION +-------- +The default location of cgitrc, defined at compile time, is /etc/cgitrc. At +runtime, cgit will consult the environment variable CGIT_CONFIG and, if +defined, use its value instead. + + +GLOBAL SETTINGS +--------------- +about-filter:: + Specifies a command which will be invoked to format the content of + about pages (both top-level and for each repository). The command will + get the content of the about-file on its STDIN, the name of the file + as the first argument, and the STDOUT from the command will be + included verbatim on the about page. Default value: none. See + also: "FILTER API". + +agefile:: + Specifies a path, relative to each repository path, which can be used + to specify the date and time of the youngest commit in the repository. + The first line in the file is used as input to the "parse_date" + function in libgit. Recommended timestamp-format is "yyyy-mm-dd + hh:mm:ss". You may want to generate this file from a post-receive + hook. Default value: "info/web/last-modified". + +auth-filter:: + Specifies a command that will be invoked for authenticating repository + access. Receives quite a few arguments, and data on both stdin and + stdout for authentication processing. Details follow later in this + document. If no auth-filter is specified, no authentication is + performed. Default value: none. See also: "FILTER API". + +branch-sort:: + Flag which, when set to "age", enables date ordering in the branch ref + list, and when set to "name" enables ordering by branch name. Default + value: "name". + +cache-about-ttl:: + Number which specifies the time-to-live, in minutes, for the cached + version of the repository about page. See also: "CACHE". Default + value: "15". + +cache-dynamic-ttl:: + Number which specifies the time-to-live, in minutes, for the cached + version of repository pages accessed without a fixed SHA1. See also: + "CACHE". Default value: "5". + +cache-repo-ttl:: + Number which specifies the time-to-live, in minutes, for the cached + version of the repository summary page. See also: "CACHE". Default + value: "5". + +cache-root:: + Path used to store the cgit cache entries. Default value: + "/var/cache/cgit". See also: "MACRO EXPANSION". + +cache-root-ttl:: + Number which specifies the time-to-live, in minutes, for the cached + version of the repository index page. See also: "CACHE". Default + value: "5". + +cache-scanrc-ttl:: + Number which specifies the time-to-live, in minutes, for the result + of scanning a path for git repositories. See also: "CACHE". Default + value: "15". + +case-sensitive-sort:: + Sort items in the repo list case sensitively. Default value: "1". + See also: repository-sort, section-sort. + +cache-size:: + The maximum number of entries in the cgit cache. When set to "0", + caching is disabled. See also: "CACHE". Default value: "0" + +cache-snapshot-ttl:: + Number which specifies the time-to-live, in minutes, for the cached + version of snapshots. See also: "CACHE". Default value: "5". + +cache-static-ttl:: + Number which specifies the time-to-live, in minutes, for the cached + version of repository pages accessed with a fixed SHA1. See also: + "CACHE". Default value: -1". + +clone-prefix:: + Space-separated list of common prefixes which, when combined with a + repository url, generates valid clone urls for the repository. This + setting is only used if `repo.clone-url` is unspecified. Default value: + none. + +clone-url:: + Space-separated list of clone-url templates. This setting is only + used if `repo.clone-url` is unspecified. Default value: none. See + also: "MACRO EXPANSION", "FILTER API". + +commit-filter:: + Specifies a command which will be invoked to format commit messages. + The command will get the message on its STDIN, and the STDOUT from the + command will be included verbatim as the commit message, i.e. this can + be used to implement bugtracker integration. Default value: none. + See also: "FILTER API". + +commit-sort:: + Flag which, when set to "date", enables strict date ordering in the + commit log, and when set to "topo" enables strict topological + ordering. If unset, the default ordering of "git log" is used. Default + value: unset. + +css:: + Url which specifies the css document to include in all cgit pages. + Default value: "/cgit.css". + +email-filter:: + Specifies a command which will be invoked to format names and email + address of committers, authors, and taggers, as represented in various + places throughout the cgit interface. This command will receive an + email address and an origin page string as its command line arguments, + and the text to format on STDIN. It is to write the formatted text back + out onto STDOUT. Default value: none. See also: "FILTER API". + +embedded:: + Flag which, when set to "1", will make cgit generate a html fragment + suitable for embedding in other html pages. Default value: none. See + also: "noheader". + +enable-blame:: + Flag which, when set to "1", will allow cgit to provide a "blame" page + for files, and will make it generate links to that page in appropriate + places. Default value: "0". + +enable-commit-graph:: + Flag which, when set to "1", will make cgit print an ASCII-art commit + history graph to the left of the commit messages in the repository + log page. Default value: "0". + +enable-filter-overrides:: + Flag which, when set to "1", allows all filter settings to be + overridden in repository-specific cgitrc files. Default value: none. + +enable-follow-links:: + Flag which, when set to "1", allows users to follow a file in the log + view. Default value: "0". + +enable-git-config:: + Flag which, when set to "1", will allow cgit to use git config to set + any repo specific settings. This option is used in conjunction with + "scan-path", and must be defined prior, to augment repo-specific + settings. The keys gitweb.owner, gitweb.category, gitweb.description, + and gitweb.homepage will map to the cgit keys repo.owner, repo.section, + repo.desc, and repo.homepage respectively. All git config keys that begin + with "cgit." will be mapped to the corresponding "repo." key in cgit. + Default value: "0". See also: scan-path, section-from-path. + +enable-http-clone:: + If set to "1", cgit will act as a dumb HTTP endpoint for git clones. + You can add "http://$HTTP_HOST$SCRIPT_NAME/$CGIT_REPO_URL" to clone-url + to expose this feature. If you use an alternate way of serving git + repositories, you may wish to disable this. Default value: "1". + +enable-html-serving:: + Flag which, when set to "1", will allow the /plain handler to serve + mimetype headers that result in the file being treated as HTML by the + browser. When set to "0", such file types are returned instead as + text/plain or application/octet-stream. Default value: "0". See also: + "repo.enable-html-serving". + +enable-index-links:: + Flag which, when set to "1", will make cgit generate extra links for + each repo in the repository index (specifically, to the "summary", + "commit" and "tree" pages). Default value: "0". + +enable-index-owner:: + Flag which, when set to "1", will make cgit display the owner of + each repo in the repository index. Default value: "1". + +enable-log-filecount:: + Flag which, when set to "1", will make cgit print the number of + modified files for each commit on the repository log page. Default + value: "0". + +enable-log-linecount:: + Flag which, when set to "1", will make cgit print the number of added + and removed lines for each commit on the repository log page. Default + value: "0". + +enable-remote-branches:: + Flag which, when set to "1", will make cgit display remote branches + in the summary and refs views. Default value: "0". See also: + "repo.enable-remote-branches". + +enable-subject-links:: + Flag which, when set to "1", will make cgit use the subject of the + parent commit as link text when generating links to parent commits + in commit view. Default value: "0". See also: + "repo.enable-subject-links". + +enable-tree-linenumbers:: + Flag which, when set to "1", will make cgit generate linenumber links + for plaintext blobs printed in the tree view. Default value: "1". + +favicon:: + Url used as link to a shortcut icon for cgit. It is suggested to use + the value "/favicon.ico" since certain browsers will ignore other + values. Default value: "/favicon.ico". + +footer:: + The content of the file specified with this option will be included + verbatim at the bottom of all pages (i.e. it replaces the standard + "generated by..." message. Default value: none. + +head-include:: + The content of the file specified with this option will be included + verbatim in the html HEAD section on all pages. Default value: none. + +header:: + The content of the file specified with this option will be included + verbatim at the top of all pages. Default value: none. + +include:: + Name of a configfile to include before the rest of the current config- + file is parsed. Default value: none. See also: "MACRO EXPANSION". + +local-time:: + Flag which, if set to "1", makes cgit print commit and tag times in the + servers timezone. Default value: "0". + +logo:: + Url which specifies the source of an image which will be used as a logo + on all cgit pages. Default value: "/cgit.png". + +logo-link:: + Url loaded when clicking on the cgit logo image. If unspecified the + calculated url of the repository index page will be used. Default + value: none. + +max-atom-items:: + Specifies the number of items to display in atom feeds view. Default + value: "10". + +max-blob-size:: + Specifies the maximum size of a blob to display HTML for in KBytes. + Default value: "0" (limit disabled). + +max-commit-count:: + Specifies the number of entries to list per page in "log" view. Default + value: "50". + +max-message-length:: + Specifies the maximum number of commit message characters to display in + "log" view. Default value: "80". + +max-repo-count:: + Specifies the number of entries to list per page on the repository + index page. Default value: "50". + +max-repodesc-length:: + Specifies the maximum number of repo description characters to display + on the repository index page. Default value: "80". + +max-stats:: + Set the default maximum statistics period. Valid values are "week", + "month", "quarter" and "year". If unspecified, statistics are + disabled. Default value: none. See also: "repo.max-stats". + +mimetype.<ext>:: + Set the mimetype for the specified filename extension. This is used + by the `plain` command when returning blob content. + +mimetype-file:: + Specifies the file to use for automatic mimetype lookup. If specified + then this field is used as a fallback when no "mimetype.<ext>" match is + found. If unspecified then no such lookup is performed. The typical file + to use on a Linux system is /etc/mime.types. The format of the file must + comply to: + - a comment line is an empty line or a line starting with a hash (#), + optionally preceded by whitespace + - a non-comment line starts with the mimetype (like image/png), followed + by one or more file extensions (like jpg), all separated by whitespace + Default value: none. See also: "mimetype.<ext>". + +module-link:: + Text which will be used as the formatstring for a hyperlink when a + submodule is printed in a directory listing. The arguments for the + formatstring are the path and SHA1 of the submodule commit. Default + value: none. + +noplainemail:: + If set to "1" showing full author email addresses will be disabled. + Default value: "0". + +noheader:: + Flag which, when set to "1", will make cgit omit the standard header + on all pages. Default value: none. See also: "embedded". + +owner-filter:: + Specifies a command which will be invoked to format the Owner + column of the main page. The command will get the owner on STDIN, + and the STDOUT from the command will be included verbatim in the + table. This can be used to link to additional context such as an + owners home page. When active this filter is used instead of the + default owner query url. Default value: none. + See also: "FILTER API". + +project-list:: + A list of subdirectories inside of scan-path, relative to it, that + should loaded as git repositories. This must be defined prior to + scan-path. Default value: none. See also: scan-path, "MACRO + EXPANSION". + +readme:: + Text which will be used as default value for "repo.readme". Multiple + config keys may be specified, and cgit will use the first found file + in this list. This is useful in conjunction with scan-path. Default + value: none. See also: scan-path, repo.readme. + +remove-suffix:: + If set to "1" and scan-path is enabled, if any repositories are found + with a suffix of ".git", this suffix will be removed for the url and + name. This must be defined prior to scan-path. Default value: "0". + See also: scan-path. + +renamelimit:: + Maximum number of files to consider when detecting renames. The value + "-1" uses the compiletime value in git (for further info, look at + `man git-diff`). Default value: "-1". + +repository-sort:: + The way in which repositories in each section are sorted. Valid values + are "name" for sorting by the repo name or "age" for sorting by the + most recently updated repository. Default value: "name". See also: + section, case-sensitive-sort, section-sort. + +robots:: + Text used as content for the "robots" meta-tag. Default value: + "index, nofollow". + +root-desc:: + Text printed below the heading on the repository index page. Default + value: "a fast webinterface for the git dscm". + +root-readme:: + The content of the file specified with this option will be included + verbatim below the "about" link on the repository index page. Default + value: none. + +root-title:: + Text printed as heading on the repository index page. Default value: + "Git Repository Browser". + +scan-hidden-path:: + If set to "1" and scan-path is enabled, scan-path will recurse into + directories whose name starts with a period ('.'). Otherwise, + scan-path will stay away from such directories (considered as + "hidden"). Note that this does not apply to the ".git" directory in + non-bare repos. This must be defined prior to scan-path. + Default value: 0. See also: scan-path. + +scan-path:: + A path which will be scanned for repositories. If caching is enabled, + the result will be cached as a cgitrc include-file in the cache + directory. If project-list has been defined prior to scan-path, + scan-path loads only the directories listed in the file pointed to by + project-list. Be advised that only the global settings taken + before the scan-path directive will be applied to each repository. + Default value: none. See also: cache-scanrc-ttl, project-list, + "MACRO EXPANSION". + +section:: + The name of the current repository section - all repositories defined + after this option will inherit the current section name. Default value: + none. + +section-sort:: + Flag which, when set to "1", will sort the sections on the repository + listing by name. Set this flag to "0" if the order in the cgitrc file should + be preserved. Default value: "1". See also: section, + case-sensitive-sort, repository-sort. + +section-from-path:: + A number which, if defined prior to scan-path, specifies how many + path elements from each repo path to use as a default section name. + If negative, cgit will discard the specified number of path elements + above the repo directory. Default value: "0". + +side-by-side-diffs:: + If set to "1" shows side-by-side diffs instead of unidiffs per + default. Default value: "0". + +snapshots:: + Text which specifies the default set of snapshot formats that cgit + generates links for. The value is a space-separated list of zero or + more of the values "tar", "tar.gz", "tar.bz2", "tar.lz", "tar.xz", + "tar.zst" and "zip". The special value "all" enables all snapshot + formats. Default value: none. + All compressors use default settings. Some settings can be influenced + with environment variables, for example set ZSTD_CLEVEL=10 in web + server environment for higher (but slower) zstd compression. + +source-filter:: + Specifies a command which will be invoked to format plaintext blobs + in the tree view. The command will get the blob content on its STDIN + and the name of the blob as its only command line argument. The STDOUT + from the command will be included verbatim as the blob contents, i.e. + this can be used to implement e.g. syntax highlighting. Default value: + none. See also: "FILTER API". + +summary-branches:: + Specifies the number of branches to display in the repository "summary" + view. Default value: "10". + +summary-log:: + Specifies the number of log entries to display in the repository + "summary" view. Default value: "10". + +summary-tags:: + Specifies the number of tags to display in the repository "summary" + view. Default value: "10". + +strict-export:: + Filename which, if specified, needs to be present within the repository + for cgit to allow access to that repository. This can be used to emulate + gitweb's EXPORT_OK and STRICT_EXPORT functionality and limit cgit's + repositories to match those exported by git-daemon. This option must + be defined prior to scan-path. + +virtual-root:: + Url which, if specified, will be used as root for all cgit links. It + will also cause cgit to generate 'virtual urls', i.e. urls like + '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default + value: none. + NOTE: cgit has recently learned how to use PATH_INFO to achieve the + same kind of virtual urls, so this option will probably be deprecated. + + +REPOSITORY SETTINGS +------------------- +repo.about-filter:: + Override the default about-filter. Default value: none. See also: + "enable-filter-overrides". See also: "FILTER API". + +repo.branch-sort:: + Flag which, when set to "age", enables date ordering in the branch ref + list, and when set to "name" enables ordering by branch name. Default + value: "name". + +repo.clone-url:: + A list of space-separated urls which can be used to clone this repo. + Default value: none. See also: "MACRO EXPANSION". + +repo.commit-filter:: + Override the default commit-filter. Default value: none. See also: + "enable-filter-overrides". See also: "FILTER API". + +repo.commit-sort:: + Flag which, when set to "date", enables strict date ordering in the + commit log, and when set to "topo" enables strict topological + ordering. If unset, the default ordering of "git log" is used. Default + value: unset. + +repo.defbranch:: + The name of the default branch for this repository. If no such branch + exists in the repository, the first branch name (when sorted) is used + as default instead. Default value: branch pointed to by HEAD, or + "master" if there is no suitable HEAD. + +repo.desc:: + The value to show as repository description. Default value: none. + +repo.email-filter:: + Override the default email-filter. Default value: none. See also: + "enable-filter-overrides". See also: "FILTER API". + +repo.enable-blame:: + A flag which can be used to disable the global setting + `enable-blame'. Default value: none. + +repo.enable-commit-graph:: + A flag which can be used to disable the global setting + `enable-commit-graph'. Default value: none. + +repo.enable-html-serving:: + A flag which can be used to override the global setting + `enable-html-serving`. Default value: none. + +repo.enable-log-filecount:: + A flag which can be used to disable the global setting + `enable-log-filecount'. Default value: none. + +repo.enable-log-linecount:: + A flag which can be used to disable the global setting + `enable-log-linecount'. Default value: none. + +repo.enable-remote-branches:: + Flag which, when set to "1", will make cgit display remote branches + in the summary and refs views. Default value: <enable-remote-branches>. + +repo.enable-subject-links:: + A flag which can be used to override the global setting + `enable-subject-links'. Default value: none. + +repo.extra-head-content:: + This value will be added verbatim to the head section of each page + displayed for this repo. Default value: none. + +repo.hide:: + Flag which, when set to "1", hides the repository from the repository + index. The repository can still be accessed by providing a direct path. + Default value: "0". See also: "repo.ignore". + +repo.homepage:: + The value to show as repository homepage. Default value: none. + +repo.ignore:: + Flag which, when set to "1", ignores the repository. The repository + is not shown in the index and cannot be accessed by providing a direct + path. Default value: "0". See also: "repo.hide". + +repo.logo:: + Url which specifies the source of an image which will be used as a logo + on this repo's pages. Default value: global logo. + +repo.logo-link:: + Url loaded when clicking on the cgit logo image. If unspecified the + calculated url of the repository index page will be used. Default + value: global logo-link. + +repo.module-link:: + Text which will be used as the formatstring for a hyperlink when a + submodule is printed in a directory listing. The arguments for the + formatstring are the path and SHA1 of the submodule commit. Default + value: <module-link> + +repo.module-link.<path>:: + Text which will be used as the formatstring for a hyperlink when a + submodule with the specified subdirectory path is printed in a + directory listing. The only argument for the formatstring is the SHA1 + of the submodule commit. Default value: none. + +repo.max-stats:: + Override the default maximum statistics period. Valid values are equal + to the values specified for the global "max-stats" setting. Default + value: none. + +repo.name:: + The value to show as repository name. Default value: <repo.url>. + +repo.owner:: + A value used to identify the owner of the repository. Default value: + none. + +repo.owner-filter:: + Override the default owner-filter. Default value: none. See also: + "enable-filter-overrides". See also: "FILTER API". + +repo.path:: + An absolute path to the repository directory. For non-bare repositories + this is the .git-directory. Default value: none. + +repo.readme:: + A path (relative to <repo.path>) which specifies a file to include + verbatim as the "About" page for this repo. You may also specify a + git refspec by head or by hash by prepending the refspec followed by + a colon. For example, "master:docs/readme.mkd". If the value begins + with a colon, i.e. ":docs/readme.rst", the default branch of the + repository will be used. Sharing any file will expose that entire + directory tree to the "/about/PATH" endpoints, so be sure that there + are no non-public files located in the same directory as the readme + file. Default value: <readme>. + +repo.section:: + Override the current section name for this repository. Default value: + none. + +repo.snapshots:: + A mask of snapshot formats for this repo that cgit generates links for, + restricted by the global "snapshots" setting. Default value: + <snapshots>. + +repo.snapshot-prefix:: + Prefix to use for snapshot links instead of the repository basename. + For example, the "linux-stable" repository may wish to set this to + "linux" so that snapshots are in the format "linux-3.15.4" instead + of "linux-stable-3.15.4". Default value: <empty> meaning to use + the repository basename. + +repo.source-filter:: + Override the default source-filter. Default value: none. See also: + "enable-filter-overrides". See also: "FILTER API". + +repo.url:: + The relative url used to access the repository. This must be the first + setting specified for each repo. Default value: none. + + +REPOSITORY-SPECIFIC CGITRC FILE +------------------------------- +When the option "scan-path" is used to auto-discover git repositories, cgit +will try to parse the file "cgitrc" within any found repository. Such a +repo-specific config file may contain any of the repo-specific options +described above, except "repo.url" and "repo.path". Additionally, the "filter" +options are only acknowledged in repo-specific config files when +"enable-filter-overrides" is set to "1". + +Note: the "repo." prefix is dropped from the option names in repo-specific +config files, e.g. "repo.desc" becomes "desc". + + +FILTER API +---------- +By default, filters are separate processes that are executed each time they +are needed. Alternative technologies may be used by prefixing the filter +specification with the relevant string; available values are: + +'exec:':: + The default "one process per filter" mode. + + +Parameters are provided to filters as follows. + +about filter:: + This filter is given a single parameter: the filename of the source + file to filter. The filter can use the filename to determine (for + example) the type of syntax to follow when formatting the readme file. + The about text that is to be filtered is available on standard input + and the filtered text is expected on standard output. + +auth filter:: + The authentication filter receives 12 parameters: + - filter action, explained below, which specifies which action the + filter is called for + - http cookie + - http method + - http referer + - http path + - http https flag + - cgit repo + - cgit page + - cgit url + - cgit login url + When the filter action is "body", this filter must write to output the + HTML for displaying the login form, which POSTs to the login url. When + the filter action is "authenticate-cookie", this filter must validate + the http cookie and return a 0 if it is invalid or 1 if it is invalid, + in the exit code / close function. If the filter action is + "authenticate-post", this filter receives POST'd parameters on + standard input, and should write a complete CGI response, preferably + with a 302 redirect, and write to output one or more "Set-Cookie" + HTTP headers, each followed by a newline. + +commit filter:: + This filter is given no arguments. The commit message text that is to + be filtered is available on standard input and the filtered text is + expected on standard output. + +email filter:: + This filter is given two parameters: the email address of the relevant + author and a string indicating the originating page. The filter will + then receive the text string to format on standard input and is + expected to write to standard output the formatted text to be included + in the page. + +owner filter:: + This filter is given no arguments. The owner text is available on + standard input and the filter is expected to write to standard + output. The output is included in the Owner column. + +source filter:: + This filter is given a single parameter: the filename of the source + file to filter. The filter can use the filename to determine (for + example) the syntax highlighting mode. The contents of the source + file that is to be filtered is available on standard input and the + filtered contents is expected on standard output. + + +All filters are handed the following environment variables: + +- CGIT_REPO_URL (from repo.url) +- CGIT_REPO_NAME (from repo.name) +- CGIT_REPO_PATH (from repo.path) +- CGIT_REPO_OWNER (from repo.owner) +- CGIT_REPO_DEFBRANCH (from repo.defbranch) +- CGIT_REPO_SECTION (from repo.section) +- CGIT_REPO_CLONE_URL (from repo.clone-url) + +If a setting is not defined for a repository and the corresponding global +setting is also not defined (if applicable), then the corresponding +environment variable will be unset. + + +MACRO EXPANSION +--------------- +The following cgitrc options support a simple macro expansion feature, +where tokens prefixed with "$" are replaced with the value of a similarly +named environment variable: + +- cache-root +- include +- project-list +- scan-path + +Macro expansion will also happen on the content of $CGIT_CONFIG, if +defined. + +One usage of this feature is virtual hosting, which in its simplest form +can be accomplished by adding the following line to /etc/cgitrc: + + include=/etc/cgitrc.d/$HTTP_HOST + +The following options are expanded during request processing, and support +the environment variables defined in "FILTER API": + +- clone-url +- repo.clone-url + + +CACHE +----- + +All cache ttl values are in minutes. Negative ttl values indicate that a page +type will never expire, and thus the first time a URL is accessed, the result +will be cached indefinitely, even if the underlying git repository changes. +Conversely, when a ttl value is zero, the cache is disabled for that +particular page type, and the page type is never cached. + +SIGNATURES +---------- + +Cgit can host .asc signatures corresponding to various snapshot formats, +through use of git notes. For example, the following command may be used to +add a signature to a .tar.xz archive: + + git notes --ref=refs/notes/signatures/tar.xz add -C "$( + gpg --output - --armor --detach-sign cgit-1.1.tar.xz | + git hash-object -w --stdin + )" v1.1 + +If it is instead desirable to attach a signature of the underlying .tar, this +will be linked, as a special case, beside a .tar.* link that does not have its +own signature. For example, a signature of a tarball of the latest tag might +be added with a similar command: + + tag="$(git describe --abbrev=0)" + git notes --ref=refs/notes/signatures/tar add -C "$( + git archive --format tar --prefix "cgit-${tag#v}/" "$tag" | + gpg --output - --armor --detach-sign | + git hash-object -w --stdin + )" "$tag" + +Since git-archive(1) is expected to produce stable output between versions, +this allows one to generate a long-term signature of the contents of a given +tag. + +EXAMPLE CGITRC FILE +------------------- + +.... +# Enable caching of up to 1000 output entries +cache-size=1000 + + +# Specify some default clone urls using macro expansion +clone-url=git://foo.org/$CGIT_REPO_URL git@foo.org:$CGIT_REPO_URL + +# Specify the css url +css=/css/cgit.css + + +# Show owner on index page +enable-index-owner=1 + + +# Allow http transport git clone +enable-http-clone=1 + + +# Show extra links for each repository on the index page +enable-index-links=1 + + +# Enable blame page and create links to it from tree page +enable-blame=1 + + +# Enable ASCII art commit history graph on the log pages +enable-commit-graph=1 + + +# Show number of affected files per commit on the log pages +enable-log-filecount=1 + + +# Show number of added/removed lines per commit on the log pages +enable-log-linecount=1 + + +# Sort branches by date +branch-sort=age + + +# Add a cgit favicon +favicon=/favicon.ico + + +# Use a custom logo +logo=/img/mylogo.png + + +# Enable statistics per week, month and quarter +max-stats=quarter + + +# Set the title and heading of the repository index page +root-title=example.com git repositories + + +# Set a subheading for the repository index page +root-desc=tracking the foobar development + + +# Include some more info about example.com on the index page +root-readme=/var/www/htdocs/about.html + + +# Allow download of tar.gz, tar.bz2 and zip-files +snapshots=tar.gz tar.bz2 zip + + +## +## List of common mimetypes +## + +mimetype.gif=image/gif +mimetype.html=text/html +mimetype.jpg=image/jpeg +mimetype.jpeg=image/jpeg +mimetype.pdf=application/pdf +mimetype.png=image/png +mimetype.svg=image/svg+xml + + +# Highlight source code with python pygments-based highlighter +source-filter=/var/www/cgit/filters/syntax-highlighting.py + +# Format markdown, restructuredtext, manpages, text files, and html files +# through the right converters +about-filter=/var/www/cgit/filters/about-formatting.sh + +## +## Search for these files in the root of the default branch of repositories +## for coming up with the about page: +## +readme=:README.md +readme=:readme.md +readme=:README.mkd +readme=:readme.mkd +readme=:README.rst +readme=:readme.rst +readme=:README.html +readme=:readme.html +readme=:README.htm +readme=:readme.htm +readme=:README.txt +readme=:readme.txt +readme=:README +readme=:readme +readme=:INSTALL.md +readme=:install.md +readme=:INSTALL.mkd +readme=:install.mkd +readme=:INSTALL.rst +readme=:install.rst +readme=:INSTALL.html +readme=:install.html +readme=:INSTALL.htm +readme=:install.htm +readme=:INSTALL.txt +readme=:install.txt +readme=:INSTALL +readme=:install + + +## +## List of repositories. +## PS: Any repositories listed when section is unset will not be +## displayed under a section heading +## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos') +## and included like this: +## include=/etc/cgitrepos +## + + +repo.url=foo +repo.path=/pub/git/foo.git +repo.desc=the master foo repository +repo.owner=fooman@example.com +repo.readme=info/web/about.html + + +repo.url=bar +repo.path=/pub/git/bar.git +repo.desc=the bars for your foo +repo.owner=barman@example.com +repo.readme=info/web/about.html + + +# The next repositories will be displayed under the 'extras' heading +section=extras + + +repo.url=baz +repo.path=/pub/git/baz.git +repo.desc=a set of extensions for bar users + +repo.url=wiz +repo.path=/pub/git/wiz.git +repo.desc=the wizard of foo + + +# Add some mirrored repositories +section=mirrors + + +repo.url=git +repo.path=/pub/git/git.git +repo.desc=the dscm + + +repo.url=linux +repo.path=/pub/git/linux.git +repo.desc=the kernel + +# Disable adhoc downloads of this repo +repo.snapshots=0 + +# Disable line-counts for this repo +repo.enable-log-linecount=0 + +# Restrict the max statistics period for this repo +repo.max-stats=month +.... + + +BUGS +---- +Comments currently cannot appear on the same line as a setting; the comment +will be included as part of the value. E.g. this line: + + robots=index # allow indexing + +will generate the following html element: + + <meta name='robots' content='index # allow indexing'/> + + + +AUTHOR +------ +Lars Hjemli <hjemli@gmail.com> +Jason A. Donenfeld <Jason@zx2c4.com> diff --git a/www/git.causal.agency/cgit/cmd.c b/www/git.causal.agency/cgit/cmd.c new file mode 100644 index 00000000..0eb75b1d --- /dev/null +++ b/www/git.causal.agency/cgit/cmd.c @@ -0,0 +1,208 @@ +/* cmd.c: the cgit command dispatcher + * + * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "cmd.h" +#include "cache.h" +#include "ui-shared.h" +#include "ui-atom.h" +#include "ui-blame.h" +#include "ui-blob.h" +#include "ui-clone.h" +#include "ui-commit.h" +#include "ui-diff.h" +#include "ui-log.h" +#include "ui-patch.h" +#include "ui-plain.h" +#include "ui-refs.h" +#include "ui-repolist.h" +#include "ui-snapshot.h" +#include "ui-stats.h" +#include "ui-summary.h" +#include "ui-tag.h" +#include "ui-tree.h" + +static void HEAD_fn(void) +{ + cgit_clone_head(); +} + +static void atom_fn(void) +{ + cgit_print_atom(ctx.qry.head, ctx.qry.path, ctx.cfg.max_atom_items); +} + +static void about_fn(void) +{ + if (ctx.repo) { + size_t path_info_len = ctx.env.path_info ? strlen(ctx.env.path_info) : 0; + if (!ctx.qry.path && + ctx.qry.url[strlen(ctx.qry.url) - 1] != '/' && + (!path_info_len || ctx.env.path_info[path_info_len - 1] != '/')) { + char *currenturl = cgit_currenturl(); + char *redirect = fmtalloc("%s/", currenturl); + cgit_redirect(redirect, true); + free(currenturl); + free(redirect); + } else if (ctx.repo->readme.nr) + cgit_print_repo_readme(ctx.qry.path); + else if (ctx.repo->homepage) + cgit_redirect(ctx.repo->homepage, false); + else { + char *currenturl = cgit_currenturl(); + char *redirect = fmtalloc("%s../", currenturl); + cgit_redirect(redirect, false); + free(currenturl); + free(redirect); + } + } else + cgit_print_site_readme(); +} + +static void blame_fn(void) +{ + if (ctx.repo->enable_blame) + cgit_print_blame(); + else + cgit_print_error_page(403, "Forbidden", "Blame is disabled"); +} + +static void blob_fn(void) +{ + cgit_print_blob(ctx.qry.oid, ctx.qry.path, ctx.qry.head, 0); +} + +static void commit_fn(void) +{ + cgit_print_commit(ctx.qry.oid, ctx.qry.path); +} + +static void diff_fn(void) +{ + cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 1, 0); +} + +static void rawdiff_fn(void) +{ + cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 1, 1); +} + +static void info_fn(void) +{ + cgit_clone_info(); +} + +static void log_fn(void) +{ + cgit_print_log(ctx.qry.oid, ctx.qry.ofs, ctx.cfg.max_commit_count, + ctx.qry.grep, ctx.qry.search, ctx.qry.path, 1, + ctx.repo->enable_commit_graph, + ctx.repo->commit_sort); +} + +static void ls_cache_fn(void) +{ + ctx.page.mimetype = "text/plain"; + ctx.page.filename = "ls-cache.txt"; + cgit_print_http_headers(); + cache_ls(ctx.cfg.cache_root); +} + +static void objects_fn(void) +{ + cgit_clone_objects(); +} + +static void repolist_fn(void) +{ + cgit_print_repolist(); +} + +static void patch_fn(void) +{ + cgit_print_patch(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path); +} + +static void plain_fn(void) +{ + cgit_print_plain(); +} + +static void refs_fn(void) +{ + cgit_print_refs(); +} + +static void snapshot_fn(void) +{ + cgit_print_snapshot(ctx.qry.head, ctx.qry.oid, ctx.qry.path, + ctx.qry.nohead); +} + +static void stats_fn(void) +{ + cgit_show_stats(); +} + +static void summary_fn(void) +{ + cgit_print_summary(); +} + +static void tag_fn(void) +{ + cgit_print_tag(ctx.qry.oid); +} + +static void tree_fn(void) +{ + cgit_print_tree(ctx.qry.oid, ctx.qry.path); +} + +#define def_cmd(name, want_repo, want_vpath, is_clone) \ + {#name, name##_fn, want_repo, want_vpath, is_clone} + +struct cgit_cmd *cgit_get_cmd(void) +{ + static struct cgit_cmd cmds[] = { + def_cmd(HEAD, 1, 0, 1), + def_cmd(atom, 1, 0, 0), + def_cmd(about, 0, 0, 0), + def_cmd(blame, 1, 1, 0), + def_cmd(blob, 1, 0, 0), + def_cmd(commit, 1, 1, 0), + def_cmd(diff, 1, 1, 0), + def_cmd(info, 1, 0, 1), + def_cmd(log, 1, 1, 0), + def_cmd(ls_cache, 0, 0, 0), + def_cmd(objects, 1, 0, 1), + def_cmd(patch, 1, 1, 0), + def_cmd(plain, 1, 0, 0), + def_cmd(rawdiff, 1, 1, 0), + def_cmd(refs, 1, 0, 0), + def_cmd(repolist, 0, 0, 0), + def_cmd(snapshot, 1, 0, 0), + def_cmd(stats, 1, 1, 0), + def_cmd(summary, 1, 0, 0), + def_cmd(tag, 1, 0, 0), + def_cmd(tree, 1, 1, 0), + }; + int i; + + if (ctx.qry.page == NULL) { + if (ctx.repo) + ctx.qry.page = "summary"; + else + ctx.qry.page = "repolist"; + } + + for (i = 0; i < sizeof(cmds)/sizeof(*cmds); i++) + if (!strcmp(ctx.qry.page, cmds[i].name)) + return &cmds[i]; + return NULL; +} diff --git a/www/git.causal.agency/cgit/cmd.h b/www/git.causal.agency/cgit/cmd.h new file mode 100644 index 00000000..6249b1d8 --- /dev/null +++ b/www/git.causal.agency/cgit/cmd.h @@ -0,0 +1,16 @@ +#ifndef CMD_H +#define CMD_H + +typedef void (*cgit_cmd_fn)(void); + +struct cgit_cmd { + const char *name; + cgit_cmd_fn fn; + unsigned int want_repo:1, + want_vpath:1, + is_clone:1; +}; + +extern struct cgit_cmd *cgit_get_cmd(void); + +#endif /* CMD_H */ diff --git a/www/git.causal.agency/cgit/configfile.c b/www/git.causal.agency/cgit/configfile.c new file mode 100644 index 00000000..e0391091 --- /dev/null +++ b/www/git.causal.agency/cgit/configfile.c @@ -0,0 +1,90 @@ +/* configfile.c: parsing of config files + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include <git-compat-util.h> +#include "configfile.h" + +static int next_char(FILE *f) +{ + int c = fgetc(f); + if (c == '\r') { + c = fgetc(f); + if (c != '\n') { + ungetc(c, f); + c = '\r'; + } + } + return c; +} + +static void skip_line(FILE *f) +{ + int c; + + while ((c = next_char(f)) && c != '\n' && c != EOF) + ; +} + +static int read_config_line(FILE *f, struct strbuf *name, struct strbuf *value) +{ + int c = next_char(f); + + strbuf_reset(name); + strbuf_reset(value); + + /* Skip comments and preceding spaces. */ + for(;;) { + if (c == EOF) + return 0; + else if (c == '#' || c == ';') + skip_line(f); + else if (!isspace(c)) + break; + c = next_char(f); + } + + /* Read variable name. */ + while (c != '=') { + if (c == '\n' || c == EOF) + return 0; + strbuf_addch(name, c); + c = next_char(f); + } + + /* Read variable value. */ + c = next_char(f); + while (c != '\n' && c != EOF) { + strbuf_addch(value, c); + c = next_char(f); + } + + return 1; +} + +int parse_configfile(const char *filename, configfile_value_fn fn) +{ + static int nesting; + struct strbuf name = STRBUF_INIT; + struct strbuf value = STRBUF_INIT; + FILE *f; + + /* cancel deeply nested include-commands */ + if (nesting > 8) + return -1; + if (!(f = fopen(filename, "r"))) + return -1; + nesting++; + while (read_config_line(f, &name, &value)) + fn(name.buf, value.buf); + nesting--; + fclose(f); + strbuf_release(&name); + strbuf_release(&value); + return 0; +} + diff --git a/www/git.causal.agency/cgit/configfile.h b/www/git.causal.agency/cgit/configfile.h new file mode 100644 index 00000000..af7ca197 --- /dev/null +++ b/www/git.causal.agency/cgit/configfile.h @@ -0,0 +1,10 @@ +#ifndef CONFIGFILE_H +#define CONFIGFILE_H + +#include "cgit.h" + +typedef void (*configfile_value_fn)(const char *name, const char *value); + +extern int parse_configfile(const char *filename, configfile_value_fn fn); + +#endif /* CONFIGFILE_H */ diff --git a/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile b/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile new file mode 100755 index 00000000..2f72ae9c --- /dev/null +++ b/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile @@ -0,0 +1,19 @@ +#!/bin/sh +# +# An example hook to update the "agefile" for CGit's idle time calculation. +# +# This hook assumes that you are using the default agefile location of +# "info/web/last-modified". If you change the value in your cgitrc then you +# must also change it here. +# +# To install the hook, copy (or link) it to the file "hooks/post-receive" in +# each of your repositories. +# + +agefile="$(git rev-parse --git-dir)"/info/web/last-modified + +mkdir -p "$(dirname "$agefile")" && +git for-each-ref \ + --sort=-authordate --count=1 \ + --format='%(authordate:iso8601)' \ + >"$agefile" diff --git a/www/git.causal.agency/cgit/favicon.ico b/www/git.causal.agency/cgit/favicon.ico new file mode 100644 index 00000000..56ff5938 --- /dev/null +++ b/www/git.causal.agency/cgit/favicon.ico Binary files differdiff --git a/www/git.causal.agency/cgit/filter.c b/www/git.causal.agency/cgit/filter.c new file mode 100644 index 00000000..2b6c838e --- /dev/null +++ b/www/git.causal.agency/cgit/filter.c @@ -0,0 +1,222 @@ +/* filter.c: filter framework functions + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "html.h" + +static inline void reap_filter(struct cgit_filter *filter) +{ + if (filter && filter->cleanup) + filter->cleanup(filter); +} + +void cgit_cleanup_filters(void) +{ + int i; + reap_filter(ctx.cfg.about_filter); + reap_filter(ctx.cfg.commit_filter); + reap_filter(ctx.cfg.source_filter); + reap_filter(ctx.cfg.email_filter); + reap_filter(ctx.cfg.owner_filter); + reap_filter(ctx.cfg.auth_filter); + for (i = 0; i < cgit_repolist.count; ++i) { + reap_filter(cgit_repolist.repos[i].about_filter); + reap_filter(cgit_repolist.repos[i].commit_filter); + reap_filter(cgit_repolist.repos[i].source_filter); + reap_filter(cgit_repolist.repos[i].email_filter); + reap_filter(cgit_repolist.repos[i].owner_filter); + } +} + +static int open_exec_filter(struct cgit_filter *base, va_list ap) +{ + struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base; + int pipe_fh[2]; + int i; + + for (i = 0; i < filter->base.argument_count; i++) + filter->argv[i + 1] = va_arg(ap, char *); + + chk_zero(fflush(stdout), "unable to flush STDOUT"); + filter->old_stdout = chk_positive(dup(STDOUT_FILENO), + "Unable to duplicate STDOUT"); + chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess"); + filter->pid = chk_non_negative(fork(), "Unable to create subprocess"); + if (filter->pid == 0) { + close(pipe_fh[1]); + chk_non_negative(dup2(pipe_fh[0], STDIN_FILENO), + "Unable to use pipe as STDIN"); + execvp(filter->cmd, filter->argv); + die_errno("Unable to exec subprocess %s", filter->cmd); + } + close(pipe_fh[0]); + chk_non_negative(dup2(pipe_fh[1], STDOUT_FILENO), + "Unable to use pipe as STDOUT"); + close(pipe_fh[1]); + return 0; +} + +static int close_exec_filter(struct cgit_filter *base) +{ + struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base; + int i, exit_status = 0; + + chk_zero(fflush(stdout), "unable to flush STDOUT"); + chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO), + "Unable to restore STDOUT"); + close(filter->old_stdout); + if (filter->pid < 0) + goto done; + waitpid(filter->pid, &exit_status, 0); + if (WIFEXITED(exit_status)) + goto done; + die("Subprocess %s exited abnormally", filter->cmd); + +done: + for (i = 0; i < filter->base.argument_count; i++) + filter->argv[i + 1] = NULL; + return WEXITSTATUS(exit_status); + +} + +static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, const char *prefix) +{ + struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base; + fprintf(f, "%sexec:%s\n", prefix, filter->cmd); +} + +static void cleanup_exec_filter(struct cgit_filter *base) +{ + struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base; + if (filter->argv) { + free(filter->argv); + filter->argv = NULL; + } + if (filter->cmd) { + free(filter->cmd); + filter->cmd = NULL; + } +} + +static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count) +{ + struct cgit_exec_filter *f; + int args_size = 0; + + f = xmalloc(sizeof(*f)); + /* We leave argv for now and assign it below. */ + cgit_exec_filter_init(f, xstrdup(cmd), NULL); + f->base.argument_count = argument_count; + args_size = (2 + argument_count) * sizeof(char *); + f->argv = xmalloc(args_size); + memset(f->argv, 0, args_size); + f->argv[0] = f->cmd; + return &f->base; +} + +void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv) +{ + memset(filter, 0, sizeof(*filter)); + filter->base.open = open_exec_filter; + filter->base.close = close_exec_filter; + filter->base.fprintf = fprintf_exec_filter; + filter->base.cleanup = cleanup_exec_filter; + filter->cmd = cmd; + filter->argv = argv; + /* The argument count for open_filter is zero by default, unless called from new_filter, above. */ + filter->base.argument_count = 0; +} + +int cgit_open_filter(struct cgit_filter *filter, ...) +{ + int result; + va_list ap; + if (!filter) + return 0; + va_start(ap, filter); + result = filter->open(filter, ap); + va_end(ap); + return result; +} + +int cgit_close_filter(struct cgit_filter *filter) +{ + if (!filter) + return 0; + return filter->close(filter); +} + +void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix) +{ + filter->fprintf(filter, f, prefix); +} + + + +static const struct { + const char *prefix; + struct cgit_filter *(*ctor)(const char *cmd, int argument_count); +} filter_specs[] = { + { "exec", new_exec_filter }, +}; + +struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) +{ + char *colon; + int i; + size_t len; + int argument_count; + + if (!cmd || !cmd[0]) + return NULL; + + colon = strchr(cmd, ':'); + len = colon - cmd; + /* + * In case we're running on Windows, don't allow a single letter before + * the colon. + */ + if (len == 1) + colon = NULL; + + switch (filtertype) { + case AUTH: + argument_count = 12; + break; + + case EMAIL: + argument_count = 2; + break; + + case OWNER: + argument_count = 0; + break; + + case SOURCE: + case ABOUT: + argument_count = 1; + break; + + case COMMIT: + default: + argument_count = 0; + break; + } + + /* If no prefix is given, exec filter is the default. */ + if (!colon) + return new_exec_filter(cmd, argument_count); + + for (i = 0; i < ARRAY_SIZE(filter_specs); i++) { + if (len == strlen(filter_specs[i].prefix) && + !strncmp(filter_specs[i].prefix, cmd, len)) + return filter_specs[i].ctor(colon + 1, argument_count); + } + + die("Invalid filter type: %.*s", (int) len, cmd); +} diff --git a/www/git.causal.agency/cgit/filters/about-formatting.sh b/www/git.causal.agency/cgit/filters/about-formatting.sh new file mode 100755 index 00000000..85daf9c2 --- /dev/null +++ b/www/git.causal.agency/cgit/filters/about-formatting.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# This may be used with the about-filter or repo.about-filter setting in cgitrc. +# It passes formatting of about pages to differing programs, depending on the usage. + +# Markdown support requires python and markdown-python. +# RestructuredText support requires python and docutils. +# Man page support requires groff. + +# The following environment variables can be used to retrieve the configuration +# of the repository for which this script is called: +# CGIT_REPO_URL ( = repo.url setting ) +# CGIT_REPO_NAME ( = repo.name setting ) +# CGIT_REPO_PATH ( = repo.path setting ) +# CGIT_REPO_OWNER ( = repo.owner setting ) +# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting ) +# CGIT_REPO_SECTION ( = section setting ) +# CGIT_REPO_CLONE_URL ( = repo.clone-url setting ) + +cd "$(dirname $0)/html-converters/" +case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" in + *.markdown|*.mdown|*.md|*.mkd) exec ./md2html; ;; + *.rst) exec ./rst2html; ;; + *.[1-9]) exec ./man2html; ;; + *.htm|*.html) exec cat; ;; + *.txt|*) exec ./txt2html; ;; +esac diff --git a/www/git.causal.agency/cgit/filters/commit-links.sh b/www/git.causal.agency/cgit/filters/commit-links.sh new file mode 100755 index 00000000..796ac308 --- /dev/null +++ b/www/git.causal.agency/cgit/filters/commit-links.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# This script can be used to generate links in commit messages. +# +# To use this script, refer to this file with either the commit-filter or the +# repo.commit-filter options in cgitrc. +# +# The following environment variables can be used to retrieve the configuration +# of the repository for which this script is called: +# CGIT_REPO_URL ( = repo.url setting ) +# CGIT_REPO_NAME ( = repo.name setting ) +# CGIT_REPO_PATH ( = repo.path setting ) +# CGIT_REPO_OWNER ( = repo.owner setting ) +# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting ) +# CGIT_REPO_SECTION ( = section setting ) +# CGIT_REPO_CLONE_URL ( = repo.clone-url setting ) +# + +regex='' + +# This expression generates links to commits referenced by their SHA1. +regex=$regex' +s|\b([0-9a-fA-F]{7,64})\b|<a href="./?id=\1">\1</a>|g' + +# This expression generates links to a fictional bugtracker. +regex=$regex' +s|#([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g' + +sed -re "$regex" diff --git a/www/git.causal.agency/cgit/filters/email-gravatar.py b/www/git.causal.agency/cgit/filters/email-gravatar.py new file mode 100755 index 00000000..012113c5 --- /dev/null +++ b/www/git.causal.agency/cgit/filters/email-gravatar.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +# This script may be used with the email-filter or repo.email-filter settings in cgitrc. +# +# The following environment variables can be used to retrieve the configuration +# of the repository for which this script is called: +# CGIT_REPO_URL ( = repo.url setting ) +# CGIT_REPO_NAME ( = repo.name setting ) +# CGIT_REPO_PATH ( = repo.path setting ) +# CGIT_REPO_OWNER ( = repo.owner setting ) +# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting ) +# CGIT_REPO_SECTION ( = section setting ) +# CGIT_REPO_CLONE_URL ( = repo.clone-url setting ) +# +# It receives an email address on argv[1] and text on stdin. It prints +# to stdout that text prepended by a gravatar at 10pt. + +import sys +import hashlib +import codecs + +email = sys.argv[1].lower().strip() +if email[0] == '<': + email = email[1:] +if email[-1] == '>': + email = email[0:-1] + +page = sys.argv[2] + +sys.stdin = codecs.getreader("utf-8")(sys.stdin.detach()) +sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) + +md5 = hashlib.md5(email.encode()).hexdigest() +text = sys.stdin.read().strip() + +print("<img src='//www.gravatar.com/avatar/" + md5 + "?s=13&d=retro' width='13' height='13' alt='Gravatar' /> " + text) diff --git a/www/git.causal.agency/cgit/filters/html-converters/man2html b/www/git.causal.agency/cgit/filters/html-converters/man2html new file mode 100755 index 00000000..0ef78841 --- /dev/null +++ b/www/git.causal.agency/cgit/filters/html-converters/man2html @@ -0,0 +1,4 @@ +#!/bin/sh +echo "<div style=\"font-family: monospace\">" +groff -mandoc -T html -P -r -P -l | egrep -v '(<html>|<head>|<meta|<title>|</title>|</head>|<body>|</body>|</html>|<!DOCTYPE|"http://www.w3.org)' +echo "</div>" diff --git a/www/git.causal.agency/cgit/filters/html-converters/md2html b/www/git.causal.agency/cgit/filters/html-converters/md2html new file mode 100755 index 00000000..59f43a84 --- /dev/null +++ b/www/git.causal.agency/cgit/filters/html-converters/md2html @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +import markdown +import sys +import io +from pygments.formatters import HtmlFormatter +from markdown.extensions.toc import TocExtension +sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8') +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +sys.stdout.write(''' +<style> +.markdown-body { + font-size: 14px; + line-height: 1.6; + overflow: hidden; +} +.markdown-body>*:first-child { + margin-top: 0 !important; +} +.markdown-body>*:last-child { + margin-bottom: 0 !important; +} +.markdown-body a.absent { + color: #c00; +} +.markdown-body a.anchor { + display: block; + padding-left: 30px; + margin-left: -30px; + cursor: pointer; + position: absolute; + top: 0; + left: 0; + bottom: 0; +} +.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { + margin: 20px 0 10px; + padding: 0; + font-weight: bold; + -webkit-font-smoothing: antialiased; + cursor: text; + position: relative; +} +.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link { + display: none; + color: #000; +} +.markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor { + text-decoration: none; + line-height: 1; + padding-left: 0; + margin-left: -22px; + top: 15%; +} +.markdown-body h1:hover a.anchor .mini-icon-link, .markdown-body h2:hover a.anchor .mini-icon-link, .markdown-body h3:hover a.anchor .mini-icon-link, .markdown-body h4:hover a.anchor .mini-icon-link, .markdown-body h5:hover a.anchor .mini-icon-link, .markdown-body h6:hover a.anchor .mini-icon-link { + display: inline-block; +} +div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#cgit .markdown-body h3 a.toclink, div#cgit .markdown-body h4 a.toclink, div#cgit .markdown-body h5 a.toclink, div#cgit .markdown-body h6 a.toclink { + color: black; +} +.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code { + font-size: inherit; +} +.markdown-body h1 { + font-size: 28px; + color: #000; +} +.markdown-body h2 { + font-size: 24px; + border-bottom: 1px solid #ccc; + color: #000; +} +.markdown-body h3 { + font-size: 18px; +} +.markdown-body h4 { + font-size: 16px; +} +.markdown-body h5 { + font-size: 14px; +} +.markdown-body h6 { + color: #777; + font-size: 14px; +} +.markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre { + margin: 15px 0; +} +.markdown-body hr { + border: 2px solid #ccc; +} +.markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child { + margin-top: 0; + padding-top: 0; +} +.markdown-body a:first-child h1, .markdown-body a:first-child h2, .markdown-body a:first-child h3, .markdown-body a:first-child h4, .markdown-body a:first-child h5, .markdown-body a:first-child h6 { + margin-top: 0; + padding-top: 0; +} +.markdown-body h1+p, .markdown-body h2+p, .markdown-body h3+p, .markdown-body h4+p, .markdown-body h5+p, .markdown-body h6+p { + margin-top: 0; +} +.markdown-body li p.first { + display: inline-block; +} +.markdown-body ul, .markdown-body ol { + padding-left: 30px; +} +.markdown-body ul.no-list, .markdown-body ol.no-list { + list-style-type: none; + padding: 0; +} +.markdown-body ul li>:first-child, .markdown-body ul li ul:first-of-type, .markdown-body ul li ol:first-of-type, .markdown-body ol li>:first-child, .markdown-body ol li ul:first-of-type, .markdown-body ol li ol:first-of-type { + margin-top: 0px; +} +.markdown-body ul li p:last-of-type, .markdown-body ol li p:last-of-type { + margin-bottom: 0; +} +.markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul { + margin-bottom: 0; +} +.markdown-body dl { + padding: 0; +} +.markdown-body dl dt { + font-size: 14px; + font-weight: bold; + font-style: italic; + padding: 0; + margin: 15px 0 5px; +} +.markdown-body dl dt:first-child { + padding: 0; +} +.markdown-body dl dt>:first-child { + margin-top: 0px; +} +.markdown-body dl dt>:last-child { + margin-bottom: 0px; +} +.markdown-body dl dd { + margin: 0 0 15px; + padding: 0 15px; +} +.markdown-body dl dd>:first-child { + margin-top: 0px; +} +.markdown-body dl dd>:last-child { + margin-bottom: 0px; +} +.markdown-body blockquote { + border-left: 4px solid #DDD; + padding: 0 15px; + color: #777; +} +.markdown-body blockquote>:first-child { + margin-top: 0px; +} +.markdown-body blockquote>:last-child { + margin-bottom: 0px; +} +.markdown-body table th { + font-weight: bold; +} +.markdown-body table th, .markdown-body table td { + border: 1px solid #ccc; + padding: 6px 13px; +} +.markdown-body table tr { + border-top: 1px solid #ccc; + background-color: #fff; +} +.markdown-body table tr:nth-child(2n) { + background-color: #f8f8f8; +} +.markdown-body img { + max-width: 100%; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.markdown-body span.frame { + display: block; + overflow: hidden; +} +.markdown-body span.frame>span { + border: 1px solid #ddd; + display: block; + float: left; + overflow: hidden; + margin: 13px 0 0; + padding: 7px; + width: auto; +} +.markdown-body span.frame span img { + display: block; + float: left; +} +.markdown-body span.frame span span { + clear: both; + color: #333; + display: block; + padding: 5px 0 0; +} +.markdown-body span.align-center { + display: block; + overflow: hidden; + clear: both; +} +.markdown-body span.align-center>span { + display: block; + overflow: hidden; + margin: 13px auto 0; + text-align: center; +} +.markdown-body span.align-center span img { + margin: 0 auto; + text-align: center; +} +.markdown-body span.align-right { + display: block; + overflow: hidden; + clear: both; +} +.markdown-body span.align-right>span { + display: block; + overflow: hidden; + margin: 13px 0 0; + text-align: right; +} +.markdown-body span.align-right span img { + margin: 0; + text-align: right; +} +.markdown-body span.float-left { + display: block; + margin-right: 13px; + overflow: hidden; + float: left; +} +.markdown-body span.float-left span { + margin: 13px 0 0; +} +.markdown-body span.float-right { + display: block; + margin-left: 13px; + overflow: hidden; + float: right; +} +.markdown-body span.float-right>span { + display: block; + overflow: hidden; + margin: 13px auto 0; + text-align: right; +} +.markdown-body code, .markdown-body tt { + margin: 0 2px; + padding: 0px 5px; + border: 1px solid #eaeaea; + background-color: #f8f8f8; + border-radius: 3px; +} +.markdown-body code { + white-space: nowrap; +} +.markdown-body pre>code { + margin: 0; + padding: 0; + white-space: pre; + border: none; + background: transparent; +} +.markdown-body .highlight pre, .markdown-body pre { + background-color: #f8f8f8; + border: 1px solid #ccc; + font-size: 13px; + line-height: 19px; + overflow: auto; + padding: 6px 10px; + border-radius: 3px; +} +.markdown-body pre code, .markdown-body pre tt { + margin: 0; + padding: 0; + background-color: transparent; + border: none; +} +''') +sys.stdout.write(HtmlFormatter(style='pastie').get_style_defs('.highlight')) +sys.stdout.write(''' +</style> +''') +sys.stdout.write("<div class='markdown-body'>") +sys.stdout.flush() +# Note: you may want to run this through bleach for sanitization +markdown.markdownFromFile( + output_format="html5", + extensions=[ + "markdown.extensions.fenced_code", + "markdown.extensions.codehilite", + "markdown.extensions.tables", + "markdown.extensions.sane_lists", + TocExtension(anchorlink=True)], + extension_configs={ + "markdown.extensions.codehilite":{"css_class":"highlight"}}) +sys.stdout.write("</div>") diff --git a/www/git.causal.agency/cgit/filters/html-converters/rst2html b/www/git.causal.agency/cgit/filters/html-converters/rst2html new file mode 100755 index 00000000..02d90f81 --- /dev/null +++ b/www/git.causal.agency/cgit/filters/html-converters/rst2html @@ -0,0 +1,2 @@ +#!/bin/bash +exec rst2html.py --template <(echo -e "%(stylesheet)s\n%(body_pre_docinfo)s\n%(docinfo)s\n%(body)s") diff --git a/www/git.causal.agency/cgit/filters/html-converters/txt2html b/www/git.causal.agency/cgit/filters/html-converters/txt2html new file mode 100755 index 00000000..495eeceb --- /dev/null +++ b/www/git.causal.agency/cgit/filters/html-converters/txt2html @@ -0,0 +1,4 @@ +#!/bin/sh +echo "<pre>" +sed "s|&|\\&|g;s|'|\\'|g;s|\"|\\"|g;s|<|\\<|g;s|>|\\>|g" +echo "</pre>" diff --git a/www/git.causal.agency/cgit/filters/syntax-highlighting.py b/www/git.causal.agency/cgit/filters/syntax-highlighting.py new file mode 100755 index 00000000..e912594c --- /dev/null +++ b/www/git.causal.agency/cgit/filters/syntax-highlighting.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +# This script uses Pygments and Python3. You must have both installed +# for this to work. +# +# http://pygments.org/ +# http://python.org/ +# +# It may be used with the source-filter or repo.source-filter settings +# in cgitrc. +# +# The following environment variables can be used to retrieve the +# configuration of the repository for which this script is called: +# CGIT_REPO_URL ( = repo.url setting ) +# CGIT_REPO_NAME ( = repo.name setting ) +# CGIT_REPO_PATH ( = repo.path setting ) +# CGIT_REPO_OWNER ( = repo.owner setting ) +# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting ) +# CGIT_REPO_SECTION ( = section setting ) +# CGIT_REPO_CLONE_URL ( = repo.clone-url setting ) + + +import sys +import io +from pygments import highlight +from pygments.util import ClassNotFound +from pygments.lexers import TextLexer +from pygments.lexers import guess_lexer +from pygments.lexers import guess_lexer_for_filename +from pygments.formatters import HtmlFormatter + + +sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace') +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') +data = sys.stdin.read() +filename = sys.argv[1] +formatter = HtmlFormatter(style='pastie', nobackground=True) + +try: + lexer = guess_lexer_for_filename(filename, data) +except ClassNotFound: + # check if there is any shebang + if data[0:2] == '#!': + lexer = guess_lexer(data) + else: + lexer = TextLexer() +except TypeError: + lexer = TextLexer() + +# highlight! :-) +# printout pygments' css definitions as well +sys.stdout.write('<style>') +sys.stdout.write(formatter.get_style_defs('.highlight')) +sys.stdout.write('</style>') +sys.stdout.write(highlight(data, lexer, formatter, outfile=None)) diff --git a/www/git.causal.agency/cgit/filters/syntax-highlighting.sh b/www/git.causal.agency/cgit/filters/syntax-highlighting.sh new file mode 100755 index 00000000..840bc34f --- /dev/null +++ b/www/git.causal.agency/cgit/filters/syntax-highlighting.sh @@ -0,0 +1,121 @@ +#!/bin/sh +# This script can be used to implement syntax highlighting in the cgit +# tree-view by referring to this file with the source-filter or repo.source- +# filter options in cgitrc. +# +# This script requires a shell supporting the ${var##pattern} syntax. +# It is supported by at least dash and bash, however busybox environments +# might have to use an external call to sed instead. +# +# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax +# highlighting, so you'll probably want something like the following included +# in your css file: +# +# Style definition file generated by highlight 2.4.8, http://www.andre-simon.de/ +# +# table.blob .num { color:#2928ff; } +# table.blob .esc { color:#ff00ff; } +# table.blob .str { color:#ff0000; } +# table.blob .dstr { color:#818100; } +# table.blob .slc { color:#838183; font-style:italic; } +# table.blob .com { color:#838183; font-style:italic; } +# table.blob .dir { color:#008200; } +# table.blob .sym { color:#000000; } +# table.blob .kwa { color:#000000; font-weight:bold; } +# table.blob .kwb { color:#830000; } +# table.blob .kwc { color:#000000; font-weight:bold; } +# table.blob .kwd { color:#010181; } +# +# +# Style definition file generated by highlight 2.6.14, http://www.andre-simon.de/ +# +# body.hl { background-color:#ffffff; } +# pre.hl { color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New';} +# .hl.num { color:#2928ff; } +# .hl.esc { color:#ff00ff; } +# .hl.str { color:#ff0000; } +# .hl.dstr { color:#818100; } +# .hl.slc { color:#838183; font-style:italic; } +# .hl.com { color:#838183; font-style:italic; } +# .hl.dir { color:#008200; } +# .hl.sym { color:#000000; } +# .hl.line { color:#555555; } +# .hl.mark { background-color:#ffffbb;} +# .hl.kwa { color:#000000; font-weight:bold; } +# .hl.kwb { color:#830000; } +# .hl.kwc { color:#000000; font-weight:bold; } +# .hl.kwd { color:#010181; } +# +# +# Style definition file generated by highlight 3.8, http://www.andre-simon.de/ +# +# body.hl { background-color:#e0eaee; } +# pre.hl { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New';} +# .hl.num { color:#b07e00; } +# .hl.esc { color:#ff00ff; } +# .hl.str { color:#bf0303; } +# .hl.pps { color:#818100; } +# .hl.slc { color:#838183; font-style:italic; } +# .hl.com { color:#838183; font-style:italic; } +# .hl.ppc { color:#008200; } +# .hl.opt { color:#000000; } +# .hl.lin { color:#555555; } +# .hl.kwa { color:#000000; font-weight:bold; } +# .hl.kwb { color:#0057ae; } +# .hl.kwc { color:#000000; font-weight:bold; } +# .hl.kwd { color:#010181; } +# +# +# Style definition file generated by highlight 3.13, http://www.andre-simon.de/ +# +# body.hl { background-color:#e0eaee; } +# pre.hl { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New',monospace;} +# .hl.num { color:#b07e00; } +# .hl.esc { color:#ff00ff; } +# .hl.str { color:#bf0303; } +# .hl.pps { color:#818100; } +# .hl.slc { color:#838183; font-style:italic; } +# .hl.com { color:#838183; font-style:italic; } +# .hl.ppc { color:#008200; } +# .hl.opt { color:#000000; } +# .hl.ipl { color:#0057ae; } +# .hl.lin { color:#555555; } +# .hl.kwa { color:#000000; font-weight:bold; } +# .hl.kwb { color:#0057ae; } +# .hl.kwc { color:#000000; font-weight:bold; } +# .hl.kwd { color:#010181; } +# +# +# The following environment variables can be used to retrieve the configuration +# of the repository for which this script is called: +# CGIT_REPO_URL ( = repo.url setting ) +# CGIT_REPO_NAME ( = repo.name setting ) +# CGIT_REPO_PATH ( = repo.path setting ) +# CGIT_REPO_OWNER ( = repo.owner setting ) +# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting ) +# CGIT_REPO_SECTION ( = section setting ) +# CGIT_REPO_CLONE_URL ( = repo.clone-url setting ) +# + +# store filename and extension in local vars +BASENAME="$1" +EXTENSION="${BASENAME##*.}" + +[ "${BASENAME}" = "${EXTENSION}" ] && EXTENSION=txt +[ -z "${EXTENSION}" ] && EXTENSION=txt + +# map Makefile and Makefile.* to .mk +[ "${BASENAME%%.*}" = "Makefile" ] && EXTENSION=mk + +# highlight versions 2 and 3 have different commandline options. Specifically, +# the -X option that is used for version 2 is replaced by the -O xhtml option +# for version 3. +# +# Version 2 can be found (for example) on EPEL 5, while version 3 can be +# found (for example) on EPEL 6. +# +# This is for version 2 +exec highlight --force -f -I -X -S "$EXTENSION" 2>/dev/null + +# This is for version 3 +#exec highlight --force -f -I -O xhtml -S "$EXTENSION" 2>/dev/null diff --git a/www/git.causal.agency/cgit/gen-version.sh b/www/git.causal.agency/cgit/gen-version.sh new file mode 100755 index 00000000..80cf49af --- /dev/null +++ b/www/git.causal.agency/cgit/gen-version.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# Get version-info specified in Makefile +V=$1 + +# Use `git describe` to get current version if we're inside a git repo +if test "$(git rev-parse --git-dir 2>/dev/null)" = '.git' +then + V=$(git describe --abbrev=4 HEAD 2>/dev/null) +fi + +new="CGIT_VERSION = $V" +old=$(cat VERSION 2>/dev/null) + +# Exit if VERSION is uptodate +test "$old" = "$new" && exit 0 + +# Update VERSION with new version-info +echo "$new" > VERSION +cat VERSION diff --git a/www/git.causal.agency/cgit/html.c b/www/git.causal.agency/cgit/html.c new file mode 100644 index 00000000..cefcf5e7 --- /dev/null +++ b/www/git.causal.agency/cgit/html.c @@ -0,0 +1,344 @@ +/* html.c: helper functions for html output + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "html.h" +#include "url.h" + +/* Percent-encoding of each character, except: a-zA-Z0-9!$()*,./:;@- */ +static const char* url_escape_table[256] = { + "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", + "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f", + "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", + "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f", + "%20", NULL, "%22", "%23", NULL, "%25", "%26", "%27", + NULL, NULL, NULL, "%2b", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "%3c", "%3d", "%3e", "%3f", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "%5c", NULL, "%5e", NULL, + "%60", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "%7b", "%7c", "%7d", NULL, "%7f", + "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", + "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f", + "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", + "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f", + "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7", + "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af", + "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7", + "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf", + "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7", + "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf", + "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", + "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df", + "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7", + "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef", + "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", + "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff" +}; + +char *fmt(const char *format, ...) +{ + static char buf[8][1024]; + static int bufidx; + int len; + va_list args; + + bufidx++; + bufidx &= 7; + + va_start(args, format); + len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args); + va_end(args); + if (len > sizeof(buf[bufidx])) { + fprintf(stderr, "[html.c] string truncated: %s\n", format); + exit(1); + } + return buf[bufidx]; +} + +char *fmtalloc(const char *format, ...) +{ + struct strbuf sb = STRBUF_INIT; + va_list args; + + va_start(args, format); + strbuf_vaddf(&sb, format, args); + va_end(args); + + return strbuf_detach(&sb, NULL); +} + +void html_raw(const char *data, size_t size) +{ + if (fwrite(data, 1, size, stdout) != size) + die_errno("write error on html output"); +} + +void html(const char *txt) +{ + html_raw(txt, strlen(txt)); +} + +void htmlf(const char *format, ...) +{ + va_list args; + struct strbuf buf = STRBUF_INIT; + + va_start(args, format); + strbuf_vaddf(&buf, format, args); + va_end(args); + html(buf.buf); + strbuf_release(&buf); +} + +void html_txtf(const char *format, ...) +{ + va_list args; + + va_start(args, format); + html_vtxtf(format, args); + va_end(args); +} + +void html_vtxtf(const char *format, va_list ap) +{ + va_list cp; + struct strbuf buf = STRBUF_INIT; + + va_copy(cp, ap); + strbuf_vaddf(&buf, format, cp); + va_end(cp); + html_txt(buf.buf); + strbuf_release(&buf); +} + +void html_txt(const char *txt) +{ + if (txt) + html_ntxt(txt, strlen(txt)); +} + +ssize_t html_ntxt(const char *txt, size_t len) +{ + const char *t = txt; + ssize_t slen; + + if (len > SSIZE_MAX) + return -1; + + slen = (ssize_t) len; + while (t && *t && slen--) { + int c = *t; + if (c == '<' || c == '>' || c == '&') { + html_raw(txt, t - txt); + if (c == '>') + html(">"); + else if (c == '<') + html("<"); + else if (c == '&') + html("&"); + txt = t + 1; + } + t++; + } + if (t != txt) + html_raw(txt, t - txt); + return slen; +} + +void html_attrf(const char *fmt, ...) +{ + va_list ap; + struct strbuf sb = STRBUF_INIT; + + va_start(ap, fmt); + strbuf_vaddf(&sb, fmt, ap); + va_end(ap); + + html_attr(sb.buf); + strbuf_release(&sb); +} + +void html_attr(const char *txt) +{ + const char *t = txt; + while (t && *t) { + int c = *t; + if (c == '<' || c == '>' || c == '\'' || c == '\"' || c == '&') { + html_raw(txt, t - txt); + if (c == '>') + html(">"); + else if (c == '<') + html("<"); + else if (c == '\'') + html("'"); + else if (c == '"') + html("""); + else if (c == '&') + html("&"); + txt = t + 1; + } + t++; + } + if (t != txt) + html(txt); +} + +void html_url_path(const char *txt) +{ + const char *t = txt; + while (t && *t) { + unsigned char c = *t; + const char *e = url_escape_table[c]; + if (e && c != '+' && c != '&') { + html_raw(txt, t - txt); + html(e); + txt = t + 1; + } + t++; + } + if (t != txt) + html(txt); +} + +void html_url_arg(const char *txt) +{ + const char *t = txt; + while (t && *t) { + unsigned char c = *t; + const char *e = url_escape_table[c]; + if (c == ' ') + e = "+"; + if (e) { + html_raw(txt, t - txt); + html(e); + txt = t + 1; + } + t++; + } + if (t != txt) + html(txt); +} + +void html_header_arg_in_quotes(const char *txt) +{ + const char *t = txt; + while (t && *t) { + unsigned char c = *t; + const char *e = NULL; + if (c == '\\') + e = "\\\\"; + else if (c == '\r') + e = "\\r"; + else if (c == '\n') + e = "\\n"; + else if (c == '"') + e = "\\\""; + if (e) { + html_raw(txt, t - txt); + html(e); + txt = t + 1; + } + t++; + } + if (t != txt) + html(txt); + +} + +void html_hidden(const char *name, const char *value) +{ + html("<input type='hidden' name='"); + html_attr(name); + html("' value='"); + html_attr(value); + html("'/>"); +} + +void html_option(const char *value, const char *text, const char *selected_value) +{ + html("<option value='"); + html_attr(value); + html("'"); + if (selected_value && !strcmp(selected_value, value)) + html(" selected='selected'"); + html(">"); + html_txt(text); + html("</option>\n"); +} + +void html_intoption(int value, const char *text, int selected_value) +{ + htmlf("<option value='%d'%s>", value, + value == selected_value ? " selected='selected'" : ""); + html_txt(text); + html("</option>"); +} + +void html_link_open(const char *url, const char *title, const char *class) +{ + html("<a href='"); + html_attr(url); + if (title) { + html("' title='"); + html_attr(title); + } + if (class) { + html("' class='"); + html_attr(class); + } + html("'>"); +} + +void html_link_close(void) +{ + html("</a>"); +} + +void html_fileperm(unsigned short mode) +{ + htmlf("%c%c%c", (mode & 4 ? 'r' : '-'), + (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-')); +} + +int html_include(const char *filename) +{ + FILE *f; + char buf[4096]; + size_t len; + + if (!(f = fopen(filename, "r"))) { + fprintf(stderr, "[cgit] Failed to include file %s: %s (%d).\n", + filename, strerror(errno), errno); + return -1; + } + while ((len = fread(buf, 1, 4096, f)) > 0) + html_raw(buf, len); + fclose(f); + return 0; +} + +void http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value)) +{ + const char *t = txt; + + while (t && *t) { + char *name = url_decode_parameter_name(&t); + if (*name) { + char *value = url_decode_parameter_value(&t); + fn(name, value); + free(value); + } + free(name); + } +} diff --git a/www/git.causal.agency/cgit/html.h b/www/git.causal.agency/cgit/html.h new file mode 100644 index 00000000..fa4de775 --- /dev/null +++ b/www/git.causal.agency/cgit/html.h @@ -0,0 +1,37 @@ +#ifndef HTML_H +#define HTML_H + +#include "cgit.h" + +extern void html_raw(const char *txt, size_t size); +extern void html(const char *txt); + +__attribute__((format (printf,1,2))) +extern void htmlf(const char *format,...); + +__attribute__((format (printf,1,2))) +extern void html_txtf(const char *format,...); + +__attribute__((format (printf,1,0))) +extern void html_vtxtf(const char *format, va_list ap); + +__attribute__((format (printf,1,2))) +extern void html_attrf(const char *format,...); + +extern void html_txt(const char *txt); +extern ssize_t html_ntxt(const char *txt, size_t len); +extern void html_attr(const char *txt); +extern void html_url_path(const char *txt); +extern void html_url_arg(const char *txt); +extern void html_header_arg_in_quotes(const char *txt); +extern void html_hidden(const char *name, const char *value); +extern void html_option(const char *value, const char *text, const char *selected_value); +extern void html_intoption(int value, const char *text, int selected_value); +extern void html_link_open(const char *url, const char *title, const char *class); +extern void html_link_close(void); +extern void html_fileperm(unsigned short mode); +extern int html_include(const char *filename); + +extern void http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value)); + +#endif /* HTML_H */ diff --git a/www/git.causal.agency/cgit/parsing.c b/www/git.causal.agency/cgit/parsing.c new file mode 100644 index 00000000..72b59b3c --- /dev/null +++ b/www/git.causal.agency/cgit/parsing.c @@ -0,0 +1,223 @@ +/* parsing.c: parsing of config files + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" + +/* + * url syntax: [repo ['/' cmd [ '/' path]]] + * repo: any valid repo url, may contain '/' + * cmd: log | commit | diff | tree | view | blob | snapshot + * path: any valid path, may contain '/' + * + */ +void cgit_parse_url(const char *url) +{ + char *c, *cmd, *p; + struct cgit_repo *repo; + + if (!url || url[0] == '\0') + return; + + ctx.qry.page = NULL; + ctx.repo = cgit_get_repoinfo(url); + if (ctx.repo) { + ctx.qry.repo = ctx.repo->url; + return; + } + + cmd = NULL; + c = strchr(url, '/'); + while (c) { + c[0] = '\0'; + repo = cgit_get_repoinfo(url); + if (repo) { + ctx.repo = repo; + cmd = c; + } + c[0] = '/'; + c = strchr(c + 1, '/'); + } + + if (ctx.repo) { + ctx.qry.repo = ctx.repo->url; + p = strchr(cmd + 1, '/'); + if (p) { + p[0] = '\0'; + if (p[1]) + ctx.qry.path = trim_end(p + 1, '/'); + } + if (cmd[1]) + ctx.qry.page = xstrdup(cmd + 1); + } +} + +static char *substr(const char *head, const char *tail) +{ + char *buf; + + if (tail < head) + return xstrdup(""); + buf = xmalloc(tail - head + 1); + strlcpy(buf, head, tail - head + 1); + return buf; +} + +static void parse_user(const char *t, char **name, char **email, unsigned long *date, int *tz) +{ + struct ident_split ident; + unsigned email_len; + + if (!split_ident_line(&ident, t, strchrnul(t, '\n') - t)) { + *name = substr(ident.name_begin, ident.name_end); + + email_len = ident.mail_end - ident.mail_begin; + *email = xmalloc(strlen("<") + email_len + strlen(">") + 1); + xsnprintf(*email, email_len + 3, "<%.*s>", email_len, ident.mail_begin); + + if (ident.date_begin) + *date = strtoul(ident.date_begin, NULL, 10); + if (ident.tz_begin) + *tz = atoi(ident.tz_begin); + } +} + +#ifdef NO_ICONV +#define reencode(a, b, c) +#else +static const char *reencode(char **txt, const char *src_enc, const char *dst_enc) +{ + char *tmp; + + if (!txt) + return NULL; + + if (!*txt || !src_enc || !dst_enc) + return *txt; + + /* no encoding needed if src_enc equals dst_enc */ + if (!strcasecmp(src_enc, dst_enc)) + return *txt; + + tmp = reencode_string(*txt, dst_enc, src_enc); + if (tmp) { + free(*txt); + *txt = tmp; + } + return *txt; +} +#endif + +static const char *next_header_line(const char *p) +{ + p = strchr(p, '\n'); + if (!p) + return NULL; + return p + 1; +} + +static int end_of_header(const char *p) +{ + return !p || (*p == '\n'); +} + +struct commitinfo *cgit_parse_commit(struct commit *commit) +{ + struct commitinfo *ret; + const char *p = repo_get_commit_buffer(the_repository, commit, NULL); + const char *t; + + ret = xcalloc(1, sizeof(struct commitinfo)); + ret->commit = commit; + + if (!p) + return ret; + + if (!skip_prefix(p, "tree ", &p)) + die("Bad commit: %s", oid_to_hex(&commit->object.oid)); + p += the_hash_algo->hexsz + 1; + + while (skip_prefix(p, "parent ", &p)) + p += the_hash_algo->hexsz + 1; + + if (p && skip_prefix(p, "author ", &p)) { + parse_user(p, &ret->author, &ret->author_email, + &ret->author_date, &ret->author_tz); + p = next_header_line(p); + } + + if (p && skip_prefix(p, "committer ", &p)) { + parse_user(p, &ret->committer, &ret->committer_email, + &ret->committer_date, &ret->committer_tz); + p = next_header_line(p); + } + + if (p && skip_prefix(p, "encoding ", &p)) { + t = strchr(p, '\n'); + if (t) { + ret->msg_encoding = substr(p, t + 1); + p = t + 1; + } + } + + if (!ret->msg_encoding) + ret->msg_encoding = xstrdup("UTF-8"); + + while (!end_of_header(p)) + p = next_header_line(p); + while (p && *p == '\n') + p++; + if (!p) + return ret; + + t = strchrnul(p, '\n'); + ret->subject = substr(p, t); + while (*t == '\n') + t++; + ret->msg = xstrdup(t); + + reencode(&ret->author, ret->msg_encoding, PAGE_ENCODING); + reencode(&ret->author_email, ret->msg_encoding, PAGE_ENCODING); + reencode(&ret->committer, ret->msg_encoding, PAGE_ENCODING); + reencode(&ret->committer_email, ret->msg_encoding, PAGE_ENCODING); + reencode(&ret->subject, ret->msg_encoding, PAGE_ENCODING); + reencode(&ret->msg, ret->msg_encoding, PAGE_ENCODING); + + return ret; +} + +struct taginfo *cgit_parse_tag(struct tag *tag) +{ + void *data; + enum object_type type; + unsigned long size; + const char *p; + struct taginfo *ret = NULL; + + data = read_object_file(&tag->object.oid, &type, &size); + if (!data || type != OBJ_TAG) + goto cleanup; + + ret = xcalloc(1, sizeof(struct taginfo)); + + for (p = data; !end_of_header(p); p = next_header_line(p)) { + if (skip_prefix(p, "tagger ", &p)) { + parse_user(p, &ret->tagger, &ret->tagger_email, + &ret->tagger_date, &ret->tagger_tz); + } + } + + while (p && *p == '\n') + p++; + + if (p && *p) + ret->msg = xstrdup(p); + +cleanup: + free(data); + return ret; +} diff --git a/www/git.causal.agency/cgit/robots.txt b/www/git.causal.agency/cgit/robots.txt new file mode 100644 index 00000000..1b33266d --- /dev/null +++ b/www/git.causal.agency/cgit/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: /*/snapshot/* +Disallow: /*/blame/* +Allow: / diff --git a/www/git.causal.agency/cgit/scan-tree.c b/www/git.causal.agency/cgit/scan-tree.c new file mode 100644 index 00000000..6a2f65a8 --- /dev/null +++ b/www/git.causal.agency/cgit/scan-tree.c @@ -0,0 +1,270 @@ +/* scan-tree.c + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "scan-tree.h" +#include "configfile.h" +#include "html.h" +#include <config.h> + +/* return 1 if path contains a objects/ directory and a HEAD file */ +static int is_git_dir(const char *path) +{ + struct stat st; + struct strbuf pathbuf = STRBUF_INIT; + int result = 0; + + strbuf_addf(&pathbuf, "%s/objects", path); + if (stat(pathbuf.buf, &st)) { + if (errno != ENOENT) + fprintf(stderr, "Error checking path %s: %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + if (!S_ISDIR(st.st_mode)) + goto out; + + strbuf_reset(&pathbuf); + strbuf_addf(&pathbuf, "%s/HEAD", path); + if (stat(pathbuf.buf, &st)) { + if (errno != ENOENT) + fprintf(stderr, "Error checking path %s: %s (%d)\n", + path, strerror(errno), errno); + goto out; + } + if (!S_ISREG(st.st_mode)) + goto out; + + result = 1; +out: + strbuf_release(&pathbuf); + return result; +} + +static struct cgit_repo *repo; +static repo_config_fn config_fn; + +static void scan_tree_repo_config(const char *name, const char *value) +{ + config_fn(repo, name, value); +} + +static int gitconfig_config(const char *key, const char *value, void *cb) +{ + const char *name; + + if (!strcmp(key, "gitweb.owner")) + config_fn(repo, "owner", value); + else if (!strcmp(key, "gitweb.description")) + config_fn(repo, "desc", value); + else if (!strcmp(key, "gitweb.category")) + config_fn(repo, "section", value); + else if (!strcmp(key, "gitweb.homepage")) + config_fn(repo, "homepage", value); + else if (skip_prefix(key, "cgit.", &name)) + config_fn(repo, name, value); + + return 0; +} + +static char *xstrrchr(char *s, char *from, int c) +{ + while (from >= s && *from != c) + from--; + return from < s ? NULL : from; +} + +static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn) +{ + struct stat st; + struct passwd *pwd; + size_t pathlen; + struct strbuf rel = STRBUF_INIT; + char *p, *slash; + int n; + size_t size; + + if (stat(path->buf, &st)) { + fprintf(stderr, "Error accessing %s: %s (%d)\n", + path->buf, strerror(errno), errno); + return; + } + + strbuf_addch(path, '/'); + pathlen = path->len; + + if (ctx.cfg.strict_export) { + strbuf_addstr(path, ctx.cfg.strict_export); + if(stat(path->buf, &st)) + return; + strbuf_setlen(path, pathlen); + } + + strbuf_addstr(path, "noweb"); + if (!stat(path->buf, &st)) + return; + strbuf_setlen(path, pathlen); + + if (!starts_with(path->buf, base)) + strbuf_addbuf(&rel, path); + else + strbuf_addstr(&rel, path->buf + strlen(base) + 1); + + if (!strcmp(rel.buf + rel.len - 5, "/.git")) + strbuf_setlen(&rel, rel.len - 5); + else if (rel.len && rel.buf[rel.len - 1] == '/') + strbuf_setlen(&rel, rel.len - 1); + + repo = cgit_add_repo(rel.buf); + config_fn = fn; + if (ctx.cfg.enable_git_config) { + strbuf_addstr(path, "config"); + git_config_from_file(gitconfig_config, path->buf, NULL); + strbuf_setlen(path, pathlen); + } + + if (ctx.cfg.remove_suffix) { + size_t urllen; + strip_suffix(repo->url, ".git", &urllen); + strip_suffix_mem(repo->url, &urllen, "/"); + repo->url[urllen] = '\0'; + } + repo->path = xstrdup(path->buf); + while (!repo->owner) { + if ((pwd = getpwuid(st.st_uid)) == NULL) { + fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", + path->buf, strerror(errno), errno); + break; + } + if (pwd->pw_gecos) + if ((p = strchr(pwd->pw_gecos, ','))) + *p = '\0'; + repo->owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name); + } + + if (repo->desc == cgit_default_repo_desc || !repo->desc) { + strbuf_addstr(path, "description"); + if (!stat(path->buf, &st)) + readfile(path->buf, &repo->desc, &size); + strbuf_setlen(path, pathlen); + } + + if (ctx.cfg.section_from_path) { + n = ctx.cfg.section_from_path; + if (n > 0) { + slash = rel.buf - 1; + while (slash && n && (slash = strchr(slash + 1, '/'))) + n--; + } else { + slash = rel.buf + rel.len; + while (slash && n && (slash = xstrrchr(rel.buf, slash - 1, '/'))) + n++; + } + if (slash && !n) { + *slash = '\0'; + repo->section = xstrdup(rel.buf); + *slash = '/'; + if (starts_with(repo->name, repo->section)) { + repo->name += strlen(repo->section); + if (*repo->name == '/') + repo->name++; + } + } + } + + strbuf_addstr(path, "cgitrc"); + if (!stat(path->buf, &st)) + parse_configfile(path->buf, &scan_tree_repo_config); + + strbuf_release(&rel); +} + +static void scan_path(const char *base, const char *path, repo_config_fn fn) +{ + DIR *dir = opendir(path); + struct dirent *ent; + struct strbuf pathbuf = STRBUF_INIT; + size_t pathlen = strlen(path); + struct stat st; + + if (!dir) { + fprintf(stderr, "Error opening directory %s: %s (%d)\n", + path, strerror(errno), errno); + return; + } + + strbuf_add(&pathbuf, path, strlen(path)); + if (is_git_dir(pathbuf.buf)) { + add_repo(base, &pathbuf, fn); + goto end; + } + strbuf_addstr(&pathbuf, "/.git"); + if (is_git_dir(pathbuf.buf)) { + add_repo(base, &pathbuf, fn); + goto end; + } + /* + * Add one because we don't want to lose the trailing '/' when we + * reset the length of pathbuf in the loop below. + */ + pathlen++; + while ((ent = readdir(dir)) != NULL) { + if (ent->d_name[0] == '.') { + if (ent->d_name[1] == '\0') + continue; + if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') + continue; + if (!ctx.cfg.scan_hidden_path) + continue; + } + strbuf_setlen(&pathbuf, pathlen); + strbuf_addstr(&pathbuf, ent->d_name); + if (stat(pathbuf.buf, &st)) { + fprintf(stderr, "Error checking path %s: %s (%d)\n", + pathbuf.buf, strerror(errno), errno); + continue; + } + if (S_ISDIR(st.st_mode)) + scan_path(base, pathbuf.buf, fn); + } +end: + strbuf_release(&pathbuf); + closedir(dir); +} + +void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) +{ + struct strbuf line = STRBUF_INIT; + FILE *projects; + int err; + + projects = fopen(projectsfile, "r"); + if (!projects) { + fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n", + projectsfile, strerror(errno), errno); + return; + } + while (strbuf_getline(&line, projects) != EOF) { + if (!line.len) + continue; + strbuf_insert(&line, 0, "/", 1); + strbuf_insert(&line, 0, path, strlen(path)); + scan_path(path, line.buf, fn); + } + if ((err = ferror(projects))) { + fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n", + projectsfile, strerror(err), err); + } + fclose(projects); + strbuf_release(&line); +} + +void scan_tree(const char *path, repo_config_fn fn) +{ + scan_path(path, path, fn); +} diff --git a/www/git.causal.agency/cgit/scan-tree.h b/www/git.causal.agency/cgit/scan-tree.h new file mode 100644 index 00000000..1afbd4bb --- /dev/null +++ b/www/git.causal.agency/cgit/scan-tree.h @@ -0,0 +1,2 @@ +extern void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn); +extern void scan_tree(const char *path, repo_config_fn fn); diff --git a/www/git.causal.agency/cgit/shared.c b/www/git.causal.agency/cgit/shared.c new file mode 100644 index 00000000..8115469a --- /dev/null +++ b/www/git.causal.agency/cgit/shared.c @@ -0,0 +1,579 @@ +/* shared.c: global vars + some callback functions + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" + +struct cgit_repolist cgit_repolist; +struct cgit_context ctx; + +int chk_zero(int result, char *msg) +{ + if (result != 0) + die_errno("%s", msg); + return result; +} + +int chk_positive(int result, char *msg) +{ + if (result <= 0) + die_errno("%s", msg); + return result; +} + +int chk_non_negative(int result, char *msg) +{ + if (result < 0) + die_errno("%s", msg); + return result; +} + +char *cgit_default_repo_desc = "[no description]"; +struct cgit_repo *cgit_add_repo(const char *url) +{ + struct cgit_repo *ret; + + if (++cgit_repolist.count > cgit_repolist.length) { + if (cgit_repolist.length == 0) + cgit_repolist.length = 8; + else + cgit_repolist.length *= 2; + cgit_repolist.repos = xrealloc(cgit_repolist.repos, + cgit_repolist.length * + sizeof(struct cgit_repo)); + } + + ret = &cgit_repolist.repos[cgit_repolist.count-1]; + memset(ret, 0, sizeof(struct cgit_repo)); + ret->url = trim_end(url, '/'); + ret->name = ret->url; + ret->path = NULL; + ret->desc = cgit_default_repo_desc; + ret->extra_head_content = NULL; + ret->owner = NULL; + ret->homepage = NULL; + ret->section = ctx.cfg.section; + ret->snapshots = ctx.cfg.snapshots; + ret->enable_blame = ctx.cfg.enable_blame; + ret->enable_commit_graph = ctx.cfg.enable_commit_graph; + ret->enable_log_filecount = ctx.cfg.enable_log_filecount; + ret->enable_log_linecount = ctx.cfg.enable_log_linecount; + ret->enable_remote_branches = ctx.cfg.enable_remote_branches; + ret->enable_subject_links = ctx.cfg.enable_subject_links; + ret->enable_html_serving = ctx.cfg.enable_html_serving; + ret->max_stats = ctx.cfg.max_stats; + ret->branch_sort = ctx.cfg.branch_sort; + ret->commit_sort = ctx.cfg.commit_sort; + ret->module_link = ctx.cfg.module_link; + ret->readme = ctx.cfg.readme; + ret->mtime = -1; + ret->about_filter = ctx.cfg.about_filter; + ret->commit_filter = ctx.cfg.commit_filter; + ret->source_filter = ctx.cfg.source_filter; + ret->email_filter = ctx.cfg.email_filter; + ret->owner_filter = ctx.cfg.owner_filter; + ret->clone_url = ctx.cfg.clone_url; + ret->submodules.strdup_strings = 1; + ret->hide = ret->ignore = 0; + return ret; +} + +struct cgit_repo *cgit_get_repoinfo(const char *url) +{ + int i; + struct cgit_repo *repo; + + for (i = 0; i < cgit_repolist.count; i++) { + repo = &cgit_repolist.repos[i]; + if (repo->ignore) + continue; + if (!strcmp(repo->url, url)) + return repo; + } + return NULL; +} + +void cgit_free_commitinfo(struct commitinfo *info) +{ + free(info->author); + free(info->author_email); + free(info->committer); + free(info->committer_email); + free(info->subject); + free(info->msg); + free(info->msg_encoding); + free(info); +} + +char *trim_end(const char *str, char c) +{ + int len; + + if (str == NULL) + return NULL; + len = strlen(str); + while (len > 0 && str[len - 1] == c) + len--; + if (len == 0) + return NULL; + return xstrndup(str, len); +} + +char *ensure_end(const char *str, char c) +{ + size_t len = strlen(str); + char *result; + + if (len && str[len - 1] == c) + return xstrndup(str, len); + + result = xmalloc(len + 2); + memcpy(result, str, len); + result[len] = '/'; + result[len + 1] = '\0'; + return result; +} + +void strbuf_ensure_end(struct strbuf *sb, char c) +{ + if (!sb->len || sb->buf[sb->len - 1] != c) + strbuf_addch(sb, c); +} + +void cgit_add_ref(struct reflist *list, struct refinfo *ref) +{ + size_t size; + + if (list->count >= list->alloc) { + list->alloc += (list->alloc ? list->alloc : 4); + size = list->alloc * sizeof(struct refinfo *); + list->refs = xrealloc(list->refs, size); + } + list->refs[list->count++] = ref; +} + +static struct refinfo *cgit_mk_refinfo(const char *refname, const struct object_id *oid) +{ + struct refinfo *ref; + + ref = xmalloc(sizeof (struct refinfo)); + ref->refname = xstrdup(refname); + ref->object = parse_object(the_repository, oid); + switch (ref->object->type) { + case OBJ_TAG: + ref->tag = cgit_parse_tag((struct tag *)ref->object); + break; + case OBJ_COMMIT: + ref->commit = cgit_parse_commit((struct commit *)ref->object); + break; + } + return ref; +} + +void cgit_free_taginfo(struct taginfo *tag) +{ + if (tag->tagger) + free(tag->tagger); + if (tag->tagger_email) + free(tag->tagger_email); + if (tag->msg) + free(tag->msg); + free(tag); +} + +static void cgit_free_refinfo(struct refinfo *ref) +{ + if (ref->refname) + free((char *)ref->refname); + switch (ref->object->type) { + case OBJ_TAG: + cgit_free_taginfo(ref->tag); + break; + case OBJ_COMMIT: + cgit_free_commitinfo(ref->commit); + break; + } + free(ref); +} + +void cgit_free_reflist_inner(struct reflist *list) +{ + int i; + + for (i = 0; i < list->count; i++) { + cgit_free_refinfo(list->refs[i]); + } + free(list->refs); +} + +int cgit_refs_cb(const char *refname, const struct object_id *oid, int flags, + void *cb_data) +{ + struct reflist *list = (struct reflist *)cb_data; + struct refinfo *info = cgit_mk_refinfo(refname, oid); + + if (info) + cgit_add_ref(list, info); + return 0; +} + +void cgit_diff_tree_cb(struct diff_queue_struct *q, + struct diff_options *options, void *data) +{ + int i; + + for (i = 0; i < q->nr; i++) { + if (q->queue[i]->status == 'U') + continue; + ((filepair_fn)data)(q->queue[i]); + } +} + +static int load_mmfile(mmfile_t *file, const struct object_id *oid) +{ + enum object_type type; + + if (is_null_oid(oid)) { + file->ptr = (char *)""; + file->size = 0; + } else { + file->ptr = read_object_file(oid, &type, + (unsigned long *)&file->size); + } + return 1; +} + +/* + * Receive diff-buffers from xdiff and concatenate them as + * needed across multiple callbacks. + * + * This is basically a copy of xdiff-interface.c/xdiff_outf(), + * ripped from git and modified to use globals instead of + * a special callback-struct. + */ +static char *diffbuf = NULL; +static int buflen = 0; + +static int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) +{ + int i; + + for (i = 0; i < nbuf; i++) { + if (mb[i].ptr[mb[i].size-1] != '\n') { + /* Incomplete line */ + diffbuf = xrealloc(diffbuf, buflen + mb[i].size); + memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); + buflen += mb[i].size; + continue; + } + + /* we have a complete line */ + if (!diffbuf) { + ((linediff_fn)priv)(mb[i].ptr, mb[i].size); + continue; + } + diffbuf = xrealloc(diffbuf, buflen + mb[i].size); + memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); + ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); + free(diffbuf); + diffbuf = NULL; + buflen = 0; + } + if (diffbuf) { + ((linediff_fn)priv)(diffbuf, buflen); + free(diffbuf); + diffbuf = NULL; + buflen = 0; + } + return 0; +} + +int cgit_diff_files(const struct object_id *old_oid, + const struct object_id *new_oid, unsigned long *old_size, + unsigned long *new_size, int *binary, int context, + int ignorews, linediff_fn fn) +{ + mmfile_t file1, file2; + xpparam_t diff_params; + xdemitconf_t emit_params; + xdemitcb_t emit_cb; + + if (!load_mmfile(&file1, old_oid) || !load_mmfile(&file2, new_oid)) + return 1; + + *old_size = file1.size; + *new_size = file2.size; + + if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || + (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { + *binary = 1; + if (file1.size) + free(file1.ptr); + if (file2.size) + free(file2.ptr); + return 0; + } + + memset(&diff_params, 0, sizeof(diff_params)); + memset(&emit_params, 0, sizeof(emit_params)); + memset(&emit_cb, 0, sizeof(emit_cb)); + diff_params.flags = XDF_NEED_MINIMAL; + if (ignorews) + diff_params.flags |= XDF_IGNORE_WHITESPACE; + emit_params.ctxlen = context > 0 ? context : 3; + emit_params.flags = XDL_EMIT_FUNCNAMES; + emit_cb.out_line = filediff_cb; + emit_cb.priv = fn; + xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); + if (file1.size) + free(file1.ptr); + if (file2.size) + free(file2.ptr); + return 0; +} + +void cgit_diff_tree(const struct object_id *old_oid, + const struct object_id *new_oid, + filepair_fn fn, const char *prefix, int ignorews) +{ + struct diff_options opt; + struct pathspec_item item; + + memset(&item, 0, sizeof(item)); + diff_setup(&opt); + opt.output_format = DIFF_FORMAT_CALLBACK; + opt.detect_rename = 1; + opt.rename_limit = ctx.cfg.renamelimit; + opt.flags.recursive = 1; + if (ignorews) + DIFF_XDL_SET(&opt, IGNORE_WHITESPACE); + opt.format_callback = cgit_diff_tree_cb; + opt.format_callback_data = fn; + if (prefix) { + item.match = xstrdup(prefix); + item.len = strlen(prefix); + opt.pathspec.nr = 1; + opt.pathspec.items = &item; + } + diff_setup_done(&opt); + + if (old_oid && !is_null_oid(old_oid)) + diff_tree_oid(old_oid, new_oid, "", &opt); + else + diff_root_tree_oid(new_oid, "", &opt); + diffcore_std(&opt); + diff_flush(&opt); + + free(item.match); +} + +void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix) +{ + const struct object_id *old_oid = NULL; + + if (commit->parents) + old_oid = &commit->parents->item->object.oid; + cgit_diff_tree(old_oid, &commit->object.oid, fn, prefix, + ctx.qry.ignorews); +} + +int cgit_parse_snapshots_mask(const char *str) +{ + struct string_list tokens = STRING_LIST_INIT_DUP; + struct string_list_item *item; + const struct cgit_snapshot_format *f; + int rv = 0; + + /* favor legacy setting */ + if (atoi(str)) + return 1; + + if (strcmp(str, "all") == 0) + return INT_MAX; + + string_list_split(&tokens, str, ' ', -1); + string_list_remove_empty_items(&tokens, 0); + + for_each_string_list_item(item, &tokens) { + for (f = cgit_snapshot_formats; f->suffix; f++) { + if (!strcmp(item->string, f->suffix) || + !strcmp(item->string, f->suffix + 1)) { + rv |= cgit_snapshot_format_bit(f); + break; + } + } + } + + string_list_clear(&tokens, 0); + return rv; +} + +typedef struct { + char * name; + char * value; +} cgit_env_var; + +void cgit_prepare_repo_env(struct cgit_repo * repo) +{ + cgit_env_var env_vars[] = { + { .name = "CGIT_REPO_URL", .value = repo->url }, + { .name = "CGIT_REPO_NAME", .value = repo->name }, + { .name = "CGIT_REPO_PATH", .value = repo->path }, + { .name = "CGIT_REPO_OWNER", .value = repo->owner }, + { .name = "CGIT_REPO_DEFBRANCH", .value = repo->defbranch }, + { .name = "CGIT_REPO_SECTION", .value = repo->section }, + { .name = "CGIT_REPO_CLONE_URL", .value = repo->clone_url } + }; + int env_var_count = ARRAY_SIZE(env_vars); + cgit_env_var *p, *q; + static char *warn = "cgit warning: failed to set env: %s=%s\n"; + + p = env_vars; + q = p + env_var_count; + for (; p < q; p++) + if (p->value && setenv(p->name, p->value, 1)) + fprintf(stderr, warn, p->name, p->value); +} + +/* Read the content of the specified file into a newly allocated buffer, + * zeroterminate the buffer and return 0 on success, errno otherwise. + */ +int readfile(const char *path, char **buf, size_t *size) +{ + int fd, e; + struct stat st; + + fd = open(path, O_RDONLY); + if (fd == -1) + return errno; + if (fstat(fd, &st)) { + e = errno; + close(fd); + return e; + } + if (!S_ISREG(st.st_mode)) { + close(fd); + return EISDIR; + } + *buf = xmalloc(st.st_size + 1); + *size = read_in_full(fd, *buf, st.st_size); + e = errno; + (*buf)[*size] = '\0'; + close(fd); + return (*size == st.st_size ? 0 : e); +} + +static int is_token_char(char c) +{ + return isalnum(c) || c == '_'; +} + +/* Replace name with getenv(name), return pointer to zero-terminating char + */ +static char *expand_macro(char *name, int maxlength) +{ + char *value; + size_t len; + + len = 0; + value = getenv(name); + if (value) { + len = strlen(value) + 1; + if (len > maxlength) + len = maxlength; + strlcpy(name, value, len); + --len; + } + return name + len; +} + +#define EXPBUFSIZE (1024 * 8) + +/* Replace all tokens prefixed by '$' in the specified text with the + * value of the named environment variable. + * NB: the return value is a static buffer, i.e. it must be strdup'd + * by the caller. + */ +char *expand_macros(const char *txt) +{ + static char result[EXPBUFSIZE]; + char *p, *start; + int len; + + p = result; + start = NULL; + while (p < result + EXPBUFSIZE - 1 && txt && *txt) { + *p = *txt; + if (start) { + if (!is_token_char(*txt)) { + if (p - start > 0) { + *p = '\0'; + len = result + EXPBUFSIZE - start - 1; + p = expand_macro(start, len) - 1; + } + start = NULL; + txt--; + } + p++; + txt++; + continue; + } + if (*txt == '$') { + start = p; + txt++; + continue; + } + p++; + txt++; + } + *p = '\0'; + if (start && p - start > 0) { + len = result + EXPBUFSIZE - start - 1; + p = expand_macro(start, len); + *p = '\0'; + } + return result; +} + +char *get_mimetype_for_filename(const char *filename) +{ + char *ext, *mimetype, *token, line[1024], *saveptr; + FILE *file; + struct string_list_item *mime; + + if (!filename) + return NULL; + + ext = strrchr(filename, '.'); + if (!ext) + return NULL; + ++ext; + if (!ext[0]) + return NULL; + mime = string_list_lookup(&ctx.cfg.mimetypes, ext); + if (mime) + return xstrdup(mime->util); + + if (!ctx.cfg.mimetype_file) + return NULL; + file = fopen(ctx.cfg.mimetype_file, "r"); + if (!file) + return NULL; + while (fgets(line, sizeof(line), file)) { + if (!line[0] || line[0] == '#') + continue; + mimetype = strtok_r(line, " \t\r\n", &saveptr); + while ((token = strtok_r(NULL, " \t\r\n", &saveptr))) { + if (!strcasecmp(ext, token)) { + fclose(file); + return xstrdup(mimetype); + } + } + } + fclose(file); + return NULL; +} diff --git a/www/git.causal.agency/cgit/tests/.gitignore b/www/git.causal.agency/cgit/tests/.gitignore new file mode 100644 index 00000000..3fd2e965 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/.gitignore @@ -0,0 +1,2 @@ +trash\ directory.t* +test-results diff --git a/www/git.causal.agency/cgit/tests/Makefile b/www/git.causal.agency/cgit/tests/Makefile new file mode 100644 index 00000000..65e11173 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/Makefile @@ -0,0 +1,17 @@ +include ../git/config.mak.uname +-include ../cgit.conf + +SHELL_PATH ?= $(SHELL) +SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) + +T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh) + +all: $(T) + +$(T): + @'$(SHELL_PATH_SQ)' $@ $(CGIT_TEST_OPTS) + +clean: + $(RM) -rf trash + +.PHONY: $(T) clean diff --git a/www/git.causal.agency/cgit/tests/filters/dump.sh b/www/git.causal.agency/cgit/tests/filters/dump.sh new file mode 100755 index 00000000..da6f7a1b --- /dev/null +++ b/www/git.causal.agency/cgit/tests/filters/dump.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +[ "$#" -gt 0 ] && printf "%s " "$*" +tr '[:lower:]' '[:upper:]' diff --git a/www/git.causal.agency/cgit/tests/setup.sh b/www/git.causal.agency/cgit/tests/setup.sh new file mode 100755 index 00000000..31e7d5bb --- /dev/null +++ b/www/git.causal.agency/cgit/tests/setup.sh @@ -0,0 +1,161 @@ +# This file should be sourced by all test-scripts +# +# Main functions: +# prepare_tests(description) - setup for testing, i.e. create repos+config +# run_test(description, script) - run one test, i.e. eval script +# +# Helper functions +# cgit_query(querystring) - call cgit with the specified querystring +# cgit_url(url) - call cgit with the specified virtual url +# +# Example script: +# +# . setup.sh +# prepare_tests "html validation" +# run_test 'repo index' 'cgit_url "/" | tidy -e' +# run_test 'repo summary' 'cgit_url "/foo" | tidy -e' + +# We don't want to run Git commands through Valgrind, so we filter out the +# --valgrind option here and handle it ourselves. We copy the arguments +# assuming that none contain a newline, although other whitespace is +# preserved. +LF=' +' +test_argv= + +while test $# != 0 +do + case "$1" in + --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind) + cgit_valgrind=t + test_argv="$test_argv${LF}--verbose" + ;; + *) + test_argv="$test_argv$LF$1" + ;; + esac + shift +done + +OLDIFS=$IFS +IFS=$LF +set -- $test_argv +IFS=$OLDIFS + +: ${TEST_DIRECTORY=$(pwd)/../git/t} +: ${TEST_OUTPUT_DIRECTORY=$(pwd)} +TEST_NO_CREATE_REPO=YesPlease +. "$TEST_DIRECTORY"/test-lib.sh + +# Prepend the directory containing cgit to PATH. +if test -n "$cgit_valgrind" +then + GIT_VALGRIND="$TEST_DIRECTORY/valgrind" + CGIT_VALGRIND=$(cd ../valgrind && pwd) + PATH="$CGIT_VALGRIND/bin:$PATH" + export GIT_VALGRIND CGIT_VALGRIND +else + PATH="$(pwd)/../..:$PATH" +fi + +FILTER_DIRECTORY=$(cd ../filters && pwd) + +mkrepo() { + name=$1 + count=$2 + test_create_repo "$name" + ( + cd "$name" + n=1 + while test $n -le $count + do + echo $n >file-$n + git add file-$n + git commit -m "commit $n" + n=$(expr $n + 1) + done + case "$3" in + testplus) + echo "hello" >a+b + git add a+b + git commit -m "add a+b" + git branch "1+2" + ;; + commit-graph) + git commit-graph write + ;; + esac + ) +} + +setup_repos() +{ + rm -rf cache + mkdir -p cache + mkrepo repos/foo 5 >/dev/null + mkrepo repos/bar 50 commit-graph >/dev/null + mkrepo repos/foo+bar 10 testplus >/dev/null + mkrepo "repos/with space" 2 >/dev/null + mkrepo repos/filter 5 testplus >/dev/null + cat >cgitrc <<EOF +virtual-root=/ +cache-root=$PWD/cache + +cache-size=1021 +snapshots=tar.gz tar.bz tar.lz tar.xz tar.zst zip +enable-log-filecount=1 +enable-log-linecount=1 +summary-log=5 +summary-branches=5 +summary-tags=5 +clone-url=git://example.org/\$CGIT_REPO_URL.git +enable-filter-overrides=1 + +repo.url=foo +repo.path=$PWD/repos/foo/.git +# Do not specify a description for this repo, as it then will be assigned +# the constant value "[no description]" (which actually used to cause a +# segfault). + +repo.url=bar +repo.path=$PWD/repos/bar/.git +repo.desc=the bar repo + +repo.url=foo+bar +repo.path=$PWD/repos/foo+bar/.git +repo.desc=the foo+bar repo + +repo.url=with space +repo.path=$PWD/repos/with space/.git +repo.desc=spaced repo + +repo.url=filter-exec +repo.path=$PWD/repos/filter/.git +repo.desc=filtered repo +repo.about-filter=exec:$FILTER_DIRECTORY/dump.sh +repo.commit-filter=exec:$FILTER_DIRECTORY/dump.sh +repo.email-filter=exec:$FILTER_DIRECTORY/dump.sh +repo.source-filter=exec:$FILTER_DIRECTORY/dump.sh +repo.readme=master:a+b +EOF +} + +cgit_query() +{ + CGIT_CONFIG="$PWD/cgitrc" QUERY_STRING="$1" cgit +} + +cgit_url() +{ + CGIT_CONFIG="$PWD/cgitrc" QUERY_STRING="url=$1" cgit +} + +strip_headers() { + while read -r line + do + test -z "$line" && break + done + cat +} + +test -z "$CGIT_TEST_NO_CREATE_REPOS" && setup_repos diff --git a/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh b/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh new file mode 100755 index 00000000..dd84fe3f --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +if [ "${CGIT_TEST_NO_GIT_VERSION}" = "YesPlease" ]; then + exit 0 +fi + +test_description='Check Git version is correct' +CGIT_TEST_NO_CREATE_REPOS=YesPlease +. ./setup.sh + +test_expect_success 'extract Git version from Makefile' ' + sed -n -e "/^GIT_VER[ ]*=/ { + s/^GIT_VER[ ]*=[ ]*// + p + }" ../../Makefile >makefile_version +' + +# Note that Git's GIT-VERSION-GEN script applies "s/-/./g" to the version +# string to produce the internal version in the GIT-VERSION-FILE, so we +# must apply the same transformation to the version in the Makefile before +# comparing them. +test_expect_success 'test Git version matches Makefile' ' + ( cat ../../git/GIT-VERSION-FILE || echo "No GIT-VERSION-FILE" ) | + sed -e "s/GIT_VERSION[ ]*=[ ]*//" -e "s/\\.dirty$//" >git_version && + sed -e "s/-/./g" makefile_version >makefile_git_version && + test_cmp git_version makefile_git_version +' + +test_expect_success 'test submodule version matches Makefile' ' + if ! test -e ../../git/.git + then + echo "git/ is not a Git repository" >&2 + else + ( + cd ../.. && + sm_oid=$(git ls-files --stage -- git | + sed -e "s/^[0-9]* \\([0-9a-f]*\\) [0-9] .*$/\\1/") && + cd git && + git describe --match "v[0-9]*" $sm_oid + ) | sed -e "s/^v//" -e "s/-/./" >sm_version && + test_cmp sm_version makefile_version + fi +' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0010-validate-html.sh b/www/git.causal.agency/cgit/tests/t0010-validate-html.sh new file mode 100755 index 00000000..ca08d69d --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0010-validate-html.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +test_description='Validate html with tidy' +. ./setup.sh + + +test_url() +{ + tidy_opt="-eq" + test -z "$NO_TIDY_WARNINGS" || tidy_opt+=" --show-warnings no" + cgit_url "$1" >tidy-$test_count.tmp || return + sed -e "1,4d" tidy-$test_count.tmp >tidy-$test_count || return + "$tidy" $tidy_opt tidy-$test_count + rc=$? + + # tidy returns with exitcode 1 on warnings, 2 on error + if test $rc = 2 + then + false + else + : + fi +} + +tidy=`which tidy 2>/dev/null` +test -n "$tidy" || { + skip_all='Skipping html validation tests: tidy not found' + test_done + exit +} + +test_expect_success 'index page' 'test_url ""' +test_expect_success 'foo' 'test_url "foo"' +test_expect_success 'foo/log' 'test_url "foo/log"' +test_expect_success 'foo/tree' 'test_url "foo/tree"' +test_expect_success 'foo/tree/file-1' 'test_url "foo/tree/file-1"' +test_expect_success 'foo/commit' 'test_url "foo/commit"' +test_expect_success 'foo/diff' 'test_url "foo/diff"' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh b/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh new file mode 100755 index 00000000..657765d8 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +test_description='Validate cache' +. ./setup.sh + +test_expect_success 'verify cache-size=0' ' + + rm -f cache/* && + sed -e "s/cache-size=1021$/cache-size=0/" cgitrc >cgitrc.tmp && + mv -f cgitrc.tmp cgitrc && + cgit_url "" && + cgit_url "foo" && + cgit_url "foo/refs" && + cgit_url "foo/tree" && + cgit_url "foo/log" && + cgit_url "foo/diff" && + cgit_url "foo/patch" && + cgit_url "bar" && + cgit_url "bar/refs" && + cgit_url "bar/tree" && + cgit_url "bar/log" && + cgit_url "bar/diff" && + cgit_url "bar/patch" && + ls cache >output && + test_line_count = 0 output +' + +test_expect_success 'verify cache-size=1' ' + + rm -f cache/* && + sed -e "s/cache-size=0$/cache-size=1/" cgitrc >cgitrc.tmp && + mv -f cgitrc.tmp cgitrc && + cgit_url "" && + cgit_url "foo" && + cgit_url "foo/refs" && + cgit_url "foo/tree" && + cgit_url "foo/log" && + cgit_url "foo/diff" && + cgit_url "foo/patch" && + cgit_url "bar" && + cgit_url "bar/refs" && + cgit_url "bar/tree" && + cgit_url "bar/log" && + cgit_url "bar/diff" && + cgit_url "bar/patch" && + ls cache >output && + test_line_count = 1 output +' + +test_expect_success 'verify cache-size=1021' ' + + rm -f cache/* && + sed -e "s/cache-size=1$/cache-size=1021/" cgitrc >cgitrc.tmp && + mv -f cgitrc.tmp cgitrc && + cgit_url "" && + cgit_url "foo" && + cgit_url "foo/refs" && + cgit_url "foo/tree" && + cgit_url "foo/log" && + cgit_url "foo/diff" && + cgit_url "foo/patch" && + cgit_url "bar" && + cgit_url "bar/refs" && + cgit_url "bar/tree" && + cgit_url "bar/log" && + cgit_url "bar/diff" && + cgit_url "bar/patch" && + ls cache >output && + test_line_count = 13 output && + cgit_url "foo/ls_cache" >output.full && + strip_headers <output.full >output && + test_line_count = 13 output && + # Check that ls_cache output is cached correctly + cgit_url "foo/ls_cache" >output.second && + test_cmp output.full output.second +' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0101-index.sh b/www/git.causal.agency/cgit/tests/t0101-index.sh new file mode 100755 index 00000000..82ef9b04 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0101-index.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +test_description='Check content on index page' +. ./setup.sh + +test_expect_success 'generate index page' 'cgit_url "" >tmp' +test_expect_success 'find foo repo' 'grep "foo" tmp' +test_expect_success 'find foo description' 'grep "\[no description\]" tmp' +test_expect_success 'find bar repo' 'grep "bar" tmp' +test_expect_success 'find bar description' 'grep "the bar repo" tmp' +test_expect_success 'find foo+bar repo' 'grep ">foo+bar<" tmp' +test_expect_success 'verify foo+bar link' 'grep "/foo+bar/" tmp' +test_expect_success 'verify "with%20space" link' 'grep "/with%20space/" tmp' +test_expect_success 'no tree-link' '! grep "foo/tree" tmp' +test_expect_success 'no log-link' '! grep "foo/log" tmp' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0102-summary.sh b/www/git.causal.agency/cgit/tests/t0102-summary.sh new file mode 100755 index 00000000..b8864cb1 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0102-summary.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +test_description='Check content on summary page' +. ./setup.sh + +test_expect_success 'generate foo summary' 'cgit_url "foo" >tmp' +test_expect_success 'find commit 1' 'grep "commit 1" tmp' +test_expect_success 'find commit 5' 'grep "commit 5" tmp' +test_expect_success 'find branch master' 'grep "master" tmp' +test_expect_success 'no tags' '! grep "tags" tmp' +test_expect_success 'clone-url expanded correctly' ' + grep "git://example.org/foo.git" tmp +' + +test_expect_success 'generate bar summary' 'cgit_url "bar" >tmp' +test_expect_success 'no commit 45' '! grep "commit 45" tmp' +test_expect_success 'find commit 46' 'grep "commit 46" tmp' +test_expect_success 'find commit 50' 'grep "commit 50" tmp' +test_expect_success 'find branch master' 'grep "master" tmp' +test_expect_success 'no tags' '! grep "tags" tmp' +test_expect_success 'clone-url expanded correctly' ' + grep "git://example.org/bar.git" tmp +' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0103-log.sh b/www/git.causal.agency/cgit/tests/t0103-log.sh new file mode 100755 index 00000000..bdf1435a --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0103-log.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +test_description='Check content on log page' +. ./setup.sh + +test_expect_success 'generate foo/log' 'cgit_url "foo/log" >tmp' +test_expect_success 'find commit 1' 'grep "commit 1" tmp' +test_expect_success 'find commit 5' 'grep "commit 5" tmp' + +test_expect_success 'generate bar/log' 'cgit_url "bar/log" >tmp' +test_expect_success 'find commit 1' 'grep "commit 1" tmp' +test_expect_success 'find commit 50' 'grep "commit 50" tmp' + +test_expect_success 'generate "with%20space/log?qt=grep&q=commit+1"' ' + cgit_url "with+space/log&qt=grep&q=commit+1" >tmp +' +test_expect_success 'find commit 1' 'grep "commit 1" tmp' +test_expect_success 'find link with %20 in path' 'grep "/with%20space/log/?qt=grep" tmp' +test_expect_success 'find link with + in arg' 'grep "/log/?qt=grep&q=commit+1" tmp' +test_expect_success 'no links with space in path' '! grep "href=./with space/" tmp' +test_expect_success 'no links with space in arg' '! grep "q=commit 1" tmp' +test_expect_success 'commit 2 is not visible' '! grep "commit 2" tmp' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0104-tree.sh b/www/git.causal.agency/cgit/tests/t0104-tree.sh new file mode 100755 index 00000000..2e140f59 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0104-tree.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +test_description='Check content on tree page' +. ./setup.sh + +test_expect_success 'generate bar/tree' 'cgit_url "bar/tree" >tmp' +test_expect_success 'find file-1' 'grep "file-1" tmp' +test_expect_success 'find file-50' 'grep "file-50" tmp' + +test_expect_success 'generate bar/tree/file-50' 'cgit_url "bar/tree/file-50" >tmp' + +test_expect_success 'find line 1' ' + grep "<a id=.n1. href=.#n1.>1</a>" tmp +' + +test_expect_success 'no line 2' ' + ! grep "<a id=.n2. href=.#n2.>2</a>" tmp +' + +test_expect_success 'generate foo+bar/tree' 'cgit_url "foo%2bbar/tree" >tmp' + +test_expect_success 'verify a+b link' ' + grep "/foo+bar/tree/a+b" tmp +' + +test_expect_success 'generate foo+bar/tree?h=1+2' 'cgit_url "foo%2bbar/tree&h=1%2b2" >tmp' + +test_expect_success 'verify a+b?h=1+2 link' ' + grep "/foo+bar/tree/a+b?h=1%2b2" tmp +' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0105-commit.sh b/www/git.causal.agency/cgit/tests/t0105-commit.sh new file mode 100755 index 00000000..cfed1e7d --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0105-commit.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +test_description='Check content on commit page' +. ./setup.sh + +test_expect_success 'generate foo/commit' 'cgit_url "foo/commit" >tmp' +test_expect_success 'find tree link' 'grep "<a href=./foo/tree/.>" tmp' +test_expect_success 'find parent link' 'grep -E "<a href=./foo/commit/\?id=.+>" tmp' + +test_expect_success 'find commit subject' ' + grep "<div class=.commit-subject.>commit 5<" tmp +' + +test_expect_success 'find commit msg' 'grep "<pre class=.commit-msg.></pre>" tmp' +test_expect_success 'find diffstat' 'grep "<table summary=.diffstat. class=.diffstat.>" tmp' + +test_expect_success 'find diff summary' ' + grep "1 files changed, 1 insertions, 0 deletions" tmp +' + +test_expect_success 'get root commit' ' + root=$(cd repos/foo && git rev-list --reverse HEAD | head -1) && + cgit_url "foo/commit&id=$root" >tmp && + grep "</html>" tmp +' + +test_expect_success 'root commit contains diffstat' ' + grep "<a href=./foo/diff/file-1.id=[0-9a-f]\{40,64\}.>file-1</a>" tmp +' + +test_expect_success 'root commit contains diff' ' + grep ">diff --git a/file-1 b/file-1" tmp && + grep "<span class=.add.>+1</span>" tmp +' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0106-diff.sh b/www/git.causal.agency/cgit/tests/t0106-diff.sh new file mode 100755 index 00000000..62a0a74a --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0106-diff.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +test_description='Check content on diff page' +. ./setup.sh + +test_expect_success 'generate foo/diff' 'cgit_url "foo/diff" >tmp' +test_expect_success 'find diff header' 'grep "a/file-5 b/file-5" tmp' +test_expect_success 'find blob link' 'grep "<a href=./foo/tree/file-5?id=" tmp' +test_expect_success 'find added file' 'grep "new file mode 100644" tmp' + +test_expect_success 'find hunk header' ' + grep "<span class=.hunk.>@@ -0,0 +1 @@</span>" tmp +' + +test_expect_success 'find added line' ' + grep "<span class=.add.>+5</span>" tmp +' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0107-snapshot.sh b/www/git.causal.agency/cgit/tests/t0107-snapshot.sh new file mode 100755 index 00000000..0811ec40 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0107-snapshot.sh @@ -0,0 +1,205 @@ +#!/bin/sh + +test_description='Verify snapshot' +. ./setup.sh + +test_expect_success 'get foo/snapshot/master.tar.gz' ' + cgit_url "foo/snapshot/master.tar.gz" >tmp +' + +test_expect_success 'check html headers' ' + head -n 1 tmp | + grep "Content-Type: application/x-gzip" && + + head -n 2 tmp | + grep "Content-Disposition: inline; filename=.master.tar.gz." +' + +test_expect_success 'strip off the header lines' ' + strip_headers <tmp >master.tar.gz +' + +test_expect_success 'verify gzip format' ' + gunzip --test master.tar.gz +' + +test_expect_success 'untar' ' + rm -rf master && + gzip -dc master.tar.gz | tar -xf - +' + +test_expect_success 'count files' ' + ls master/ >output && + test_line_count = 5 output +' + +test_expect_success 'verify untarred file-5' ' + grep "^5$" master/file-5 && + test_line_count = 1 master/file-5 +' + +if test -n "$(which lzip 2>/dev/null)"; then + test_set_prereq LZIP +else + say 'Skipping LZIP validation tests: lzip not found' +fi + +test_expect_success LZIP 'get foo/snapshot/master.tar.lz' ' + cgit_url "foo/snapshot/master.tar.lz" >tmp +' + +test_expect_success LZIP 'check html headers' ' + head -n 1 tmp | + grep "Content-Type: application/x-lzip" && + + head -n 2 tmp | + grep "Content-Disposition: inline; filename=.master.tar.lz." +' + +test_expect_success LZIP 'strip off the header lines' ' + strip_headers <tmp >master.tar.lz +' + +test_expect_success LZIP 'verify lzip format' ' + lzip --test master.tar.lz +' + +test_expect_success LZIP 'untar' ' + rm -rf master && + lzip -dc master.tar.lz | tar -xf - +' + +test_expect_success LZIP 'count files' ' + ls master/ >output && + test_line_count = 5 output +' + +test_expect_success LZIP 'verify untarred file-5' ' + grep "^5$" master/file-5 && + test_line_count = 1 master/file-5 +' + +if test -n "$(which xz 2>/dev/null)"; then + test_set_prereq XZ +else + say 'Skipping XZ validation tests: xz not found' +fi + +test_expect_success XZ 'get foo/snapshot/master.tar.xz' ' + cgit_url "foo/snapshot/master.tar.xz" >tmp +' + +test_expect_success XZ 'check html headers' ' + head -n 1 tmp | + grep "Content-Type: application/x-xz" && + + head -n 2 tmp | + grep "Content-Disposition: inline; filename=.master.tar.xz." +' + +test_expect_success XZ 'strip off the header lines' ' + strip_headers <tmp >master.tar.xz +' + +test_expect_success XZ 'verify xz format' ' + xz --test master.tar.xz +' + +test_expect_success XZ 'untar' ' + rm -rf master && + xz -dc master.tar.xz | tar -xf - +' + +test_expect_success XZ 'count files' ' + ls master/ >output && + test_line_count = 5 output +' + +test_expect_success XZ 'verify untarred file-5' ' + grep "^5$" master/file-5 && + test_line_count = 1 master/file-5 +' + +if test -n "$(which zstd 2>/dev/null)"; then + test_set_prereq ZSTD +else + say 'Skipping ZSTD validation tests: zstd not found' +fi + +test_expect_success ZSTD 'get foo/snapshot/master.tar.zst' ' + cgit_url "foo/snapshot/master.tar.zst" >tmp +' + +test_expect_success ZSTD 'check html headers' ' + head -n 1 tmp | + grep "Content-Type: application/x-zstd" && + + head -n 2 tmp | + grep "Content-Disposition: inline; filename=.master.tar.zst." +' + +test_expect_success ZSTD 'strip off the header lines' ' + strip_headers <tmp >master.tar.zst +' + +test_expect_success ZSTD 'verify zstd format' ' + zstd --test master.tar.zst +' + +test_expect_success ZSTD 'untar' ' + rm -rf master && + zstd -dc master.tar.zst | tar -xf - +' + +test_expect_success ZSTD 'count files' ' + ls master/ >output && + test_line_count = 5 output +' + +test_expect_success ZSTD 'verify untarred file-5' ' + grep "^5$" master/file-5 && + test_line_count = 1 master/file-5 +' + +test_expect_success 'get foo/snapshot/master.zip' ' + cgit_url "foo/snapshot/master.zip" >tmp +' + +test_expect_success 'check HTML headers (zip)' ' + head -n 1 tmp | + grep "Content-Type: application/x-zip" && + + head -n 2 tmp | + grep "Content-Disposition: inline; filename=.master.zip." +' + +test_expect_success 'strip off the header lines (zip)' ' + strip_headers <tmp >master.zip +' + +if test -n "$(which unzip 2>/dev/null)"; then + test_set_prereq UNZIP +else + say 'Skipping ZIP validation tests: unzip not found' +fi + +test_expect_success UNZIP 'verify zip format' ' + unzip -t master.zip +' + +test_expect_success UNZIP 'unzip' ' + rm -rf master && + unzip master.zip +' + +test_expect_success UNZIP 'count files (zip)' ' + ls master/ >output && + test_line_count = 5 output +' + +test_expect_success UNZIP 'verify unzipped file-5' ' + grep "^5$" master/file-5 && + test_line_count = 1 master/file-5 +' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0108-patch.sh b/www/git.causal.agency/cgit/tests/t0108-patch.sh new file mode 100755 index 00000000..013d6802 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0108-patch.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +test_description='Check content on patch page' +. ./setup.sh + +test_expect_success 'generate foo/patch' ' + cgit_query "url=foo/patch" >tmp +' + +test_expect_success 'find `From:` line' ' + grep "^From: " tmp +' + +test_expect_success 'find `Date:` line' ' + grep "^Date: " tmp +' + +test_expect_success 'find `Subject:` line' ' + grep "^Subject: commit 5" tmp +' + +test_expect_success 'find `cgit` signature' ' + tail -2 tmp | head -1 | grep "^cgit" +' + +test_expect_success 'compare with output of git-format-patch(1)' ' + CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" ../../VERSION) && + git --git-dir="$PWD/repos/foo/.git" format-patch --subject-prefix="" --signature="cgit $CGIT_VERSION" --stdout HEAD^ >tmp2 && + strip_headers <tmp >tmp_ && + test_cmp tmp_ tmp2 +' + +test_expect_success 'find initial commit' ' + root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD) +' + +test_expect_success 'generate patch for initial commit' ' + cgit_query "url=foo/patch&id=$root" >tmp +' + +test_expect_success 'find `cgit` signature' ' + tail -2 tmp | head -1 | grep "^cgit" +' + +test_expect_success 'generate patches for multiple commits' ' + id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) && + id2=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD~3) && + cgit_query "url=foo/patch&id=$id&id2=$id2" >tmp +' + +test_expect_success 'find `cgit` signature' ' + tail -2 tmp | head -1 | grep "^cgit" +' + +test_expect_success 'compare with output of git-format-patch(1)' ' + CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" ../../VERSION) && + git --git-dir="$PWD/repos/foo/.git" format-patch -N --subject-prefix="" --signature="cgit $CGIT_VERSION" --stdout HEAD~3..HEAD >tmp2 && + strip_headers <tmp >tmp_ && + test_cmp tmp_ tmp2 +' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh b/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh new file mode 100755 index 00000000..189ef281 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +test_description='Ensure that git does not access $HOME' +. ./setup.sh + +test -n "$(which strace 2>/dev/null)" || { + skip_all='Skipping access validation tests: strace not found' + test_done + exit +} + +strace true 2>/dev/null || { + skip_all='Skipping access validation tests: strace not functional' + test_done + exit +} + +test_no_home_access () { + non_existent_path="/path/to/some/place/that/does/not/possibly/exist" + while test -d "$non_existent_path"; do + non_existent_path="$non_existent_path/$(date +%N)" + done && + strace \ + -E HOME="$non_existent_path" \ + -E CGIT_CONFIG="$PWD/cgitrc" \ + -E QUERY_STRING="url=$1" \ + -e access -f -o strace.out cgit && + ! grep "$non_existent_path" strace.out +} + +test_no_home_access_success() { + test_expect_success "do not access \$HOME: $1" " + test_no_home_access '$1' + " +} + +test_no_home_access_success +test_no_home_access_success foo +test_no_home_access_success foo/refs +test_no_home_access_success foo/log +test_no_home_access_success foo/tree +test_no_home_access_success foo/tree/file-1 +test_no_home_access_success foo/commit +test_no_home_access_success foo/diff +test_no_home_access_success foo/patch +test_no_home_access_success foo/snapshot/master.tar.gz + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh b/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh new file mode 100755 index 00000000..66fa7d5d --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='Check content on rawdiff page' +. ./setup.sh + +test_expect_success 'generate foo/rawdiff' ' + cgit_query "url=foo/rawdiff" >tmp +' + +test_expect_success 'compare with output of git-diff(1)' ' + git --git-dir="$PWD/repos/foo/.git" diff HEAD^.. >tmp2 && + sed "1,4d" tmp >tmp_ && + cmp tmp_ tmp2 +' + +test_expect_success 'find initial commit' ' + root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD) +' + +test_expect_success 'generate diff for initial commit' ' + cgit_query "url=foo/rawdiff&id=$root" >tmp +' + +test_expect_success 'compare with output of git-diff-tree(1)' ' + git --git-dir="$PWD/repos/foo/.git" diff-tree -p --no-commit-id --root "$root" >tmp2 && + sed "1,4d" tmp >tmp_ && + cmp tmp_ tmp2 +' + +test_expect_success 'generate diff for multiple commits' ' + id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) && + id2=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD~3) && + cgit_query "url=foo/rawdiff&id=$id&id2=$id2" >tmp +' + +test_expect_success 'compare with output of git-diff(1)' ' + git --git-dir="$PWD/repos/foo/.git" diff HEAD~3..HEAD >tmp2 && + sed "1,4d" tmp >tmp_ && + cmp tmp_ tmp2 +' + +test_done diff --git a/www/git.causal.agency/cgit/tests/t0111-filter.sh b/www/git.causal.agency/cgit/tests/t0111-filter.sh new file mode 100755 index 00000000..e5d35750 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/t0111-filter.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +test_description='Check filtered content' +. ./setup.sh + +prefixes="exec" + +for prefix in $prefixes +do + test_expect_success "generate filter-$prefix/tree/a%2bb" " + cgit_url 'filter-$prefix/tree/a%2bb' >tmp + " + + test_expect_success "check whether the $prefix source filter works" ' + grep "<code>a+b HELLO$" tmp + ' + + test_expect_success "generate filter-$prefix/about/" " + cgit_url 'filter-$prefix/about/' >tmp + " + + test_expect_success "check whether the $prefix about filter works" ' + grep "<div id='"'"'summary'"'"'>a+b HELLO$" tmp + ' + + test_expect_success "generate filter-$prefix/commit/" " + cgit_url 'filter-$prefix/commit/' >tmp + " + + test_expect_success "check whether the $prefix commit filter works" ' + grep "<div class='"'"'commit-subject'"'"'>ADD A+B" tmp + ' + + test_expect_success "check whether the $prefix email filter works for authors" ' + grep "<author@example.com> commit A U THOR <AUTHOR@EXAMPLE.COM>" tmp + ' + + test_expect_success "check whether the $prefix email filter works for committers" ' + grep "<committer@example.com> commit C O MITTER <COMMITTER@EXAMPLE.COM>" tmp + ' +done + +test_done diff --git a/www/git.causal.agency/cgit/tests/valgrind/bin/cgit b/www/git.causal.agency/cgit/tests/valgrind/bin/cgit new file mode 100755 index 00000000..dcdfbe53 --- /dev/null +++ b/www/git.causal.agency/cgit/tests/valgrind/bin/cgit @@ -0,0 +1,12 @@ +#!/bin/sh + +# Note that we currently use Git's suppression file and there are variables +# $GIT_VALGRIND and $CGIT_VALGRIND which point to different places. +exec valgrind -q --error-exitcode=126 \ + --suppressions="$GIT_VALGRIND/default.supp" \ + --gen-suppressions=all \ + --leak-check=no \ + --track-origins=yes \ + --log-fd=4 \ + --input-fd=4 \ + "$CGIT_VALGRIND/../../cgit" "$@" diff --git a/www/git.causal.agency/cgit/ui-atom.c b/www/git.causal.agency/cgit/ui-atom.c new file mode 100644 index 00000000..8329e01a --- /dev/null +++ b/www/git.causal.agency/cgit/ui-atom.c @@ -0,0 +1,158 @@ +/* ui-atom.c: functions for atom feeds + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-atom.h" +#include "html.h" +#include "ui-shared.h" + +static void add_entry(struct commit *commit, const char *host) +{ + char delim = '&'; + char *hex; + char *mail, *t, *t2; + struct commitinfo *info; + + info = cgit_parse_commit(commit); + hex = oid_to_hex(&commit->object.oid); + html("<entry>\n"); + html("<title>"); + html_txt(info->subject); + html("</title>\n"); + html("<updated>"); + html_txt(show_date(info->committer_date, 0, + date_mode_from_type(DATE_ISO8601_STRICT))); + html("</updated>\n"); + html("<author>\n"); + if (info->author) { + html("<name>"); + html_txt(info->author); + html("</name>\n"); + } + if (info->author_email && !ctx.cfg.noplainemail) { + mail = xstrdup(info->author_email); + t = strchr(mail, '<'); + if (t) + t++; + else + t = mail; + t2 = strchr(t, '>'); + if (t2) + *t2 = '\0'; + html("<email>"); + html_txt(t); + html("</email>\n"); + free(mail); + } + html("</author>\n"); + html("<published>"); + html_txt(show_date(info->author_date, 0, + date_mode_from_type(DATE_ISO8601_STRICT))); + html("</published>\n"); + if (host) { + char *pageurl; + html("<link rel='alternate' type='text/html' href='"); + html(cgit_httpscheme()); + html_attr(host); + pageurl = cgit_pageurl(ctx.repo->url, "commit", NULL); + html_attr(pageurl); + if (ctx.cfg.virtual_root) + delim = '?'; + html_attrf("%cid=%s", delim, hex); + html("'/>\n"); + free(pageurl); + } + html("<id>"); + html_txtf("urn:%s:%s", the_hash_algo->name, hex); + html("</id>\n"); + html("<content type='text'>\n"); + html_txt(info->msg); + html("</content>\n"); + html("</entry>\n"); + cgit_free_commitinfo(info); +} + + +void cgit_print_atom(char *tip, const char *path, int max_count) +{ + char *host; + const char *argv[] = {NULL, tip, NULL, NULL, NULL}; + struct commit *commit; + struct rev_info rev; + int argc = 2; + int first = 1; + + if (ctx.qry.show_all) + argv[1] = "--all"; + else if (!tip) + argv[1] = ctx.qry.head; + + if (path) { + argv[argc++] = "--"; + argv[argc++] = path; + } + + init_revisions(&rev, NULL); + rev.abbrev = DEFAULT_ABBREV; + rev.commit_format = CMIT_FMT_DEFAULT; + rev.verbose_header = 1; + rev.show_root_diff = 0; + rev.max_count = max_count; + setup_revisions(argc, argv, &rev, NULL); + prepare_revision_walk(&rev); + + host = cgit_hosturl(); + ctx.page.mimetype = "text/xml"; + ctx.page.charset = "utf-8"; + cgit_print_http_headers(); + html("<feed xmlns='http://www.w3.org/2005/Atom'>\n"); + html("<title>"); + html_txt(ctx.repo->name); + if (path) { + html("/"); + html_txt(path); + } + if (tip && !ctx.qry.show_all) { + html(", branch "); + html_txt(tip); + } + html("</title>\n"); + html("<subtitle>"); + html_txt(ctx.repo->desc); + html("</subtitle>\n"); + if (host) { + char *fullurl = cgit_currentfullurl(); + char *repourl = cgit_repourl(ctx.repo->url); + html("<id>"); + html_txtf("%s%s%s", cgit_httpscheme(), host, fullurl); + html("</id>\n"); + html("<link rel='self' href='"); + html_attrf("%s%s%s", cgit_httpscheme(), host, fullurl); + html("'/>\n"); + html("<link rel='alternate' type='text/html' href='"); + html_attrf("%s%s%s", cgit_httpscheme(), host, repourl); + html("'/>\n"); + free(fullurl); + free(repourl); + } + while ((commit = get_revision(&rev)) != NULL) { + if (first) { + html("<updated>"); + html_txt(show_date(commit->date, 0, + date_mode_from_type(DATE_ISO8601_STRICT))); + html("</updated>\n"); + first = 0; + } + add_entry(commit, host); + free_commit_buffer(the_repository->parsed_objects, commit); + free_commit_list(commit->parents); + commit->parents = NULL; + } + html("</feed>\n"); + free(host); +} diff --git a/www/git.causal.agency/cgit/ui-atom.h b/www/git.causal.agency/cgit/ui-atom.h new file mode 100644 index 00000000..dda953bb --- /dev/null +++ b/www/git.causal.agency/cgit/ui-atom.h @@ -0,0 +1,6 @@ +#ifndef UI_ATOM_H +#define UI_ATOM_H + +extern void cgit_print_atom(char *tip, const char *path, int max_count); + +#endif diff --git a/www/git.causal.agency/cgit/ui-blame.c b/www/git.causal.agency/cgit/ui-blame.c new file mode 100644 index 00000000..4adec2b9 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-blame.c @@ -0,0 +1,306 @@ +/* ui-blame.c: functions for blame output + * + * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-blame.h" +#include "html.h" +#include "ui-shared.h" +#include "strvec.h" +#include "blame.h" + + +static char *emit_suspect_detail(struct blame_origin *suspect) +{ + struct commitinfo *info; + struct strbuf detail = STRBUF_INIT; + + info = cgit_parse_commit(suspect->commit); + + strbuf_addf(&detail, "author %s", info->author); + if (!ctx.cfg.noplainemail) + strbuf_addf(&detail, " %s", info->author_email); + strbuf_addf(&detail, " %s\n", + show_date(info->author_date, info->author_tz, + cgit_date_mode(DATE_ISO8601))); + + strbuf_addf(&detail, "committer %s", info->committer); + if (!ctx.cfg.noplainemail) + strbuf_addf(&detail, " %s", info->committer_email); + strbuf_addf(&detail, " %s\n\n", + show_date(info->committer_date, info->committer_tz, + cgit_date_mode(DATE_ISO8601))); + + strbuf_addstr(&detail, info->subject); + + cgit_free_commitinfo(info); + return strbuf_detach(&detail, NULL); +} + +static void emit_blame_entry_hash(struct blame_entry *ent) +{ + struct blame_origin *suspect = ent->suspect; + struct object_id *oid = &suspect->commit->object.oid; + unsigned long line = 0; + + char *detail = emit_suspect_detail(suspect); + html("<span class='oid'>"); + cgit_commit_link(find_unique_abbrev(oid, DEFAULT_ABBREV), detail, + NULL, ctx.qry.head, oid_to_hex(oid), suspect->path); + html("</span>"); + free(detail); + + while (line++ < ent->num_lines) + html("\n"); +} + +static void emit_blame_entry_linenumber(struct blame_entry *ent) +{ + const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n"; + + unsigned long lineno = ent->lno; + while (lineno < ent->lno + ent->num_lines) + htmlf(numberfmt, ++lineno); +} + +static void emit_blame_entry_line_background(struct blame_scoreboard *sb, + struct blame_entry *ent) +{ + unsigned long line; + size_t len, maxlen = 2; + const char* pos, *endpos; + + for (line = ent->lno; line < ent->lno + ent->num_lines; line++) { + html("\n"); + pos = blame_nth_line(sb, line); + endpos = blame_nth_line(sb, line + 1); + len = 0; + while (pos < endpos) { + len++; + if (*pos++ == '\t') + len = (len + 7) & ~7; + } + if (len > maxlen) + maxlen = len; + } + + for (len = 0; len < maxlen - 1; len++) + html(" "); +} + +struct walk_tree_context { + char *curr_rev; + int match_baselen; + int state; +}; + +static void print_object(const struct object_id *oid, const char *path, + const char *basename, const char *rev) +{ + enum object_type type; + char *buf; + unsigned long size; + struct strvec rev_argv = STRVEC_INIT; + struct rev_info revs; + struct blame_scoreboard sb; + struct blame_origin *o; + struct blame_entry *ent = NULL; + + type = oid_object_info(the_repository, oid, &size); + if (type == OBJ_BAD) { + cgit_print_error_page(404, "Not found", "Bad object name: %s", + oid_to_hex(oid)); + return; + } + + buf = read_object_file(oid, &type, &size); + if (!buf) { + cgit_print_error_page(500, "Internal server error", + "Error reading object %s", oid_to_hex(oid)); + return; + } + + strvec_push(&rev_argv, "blame"); + strvec_push(&rev_argv, rev); + init_revisions(&revs, NULL); + revs.diffopt.flags.allow_textconv = 1; + setup_revisions(rev_argv.nr, rev_argv.v, &revs, NULL); + init_scoreboard(&sb); + sb.revs = &revs; + sb.repo = the_repository; + sb.path = path; + setup_scoreboard(&sb, &o); + o->suspects = blame_entry_prepend(NULL, 0, sb.num_lines, o); + prio_queue_put(&sb.commits, o->commit); + blame_origin_decref(o); + sb.ent = NULL; + sb.path = path; + assign_blame(&sb, 0); + blame_sort_final(&sb); + blame_coalesce(&sb); + + cgit_set_title_from_path(path); + + cgit_print_layout_start(); + htmlf("blob: %s (", oid_to_hex(oid)); + cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path); + html(") ("); + cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path); + html(")\n"); + + if (buffer_is_binary(buf, size)) { + html("<div class='error'>blob is binary.</div>"); + goto cleanup; + } + if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) { + htmlf("<div class='error'>blob size (%ldKB)" + " exceeds display size limit (%dKB).</div>", + size / 1024, ctx.cfg.max_blob_size); + goto cleanup; + } + + html("<table class='blame blob'>\n<tr>\n"); + + /* Commit hashes */ + html("<td class='hashes'>"); + for (ent = sb.ent; ent; ent = ent->next) { + html("<div class='alt'><pre>"); + emit_blame_entry_hash(ent); + html("</pre></div>"); + } + html("</td>\n"); + + /* Line numbers */ + if (ctx.cfg.enable_tree_linenumbers) { + html("<td class='linenumbers'>"); + for (ent = sb.ent; ent; ent = ent->next) { + html("<div class='alt'><pre>"); + emit_blame_entry_linenumber(ent); + html("</pre></div>"); + } + html("</td>\n"); + } + + html("<td class='lines'><div>"); + + /* Colored bars behind lines */ + html("<div>"); + for (ent = sb.ent; ent; ) { + struct blame_entry *e = ent->next; + html("<div class='alt'><pre>"); + emit_blame_entry_line_background(&sb, ent); + html("</pre></div>"); + free(ent); + ent = e; + } + html("</div>"); + + free((void *)sb.final_buf); + + /* Lines */ + html("<pre><code>"); + if (ctx.repo->source_filter) { + char *filter_arg = xstrdup(basename); + cgit_open_filter(ctx.repo->source_filter, filter_arg); + html_raw(buf, size); + cgit_close_filter(ctx.repo->source_filter); + free(filter_arg); + } else { + html_txt(buf); + } + html("</code></pre>"); + + html("</div></td>\n"); + + html("</tr>\n</table>\n"); + + cgit_print_layout_end(); + +cleanup: + free(buf); +} + +static int walk_tree(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, void *cbdata) +{ + struct walk_tree_context *walk_tree_ctx = cbdata; + + if (base->len == walk_tree_ctx->match_baselen) { + if (S_ISREG(mode)) { + struct strbuf buffer = STRBUF_INIT; + strbuf_addbuf(&buffer, base); + strbuf_addstr(&buffer, pathname); + print_object(oid, buffer.buf, pathname, + walk_tree_ctx->curr_rev); + strbuf_release(&buffer); + walk_tree_ctx->state = 1; + } else if (S_ISDIR(mode)) { + walk_tree_ctx->state = 2; + } + } else if (base->len < INT_MAX + && (int)base->len > walk_tree_ctx->match_baselen) { + walk_tree_ctx->state = 2; + } else if (S_ISDIR(mode)) { + return READ_TREE_RECURSIVE; + } + return 0; +} + +static int basedir_len(const char *path) +{ + char *p = strrchr(path, '/'); + if (p) + return p - path + 1; + return 0; +} + +void cgit_print_blame(void) +{ + const char *rev = ctx.qry.oid; + struct object_id oid; + struct commit *commit; + struct pathspec_item path_items = { + .match = ctx.qry.path, + .len = ctx.qry.path ? strlen(ctx.qry.path) : 0 + }; + struct pathspec paths = { + .nr = 1, + .items = &path_items + }; + struct walk_tree_context walk_tree_ctx = { + .state = 0 + }; + + if (!rev) + rev = ctx.qry.head; + + if (get_oid(rev, &oid)) { + cgit_print_error_page(404, "Not found", + "Invalid revision name: %s", rev); + return; + } + commit = lookup_commit_reference(the_repository, &oid); + if (!commit || parse_commit(commit)) { + cgit_print_error_page(404, "Not found", + "Invalid commit reference: %s", rev); + return; + } + + walk_tree_ctx.curr_rev = xstrdup(rev); + walk_tree_ctx.match_baselen = (path_items.match) ? + basedir_len(path_items.match) : -1; + + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &paths, walk_tree, &walk_tree_ctx); + if (!walk_tree_ctx.state) + cgit_print_error_page(404, "Not found", "Not found"); + else if (walk_tree_ctx.state == 2) + cgit_print_error_page(404, "No blame for folders", + "Blame is not available for folders."); + + free(walk_tree_ctx.curr_rev); +} diff --git a/www/git.causal.agency/cgit/ui-blame.h b/www/git.causal.agency/cgit/ui-blame.h new file mode 100644 index 00000000..5b97e035 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-blame.h @@ -0,0 +1,6 @@ +#ifndef UI_BLAME_H +#define UI_BLAME_H + +extern void cgit_print_blame(void); + +#endif /* UI_BLAME_H */ diff --git a/www/git.causal.agency/cgit/ui-blob.c b/www/git.causal.agency/cgit/ui-blob.c new file mode 100644 index 00000000..c10ae42e --- /dev/null +++ b/www/git.causal.agency/cgit/ui-blob.c @@ -0,0 +1,182 @@ +/* ui-blob.c: show blob content + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-blob.h" +#include "html.h" +#include "ui-shared.h" + +struct walk_tree_context { + const char *match_path; + struct object_id *matched_oid; + unsigned int found_path:1; + unsigned int file_only:1; +}; + +static int walk_tree(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, void *cbdata) +{ + struct walk_tree_context *walk_tree_ctx = cbdata; + + if (walk_tree_ctx->file_only && !S_ISREG(mode)) + return READ_TREE_RECURSIVE; + if (strncmp(base->buf, walk_tree_ctx->match_path, base->len) + || strcmp(walk_tree_ctx->match_path + base->len, pathname)) + return READ_TREE_RECURSIVE; + oidcpy(walk_tree_ctx->matched_oid, oid); + walk_tree_ctx->found_path = 1; + return 0; +} + +int cgit_ref_path_exists(const char *path, const char *ref, int file_only) +{ + struct object_id oid; + unsigned long size; + struct pathspec_item path_items = { + .match = xstrdup(path), + .len = strlen(path) + }; + struct pathspec paths = { + .nr = 1, + .items = &path_items + }; + struct walk_tree_context walk_tree_ctx = { + .match_path = path, + .matched_oid = &oid, + .found_path = 0, + .file_only = file_only + }; + + if (get_oid(ref, &oid)) + goto done; + if (oid_object_info(the_repository, &oid, &size) != OBJ_COMMIT) + goto done; + read_tree(the_repository, + repo_get_commit_tree(the_repository, lookup_commit_reference(the_repository, &oid)), + &paths, walk_tree, &walk_tree_ctx); + +done: + free(path_items.match); + return walk_tree_ctx.found_path; +} + +int cgit_print_file(char *path, const char *head, int file_only) +{ + struct object_id oid; + enum object_type type; + char *buf; + unsigned long size; + struct commit *commit; + struct pathspec_item path_items = { + .match = path, + .len = strlen(path) + }; + struct pathspec paths = { + .nr = 1, + .items = &path_items + }; + struct walk_tree_context walk_tree_ctx = { + .match_path = path, + .matched_oid = &oid, + .found_path = 0, + .file_only = file_only + }; + + if (get_oid(head, &oid)) + return -1; + type = oid_object_info(the_repository, &oid, &size); + if (type == OBJ_COMMIT) { + commit = lookup_commit_reference(the_repository, &oid); + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &paths, walk_tree, &walk_tree_ctx); + if (!walk_tree_ctx.found_path) + return -1; + type = oid_object_info(the_repository, &oid, &size); + } + if (type == OBJ_BAD) + return -1; + buf = read_object_file(&oid, &type, &size); + if (!buf) + return -1; + buf[size] = '\0'; + html_raw(buf, size); + free(buf); + return 0; +} + +void cgit_print_blob(const char *hex, char *path, const char *head, int file_only) +{ + struct object_id oid; + enum object_type type; + char *buf; + unsigned long size; + struct commit *commit; + struct pathspec_item path_items = { + .match = path, + .len = path ? strlen(path) : 0 + }; + struct pathspec paths = { + .nr = 1, + .items = &path_items + }; + struct walk_tree_context walk_tree_ctx = { + .match_path = path, + .matched_oid = &oid, + .found_path = 0, + .file_only = file_only + }; + + if (hex) { + if (get_oid_hex(hex, &oid)) { + cgit_print_error_page(400, "Bad request", + "Bad hex value: %s", hex); + return; + } + } else { + if (get_oid(head, &oid)) { + cgit_print_error_page(404, "Not found", + "Bad ref: %s", head); + return; + } + } + + type = oid_object_info(the_repository, &oid, &size); + + if ((!hex) && type == OBJ_COMMIT && path) { + commit = lookup_commit_reference(the_repository, &oid); + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &paths, walk_tree, &walk_tree_ctx); + type = oid_object_info(the_repository, &oid, &size); + } + + if (type == OBJ_BAD) { + cgit_print_error_page(404, "Not found", + "Bad object name: %s", hex); + return; + } + + buf = read_object_file(&oid, &type, &size); + if (!buf) { + cgit_print_error_page(500, "Internal server error", + "Error reading object %s", hex); + return; + } + + buf[size] = '\0'; + if (buffer_is_binary(buf, size)) + ctx.page.mimetype = "application/octet-stream"; + else + ctx.page.mimetype = "text/plain"; + ctx.page.filename = path; + + html("X-Content-Type-Options: nosniff\n"); + html("Content-Security-Policy: default-src 'none'\n"); + cgit_print_http_headers(); + html_raw(buf, size); + free(buf); +} diff --git a/www/git.causal.agency/cgit/ui-blob.h b/www/git.causal.agency/cgit/ui-blob.h new file mode 100644 index 00000000..16847b20 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-blob.h @@ -0,0 +1,8 @@ +#ifndef UI_BLOB_H +#define UI_BLOB_H + +extern int cgit_ref_path_exists(const char *path, const char *ref, int file_only); +extern int cgit_print_file(char *path, const char *head, int file_only); +extern void cgit_print_blob(const char *hex, char *path, const char *head, int file_only); + +#endif /* UI_BLOB_H */ diff --git a/www/git.causal.agency/cgit/ui-clone.c b/www/git.causal.agency/cgit/ui-clone.c new file mode 100644 index 00000000..5dccb639 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-clone.c @@ -0,0 +1,126 @@ +/* ui-clone.c: functions for http cloning, based on + * git's http-backend.c by Shawn O. Pearce + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-clone.h" +#include "html.h" +#include "ui-shared.h" +#include "packfile.h" +#include "object-store.h" + +static int print_ref_info(const char *refname, const struct object_id *oid, + int flags, void *cb_data) +{ + struct object *obj; + + if (!(obj = parse_object(the_repository, oid))) + return 0; + + htmlf("%s\t%s\n", oid_to_hex(oid), refname); + if (obj->type == OBJ_TAG) { + if (!(obj = deref_tag(the_repository, obj, refname, 0))) + return 0; + htmlf("%s\t%s^{}\n", oid_to_hex(&obj->oid), refname); + } + return 0; +} + +static void print_pack_info(void) +{ + struct packed_git *pack; + char *offset; + + ctx.page.mimetype = "text/plain"; + ctx.page.filename = "objects/info/packs"; + cgit_print_http_headers(); + reprepare_packed_git(the_repository); + for (pack = get_packed_git(the_repository); pack; pack = pack->next) { + if (pack->pack_local) { + offset = strrchr(pack->pack_name, '/'); + if (offset && offset[1] != '\0') + ++offset; + else + offset = pack->pack_name; + htmlf("P %s\n", offset); + } + } +} + +static void send_file(const char *path) +{ + struct stat st; + + if (stat(path, &st)) { + switch (errno) { + case ENOENT: + cgit_print_error_page(404, "Not found", "Not found"); + break; + case EACCES: + cgit_print_error_page(403, "Forbidden", "Forbidden"); + break; + default: + cgit_print_error_page(400, "Bad request", "Bad request"); + } + return; + } + ctx.page.mimetype = "application/octet-stream"; + ctx.page.filename = path; + skip_prefix(path, ctx.repo->path, &ctx.page.filename); + skip_prefix(ctx.page.filename, "/", &ctx.page.filename); + cgit_print_http_headers(); + html_include(path); +} + +void cgit_clone_info(void) +{ + if (!ctx.qry.path || strcmp(ctx.qry.path, "refs")) { + cgit_print_error_page(400, "Bad request", "Bad request"); + return; + } + + ctx.page.mimetype = "text/plain"; + ctx.page.filename = "info/refs"; + cgit_print_http_headers(); + for_each_ref(print_ref_info, NULL); +} + +void cgit_clone_objects(void) +{ + char *p; + + if (!ctx.qry.path) + goto err; + + if (!strcmp(ctx.qry.path, "info/packs")) { + print_pack_info(); + return; + } + + /* Avoid directory traversal by forbidding "..", but also work around + * other funny business by just specifying a fairly strict format. For + * example, now we don't have to stress out about the Cygwin port. + */ + for (p = ctx.qry.path; *p; ++p) { + if (*p == '.' && *(p + 1) == '.') + goto err; + if (!isalnum(*p) && *p != '/' && *p != '.' && *p != '-') + goto err; + } + + send_file(git_path("objects/%s", ctx.qry.path)); + return; + +err: + cgit_print_error_page(400, "Bad request", "Bad request"); +} + +void cgit_clone_head(void) +{ + send_file(git_path("%s", "HEAD")); +} diff --git a/www/git.causal.agency/cgit/ui-clone.h b/www/git.causal.agency/cgit/ui-clone.h new file mode 100644 index 00000000..3e460a3d --- /dev/null +++ b/www/git.causal.agency/cgit/ui-clone.h @@ -0,0 +1,8 @@ +#ifndef UI_CLONE_H +#define UI_CLONE_H + +void cgit_clone_info(void); +void cgit_clone_objects(void); +void cgit_clone_head(void); + +#endif /* UI_CLONE_H */ diff --git a/www/git.causal.agency/cgit/ui-commit.c b/www/git.causal.agency/cgit/ui-commit.c new file mode 100644 index 00000000..b49259e6 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-commit.c @@ -0,0 +1,148 @@ +/* ui-commit.c: generate commit view + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-commit.h" +#include "html.h" +#include "ui-shared.h" +#include "ui-diff.h" +#include "ui-log.h" + +void cgit_print_commit(char *hex, const char *prefix) +{ + struct commit *commit, *parent; + struct commitinfo *info, *parent_info; + struct commit_list *p; + struct strbuf notes = STRBUF_INIT; + struct object_id oid; + char *tmp, *tmp2; + int parents = 0; + + if (!hex) + hex = ctx.qry.head; + + if (get_oid(hex, &oid)) { + cgit_print_error_page(400, "Bad request", + "Bad object id: %s", hex); + return; + } + commit = lookup_commit_reference(the_repository, &oid); + if (!commit) { + cgit_print_error_page(404, "Not found", + "Bad commit reference: %s", hex); + return; + } + info = cgit_parse_commit(commit); + + format_display_notes(&oid, ¬es, PAGE_ENCODING, 1); + + load_ref_decorations(NULL, DECORATE_FULL_REFS); + + ctx.page.title = fmtalloc("%s - %s", info->subject, ctx.page.title); + cgit_print_layout_start(); + cgit_print_diff_ctrls(); + html("<table summary='commit info' class='commit-info'>\n"); + html("<tr><th>author</th><td>"); + cgit_open_filter(ctx.repo->email_filter, info->author_email, "commit"); + html_txt(info->author); + if (!ctx.cfg.noplainemail) { + html(" "); + html_txt(info->author_email); + } + cgit_close_filter(ctx.repo->email_filter); + html("</td><td class='right'>"); + html_txt(show_date(info->author_date, info->author_tz, + cgit_date_mode(DATE_ISO8601))); + html("</td></tr>\n"); + html("<tr><th>committer</th><td>"); + cgit_open_filter(ctx.repo->email_filter, info->committer_email, "commit"); + html_txt(info->committer); + if (!ctx.cfg.noplainemail) { + html(" "); + html_txt(info->committer_email); + } + cgit_close_filter(ctx.repo->email_filter); + html("</td><td class='right'>"); + html_txt(show_date(info->committer_date, info->committer_tz, + cgit_date_mode(DATE_ISO8601))); + html("</td></tr>\n"); + html("<tr><th>commit</th><td colspan='2' class='oid'>"); + tmp = oid_to_hex(&commit->object.oid); + cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, prefix); + html(" ("); + cgit_patch_link("patch", NULL, NULL, NULL, tmp, prefix); + html(")</td></tr>\n"); + html("<tr><th>tree</th><td colspan='2' class='oid'>"); + tmp = xstrdup(hex); + cgit_tree_link(oid_to_hex(get_commit_tree_oid(commit)), NULL, NULL, + ctx.qry.head, tmp, NULL); + if (prefix) { + html(" /"); + cgit_tree_link(prefix, NULL, NULL, ctx.qry.head, tmp, prefix); + } + free(tmp); + html("</td></tr>\n"); + for (p = commit->parents; p; p = p->next) { + parent = lookup_commit_reference(the_repository, &p->item->object.oid); + if (!parent) { + html("<tr><td colspan='3'>"); + cgit_print_error("Error reading parent commit"); + html("</td></tr>"); + continue; + } + html("<tr><th>parent</th>" + "<td colspan='2' class='oid'>"); + tmp = tmp2 = oid_to_hex(&p->item->object.oid); + if (ctx.repo->enable_subject_links) { + parent_info = cgit_parse_commit(parent); + tmp2 = parent_info->subject; + } + cgit_commit_link(tmp2, NULL, NULL, ctx.qry.head, tmp, prefix); + html(" ("); + cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, + oid_to_hex(&p->item->object.oid), prefix); + html(")</td></tr>"); + parents++; + } + if (ctx.repo->snapshots) { + html("<tr><th>download</th><td colspan='2' class='oid'>"); + cgit_print_snapshot_links(ctx.repo, hex, "<br/>"); + html("</td></tr>"); + } + html("</table>\n"); + html("<div class='commit-subject'>"); + cgit_open_filter(ctx.repo->commit_filter); + html_txt(info->subject); + cgit_close_filter(ctx.repo->commit_filter); + show_commit_decorations(commit); + html("</div>"); + html("<pre class='commit-msg'>"); + cgit_open_filter(ctx.repo->commit_filter); + html_txt(info->msg); + cgit_close_filter(ctx.repo->commit_filter); + html("</pre>"); + if (notes.len != 0) { + html("<div class='notes-header'>Notes</div>"); + html("<div class='notes'>"); + cgit_open_filter(ctx.repo->commit_filter); + html_txt(notes.buf); + cgit_close_filter(ctx.repo->commit_filter); + html("</div>"); + html("<div class='notes-footer'></div>"); + } + if (parents < 3) { + if (parents) + tmp = oid_to_hex(&commit->parents->item->object.oid); + else + tmp = NULL; + cgit_print_diff(ctx.qry.oid, tmp, prefix, 0, 0); + } + strbuf_release(¬es); + cgit_free_commitinfo(info); + cgit_print_layout_end(); +} diff --git a/www/git.causal.agency/cgit/ui-commit.h b/www/git.causal.agency/cgit/ui-commit.h new file mode 100644 index 00000000..8198b4ba --- /dev/null +++ b/www/git.causal.agency/cgit/ui-commit.h @@ -0,0 +1,6 @@ +#ifndef UI_COMMIT_H +#define UI_COMMIT_H + +extern void cgit_print_commit(char *hex, const char *prefix); + +#endif /* UI_COMMIT_H */ diff --git a/www/git.causal.agency/cgit/ui-diff.c b/www/git.causal.agency/cgit/ui-diff.c new file mode 100644 index 00000000..2a64ae8f --- /dev/null +++ b/www/git.causal.agency/cgit/ui-diff.c @@ -0,0 +1,505 @@ +/* ui-diff.c: show diff between two blobs + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-diff.h" +#include "html.h" +#include "ui-shared.h" +#include "ui-ssdiff.h" + +struct object_id old_rev_oid[1]; +struct object_id new_rev_oid[1]; + +static int files, slots; +static int total_adds, total_rems, max_changes; +static int lines_added, lines_removed; + +static struct fileinfo { + char status; + struct object_id old_oid[1]; + struct object_id new_oid[1]; + unsigned short old_mode; + unsigned short new_mode; + char *old_path; + char *new_path; + unsigned int added; + unsigned int removed; + unsigned long old_size; + unsigned long new_size; + unsigned int binary:1; +} *items; + +static int use_ssdiff = 0; +static struct diff_filepair *current_filepair; +static const char *current_prefix; + +struct diff_filespec *cgit_get_current_old_file(void) +{ + return current_filepair->one; +} + +struct diff_filespec *cgit_get_current_new_file(void) +{ + return current_filepair->two; +} + +static void print_fileinfo(struct fileinfo *info) +{ + char *class; + + switch (info->status) { + case DIFF_STATUS_ADDED: + class = "add"; + break; + case DIFF_STATUS_COPIED: + class = "cpy"; + break; + case DIFF_STATUS_DELETED: + class = "del"; + break; + case DIFF_STATUS_MODIFIED: + class = "upd"; + break; + case DIFF_STATUS_RENAMED: + class = "mov"; + break; + case DIFF_STATUS_TYPE_CHANGED: + class = "typ"; + break; + case DIFF_STATUS_UNKNOWN: + class = "unk"; + break; + case DIFF_STATUS_UNMERGED: + class = "stg"; + break; + default: + die("bug: unhandled diff status %c", info->status); + } + + html("<tr>"); + html("<td class='mode'>"); + if (is_null_oid(info->new_oid)) { + cgit_print_filemode(info->old_mode); + } else { + cgit_print_filemode(info->new_mode); + } + + if (info->old_mode != info->new_mode && + !is_null_oid(info->old_oid) && + !is_null_oid(info->new_oid)) { + html("<span class='modechange'>["); + cgit_print_filemode(info->old_mode); + html("]</span>"); + } + htmlf("</td><td class='%s'>", class); + cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.oid, + ctx.qry.oid2, info->new_path); + if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) { + htmlf(" (%s from ", + info->status == DIFF_STATUS_COPIED ? "copied" : "renamed"); + html_txt(info->old_path); + html(")"); + } + html("</td><td class='right'>"); + if (info->binary) { + htmlf("bin</td><td class='graph'>%ld -> %ld bytes", + info->old_size, info->new_size); + return; + } + htmlf("%d", info->added + info->removed); + html("</td><td class='graph'>"); + htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes)); + htmlf("<td class='add' style='width: %.1f%%;'/>", + info->added * 100.0 / max_changes); + htmlf("<td class='rem' style='width: %.1f%%;'/>", + info->removed * 100.0 / max_changes); + htmlf("<td class='none' style='width: %.1f%%;'/>", + (max_changes - info->removed - info->added) * 100.0 / max_changes); + html("</tr></table></td></tr>\n"); +} + +static void count_diff_lines(char *line, int len) +{ + if (line && (len > 0)) { + if (line[0] == '+') + lines_added++; + else if (line[0] == '-') + lines_removed++; + } +} + +static int show_filepair(struct diff_filepair *pair) +{ + /* Always show if we have no limiting prefix. */ + if (!current_prefix) + return 1; + + /* Show if either path in the pair begins with the prefix. */ + if (starts_with(pair->one->path, current_prefix) || + starts_with(pair->two->path, current_prefix)) + return 1; + + /* Otherwise we don't want to show this filepair. */ + return 0; +} + +static void inspect_filepair(struct diff_filepair *pair) +{ + int binary = 0; + unsigned long old_size = 0; + unsigned long new_size = 0; + + if (!show_filepair(pair)) + return; + + files++; + lines_added = 0; + lines_removed = 0; + cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size, &new_size, + &binary, 0, ctx.qry.ignorews, count_diff_lines); + if (files >= slots) { + if (slots == 0) + slots = 4; + else + slots = slots * 2; + items = xrealloc(items, slots * sizeof(struct fileinfo)); + } + items[files-1].status = pair->status; + oidcpy(items[files-1].old_oid, &pair->one->oid); + oidcpy(items[files-1].new_oid, &pair->two->oid); + items[files-1].old_mode = pair->one->mode; + items[files-1].new_mode = pair->two->mode; + items[files-1].old_path = xstrdup(pair->one->path); + items[files-1].new_path = xstrdup(pair->two->path); + items[files-1].added = lines_added; + items[files-1].removed = lines_removed; + items[files-1].old_size = old_size; + items[files-1].new_size = new_size; + items[files-1].binary = binary; + if (lines_added + lines_removed > max_changes) + max_changes = lines_added + lines_removed; + total_adds += lines_added; + total_rems += lines_removed; +} + +static void cgit_print_diffstat(const struct object_id *old_oid, + const struct object_id *new_oid, + const char *prefix) +{ + int i; + + html("<div class='diffstat-header'>"); + cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.oid, + ctx.qry.oid2, NULL); + if (prefix) { + html(" (limited to '"); + html_txt(prefix); + html("')"); + } + html("</div>"); + html("<table summary='diffstat' class='diffstat'>"); + max_changes = 0; + cgit_diff_tree(old_oid, new_oid, inspect_filepair, prefix, + ctx.qry.ignorews); + for (i = 0; i<files; i++) + print_fileinfo(&items[i]); + html("</table>"); + html("<div class='diffstat-summary'>"); + htmlf("%d files changed, %d insertions, %d deletions", + files, total_adds, total_rems); + html("</div>"); +} + + +/* + * print a single line returned from xdiff + */ +static void print_line(char *line, int len) +{ + char *class = "ctx"; + char c = line[len-1]; + + if (line[0] == '+') + class = "add"; + else if (line[0] == '-') + class = "del"; + else if (line[0] == '@') + class = "hunk"; + + htmlf("<span class='%s'>", class); + line[len-1] = '\0'; + html_txt(line); + line[len-1] = c; + html("</span>\n"); +} + +static void header(const struct object_id *oid1, char *path1, int mode1, + const struct object_id *oid2, char *path2, int mode2) +{ + char *abbrev1, *abbrev2; + int subproject; + + subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); + html("<span class='head'>"); + html("diff --git a/"); + html_txt(path1); + html(" b/"); + html_txt(path2); + html("\n"); + + if (mode1 == 0) + htmlf("new file mode %.6o\n", mode2); + + if (mode2 == 0) + htmlf("deleted file mode %.6o\n", mode1); + + if (!subproject) { + abbrev1 = xstrdup(find_unique_abbrev(oid1, DEFAULT_ABBREV)); + abbrev2 = xstrdup(find_unique_abbrev(oid2, DEFAULT_ABBREV)); + htmlf("index %s..%s", abbrev1, abbrev2); + free(abbrev1); + free(abbrev2); + if (mode1 != 0 && mode2 != 0) { + htmlf(" %.6o", mode1); + if (mode2 != mode1) + htmlf("..%.6o", mode2); + } + html("\n"); + if (is_null_oid(oid1)) { + path1 = "dev/null"; + html("--- /"); + } else + html("--- a/"); + if (mode1 != 0) + cgit_tree_link(path1, NULL, NULL, ctx.qry.head, + oid_to_hex(old_rev_oid), path1); + else + html_txt(path1); + html("\n"); + if (is_null_oid(oid2)) { + path2 = "dev/null"; + html("+++ /"); + } else + html("+++ b/"); + if (mode2 != 0) + cgit_tree_link(path2, NULL, NULL, ctx.qry.head, + oid_to_hex(new_rev_oid), path2); + else + html_txt(path2); + html("\n"); + } + html("</span>"); +} + +static void filepair_cb(struct diff_filepair *pair) +{ + unsigned long old_size = 0; + unsigned long new_size = 0; + int binary = 0; + linediff_fn print_line_fn = print_line; + + if (!show_filepair(pair)) + return; + + current_filepair = pair; + if (use_ssdiff) { + cgit_ssdiff_header_begin(); + print_line_fn = cgit_ssdiff_line_cb; + } + header(&pair->one->oid, pair->one->path, pair->one->mode, + &pair->two->oid, pair->two->path, pair->two->mode); + if (use_ssdiff) + cgit_ssdiff_header_end(); + if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { + if (S_ISGITLINK(pair->one->mode)) + print_line_fn(fmt("-Subproject %s", oid_to_hex(&pair->one->oid)), 52); + if (S_ISGITLINK(pair->two->mode)) + print_line_fn(fmt("+Subproject %s", oid_to_hex(&pair->two->oid)), 52); + if (use_ssdiff) + cgit_ssdiff_footer(); + return; + } + if (cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size, + &new_size, &binary, ctx.qry.context, + ctx.qry.ignorews, print_line_fn)) + cgit_print_error("Error running diff"); + if (binary) { + if (use_ssdiff) + html("<tr><td colspan='4'>Binary files differ</td></tr>"); + else + html("Binary files differ"); + } + if (use_ssdiff) + cgit_ssdiff_footer(); +} + +void cgit_print_diff_ctrls(void) +{ + int i, curr; + + html("<div class='cgit-panel'>"); + html("<b>diff options</b>"); + html("<form method='get'>"); + cgit_add_hidden_formfields(1, 0, ctx.qry.page); + html("<table>"); + html("<tr><td colspan='2'/></tr>"); + html("<tr>"); + html("<td class='label'>context:</td>"); + html("<td class='ctrl'>"); + html("<select name='context' onchange='this.form.submit();'>"); + curr = ctx.qry.context; + if (!curr) + curr = 3; + for (i = 1; i <= 10; i++) + html_intoption(i, fmt("%d", i), curr); + for (i = 15; i <= 40; i += 5) + html_intoption(i, fmt("%d", i), curr); + html("</select>"); + html("</td>"); + html("</tr><tr>"); + html("<td class='label'>space:</td>"); + html("<td class='ctrl'>"); + html("<select name='ignorews' onchange='this.form.submit();'>"); + html_intoption(0, "include", ctx.qry.ignorews); + html_intoption(1, "ignore", ctx.qry.ignorews); + html("</select>"); + html("</td>"); + html("</tr><tr>"); + html("<td class='label'>mode:</td>"); + html("<td class='ctrl'>"); + html("<select name='dt' onchange='this.form.submit();'>"); + curr = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype; + html_intoption(0, "unified", curr); + html_intoption(1, "ssdiff", curr); + html_intoption(2, "stat only", curr); + html("</select></td></tr>"); + html("<tr><td/><td class='ctrl'>"); + html("<noscript><input type='submit' value='reload'/></noscript>"); + html("</td></tr></table>"); + html("</form>"); + html("</div>"); +} + +void cgit_print_diff(const char *new_rev, const char *old_rev, + const char *prefix, int show_ctrls, int raw) +{ + struct commit *commit, *commit2; + const struct object_id *old_tree_oid, *new_tree_oid; + diff_type difftype; + + /* + * If "follow" is set then the diff machinery needs to examine the + * entire commit to detect renames so we must limit the paths in our + * own callbacks and not pass the prefix to the diff machinery. + */ + if (ctx.qry.follow && ctx.cfg.enable_follow_links) { + current_prefix = prefix; + prefix = ""; + } else { + current_prefix = NULL; + } + + if (!new_rev) + new_rev = ctx.qry.head; + if (get_oid(new_rev, new_rev_oid)) { + cgit_print_error_page(404, "Not found", + "Bad object name: %s", new_rev); + return; + } + commit = lookup_commit_reference(the_repository, new_rev_oid); + if (!commit || parse_commit(commit)) { + cgit_print_error_page(404, "Not found", + "Bad commit: %s", oid_to_hex(new_rev_oid)); + return; + } + new_tree_oid = get_commit_tree_oid(commit); + + if (old_rev) { + if (get_oid(old_rev, old_rev_oid)) { + cgit_print_error_page(404, "Not found", + "Bad object name: %s", old_rev); + return; + } + } else if (commit->parents && commit->parents->item) { + oidcpy(old_rev_oid, &commit->parents->item->object.oid); + } else { + oidclr(old_rev_oid); + } + + if (!is_null_oid(old_rev_oid)) { + commit2 = lookup_commit_reference(the_repository, old_rev_oid); + if (!commit2 || parse_commit(commit2)) { + cgit_print_error_page(404, "Not found", + "Bad commit: %s", oid_to_hex(old_rev_oid)); + return; + } + old_tree_oid = get_commit_tree_oid(commit2); + } else { + old_tree_oid = NULL; + } + + if (raw) { + struct diff_options diffopt; + + diff_setup(&diffopt); + diffopt.output_format = DIFF_FORMAT_PATCH; + diffopt.flags.recursive = 1; + diff_setup_done(&diffopt); + + ctx.page.mimetype = "text/plain"; + cgit_print_http_headers(); + if (old_tree_oid) { + diff_tree_oid(old_tree_oid, new_tree_oid, "", + &diffopt); + } else { + diff_root_tree_oid(new_tree_oid, "", &diffopt); + } + diffcore_std(&diffopt); + diff_flush(&diffopt); + + return; + } + + difftype = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype; + use_ssdiff = difftype == DIFF_SSDIFF; + + if (show_ctrls) { + cgit_print_layout_start(); + cgit_print_diff_ctrls(); + } + + /* + * Clicking on a link to a file in the diff stat should show a diff + * of the file, showing the diff stat limited to a single file is + * pretty useless. All links from this point on will be to + * individual files, so we simply reset the difftype in the query + * here to avoid propagating DIFF_STATONLY to the individual files. + */ + if (difftype == DIFF_STATONLY) + ctx.qry.difftype = ctx.cfg.difftype; + + cgit_print_diffstat(old_rev_oid, new_rev_oid, prefix); + + if (difftype == DIFF_STATONLY) + return; + + if (use_ssdiff) { + html("<table summary='ssdiff' class='ssdiff'>"); + } else { + html("<table summary='diff' class='diff'>"); + html("<tr><td><pre>"); + } + cgit_diff_tree(old_rev_oid, new_rev_oid, filepair_cb, prefix, + ctx.qry.ignorews); + if (!use_ssdiff) + html("</pre></td></tr>"); + html("</table>"); + + if (show_ctrls) + cgit_print_layout_end(); +} diff --git a/www/git.causal.agency/cgit/ui-diff.h b/www/git.causal.agency/cgit/ui-diff.h new file mode 100644 index 00000000..39264a16 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-diff.h @@ -0,0 +1,15 @@ +#ifndef UI_DIFF_H +#define UI_DIFF_H + +extern void cgit_print_diff_ctrls(void); + +extern void cgit_print_diff(const char *new_hex, const char *old_hex, + const char *prefix, int show_ctrls, int raw); + +extern struct diff_filespec *cgit_get_current_old_file(void); +extern struct diff_filespec *cgit_get_current_new_file(void); + +extern struct object_id old_rev_oid[1]; +extern struct object_id new_rev_oid[1]; + +#endif /* UI_DIFF_H */ diff --git a/www/git.causal.agency/cgit/ui-log.c b/www/git.causal.agency/cgit/ui-log.c new file mode 100644 index 00000000..b443ca73 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-log.c @@ -0,0 +1,555 @@ +/* ui-log.c: functions for log output + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-log.h" +#include "html.h" +#include "ui-shared.h" +#include "strvec.h" + +static int files, add_lines, rem_lines, lines_counted; + +/* + * The list of available column colors in the commit graph. + */ +static const char *column_colors_html[] = { + "<span class='column1'>", + "<span class='column2'>", + "<span class='column3'>", + "<span class='column4'>", + "<span class='column5'>", + "<span class='column6'>", + "</span>", +}; + +#define COLUMN_COLORS_HTML_MAX (ARRAY_SIZE(column_colors_html) - 1) + +static void count_lines(char *line, int size) +{ + if (size <= 0) + return; + + if (line[0] == '+') + add_lines++; + + else if (line[0] == '-') + rem_lines++; +} + +static void inspect_files(struct diff_filepair *pair) +{ + unsigned long old_size = 0; + unsigned long new_size = 0; + int binary = 0; + + files++; + if (ctx.repo->enable_log_linecount) + cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size, + &new_size, &binary, 0, ctx.qry.ignorews, + count_lines); +} + +void show_commit_decorations(struct commit *commit) +{ + const struct name_decoration *deco; + static char buf[1024]; + + buf[sizeof(buf) - 1] = 0; + deco = get_name_decoration(&commit->object); + if (!deco) + return; + html("<span class='decoration'>"); + while (deco) { + struct object_id oid_tag, peeled; + int is_annotated = 0; + + strlcpy(buf, prettify_refname(deco->name), sizeof(buf)); + switch(deco->type) { + case DECORATION_NONE: + /* If the git-core doesn't recognize it, + * don't display anything. */ + break; + case DECORATION_REF_LOCAL: + html(" "); + cgit_log_link(buf, NULL, "branch-deco", buf, NULL, + ctx.qry.vpath, 0, NULL, NULL, + ctx.qry.showmsg, 0); + break; + case DECORATION_REF_TAG: + html(" "); + if (!read_ref(deco->name, &oid_tag) && !peel_iterated_oid(&oid_tag, &peeled)) + is_annotated = !oideq(&oid_tag, &peeled); + cgit_tag_link(buf, NULL, is_annotated ? "tag-annotated-deco" : "tag-deco", buf); + break; + case DECORATION_REF_REMOTE: + if (!ctx.repo->enable_remote_branches) + break; + html(" "); + cgit_log_link(buf, NULL, "remote-deco", NULL, + oid_to_hex(&commit->object.oid), + ctx.qry.vpath, 0, NULL, NULL, + ctx.qry.showmsg, 0); + break; + default: + html(" "); + cgit_commit_link(buf, NULL, "deco", ctx.qry.head, + oid_to_hex(&commit->object.oid), + ctx.qry.vpath); + break; + } + deco = deco->next; + } + html("</span>"); +} + +static void handle_rename(struct diff_filepair *pair) +{ + /* + * After we have seen a rename, we generate links to the previous + * name of the file so that commit & diff views get fed the path + * that is correct for the commit they are showing, avoiding the + * need to walk the entire history leading back to every commit we + * show in order detect renames. + */ + if (0 != strcmp(ctx.qry.vpath, pair->two->path)) { + free(ctx.qry.vpath); + ctx.qry.vpath = xstrdup(pair->two->path); + } + inspect_files(pair); +} + +static int show_commit(struct commit *commit, struct rev_info *revs) +{ + struct commit_list *parents = commit->parents; + struct commit *parent; + int found = 0, saved_fmt; + struct diff_flags saved_flags = revs->diffopt.flags; + + /* Always show if we're not in "follow" mode with a single file. */ + if (!ctx.qry.follow) + return 1; + + /* + * In "follow" mode, we don't show merges. This is consistent with + * "git log --follow -- <file>". + */ + if (parents && parents->next) + return 0; + + /* + * If this is the root commit, do what rev_info tells us. + */ + if (!parents) + return revs->show_root_diff; + + /* When we get here we have precisely one parent. */ + parent = parents->item; + /* If we can't parse the commit, let print_commit() report an error. */ + if (parse_commit(parent)) + return 1; + + files = 0; + add_lines = 0; + rem_lines = 0; + + revs->diffopt.flags.recursive = 1; + diff_tree_oid(get_commit_tree_oid(parent), + get_commit_tree_oid(commit), + "", &revs->diffopt); + diffcore_std(&revs->diffopt); + + found = !diff_queue_is_empty(); + saved_fmt = revs->diffopt.output_format; + revs->diffopt.output_format = DIFF_FORMAT_CALLBACK; + revs->diffopt.format_callback = cgit_diff_tree_cb; + revs->diffopt.format_callback_data = handle_rename; + diff_flush(&revs->diffopt); + revs->diffopt.output_format = saved_fmt; + revs->diffopt.flags = saved_flags; + + lines_counted = 1; + return found; +} + +static void print_commit(struct commit *commit, struct rev_info *revs) +{ + struct commitinfo *info; + int columns = revs->graph ? 4 : 3; + struct strbuf graphbuf = STRBUF_INIT; + struct strbuf msgbuf = STRBUF_INIT; + + if (ctx.repo->enable_log_filecount) + columns++; + if (ctx.repo->enable_log_linecount) + columns++; + + if (revs->graph) { + /* Advance graph until current commit */ + while (!graph_next_line(revs->graph, &graphbuf)) { + /* Print graph segment in otherwise empty table row */ + html("<tr class='nohover'><td class='commitgraph'>"); + html(graphbuf.buf); + htmlf("</td><td colspan='%d' /></tr>\n", columns); + strbuf_setlen(&graphbuf, 0); + } + /* Current commit's graph segment is now ready in graphbuf */ + } + + info = cgit_parse_commit(commit); + htmlf("<tr%s>", ctx.qry.showmsg ? " class='logheader'" : ""); + + if (revs->graph) { + /* Print graph segment for current commit */ + html("<td class='commitgraph'>"); + html(graphbuf.buf); + html("</td>"); + strbuf_setlen(&graphbuf, 0); + } + else { + html("<td>"); + cgit_print_age(info->committer_date, info->committer_tz, TM_WEEK * 2); + html("</td>"); + } + + htmlf("<td%s>", ctx.qry.showmsg ? " class='logsubject'" : ""); + if (ctx.qry.showmsg) { + /* line-wrap long commit subjects instead of truncating them */ + size_t subject_len = strlen(info->subject); + + if (subject_len > ctx.cfg.max_msg_len && + ctx.cfg.max_msg_len >= 15) { + /* symbol for signaling line-wrap (in PAGE_ENCODING) */ + const char wrap_symbol[] = { ' ', 0xE2, 0x86, 0xB5, 0 }; + int i = ctx.cfg.max_msg_len - strlen(wrap_symbol); + + /* Rewind i to preceding space character */ + while (i > 0 && !isspace(info->subject[i])) + --i; + if (!i) /* Oops, zero spaces. Reset i */ + i = ctx.cfg.max_msg_len - strlen(wrap_symbol); + + /* add remainder starting at i to msgbuf */ + strbuf_add(&msgbuf, info->subject + i, subject_len - i); + strbuf_trim(&msgbuf); + strbuf_add(&msgbuf, "\n\n", 2); + + /* Place wrap_symbol at position i in info->subject */ + strlcpy(info->subject + i, wrap_symbol, subject_len - i + 1); + } + } + cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, + oid_to_hex(&commit->object.oid), ctx.qry.vpath); + show_commit_decorations(commit); + html("</td><td>"); + cgit_open_filter(ctx.repo->email_filter, info->author_email, "log"); + html_txt(info->author); + cgit_close_filter(ctx.repo->email_filter); + + if (revs->graph) { + html("</td><td>"); + cgit_print_age(info->committer_date, info->committer_tz, TM_WEEK * 2); + } + + if (!lines_counted && (ctx.repo->enable_log_filecount || + ctx.repo->enable_log_linecount)) { + files = 0; + add_lines = 0; + rem_lines = 0; + cgit_diff_commit(commit, inspect_files, ctx.qry.vpath); + } + + if (ctx.repo->enable_log_filecount) + htmlf("</td><td>%d", files); + if (ctx.repo->enable_log_linecount) + htmlf("</td><td><span class='deletions'>-%d</span>/" + "<span class='insertions'>+%d</span>", rem_lines, add_lines); + + html("</td></tr>\n"); + + if ((revs->graph && !graph_is_commit_finished(revs->graph)) + || ctx.qry.showmsg) { /* Print a second table row */ + html("<tr class='nohover-highlight'>"); + + if (ctx.qry.showmsg) { + /* Concatenate commit message + notes in msgbuf */ + if (info->msg && *(info->msg)) { + strbuf_addstr(&msgbuf, info->msg); + strbuf_addch(&msgbuf, '\n'); + } + format_display_notes(&commit->object.oid, + &msgbuf, PAGE_ENCODING, 0); + strbuf_addch(&msgbuf, '\n'); + strbuf_ltrim(&msgbuf); + } + + if (revs->graph) { + int lines = 0; + + /* Calculate graph padding */ + if (ctx.qry.showmsg) { + /* Count #lines in commit message + notes */ + const char *p = msgbuf.buf; + lines = 1; + while ((p = strchr(p, '\n'))) { + p++; + lines++; + } + } + + /* Print graph padding */ + html("<td class='commitgraph'>"); + while (lines > 0 || !graph_is_commit_finished(revs->graph)) { + if (graphbuf.len) + html("\n"); + strbuf_setlen(&graphbuf, 0); + graph_next_line(revs->graph, &graphbuf); + html(graphbuf.buf); + lines--; + } + html("</td>\n"); + } + else + html("<td/>"); /* Empty 'Age' column */ + + /* Print msgbuf into remainder of table row */ + htmlf("<td colspan='%d'%s>\n", columns - (revs->graph ? 1 : 0), + ctx.qry.showmsg ? " class='logmsg'" : ""); + html_txt(msgbuf.buf); + html("</td></tr>\n"); + } + + strbuf_release(&msgbuf); + strbuf_release(&graphbuf); + cgit_free_commitinfo(info); +} + +static const char *disambiguate_ref(const char *ref, int *must_free_result) +{ + struct object_id oid; + struct strbuf longref = STRBUF_INIT; + + strbuf_addf(&longref, "refs/heads/%s", ref); + if (get_oid(longref.buf, &oid) == 0) { + *must_free_result = 1; + return strbuf_detach(&longref, NULL); + } + + *must_free_result = 0; + strbuf_release(&longref); + return ref; +} + +static char *next_token(char **src) +{ + char *result; + + if (!src || !*src) + return NULL; + while (isspace(**src)) + (*src)++; + if (!**src) + return NULL; + result = *src; + while (**src) { + if (isspace(**src)) { + **src = '\0'; + (*src)++; + break; + } + (*src)++; + } + return result; +} + +void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, + const char *path, int pager, int commit_graph, int commit_sort) +{ + struct rev_info rev; + struct commit *commit; + struct strvec rev_argv = STRVEC_INIT; + int i, columns = commit_graph ? 4 : 3; + int must_free_tip = 0; + + /* rev_argv.argv[0] will be ignored by setup_revisions */ + strvec_push(&rev_argv, "log_rev_setup"); + + if (!tip) + tip = ctx.qry.head; + tip = disambiguate_ref(tip, &must_free_tip); + strvec_push(&rev_argv, tip); + + if (grep && pattern && *pattern) { + pattern = xstrdup(pattern); + if (!strcmp(grep, "grep") || !strcmp(grep, "author") || + !strcmp(grep, "committer")) { + strvec_pushf(&rev_argv, "--%s=%s", grep, pattern); + } else if (!strcmp(grep, "range")) { + char *arg; + /* Split the pattern at whitespace and add each token + * as a revision expression. Do not accept other + * rev-list options. Also, replace the previously + * pushed tip (it's no longer relevant). + */ + strvec_pop(&rev_argv); + while ((arg = next_token(&pattern))) { + if (*arg == '-') { + fprintf(stderr, "Bad range expr: %s\n", + arg); + break; + } + strvec_push(&rev_argv, arg); + } + } + } + + if (!path || !ctx.cfg.enable_follow_links) { + /* + * If we don't have a path, "follow" is a no-op so make sure + * the variable is set to false to avoid needing to check + * both this and whether we have a path everywhere. + */ + ctx.qry.follow = 0; + } + + if (commit_graph && !ctx.qry.follow) { + strvec_push(&rev_argv, "--graph"); + strvec_push(&rev_argv, "--color"); + graph_set_column_colors(column_colors_html, + COLUMN_COLORS_HTML_MAX); + } + + if (commit_sort == 1) + strvec_push(&rev_argv, "--date-order"); + else if (commit_sort == 2) + strvec_push(&rev_argv, "--topo-order"); + + if (path && ctx.qry.follow) + strvec_push(&rev_argv, "--follow"); + strvec_push(&rev_argv, "--"); + if (path) + strvec_push(&rev_argv, path); + + init_revisions(&rev, NULL); + rev.abbrev = DEFAULT_ABBREV; + rev.commit_format = CMIT_FMT_DEFAULT; + rev.verbose_header = 1; + rev.show_root_diff = 0; + rev.ignore_missing = 1; + rev.simplify_history = 1; + setup_revisions(rev_argv.nr, rev_argv.v, &rev, NULL); + load_ref_decorations(NULL, DECORATE_FULL_REFS); + rev.show_decorations = 1; + rev.grep_filter.ignore_case = 1; + + rev.diffopt.detect_rename = 1; + rev.diffopt.rename_limit = ctx.cfg.renamelimit; + if (ctx.qry.ignorews) + DIFF_XDL_SET(&rev.diffopt, IGNORE_WHITESPACE); + + compile_grep_patterns(&rev.grep_filter); + prepare_revision_walk(&rev); + + if (pager) { + cgit_print_layout_start(); + html("<table class='list nowrap'>"); + } + + html("<tr class='nohover'>"); + if (commit_graph) + html("<th></th>"); + else + html("<th class='left'>Age</th>"); + html("<th class='left'>Commit message"); + if (pager) { + html(" ("); + cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL, + NULL, ctx.qry.head, ctx.qry.oid, + ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep, + ctx.qry.search, ctx.qry.showmsg ? 0 : 1, + ctx.qry.follow); + html(")"); + } + html("</th><th class='left'>Author</th>"); + if (rev.graph) + html("<th class='left'>Age</th>"); + if (ctx.repo->enable_log_filecount) { + html("<th class='left'>Files</th>"); + columns++; + } + if (ctx.repo->enable_log_linecount) { + html("<th class='left'>Lines</th>"); + columns++; + } + html("</tr>\n"); + + if (ofs<0) + ofs = 0; + + for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; /* nop */) { + if (show_commit(commit, &rev)) + i++; + free_commit_buffer(the_repository->parsed_objects, commit); + free_commit_list(commit->parents); + commit->parents = NULL; + } + + for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; /* nop */) { + /* + * In "follow" mode, we must count the files and lines the + * first time we invoke diff on a given commit, and we need + * to do that to see if the commit touches the path we care + * about, so we do it in show_commit. Hence we must clear + * lines_counted here. + * + * This has the side effect of avoiding running diff twice + * when we are both following renames and showing file + * and/or line counts. + */ + lines_counted = 0; + if (show_commit(commit, &rev)) { + i++; + print_commit(commit, &rev); + } + free_commit_buffer(the_repository->parsed_objects, commit); + free_commit_list(commit->parents); + commit->parents = NULL; + } + if (pager) { + html("</table><ul class='pager'>"); + if (ofs > 0) { + html("<li>"); + cgit_log_link("[prev]", NULL, NULL, ctx.qry.head, + ctx.qry.oid, ctx.qry.vpath, + ofs - cnt, ctx.qry.grep, + ctx.qry.search, ctx.qry.showmsg, + ctx.qry.follow); + html("</li>"); + } + if ((commit = get_revision(&rev)) != NULL) { + html("<li>"); + cgit_log_link("[next]", NULL, NULL, ctx.qry.head, + ctx.qry.oid, ctx.qry.vpath, + ofs + cnt, ctx.qry.grep, + ctx.qry.search, ctx.qry.showmsg, + ctx.qry.follow); + html("</li>"); + } + html("</ul>"); + cgit_print_layout_end(); + } else if ((commit = get_revision(&rev)) != NULL) { + htmlf("<tr class='nohover'><td colspan='%d'>", columns); + cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL, + ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg, + ctx.qry.follow); + html("</td></tr>\n"); + } + + /* If we allocated tip then it is safe to cast away const. */ + if (must_free_tip) + free((char*) tip); +} diff --git a/www/git.causal.agency/cgit/ui-log.h b/www/git.causal.agency/cgit/ui-log.h new file mode 100644 index 00000000..325607cd --- /dev/null +++ b/www/git.causal.agency/cgit/ui-log.h @@ -0,0 +1,9 @@ +#ifndef UI_LOG_H +#define UI_LOG_H + +extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, + char *pattern, const char *path, int pager, + int commit_graph, int commit_sort); +extern void show_commit_decorations(struct commit *commit); + +#endif /* UI_LOG_H */ diff --git a/www/git.causal.agency/cgit/ui-patch.c b/www/git.causal.agency/cgit/ui-patch.c new file mode 100644 index 00000000..4ac03cbe --- /dev/null +++ b/www/git.causal.agency/cgit/ui-patch.c @@ -0,0 +1,98 @@ +/* ui-patch.c: generate patch view + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-patch.h" +#include "html.h" +#include "ui-shared.h" + +/* two commit hashes with two dots in between and termination */ +#define REV_RANGE_LEN 2 * GIT_MAX_HEXSZ + 3 + +void cgit_print_patch(const char *new_rev, const char *old_rev, + const char *prefix) +{ + struct rev_info rev; + struct commit *commit; + struct object_id new_rev_oid, old_rev_oid; + char rev_range[REV_RANGE_LEN]; + const char *rev_argv[] = { NULL, "--reverse", "--format=email", rev_range, "--", prefix, NULL }; + int rev_argc = ARRAY_SIZE(rev_argv) - 1; + char *patchname; + + if (!prefix) + rev_argc--; + + if (!new_rev) + new_rev = ctx.qry.head; + + if (get_oid(new_rev, &new_rev_oid)) { + cgit_print_error_page(404, "Not found", + "Bad object id: %s", new_rev); + return; + } + commit = lookup_commit_reference(the_repository, &new_rev_oid); + if (!commit) { + cgit_print_error_page(404, "Not found", + "Bad commit reference: %s", new_rev); + return; + } + + if (old_rev) { + if (get_oid(old_rev, &old_rev_oid)) { + cgit_print_error_page(404, "Not found", + "Bad object id: %s", old_rev); + return; + } + if (!lookup_commit_reference(the_repository, &old_rev_oid)) { + cgit_print_error_page(404, "Not found", + "Bad commit reference: %s", old_rev); + return; + } + } else if (commit->parents && commit->parents->item) { + oidcpy(&old_rev_oid, &commit->parents->item->object.oid); + } else { + oidclr(&old_rev_oid); + } + + if (is_null_oid(&old_rev_oid)) { + memcpy(rev_range, oid_to_hex(&new_rev_oid), the_hash_algo->hexsz + 1); + } else { + xsnprintf(rev_range, REV_RANGE_LEN, "%s..%s", oid_to_hex(&old_rev_oid), + oid_to_hex(&new_rev_oid)); + } + + patchname = fmt("%s.patch", rev_range); + ctx.page.mimetype = "text/plain"; + ctx.page.filename = patchname; + cgit_print_http_headers(); + + if (ctx.cfg.noplainemail) { + rev_argv[2] = "--format=format:From %H Mon Sep 17 00:00:00 " + "2001%nFrom: %an%nDate: %aD%n%w(78,0,1)Subject: " + "%s%n%n%w(0)%b"; + } + + init_revisions(&rev, NULL); + rev.abbrev = DEFAULT_ABBREV; + rev.verbose_header = 1; + rev.diff = 1; + rev.show_root_diff = 1; + rev.max_parents = 1; + rev.diffopt.output_format |= DIFF_FORMAT_DIFFSTAT | + DIFF_FORMAT_PATCH | DIFF_FORMAT_SUMMARY; + if (prefix) + rev.diffopt.stat_sep = fmt("(limited to '%s')\n\n", prefix); + setup_revisions(rev_argc, rev_argv, &rev, NULL); + prepare_revision_walk(&rev); + + while ((commit = get_revision(&rev)) != NULL) { + log_tree_commit(&rev, commit); + printf("-- \ncgit %s\n\n", cgit_version); + } +} diff --git a/www/git.causal.agency/cgit/ui-patch.h b/www/git.causal.agency/cgit/ui-patch.h new file mode 100644 index 00000000..7a6cacd5 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-patch.h @@ -0,0 +1,7 @@ +#ifndef UI_PATCH_H +#define UI_PATCH_H + +extern void cgit_print_patch(const char *new_rev, const char *old_rev, + const char *prefix); + +#endif /* UI_PATCH_H */ diff --git a/www/git.causal.agency/cgit/ui-plain.c b/www/git.causal.agency/cgit/ui-plain.c new file mode 100644 index 00000000..65a205fa --- /dev/null +++ b/www/git.causal.agency/cgit/ui-plain.c @@ -0,0 +1,207 @@ +/* ui-plain.c: functions for output of plain blobs by path + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-plain.h" +#include "html.h" +#include "ui-shared.h" + +struct walk_tree_context { + int match_baselen; + int match; +}; + +static int print_object(const struct object_id *oid, const char *path) +{ + enum object_type type; + char *buf, *mimetype; + unsigned long size; + + type = oid_object_info(the_repository, oid, &size); + if (type == OBJ_BAD) { + cgit_print_error_page(404, "Not found", "Not found"); + return 0; + } + + buf = read_object_file(oid, &type, &size); + if (!buf) { + cgit_print_error_page(404, "Not found", "Not found"); + return 0; + } + + mimetype = get_mimetype_for_filename(path); + ctx.page.mimetype = mimetype; + + if (!ctx.repo->enable_html_serving) { + html("X-Content-Type-Options: nosniff\n"); + html("Content-Security-Policy: default-src 'none'\n"); + if (mimetype) { + /* Built-in white list allows PDF and everything that isn't text/ and application/ */ + if ((!strncmp(mimetype, "text/", 5) || !strncmp(mimetype, "application/", 12)) && strcmp(mimetype, "application/pdf")) + ctx.page.mimetype = NULL; + } + } + + if (!ctx.page.mimetype) { + if (buffer_is_binary(buf, size)) { + ctx.page.mimetype = "application/octet-stream"; + ctx.page.charset = NULL; + } else { + ctx.page.mimetype = "text/plain"; + } + } + ctx.page.filename = path; + ctx.page.size = size; + ctx.page.etag = oid_to_hex(oid); + cgit_print_http_headers(); + html_raw(buf, size); + free(mimetype); + free(buf); + return 1; +} + +static char *buildpath(const char *base, int baselen, const char *path) +{ + if (path[0]) + return fmtalloc("%.*s%s/", baselen, base, path); + else + return fmtalloc("%.*s/", baselen, base); +} + +static void print_dir(const struct object_id *oid, const char *base, + int baselen, const char *path) +{ + char *fullpath, *slash; + size_t len; + + fullpath = buildpath(base, baselen, path); + slash = (fullpath[0] == '/' ? "" : "/"); + ctx.page.etag = oid_to_hex(oid); + cgit_print_http_headers(); + htmlf("<html><head><title>%s", slash); + html_txt(fullpath); + htmlf("</title></head>\n<body>\n<h2>%s", slash); + html_txt(fullpath); + html("</h2>\n<ul>\n"); + len = strlen(fullpath); + if (len > 1) { + fullpath[len - 1] = 0; + slash = strrchr(fullpath, '/'); + if (slash) + *(slash + 1) = 0; + else { + free(fullpath); + fullpath = NULL; + } + html("<li>"); + cgit_plain_link("../", NULL, NULL, ctx.qry.head, ctx.qry.oid, + fullpath); + html("</li>\n"); + } + free(fullpath); +} + +static void print_dir_entry(const struct object_id *oid, const char *base, + int baselen, const char *path, unsigned mode) +{ + char *fullpath; + + fullpath = buildpath(base, baselen, path); + if (!S_ISDIR(mode) && !S_ISGITLINK(mode)) + fullpath[strlen(fullpath) - 1] = 0; + html(" <li>"); + if (S_ISGITLINK(mode)) { + cgit_submodule_link(NULL, fullpath, oid_to_hex(oid)); + } else + cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.oid, + fullpath); + html("</li>\n"); + free(fullpath); +} + +static void print_dir_tail(void) +{ + html(" </ul>\n</body></html>\n"); +} + +static int walk_tree(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, void *cbdata) +{ + struct walk_tree_context *walk_tree_ctx = cbdata; + + if (base->len == walk_tree_ctx->match_baselen) { + if (S_ISREG(mode) || S_ISLNK(mode)) { + if (print_object(oid, pathname)) + walk_tree_ctx->match = 1; + } else if (S_ISDIR(mode)) { + print_dir(oid, base->buf, base->len, pathname); + walk_tree_ctx->match = 2; + return READ_TREE_RECURSIVE; + } + } else if (base->len < INT_MAX && (int)base->len > walk_tree_ctx->match_baselen) { + print_dir_entry(oid, base->buf, base->len, pathname, mode); + walk_tree_ctx->match = 2; + } else if (S_ISDIR(mode)) { + return READ_TREE_RECURSIVE; + } + + return 0; +} + +static int basedir_len(const char *path) +{ + char *p = strrchr(path, '/'); + if (p) + return p - path + 1; + return 0; +} + +void cgit_print_plain(void) +{ + const char *rev = ctx.qry.oid; + struct object_id oid; + struct commit *commit; + struct pathspec_item path_items = { + .match = ctx.qry.path, + .len = ctx.qry.path ? strlen(ctx.qry.path) : 0 + }; + struct pathspec paths = { + .nr = 1, + .items = &path_items + }; + struct walk_tree_context walk_tree_ctx = { + .match = 0 + }; + + if (!rev) + rev = ctx.qry.head; + + if (get_oid(rev, &oid)) { + cgit_print_error_page(404, "Not found", "Not found"); + return; + } + commit = lookup_commit_reference(the_repository, &oid); + if (!commit || parse_commit(commit)) { + cgit_print_error_page(404, "Not found", "Not found"); + return; + } + if (!path_items.match) { + path_items.match = ""; + walk_tree_ctx.match_baselen = -1; + print_dir(get_commit_tree_oid(commit), "", 0, ""); + walk_tree_ctx.match = 2; + } + else + walk_tree_ctx.match_baselen = basedir_len(path_items.match); + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &paths, walk_tree, &walk_tree_ctx); + if (!walk_tree_ctx.match) + cgit_print_error_page(404, "Not found", "Not found"); + else if (walk_tree_ctx.match == 2) + print_dir_tail(); +} diff --git a/www/git.causal.agency/cgit/ui-plain.h b/www/git.causal.agency/cgit/ui-plain.h new file mode 100644 index 00000000..5bff07b8 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-plain.h @@ -0,0 +1,6 @@ +#ifndef UI_PLAIN_H +#define UI_PLAIN_H + +extern void cgit_print_plain(void); + +#endif /* UI_PLAIN_H */ diff --git a/www/git.causal.agency/cgit/ui-refs.c b/www/git.causal.agency/cgit/ui-refs.c new file mode 100644 index 00000000..456f610d --- /dev/null +++ b/www/git.causal.agency/cgit/ui-refs.c @@ -0,0 +1,219 @@ +/* ui-refs.c: browse symbolic refs + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-refs.h" +#include "html.h" +#include "ui-shared.h" + +static inline int cmp_age(int age1, int age2) +{ + /* age1 and age2 are assumed to be non-negative */ + return age2 - age1; +} + +static int cmp_ref_name(const void *a, const void *b) +{ + struct refinfo *r1 = *(struct refinfo **)a; + struct refinfo *r2 = *(struct refinfo **)b; + + return strcmp(r1->refname, r2->refname); +} + +static int cmp_branch_age(const void *a, const void *b) +{ + struct refinfo *r1 = *(struct refinfo **)a; + struct refinfo *r2 = *(struct refinfo **)b; + + return cmp_age(r1->commit->committer_date, r2->commit->committer_date); +} + +static int get_ref_age(struct refinfo *ref) +{ + if (!ref->object) + return 0; + switch (ref->object->type) { + case OBJ_TAG: + return ref->tag ? ref->tag->tagger_date : 0; + case OBJ_COMMIT: + return ref->commit ? ref->commit->committer_date : 0; + } + return 0; +} + +static int cmp_tag_age(const void *a, const void *b) +{ + struct refinfo *r1 = *(struct refinfo **)a; + struct refinfo *r2 = *(struct refinfo **)b; + + return cmp_age(get_ref_age(r1), get_ref_age(r2)); +} + +static int print_branch(struct refinfo *ref) +{ + struct commitinfo *info = ref->commit; + char *name = (char *)ref->refname; + + if (!info) + return 1; + html("<tr><td>"); + cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL, + ctx.qry.showmsg, 0); + html("</td><td>"); + + if (ref->object->type == OBJ_COMMIT) { + cgit_commit_link(info->subject, NULL, NULL, name, NULL, NULL); + html("</td><td>"); + cgit_open_filter(ctx.repo->email_filter, info->author_email, "refs"); + html_txt(info->author); + cgit_close_filter(ctx.repo->email_filter); + html("</td><td colspan='2'>"); + cgit_print_age(info->committer_date, info->committer_tz, -1); + } else { + html("</td><td></td><td>"); + cgit_object_link(ref->object); + } + html("</td></tr>\n"); + return 0; +} + +static void print_tag_header(void) +{ + html("<tr class='nohover'><th class='left'>Tag</th>" + "<th class='left'>Download</th>" + "<th class='left'>Author</th>" + "<th class='left' colspan='2'>Age</th></tr>\n"); +} + +static int print_tag(struct refinfo *ref) +{ + struct tag *tag = NULL; + struct taginfo *info = NULL; + char *name = (char *)ref->refname; + struct object *obj = ref->object; + + if (obj->type == OBJ_TAG) { + tag = (struct tag *)obj; + obj = tag->tagged; + info = ref->tag; + if (!info) + return 1; + } + + html("<tr><td>"); + cgit_tag_link(name, NULL, NULL, name); + html("</td><td>"); + if (ctx.repo->snapshots && (obj->type == OBJ_COMMIT)) + cgit_print_snapshot_links(ctx.repo, name, " "); + else + cgit_object_link(obj); + html("</td><td>"); + if (info) { + if (info->tagger) { + cgit_open_filter(ctx.repo->email_filter, info->tagger_email, "refs"); + html_txt(info->tagger); + cgit_close_filter(ctx.repo->email_filter); + } + } else if (ref->object->type == OBJ_COMMIT) { + cgit_open_filter(ctx.repo->email_filter, ref->commit->author_email, "refs"); + html_txt(ref->commit->author); + cgit_close_filter(ctx.repo->email_filter); + } + html("</td><td colspan='2'>"); + if (info) { + if (info->tagger_date > 0) + cgit_print_age(info->tagger_date, info->tagger_tz, -1); + } else if (ref->object->type == OBJ_COMMIT) { + cgit_print_age(ref->commit->commit->date, 0, -1); + } + html("</td></tr>\n"); + + return 0; +} + +static void print_refs_link(const char *path) +{ + html("<tr class='nohover'><td colspan='5'>"); + cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path); + html("</td></tr>"); +} + +void cgit_print_branches(int maxcount) +{ + struct reflist list; + int i; + + html("<tr class='nohover'><th class='left'>Branch</th>" + "<th class='left'>Commit message</th>" + "<th class='left'>Author</th>" + "<th class='left' colspan='2'>Age</th></tr>\n"); + + list.refs = NULL; + list.alloc = list.count = 0; + for_each_branch_ref(cgit_refs_cb, &list); + if (ctx.repo->enable_remote_branches) + for_each_remote_ref(cgit_refs_cb, &list); + + if (maxcount == 0 || maxcount > list.count) + maxcount = list.count; + + qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age); + if (ctx.repo->branch_sort == 0) + qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name); + + for (i = 0; i < maxcount; i++) + print_branch(list.refs[i]); + + if (maxcount < list.count) + print_refs_link("heads"); + + cgit_free_reflist_inner(&list); +} + +void cgit_print_tags(int maxcount) +{ + struct reflist list; + int i; + + list.refs = NULL; + list.alloc = list.count = 0; + for_each_tag_ref(cgit_refs_cb, &list); + if (list.count == 0) + return; + qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age); + if (!maxcount) + maxcount = list.count; + else if (maxcount > list.count) + maxcount = list.count; + print_tag_header(); + for (i = 0; i < maxcount; i++) + print_tag(list.refs[i]); + + if (maxcount < list.count) + print_refs_link("tags"); + + cgit_free_reflist_inner(&list); +} + +void cgit_print_refs(void) +{ + cgit_print_layout_start(); + html("<table class='list nowrap'>"); + + if (ctx.qry.path && starts_with(ctx.qry.path, "heads")) + cgit_print_branches(0); + else if (ctx.qry.path && starts_with(ctx.qry.path, "tags")) + cgit_print_tags(0); + else { + cgit_print_branches(0); + html("<tr class='nohover'><td colspan='5'> </td></tr>"); + cgit_print_tags(0); + } + html("</table>"); + cgit_print_layout_end(); +} diff --git a/www/git.causal.agency/cgit/ui-refs.h b/www/git.causal.agency/cgit/ui-refs.h new file mode 100644 index 00000000..1d4a54a2 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-refs.h @@ -0,0 +1,8 @@ +#ifndef UI_REFS_H +#define UI_REFS_H + +extern void cgit_print_branches(int maxcount); +extern void cgit_print_tags(int maxcount); +extern void cgit_print_refs(void); + +#endif /* UI_REFS_H */ diff --git a/www/git.causal.agency/cgit/ui-repolist.c b/www/git.causal.agency/cgit/ui-repolist.c new file mode 100644 index 00000000..97b11c5f --- /dev/null +++ b/www/git.causal.agency/cgit/ui-repolist.c @@ -0,0 +1,381 @@ +/* ui-repolist.c: functions for generating the repolist page + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-repolist.h" +#include "html.h" +#include "ui-shared.h" + +static time_t read_agefile(const char *path) +{ + time_t result; + size_t size; + char *buf = NULL; + struct strbuf date_buf = STRBUF_INIT; + + if (readfile(path, &buf, &size)) { + free(buf); + return 0; + } + + if (parse_date(buf, &date_buf) == 0) + result = strtoul(date_buf.buf, NULL, 10); + else + result = 0; + free(buf); + strbuf_release(&date_buf); + return result; +} + +static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) +{ + struct strbuf path = STRBUF_INIT; + struct stat s; + struct cgit_repo *r = (struct cgit_repo *)repo; + + if (repo->mtime != -1) { + *mtime = repo->mtime; + return 1; + } + strbuf_addf(&path, "%s/%s", repo->path, ctx.cfg.agefile); + if (stat(path.buf, &s) == 0) { + *mtime = read_agefile(path.buf); + if (*mtime) { + r->mtime = *mtime; + goto end; + } + } + + strbuf_reset(&path); + strbuf_addf(&path, "%s/refs/heads/%s", repo->path, + repo->defbranch ? repo->defbranch : "master"); + if (stat(path.buf, &s) == 0) { + *mtime = s.st_mtime; + r->mtime = *mtime; + goto end; + } + + strbuf_reset(&path); + strbuf_addf(&path, "%s/%s", repo->path, "packed-refs"); + if (stat(path.buf, &s) == 0) { + *mtime = s.st_mtime; + r->mtime = *mtime; + goto end; + } + + *mtime = 0; + r->mtime = *mtime; +end: + strbuf_release(&path); + return (r->mtime != 0); +} + +static void print_modtime(struct cgit_repo *repo) +{ + time_t t; + if (get_repo_modtime(repo, &t)) + cgit_print_age(t, 0, -1); +} + +static int is_match(struct cgit_repo *repo) +{ + if (!ctx.qry.search) + return 1; + if (repo->url && strcasestr(repo->url, ctx.qry.search)) + return 1; + if (repo->name && strcasestr(repo->name, ctx.qry.search)) + return 1; + if (repo->desc && strcasestr(repo->desc, ctx.qry.search)) + return 1; + if (repo->owner && strcasestr(repo->owner, ctx.qry.search)) + return 1; + return 0; +} + +static int is_in_url(struct cgit_repo *repo) +{ + if (!ctx.qry.url) + return 1; + if (repo->url && starts_with(repo->url, ctx.qry.url)) + return 1; + return 0; +} + +static int is_visible(struct cgit_repo *repo) +{ + if (repo->hide || repo->ignore) + return 0; + if (!(is_match(repo) && is_in_url(repo))) + return 0; + return 1; +} + +static int any_repos_visible(void) +{ + int i; + + for (i = 0; i < cgit_repolist.count; i++) { + if (is_visible(&cgit_repolist.repos[i])) + return 1; + } + return 0; +} + +static void print_sort_header(const char *title, const char *sort) +{ + char *currenturl = cgit_currenturl(); + html("<th class='left'><a href='"); + html_attr(currenturl); + htmlf("?s=%s", sort); + if (ctx.qry.search) { + html("&q="); + html_url_arg(ctx.qry.search); + } + htmlf("'>%s</a></th>", title); + free(currenturl); +} + +static void print_header(void) +{ + html("<tr class='nohover'>"); + print_sort_header("Name", "name"); + print_sort_header("Description", "desc"); + if (ctx.cfg.enable_index_owner) + print_sort_header("Owner", "owner"); + print_sort_header("Idle", "idle"); + if (ctx.cfg.enable_index_links) + html("<th class='left'>Links</th>"); + html("</tr>\n"); +} + + +static void print_pager(int items, int pagelen, char *search, char *sort) +{ + int i, ofs; + char *class = NULL; + html("<ul class='pager'>"); + for (i = 0, ofs = 0; ofs < items; i++, ofs = i * pagelen) { + class = (ctx.qry.ofs == ofs) ? "current" : NULL; + html("<li>"); + cgit_index_link(fmt("[%d]", i + 1), fmt("Page %d", i + 1), + class, search, sort, ofs, 0); + html("</li>"); + } + html("</ul>"); +} + +static int cmp(const char *s1, const char *s2) +{ + if (s1 && s2) { + if (ctx.cfg.case_sensitive_sort) + return strcmp(s1, s2); + else + return strcasecmp(s1, s2); + } + if (s1 && !s2) + return -1; + if (s2 && !s1) + return 1; + return 0; +} + +static int sort_name(const void *a, const void *b) +{ + const struct cgit_repo *r1 = a; + const struct cgit_repo *r2 = b; + + return cmp(r1->name, r2->name); +} + +static int sort_desc(const void *a, const void *b) +{ + const struct cgit_repo *r1 = a; + const struct cgit_repo *r2 = b; + + return cmp(r1->desc, r2->desc); +} + +static int sort_owner(const void *a, const void *b) +{ + const struct cgit_repo *r1 = a; + const struct cgit_repo *r2 = b; + + return cmp(r1->owner, r2->owner); +} + +static int sort_idle(const void *a, const void *b) +{ + const struct cgit_repo *r1 = a; + const struct cgit_repo *r2 = b; + time_t t1, t2; + + t1 = t2 = 0; + get_repo_modtime(r1, &t1); + get_repo_modtime(r2, &t2); + return t2 - t1; +} + +static int sort_section(const void *a, const void *b) +{ + const struct cgit_repo *r1 = a; + const struct cgit_repo *r2 = b; + int result; + + result = cmp(r1->section, r2->section); + if (!result) { + if (!strcmp(ctx.cfg.repository_sort, "age")) + result = sort_idle(r1, r2); + if (!result) + result = cmp(r1->name, r2->name); + } + return result; +} + +struct sortcolumn { + const char *name; + int (*fn)(const void *a, const void *b); +}; + +static const struct sortcolumn sortcolumn[] = { + {"section", sort_section}, + {"name", sort_name}, + {"desc", sort_desc}, + {"owner", sort_owner}, + {"idle", sort_idle}, + {NULL, NULL} +}; + +static int sort_repolist(char *field) +{ + const struct sortcolumn *column; + + for (column = &sortcolumn[0]; column->name; column++) { + if (strcmp(field, column->name)) + continue; + qsort(cgit_repolist.repos, cgit_repolist.count, + sizeof(struct cgit_repo), column->fn); + return 1; + } + return 0; +} + + +void cgit_print_repolist(void) +{ + int i, columns = 3, hits = 0, header = 0; + char *last_section = NULL; + char *section; + char *repourl; + int sorted = 0; + + if (!any_repos_visible()) { + cgit_print_error_page(404, "Not found", "No repositories found"); + return; + } + + if (ctx.cfg.enable_index_links) + ++columns; + if (ctx.cfg.enable_index_owner) + ++columns; + + ctx.page.title = ctx.cfg.root_title; + cgit_print_http_headers(); + cgit_print_docstart(); + cgit_print_pageheader(); + + if (ctx.qry.sort) + sorted = sort_repolist(ctx.qry.sort); + else if (ctx.cfg.section_sort) + sort_repolist("section"); + + html("<table summary='repository list' class='list nowrap'>"); + for (i = 0; i < cgit_repolist.count; i++) { + ctx.repo = &cgit_repolist.repos[i]; + if (!is_visible(ctx.repo)) + continue; + hits++; + if (hits <= ctx.qry.ofs) + continue; + if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count) + continue; + if (!header++) + print_header(); + section = ctx.repo->section; + if (section && !strcmp(section, "")) + section = NULL; + if (!sorted && + ((last_section == NULL && section != NULL) || + (last_section != NULL && section == NULL) || + (last_section != NULL && section != NULL && + strcmp(section, last_section)))) { + htmlf("<tr class='nohover-highlight'><td colspan='%d' class='reposection'>", + columns); + html_txt(section); + html("</td></tr>"); + last_section = section; + } + htmlf("<tr><td class='%s'>", + !sorted && section ? "sublevel-repo" : "toplevel-repo"); + cgit_summary_link(ctx.repo->name, NULL, NULL, NULL); + html("</td><td>"); + repourl = cgit_repourl(ctx.repo->url); + html_link_open(repourl, NULL, NULL); + free(repourl); + if (html_ntxt(ctx.repo->desc, ctx.cfg.max_repodesc_len) < 0) + html("..."); + html_link_close(); + html("</td><td>"); + if (ctx.cfg.enable_index_owner) { + if (ctx.repo->owner_filter) { + cgit_open_filter(ctx.repo->owner_filter); + html_txt(ctx.repo->owner); + cgit_close_filter(ctx.repo->owner_filter); + } else { + char *currenturl = cgit_currenturl(); + html("<a href='"); + html_attr(currenturl); + html("?q="); + html_url_arg(ctx.repo->owner); + html("'>"); + html_txt(ctx.repo->owner); + html("</a>"); + free(currenturl); + } + html("</td><td>"); + } + print_modtime(ctx.repo); + html("</td>"); + if (ctx.cfg.enable_index_links) { + html("<td>"); + cgit_summary_link("summary", NULL, "button", NULL); + html(" "); + cgit_log_link("log", NULL, "button", NULL, NULL, NULL, + 0, NULL, NULL, ctx.qry.showmsg, 0); + html(" "); + cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL); + html("</td>"); + } + html("</tr>\n"); + } + html("</table>"); + if (hits > ctx.cfg.max_repo_count) + print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search, ctx.qry.sort); + cgit_print_docend(); +} + +void cgit_print_site_readme(void) +{ + cgit_print_layout_start(); + if (!ctx.cfg.root_readme) + goto done; + cgit_open_filter(ctx.cfg.about_filter, ctx.cfg.root_readme); + html_include(ctx.cfg.root_readme); + cgit_close_filter(ctx.cfg.about_filter); +done: + cgit_print_layout_end(); +} diff --git a/www/git.causal.agency/cgit/ui-repolist.h b/www/git.causal.agency/cgit/ui-repolist.h new file mode 100644 index 00000000..1b6b3227 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-repolist.h @@ -0,0 +1,7 @@ +#ifndef UI_REPOLIST_H +#define UI_REPOLIST_H + +extern void cgit_print_repolist(void); +extern void cgit_print_site_readme(void); + +#endif /* UI_REPOLIST_H */ diff --git a/www/git.causal.agency/cgit/ui-shared.c b/www/git.causal.agency/cgit/ui-shared.c new file mode 100644 index 00000000..dfaf5952 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-shared.c @@ -0,0 +1,1241 @@ +/* ui-shared.c: common web output functions + * + * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-shared.h" +#include "cmd.h" +#include "html.h" +#include "version.h" + +static const char cgit_doctype[] = +"<!DOCTYPE html>\n"; + +static char *http_date(time_t t) +{ + static char day[][4] = + {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + static char month[][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + struct tm tm; + gmtime_r(&t, &tm); + return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm.tm_wday], + tm.tm_mday, month[tm.tm_mon], 1900 + tm.tm_year, + tm.tm_hour, tm.tm_min, tm.tm_sec); +} + +void cgit_print_error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + cgit_vprint_error(fmt, ap); + va_end(ap); +} + +void cgit_vprint_error(const char *fmt, va_list ap) +{ + va_list cp; + html("<div class='error'>"); + va_copy(cp, ap); + html_vtxtf(fmt, cp); + va_end(cp); + html("</div>\n"); +} + +const char *cgit_httpscheme(void) +{ + if (ctx.env.https && !strcmp(ctx.env.https, "on")) + return "https://"; + else + return "http://"; +} + +char *cgit_hosturl(void) +{ + if (ctx.env.http_host) + return xstrdup(ctx.env.http_host); + if (!ctx.env.server_name) + return NULL; + if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80) + return xstrdup(ctx.env.server_name); + return fmtalloc("%s:%s", ctx.env.server_name, ctx.env.server_port); +} + +char *cgit_currenturl(void) +{ + const char *root = cgit_rooturl(); + + if (!ctx.qry.url) + return xstrdup(root); + if (root[0] && root[strlen(root) - 1] == '/') + return fmtalloc("%s%s", root, ctx.qry.url); + return fmtalloc("%s/%s", root, ctx.qry.url); +} + +char *cgit_currentfullurl(void) +{ + const char *root = cgit_rooturl(); + const char *orig_query = ctx.env.query_string ? ctx.env.query_string : ""; + size_t len = strlen(orig_query); + char *query = xmalloc(len + 2), *start_url, *ret; + + /* Remove all url=... parts from query string */ + memcpy(query + 1, orig_query, len + 1); + query[0] = '?'; + start_url = query; + while ((start_url = strstr(start_url, "url=")) != NULL) { + if (start_url[-1] == '?' || start_url[-1] == '&') { + const char *end_url = strchr(start_url, '&'); + if (end_url) + memmove(start_url, end_url + 1, strlen(end_url)); + else + start_url[0] = '\0'; + } else + ++start_url; + } + if (!query[1]) + query[0] = '\0'; + + if (!ctx.qry.url) + ret = fmtalloc("%s%s", root, query); + else if (root[0] && root[strlen(root) - 1] == '/') + ret = fmtalloc("%s%s%s", root, ctx.qry.url, query); + else + ret = fmtalloc("%s/%s%s", root, ctx.qry.url, query); + free(query); + return ret; +} + +const char *cgit_rooturl(void) +{ + if (ctx.cfg.virtual_root) + return ctx.cfg.virtual_root; + else + return ctx.cfg.script_name; +} + +const char *cgit_loginurl(void) +{ + static const char *login_url; + if (!login_url) + login_url = fmtalloc("%s?p=login", cgit_rooturl()); + return login_url; +} + +char *cgit_repourl(const char *reponame) +{ + if (ctx.cfg.virtual_root) + return fmtalloc("%s%s/", ctx.cfg.virtual_root, reponame); + else + return fmtalloc("?r=%s", reponame); +} + +char *cgit_fileurl(const char *reponame, const char *pagename, + const char *filename, const char *query) +{ + struct strbuf sb = STRBUF_INIT; + char *delim; + + if (ctx.cfg.virtual_root) { + strbuf_addf(&sb, "%s%s/%s/%s", ctx.cfg.virtual_root, reponame, + pagename, (filename ? filename:"")); + delim = "?"; + } else { + strbuf_addf(&sb, "?url=%s/%s/%s", reponame, pagename, + (filename ? filename : "")); + delim = "&"; + } + if (query) + strbuf_addf(&sb, "%s%s", delim, query); + return strbuf_detach(&sb, NULL); +} + +char *cgit_pageurl(const char *reponame, const char *pagename, + const char *query) +{ + return cgit_fileurl(reponame, pagename, NULL, query); +} + +const char *cgit_repobasename(const char *reponame) +{ + /* I assume we don't need to store more than one repo basename */ + static char rvbuf[1024]; + int p; + const char *rv; + size_t len; + + len = strlcpy(rvbuf, reponame, sizeof(rvbuf)); + if (len >= sizeof(rvbuf)) + die("cgit_repobasename: truncated repository name '%s'", reponame); + p = len - 1; + /* strip trailing slashes */ + while (p && rvbuf[p] == '/') + rvbuf[p--] = '\0'; + /* strip trailing .git */ + if (p >= 3 && starts_with(&rvbuf[p-3], ".git")) { + p -= 3; + rvbuf[p--] = '\0'; + } + /* strip more trailing slashes if any */ + while (p && rvbuf[p] == '/') + rvbuf[p--] = '\0'; + /* find last slash in the remaining string */ + rv = strrchr(rvbuf, '/'); + if (rv) + return ++rv; + return rvbuf; +} + +const char *cgit_snapshot_prefix(const struct cgit_repo *repo) +{ + if (repo->snapshot_prefix) + return repo->snapshot_prefix; + + return cgit_repobasename(repo->url); +} + +static void site_url(const char *page, const char *search, const char *sort, int ofs, int always_root) +{ + char *delim = "?"; + + if (always_root || page) + html_attr(cgit_rooturl()); + else { + char *currenturl = cgit_currenturl(); + html_attr(currenturl); + free(currenturl); + } + + if (page) { + htmlf("?p=%s", page); + delim = "&"; + } + if (search) { + html(delim); + html("q="); + html_attr(search); + delim = "&"; + } + if (sort) { + html(delim); + html("s="); + html_attr(sort); + delim = "&"; + } + if (ofs) { + html(delim); + htmlf("ofs=%d", ofs); + } +} + +static void site_link(const char *page, const char *name, const char *title, + const char *class, const char *search, const char *sort, int ofs, int always_root) +{ + html("<a"); + if (title) { + html(" title='"); + html_attr(title); + html("'"); + } + if (class) { + html(" class='"); + html_attr(class); + html("'"); + } + html(" href='"); + site_url(page, search, sort, ofs, always_root); + html("'>"); + html_txt(name); + html("</a>"); +} + +void cgit_index_link(const char *name, const char *title, const char *class, + const char *pattern, const char *sort, int ofs, int always_root) +{ + site_link(NULL, name, title, class, pattern, sort, ofs, always_root); +} + +static char *repolink(const char *title, const char *class, const char *page, + const char *head, const char *path) +{ + char *delim = "?"; + + html("<a"); + if (title) { + html(" title='"); + html_attr(title); + html("'"); + } + if (class) { + html(" class='"); + html_attr(class); + html("'"); + } + html(" href='"); + if (ctx.cfg.virtual_root) { + html_url_path(ctx.cfg.virtual_root); + html_url_path(ctx.repo->url); + if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') + html("/"); + if (page) { + html_url_path(page); + html("/"); + if (path) + html_url_path(path); + } + } else { + html_url_path(ctx.cfg.script_name); + html("?url="); + html_url_arg(ctx.repo->url); + if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') + html("/"); + if (page) { + html_url_arg(page); + html("/"); + if (path) + html_url_arg(path); + } + delim = "&"; + } + if (head && ctx.repo->defbranch && strcmp(head, ctx.repo->defbranch)) { + html(delim); + html("h="); + html_url_arg(head); + delim = "&"; + } + return fmt("%s", delim); +} + +static void reporevlink(const char *page, const char *name, const char *title, + const char *class, const char *head, const char *rev, + const char *path) +{ + char *delim; + + delim = repolink(title, class, page, head, path); + if (rev && ctx.qry.head != NULL && strcmp(rev, ctx.qry.head)) { + html(delim); + html("id="); + html_url_arg(rev); + } + html("'>"); + html_txt(name); + html("</a>"); +} + +void cgit_summary_link(const char *name, const char *title, const char *class, + const char *head) +{ + reporevlink(NULL, name, title, class, head, NULL, NULL); +} + +void cgit_tag_link(const char *name, const char *title, const char *class, + const char *tag) +{ + reporevlink("tag", name, title, class, tag, NULL, NULL); +} + +void cgit_tree_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path) +{ + reporevlink("tree", name, title, class, head, rev, path); +} + +void cgit_plain_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path) +{ + reporevlink("plain", name, title, class, head, rev, path); +} + +void cgit_blame_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path) +{ + reporevlink("blame", name, title, class, head, rev, path); +} + +void cgit_log_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path, + int ofs, const char *grep, const char *pattern, int showmsg, + int follow) +{ + char *delim; + + delim = repolink(title, class, "log", head, path); + if (rev && ctx.qry.head && strcmp(rev, ctx.qry.head)) { + html(delim); + html("id="); + html_url_arg(rev); + delim = "&"; + } + if (grep && pattern) { + html(delim); + html("qt="); + html_url_arg(grep); + delim = "&"; + html(delim); + html("q="); + html_url_arg(pattern); + } + if (ofs > 0) { + html(delim); + html("ofs="); + htmlf("%d", ofs); + delim = "&"; + } + if (showmsg) { + html(delim); + html("showmsg=1"); + delim = "&"; + } + if (follow) { + html(delim); + html("follow=1"); + } + html("'>"); + html_txt(name); + html("</a>"); +} + +void cgit_commit_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path) +{ + char *delim; + + delim = repolink(title, class, "commit", head, path); + if (rev && ctx.qry.head && strcmp(rev, ctx.qry.head)) { + html(delim); + html("id="); + html_url_arg(rev); + delim = "&"; + } + if (ctx.qry.difftype) { + html(delim); + htmlf("dt=%d", ctx.qry.difftype); + delim = "&"; + } + if (ctx.qry.context > 0 && ctx.qry.context != 3) { + html(delim); + html("context="); + htmlf("%d", ctx.qry.context); + delim = "&"; + } + if (ctx.qry.ignorews) { + html(delim); + html("ignorews=1"); + delim = "&"; + } + if (ctx.qry.follow) { + html(delim); + html("follow=1"); + } + html("'>"); + if (name[0] != '\0') { + if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { + html_ntxt(name, ctx.cfg.max_msg_len - 3); + html("..."); + } else + html_txt(name); + } else + html_txt("(no commit message)"); + html("</a>"); +} + +void cgit_refs_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path) +{ + reporevlink("refs", name, title, class, head, rev, path); +} + +void cgit_snapshot_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, + const char *archivename) +{ + reporevlink("snapshot", name, title, class, head, rev, archivename); +} + +void cgit_diff_link(const char *name, const char *title, const char *class, + const char *head, const char *new_rev, const char *old_rev, + const char *path) +{ + char *delim; + + delim = repolink(title, class, "diff", head, path); + if (new_rev && ctx.qry.head != NULL && strcmp(new_rev, ctx.qry.head)) { + html(delim); + html("id="); + html_url_arg(new_rev); + delim = "&"; + } + if (old_rev) { + html(delim); + html("id2="); + html_url_arg(old_rev); + delim = "&"; + } + if (ctx.qry.difftype) { + html(delim); + htmlf("dt=%d", ctx.qry.difftype); + delim = "&"; + } + if (ctx.qry.context > 0 && ctx.qry.context != 3) { + html(delim); + html("context="); + htmlf("%d", ctx.qry.context); + delim = "&"; + } + if (ctx.qry.ignorews) { + html(delim); + html("ignorews=1"); + delim = "&"; + } + if (ctx.qry.follow) { + html(delim); + html("follow=1"); + } + html("'>"); + html_txt(name); + html("</a>"); +} + +void cgit_patch_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path) +{ + reporevlink("patch", name, title, class, head, rev, path); +} + +void cgit_stats_link(const char *name, const char *title, const char *class, + const char *head, const char *path) +{ + reporevlink("stats", name, title, class, head, NULL, path); +} + +static void cgit_self_link(char *name, const char *title, const char *class) +{ + if (!strcmp(ctx.qry.page, "repolist")) + cgit_index_link(name, title, class, ctx.qry.search, ctx.qry.sort, + ctx.qry.ofs, 1); + else if (!strcmp(ctx.qry.page, "summary")) + cgit_summary_link(name, title, class, ctx.qry.head); + else if (!strcmp(ctx.qry.page, "tag")) + cgit_tag_link(name, title, class, ctx.qry.has_oid ? + ctx.qry.oid : ctx.qry.head); + else if (!strcmp(ctx.qry.page, "tree")) + cgit_tree_link(name, title, class, ctx.qry.head, + ctx.qry.has_oid ? ctx.qry.oid : NULL, + ctx.qry.path); + else if (!strcmp(ctx.qry.page, "plain")) + cgit_plain_link(name, title, class, ctx.qry.head, + ctx.qry.has_oid ? ctx.qry.oid : NULL, + ctx.qry.path); + else if (!strcmp(ctx.qry.page, "blame")) + cgit_blame_link(name, title, class, ctx.qry.head, + ctx.qry.has_oid ? ctx.qry.oid : NULL, + ctx.qry.path); + else if (!strcmp(ctx.qry.page, "log")) + cgit_log_link(name, title, class, ctx.qry.head, + ctx.qry.has_oid ? ctx.qry.oid : NULL, + ctx.qry.path, ctx.qry.ofs, + ctx.qry.grep, ctx.qry.search, + ctx.qry.showmsg, ctx.qry.follow); + else if (!strcmp(ctx.qry.page, "commit")) + cgit_commit_link(name, title, class, ctx.qry.head, + ctx.qry.has_oid ? ctx.qry.oid : NULL, + ctx.qry.path); + else if (!strcmp(ctx.qry.page, "patch")) + cgit_patch_link(name, title, class, ctx.qry.head, + ctx.qry.has_oid ? ctx.qry.oid : NULL, + ctx.qry.path); + else if (!strcmp(ctx.qry.page, "refs")) + cgit_refs_link(name, title, class, ctx.qry.head, + ctx.qry.has_oid ? ctx.qry.oid : NULL, + ctx.qry.path); + else if (!strcmp(ctx.qry.page, "snapshot")) + cgit_snapshot_link(name, title, class, ctx.qry.head, + ctx.qry.has_oid ? ctx.qry.oid : NULL, + ctx.qry.path); + else if (!strcmp(ctx.qry.page, "diff")) + cgit_diff_link(name, title, class, ctx.qry.head, + ctx.qry.oid, ctx.qry.oid2, + ctx.qry.path); + else if (!strcmp(ctx.qry.page, "stats")) + cgit_stats_link(name, title, class, ctx.qry.head, + ctx.qry.path); + else { + /* Don't known how to make link for this page */ + repolink(title, class, ctx.qry.page, ctx.qry.head, ctx.qry.path); + html("><!-- cgit_self_link() doesn't know how to make link for page '"); + html_txt(ctx.qry.page); + html("' -->"); + html_txt(name); + html("</a>"); + } +} + +void cgit_object_link(struct object *obj) +{ + char *page, *shortrev, *fullrev, *name; + + fullrev = oid_to_hex(&obj->oid); + shortrev = xstrdup(fullrev); + shortrev[10] = '\0'; + if (obj->type == OBJ_COMMIT) { + cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL, + ctx.qry.head, fullrev, NULL); + return; + } else if (obj->type == OBJ_TREE) + page = "tree"; + else if (obj->type == OBJ_TAG) + page = "tag"; + else + page = "blob"; + name = fmt("%s %s...", type_name(obj->type), shortrev); + reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL); +} + +static struct string_list_item *lookup_path(struct string_list *list, + const char *path) +{ + struct string_list_item *item; + + while (path && path[0]) { + if ((item = string_list_lookup(list, path))) + return item; + if (!(path = strchr(path, '/'))) + break; + path++; + } + return NULL; +} + +void cgit_submodule_link(const char *class, char *path, const char *rev) +{ + struct string_list *list; + struct string_list_item *item; + char tail, *dir; + size_t len; + + len = 0; + tail = 0; + list = &ctx.repo->submodules; + item = lookup_path(list, path); + if (!item) { + len = strlen(path); + tail = path[len - 1]; + if (tail == '/') { + path[len - 1] = 0; + item = lookup_path(list, path); + } + } + if (item || ctx.repo->module_link) { + html("<a "); + if (class) + htmlf("class='%s' ", class); + html("href='"); + if (item) { + html_attrf(item->util, rev); + } else { + dir = strrchr(path, '/'); + if (dir) + dir++; + else + dir = path; + html_attrf(ctx.repo->module_link, dir, rev); + } + html("'>"); + html_txt(path); + html("</a>"); + } else { + html("<span"); + if (class) + htmlf(" class='%s'", class); + html(">"); + html_txt(path); + html("</span>"); + } + html_txtf(" @ %.7s", rev); + if (item && tail) + path[len - 1] = tail; +} + +const struct date_mode *cgit_date_mode(enum date_mode_type type) +{ + static struct date_mode mode; + mode.type = type; + mode.local = ctx.cfg.local_time; + return &mode; +} + +static void print_rel_date(time_t t, int tz, double value, + const char *class, const char *suffix) +{ + htmlf("<span class='%s' title='", class); + html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601))); + htmlf("'>%.0f %s</span>", value, suffix); +} + +void cgit_print_age(time_t t, int tz, time_t max_relative) +{ + time_t now, secs; + + if (!t) + return; + time(&now); + secs = now - t; + if (secs < 0) + secs = 0; + + if (secs > max_relative && max_relative >= 0) { + html("<span title='"); + html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601))); + html("'>"); + html_txt(show_date(t, tz, cgit_date_mode(DATE_SHORT))); + html("</span>"); + return; + } + + if (secs < TM_HOUR * 2) { + print_rel_date(t, tz, secs * 1.0 / TM_MIN, "age-mins", "min."); + return; + } + if (secs < TM_DAY * 2) { + print_rel_date(t, tz, secs * 1.0 / TM_HOUR, "age-hours", "hours"); + return; + } + if (secs < TM_WEEK * 2) { + print_rel_date(t, tz, secs * 1.0 / TM_DAY, "age-days", "days"); + return; + } + if (secs < TM_MONTH * 2) { + print_rel_date(t, tz, secs * 1.0 / TM_WEEK, "age-weeks", "weeks"); + return; + } + if (secs < TM_YEAR * 2) { + print_rel_date(t, tz, secs * 1.0 / TM_MONTH, "age-months", "months"); + return; + } + print_rel_date(t, tz, secs * 1.0 / TM_YEAR, "age-years", "years"); +} + +void cgit_print_http_headers(void) +{ + if (ctx.env.no_http && !strcmp(ctx.env.no_http, "1")) + return; + + if (ctx.page.status) + htmlf("Status: %d %s\n", ctx.page.status, ctx.page.statusmsg); + if (ctx.page.mimetype && ctx.page.charset) + htmlf("Content-Type: %s; charset=%s\n", ctx.page.mimetype, + ctx.page.charset); + else if (ctx.page.mimetype) + htmlf("Content-Type: %s\n", ctx.page.mimetype); + if (ctx.page.size) + htmlf("Content-Length: %zd\n", ctx.page.size); + if (ctx.page.filename) { + html("Content-Disposition: inline; filename=\""); + html_header_arg_in_quotes(ctx.page.filename); + html("\"\n"); + } + if (!ctx.env.authenticated) + html("Cache-Control: no-cache, no-store\n"); + htmlf("Last-Modified: %s\n", http_date(ctx.page.modified)); + htmlf("Expires: %s\n", http_date(ctx.page.expires)); + if (ctx.page.etag) + htmlf("ETag: \"%s\"\n", ctx.page.etag); + html("\n"); + if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD")) + exit(0); +} + +void cgit_redirect(const char *url, bool permanent) +{ + htmlf("Status: %d %s\n", permanent ? 301 : 302, permanent ? "Moved" : "Found"); + html("Location: "); + html_url_path(url); + html("\n\n"); +} + +static void print_rel_vcs_link(const char *url) +{ + html("<link rel='vcs-git' href='"); + html_attr(url); + html("' title='"); + html_attr(ctx.repo->name); + html(" Git repository'/>\n"); +} + +void cgit_print_docstart(void) +{ + char *host = cgit_hosturl(); + + if (ctx.cfg.embedded) { + if (ctx.cfg.header) + html_include(ctx.cfg.header); + return; + } + + html(cgit_doctype); + html("<html lang='en'>\n"); + html("<head>\n"); + html("<title>"); + html_txt(ctx.page.title); + html("</title>\n"); + htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); + if (ctx.cfg.robots && *ctx.cfg.robots) + htmlf("<meta name='robots' content='%s'/>\n", ctx.cfg.robots); + html("<link rel='stylesheet' type='text/css' href='"); + html_attr(ctx.cfg.css); + html("'/>\n"); + if (ctx.cfg.favicon) { + html("<link rel='shortcut icon' href='"); + html_attr(ctx.cfg.favicon); + html("'/>\n"); + } + if (host && ctx.repo && ctx.qry.head) { + char *fileurl; + struct strbuf sb = STRBUF_INIT; + strbuf_addf(&sb, "h=%s", ctx.qry.head); + + html("<link rel='alternate' title='Atom feed' href='"); + html(cgit_httpscheme()); + html_attr(host); + fileurl = cgit_fileurl(ctx.repo->url, "atom", ctx.qry.vpath, + sb.buf); + html_attr(fileurl); + html("' type='application/atom+xml'/>\n"); + strbuf_release(&sb); + free(fileurl); + } + if (ctx.repo) + cgit_add_clone_urls(print_rel_vcs_link); + if (ctx.cfg.head_include) + html_include(ctx.cfg.head_include); + if (ctx.repo && ctx.repo->extra_head_content) + html(ctx.repo->extra_head_content); + html("</head>\n"); + html("<body>\n"); + if (ctx.cfg.header) + html_include(ctx.cfg.header); + free(host); +} + +void cgit_print_docend(void) +{ + html("</div> <!-- class=content -->\n"); + if (ctx.cfg.embedded) { + html("</div> <!-- id=cgit -->\n"); + if (ctx.cfg.footer) + html_include(ctx.cfg.footer); + return; + } + if (ctx.cfg.footer) + html_include(ctx.cfg.footer); + else { + htmlf("<div class='footer'>generated by <a href='https://git.causal.agency/src/log/www/git.causal.agency/cgit'>cgit %s</a> " + "(<a href='https://git-scm.com/'>git %s</a>) at ", cgit_version, git_version_string); + html_txt(show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601))); + html("</div>\n"); + } + html("</div> <!-- id=cgit -->\n"); + html("</body>\n</html>\n"); +} + +void cgit_print_error_page(int code, const char *msg, const char *fmt, ...) +{ + va_list ap; + ctx.page.expires = ctx.cfg.cache_dynamic_ttl; + ctx.page.status = code; + ctx.page.statusmsg = msg; + cgit_print_layout_start(); + va_start(ap, fmt); + cgit_vprint_error(fmt, ap); + va_end(ap); + cgit_print_layout_end(); +} + +void cgit_print_layout_start(void) +{ + cgit_print_http_headers(); + cgit_print_docstart(); + cgit_print_pageheader(); +} + +void cgit_print_layout_end(void) +{ + cgit_print_docend(); +} + +static void add_clone_urls(void (*fn)(const char *), char *txt, char *suffix) +{ + struct strbuf **url_list = strbuf_split_str(txt, ' ', 0); + int i; + + for (i = 0; url_list[i]; i++) { + strbuf_rtrim(url_list[i]); + if (url_list[i]->len == 0) + continue; + if (suffix && *suffix) + strbuf_addf(url_list[i], "/%s", suffix); + fn(url_list[i]->buf); + } + + strbuf_list_free(url_list); +} + +void cgit_add_clone_urls(void (*fn)(const char *)) +{ + if (ctx.repo->clone_url) + add_clone_urls(fn, expand_macros(ctx.repo->clone_url), NULL); + else if (ctx.cfg.clone_prefix) + add_clone_urls(fn, ctx.cfg.clone_prefix, ctx.repo->url); +} + +static int print_this_commit_option(void) +{ + struct object_id oid; + if (!ctx.qry.head || get_oid(ctx.qry.head, &oid)) + return 1; + html_option(oid_to_hex(&oid), "this commit", ctx.qry.head); + return 0; +} + +static int print_branch_option(const char *refname, const struct object_id *oid, + int flags, void *cb_data) +{ + char *name = (char *)refname; + html_option(name, name, ctx.qry.head); + return 0; +} + +void cgit_add_hidden_formfields(int incl_head, int incl_search, + const char *page) +{ + if (!ctx.cfg.virtual_root) { + struct strbuf url = STRBUF_INIT; + + strbuf_addf(&url, "%s/%s", ctx.qry.repo, page); + if (ctx.qry.vpath) + strbuf_addf(&url, "/%s", ctx.qry.vpath); + html_hidden("url", url.buf); + strbuf_release(&url); + } + + if (incl_head && ctx.qry.head && ctx.repo->defbranch && + strcmp(ctx.qry.head, ctx.repo->defbranch)) + html_hidden("h", ctx.qry.head); + + if (ctx.qry.oid) + html_hidden("id", ctx.qry.oid); + if (ctx.qry.oid2) + html_hidden("id2", ctx.qry.oid2); + if (ctx.qry.showmsg) + html_hidden("showmsg", "1"); + + if (incl_search) { + if (ctx.qry.grep) + html_hidden("qt", ctx.qry.grep); + if (ctx.qry.search) + html_hidden("q", ctx.qry.search); + } +} + +static const char *hc(const char *page) +{ + if (!ctx.qry.page) + return NULL; + + return strcmp(ctx.qry.page, page) ? NULL : "active"; +} + +static void cgit_print_path_crumbs(char *path) +{ + char *old_path = ctx.qry.path; + char *p = path, *q, *end = path + strlen(path); + int levels = 0; + + ctx.qry.path = NULL; + cgit_self_link("root", NULL, NULL); + ctx.qry.path = p = path; + while (p < end) { + if (!(q = strchr(p, '/')) || levels > 15) + q = end; + *q = '\0'; + html_txt("/"); + cgit_self_link(p, NULL, NULL); + if (q < end) + *q = '/'; + p = q + 1; + ++levels; + } + ctx.qry.path = old_path; +} + +static void print_header(void) +{ + char *logo = NULL, *logo_link = NULL; + + html("<table id='header'>\n"); + html("<tr>\n"); + + if (ctx.repo && ctx.repo->logo && *ctx.repo->logo) + logo = ctx.repo->logo; + else + logo = ctx.cfg.logo; + if (ctx.repo && ctx.repo->logo_link && *ctx.repo->logo_link) + logo_link = ctx.repo->logo_link; + else + logo_link = ctx.cfg.logo_link; + if (logo && *logo) { + html("<td class='logo' rowspan='2'><a href='"); + if (logo_link && *logo_link) + html_attr(logo_link); + else + html_attr(cgit_rooturl()); + html("'><img src='"); + html_attr(logo); + html("' alt='cgit logo'/></a></td>\n"); + } + + html("<td class='main'>"); + if (ctx.repo) { + cgit_index_link("index", NULL, NULL, NULL, NULL, 0, 1); + html(" : "); + cgit_summary_link(ctx.repo->name, NULL, NULL, NULL); + if (ctx.env.authenticated) { + html("</td><td class='form'>"); + html("<form method='get'>\n"); + cgit_add_hidden_formfields(0, 1, ctx.qry.page); + html("<select name='h' onchange='this.form.submit();'>\n"); + print_this_commit_option(); + html("<optgroup label='branches'>"); + for_each_branch_ref(print_branch_option, ctx.qry.head); + if (ctx.repo->enable_remote_branches) + for_each_remote_ref(print_branch_option, ctx.qry.head); + html("</optgroup>"); + html("</select> "); + html("<input type='submit' value='switch'/>"); + html("</form>"); + } + } else + html_txt(ctx.cfg.root_title); + html("</td></tr>\n"); + + html("<tr><td class='sub'>"); + if (ctx.repo) { + html_txt(ctx.repo->desc); + html("</td><td class='sub right'>"); + if (ctx.repo->owner_filter) { + cgit_open_filter(ctx.repo->owner_filter); + html_txt(ctx.repo->owner); + cgit_close_filter(ctx.repo->owner_filter); + } else { + html_txt(ctx.repo->owner); + } + } else { + if (ctx.cfg.root_desc) + html_txt(ctx.cfg.root_desc); + } + html("</td></tr></table>\n"); +} + +void cgit_print_pageheader(void) +{ + html("<div id='cgit'>"); + if (!ctx.env.authenticated || !ctx.cfg.noheader) + print_header(); + + html("<table class='tabs'><tr><td>\n"); + if (ctx.env.authenticated && ctx.repo) { + if (ctx.repo->readme.nr) { + reporevlink("about", "about", NULL, + hc("about"), ctx.qry.head, NULL, + NULL); + html(" "); + } + cgit_summary_link("summary", NULL, hc("summary"), + ctx.qry.head); + html(" "); + cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head, + ctx.qry.oid, NULL); + html(" "); + cgit_log_link("log", NULL, hc("log"), ctx.qry.head, + NULL, ctx.qry.vpath, 0, NULL, NULL, + ctx.qry.showmsg, ctx.qry.follow); + html(" "); + if (ctx.qry.page && !strcmp(ctx.qry.page, "blame")) + cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head, + ctx.qry.oid, ctx.qry.vpath); + else + cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head, + ctx.qry.oid, ctx.qry.vpath); + html(" "); + cgit_commit_link("commit", NULL, hc("commit"), + ctx.qry.head, ctx.qry.oid, ctx.qry.vpath); + html(" "); + cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head, + ctx.qry.oid, ctx.qry.oid2, ctx.qry.vpath); + if (ctx.repo->max_stats) { + html(" "); + cgit_stats_link("stats", NULL, hc("stats"), + ctx.qry.head, ctx.qry.vpath); + } + if (ctx.repo->homepage) { + html(" <a href='"); + html_attr(ctx.repo->homepage); + html("'>homepage</a>"); + } + html("</td><td class='form'>"); + html("<form class='right' method='get' action='"); + if (ctx.cfg.virtual_root) { + char *fileurl = cgit_fileurl(ctx.qry.repo, "log", + ctx.qry.vpath, NULL); + html_url_path(fileurl); + free(fileurl); + } + html("'>\n"); + cgit_add_hidden_formfields(1, 0, "log"); + html("<select name='qt'>\n"); + html_option("grep", "log msg", ctx.qry.grep); + html_option("author", "author", ctx.qry.grep); + html_option("committer", "committer", ctx.qry.grep); + html_option("range", "range", ctx.qry.grep); + html("</select>\n"); + html("<input class='txt' type='search' size='10' name='q' value='"); + html_attr(ctx.qry.search); + html("'/>\n"); + html("<input type='submit' value='search'/>\n"); + html("</form>\n"); + } else if (ctx.env.authenticated) { + char *currenturl = cgit_currenturl(); + site_link(NULL, "index", NULL, hc("repolist"), NULL, NULL, 0, 1); + if (ctx.cfg.root_readme) + site_link("about", "about", NULL, hc("about"), + NULL, NULL, 0, 1); + html("</td><td class='form'>"); + html("<form method='get' action='"); + html_attr(currenturl); + html("'>\n"); + html("<input type='search' name='q' size='10' value='"); + html_attr(ctx.qry.search); + html("'/>\n"); + html("<input type='submit' value='search'/>\n"); + html("</form>"); + free(currenturl); + } + html("</td></tr></table>\n"); + if (ctx.env.authenticated && ctx.repo && ctx.qry.vpath) { + html("<div class='path'>"); + html("path: "); + cgit_print_path_crumbs(ctx.qry.vpath); + if (ctx.cfg.enable_follow_links && !strcmp(ctx.qry.page, "log")) { + html(" ("); + ctx.qry.follow = !ctx.qry.follow; + cgit_self_link(ctx.qry.follow ? "follow" : "unfollow", + NULL, NULL); + ctx.qry.follow = !ctx.qry.follow; + html(")"); + } + html("</div>"); + } + html("<div class='content'>"); +} + +void cgit_print_filemode(unsigned short mode) +{ + if (S_ISDIR(mode)) + html("d"); + else if (S_ISLNK(mode)) + html("l"); + else if (S_ISGITLINK(mode)) + html("m"); + else + html("-"); + html_fileperm(mode >> 6); + html_fileperm(mode >> 3); + html_fileperm(mode); +} + +void cgit_compose_snapshot_prefix(struct strbuf *filename, const char *base, + const char *ref) +{ + struct object_id oid; + + /* + * Prettify snapshot names by stripping leading "v" or "V" if the tag + * name starts with {v,V}[0-9] and the prettify mapping is injective, + * i.e. each stripped tag can be inverted without ambiguities. + */ + if (get_oid(fmt("refs/tags/%s", ref), &oid) == 0 && + (ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1]) && + ((get_oid(fmt("refs/tags/%s", ref + 1), &oid) == 0) + + (get_oid(fmt("refs/tags/v%s", ref + 1), &oid) == 0) + + (get_oid(fmt("refs/tags/V%s", ref + 1), &oid) == 0) == 1)) + ref++; + + strbuf_addf(filename, "%s-%s", base, ref); +} + +void cgit_print_snapshot_links(const struct cgit_repo *repo, const char *ref, + const char *separator) +{ + const struct cgit_snapshot_format *f; + struct strbuf filename = STRBUF_INIT; + const char *basename; + size_t prefixlen; + + basename = cgit_snapshot_prefix(repo); + if (starts_with(ref, basename)) + strbuf_addstr(&filename, ref); + else + cgit_compose_snapshot_prefix(&filename, basename, ref); + + prefixlen = filename.len; + for (f = cgit_snapshot_formats; f->suffix; f++) { + if (!(repo->snapshots & cgit_snapshot_format_bit(f))) + continue; + strbuf_setlen(&filename, prefixlen); + strbuf_addstr(&filename, f->suffix); + cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL, + filename.buf); + if (cgit_snapshot_get_sig(ref, f)) { + strbuf_addstr(&filename, ".asc"); + html(" ("); + cgit_snapshot_link("sig", NULL, NULL, NULL, NULL, + filename.buf); + html(")"); + } else if (starts_with(f->suffix, ".tar") && cgit_snapshot_get_sig(ref, &cgit_snapshot_formats[0])) { + strbuf_setlen(&filename, strlen(filename.buf) - strlen(f->suffix)); + strbuf_addstr(&filename, ".tar.asc"); + html(" ("); + cgit_snapshot_link("sig", NULL, NULL, NULL, NULL, + filename.buf); + html(")"); + } + html(separator); + } + strbuf_release(&filename); +} + +void cgit_set_title_from_path(const char *path) +{ + struct strbuf sb = STRBUF_INIT; + const char *slash, *last_slash; + + if (!path) + return; + + last_slash = path + strlen(path); + for (slash = last_slash; slash > path; --slash) { + if (*slash != '/') continue; + strbuf_add(&sb, slash + 1, last_slash - slash - 1); + strbuf_addstr(&sb, " \xc2\xab "); + last_slash = slash; + } + strbuf_add(&sb, path, last_slash - path); + strbuf_addf(&sb, " - %s", ctx.page.title); + ctx.page.title = strbuf_detach(&sb, NULL); +} diff --git a/www/git.causal.agency/cgit/ui-shared.h b/www/git.causal.agency/cgit/ui-shared.h new file mode 100644 index 00000000..6964873a --- /dev/null +++ b/www/git.causal.agency/cgit/ui-shared.h @@ -0,0 +1,87 @@ +#ifndef UI_SHARED_H +#define UI_SHARED_H + +extern const char *cgit_httpscheme(void); +extern char *cgit_hosturl(void); +extern const char *cgit_rooturl(void); +extern char *cgit_currenturl(void); +extern char *cgit_currentfullurl(void); +extern const char *cgit_loginurl(void); +extern char *cgit_repourl(const char *reponame); +extern char *cgit_fileurl(const char *reponame, const char *pagename, + const char *filename, const char *query); +extern char *cgit_pageurl(const char *reponame, const char *pagename, + const char *query); + +extern void cgit_add_clone_urls(void (*fn)(const char *)); + +extern void cgit_index_link(const char *name, const char *title, + const char *class, const char *pattern, const char *sort, int ofs, int always_root); +extern void cgit_summary_link(const char *name, const char *title, + const char *class, const char *head); +extern void cgit_tag_link(const char *name, const char *title, + const char *class, const char *tag); +extern void cgit_tree_link(const char *name, const char *title, + const char *class, const char *head, + const char *rev, const char *path); +extern void cgit_plain_link(const char *name, const char *title, + const char *class, const char *head, + const char *rev, const char *path); +extern void cgit_blame_link(const char *name, const char *title, + const char *class, const char *head, + const char *rev, const char *path); +extern void cgit_log_link(const char *name, const char *title, + const char *class, const char *head, const char *rev, + const char *path, int ofs, const char *grep, + const char *pattern, int showmsg, int follow); +extern void cgit_commit_link(const char *name, const char *title, + const char *class, const char *head, + const char *rev, const char *path); +extern void cgit_patch_link(const char *name, const char *title, + const char *class, const char *head, + const char *rev, const char *path); +extern void cgit_refs_link(const char *name, const char *title, + const char *class, const char *head, + const char *rev, const char *path); +extern void cgit_snapshot_link(const char *name, const char *title, + const char *class, const char *head, + const char *rev, const char *archivename); +extern void cgit_diff_link(const char *name, const char *title, + const char *class, const char *head, + const char *new_rev, const char *old_rev, + const char *path); +extern void cgit_stats_link(const char *name, const char *title, + const char *class, const char *head, + const char *path); +extern void cgit_object_link(struct object *obj); + +extern void cgit_submodule_link(const char *class, char *path, + const char *rev); + +extern void cgit_print_layout_start(void); +extern void cgit_print_layout_end(void); + +__attribute__((format (printf,1,2))) +extern void cgit_print_error(const char *fmt, ...); +__attribute__((format (printf,1,0))) +extern void cgit_vprint_error(const char *fmt, va_list ap); +extern const struct date_mode *cgit_date_mode(enum date_mode_type type); +extern void cgit_print_age(time_t t, int tz, time_t max_relative); +extern void cgit_print_http_headers(void); +extern void cgit_redirect(const char *url, bool permanent); +extern void cgit_print_docstart(void); +extern void cgit_print_docend(void); +__attribute__((format (printf,3,4))) +extern void cgit_print_error_page(int code, const char *msg, const char *fmt, ...); +extern void cgit_print_pageheader(void); +extern void cgit_print_filemode(unsigned short mode); +extern void cgit_compose_snapshot_prefix(struct strbuf *filename, + const char *base, const char *ref); +extern void cgit_print_snapshot_links(const struct cgit_repo *repo, + const char *ref, const char *separator); +extern const char *cgit_snapshot_prefix(const struct cgit_repo *repo); +extern void cgit_add_hidden_formfields(int incl_head, int incl_search, + const char *page); + +extern void cgit_set_title_from_path(const char *path); +#endif /* UI_SHARED_H */ diff --git a/www/git.causal.agency/cgit/ui-snapshot.c b/www/git.causal.agency/cgit/ui-snapshot.c new file mode 100644 index 00000000..28013935 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-snapshot.c @@ -0,0 +1,319 @@ +/* ui-snapshot.c: generate snapshot of a commit + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-snapshot.h" +#include "html.h" +#include "ui-shared.h" + +static int write_archive_type(const char *format, const char *hex, const char *prefix) +{ + struct strvec argv = STRVEC_INIT; + const char **nargv; + int result; + strvec_push(&argv, "snapshot"); + strvec_push(&argv, format); + if (prefix) { + struct strbuf buf = STRBUF_INIT; + strbuf_addstr(&buf, prefix); + strbuf_addch(&buf, '/'); + strvec_push(&argv, "--prefix"); + strvec_push(&argv, buf.buf); + strbuf_release(&buf); + } + strvec_push(&argv, hex); + /* + * Now we need to copy the pointers to arguments into a new + * structure because write_archive will rearrange its arguments + * which may result in duplicated/missing entries causing leaks + * or double-frees in strvec_clear. + */ + nargv = xmalloc(sizeof(char *) * (argv.nr + 1)); + /* strvec guarantees a trailing NULL entry. */ + memcpy(nargv, argv.v, sizeof(char *) * (argv.nr + 1)); + + if (fflush(stdout)) + return errno; + + result = write_archive(argv.nr, nargv, NULL, the_repository, NULL, 0); + strvec_clear(&argv); + free(nargv); + return result; +} + +static int write_tar_archive(const char *hex, const char *prefix) +{ + return write_archive_type("--format=tar", hex, prefix); +} + +static int write_zip_archive(const char *hex, const char *prefix) +{ + return write_archive_type("--format=zip", hex, prefix); +} + +static int write_compressed_tar_archive(const char *hex, + const char *prefix, + char *filter_argv[]) +{ + int rv; + struct cgit_exec_filter f; + cgit_exec_filter_init(&f, filter_argv[0], filter_argv); + + cgit_open_filter(&f.base); + rv = write_tar_archive(hex, prefix); + cgit_close_filter(&f.base); + return rv; +} + +static int write_tar_gzip_archive(const char *hex, const char *prefix) +{ + char *argv[] = { "gzip", "-n", NULL }; + return write_compressed_tar_archive(hex, prefix, argv); +} + +static int write_tar_bzip2_archive(const char *hex, const char *prefix) +{ + char *argv[] = { "bzip2", NULL }; + return write_compressed_tar_archive(hex, prefix, argv); +} + +static int write_tar_lzip_archive(const char *hex, const char *prefix) +{ + char *argv[] = { "lzip", NULL }; + return write_compressed_tar_archive(hex, prefix, argv); +} + +static int write_tar_xz_archive(const char *hex, const char *prefix) +{ + char *argv[] = { "xz", NULL }; + return write_compressed_tar_archive(hex, prefix, argv); +} + +static int write_tar_zstd_archive(const char *hex, const char *prefix) +{ + char *argv[] = { "zstd", "-T0", NULL }; + return write_compressed_tar_archive(hex, prefix, argv); +} + +const struct cgit_snapshot_format cgit_snapshot_formats[] = { + /* .tar must remain the 0 index */ + { ".tar", "application/x-tar", write_tar_archive }, + { ".tar.gz", "application/x-gzip", write_tar_gzip_archive }, + { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive }, + { ".tar.lz", "application/x-lzip", write_tar_lzip_archive }, + { ".tar.xz", "application/x-xz", write_tar_xz_archive }, + { ".tar.zst", "application/x-zstd", write_tar_zstd_archive }, + { ".zip", "application/x-zip", write_zip_archive }, + { NULL } +}; + +static struct notes_tree snapshot_sig_notes[ARRAY_SIZE(cgit_snapshot_formats)]; + +const struct object_id *cgit_snapshot_get_sig(const char *ref, + const struct cgit_snapshot_format *f) +{ + struct notes_tree *tree; + struct object_id oid; + + if (get_oid(ref, &oid)) + return NULL; + + tree = &snapshot_sig_notes[f - &cgit_snapshot_formats[0]]; + if (!tree->initialized) { + struct strbuf notes_ref = STRBUF_INIT; + + strbuf_addf(¬es_ref, "refs/notes/signatures/%s", + f->suffix + 1); + + init_notes(tree, notes_ref.buf, combine_notes_ignore, 0); + strbuf_release(¬es_ref); + } + + return get_note(tree, &oid); +} + +static const struct cgit_snapshot_format *get_format(const char *filename) +{ + const struct cgit_snapshot_format *fmt; + + for (fmt = cgit_snapshot_formats; fmt->suffix; fmt++) { + if (ends_with(filename, fmt->suffix)) + return fmt; + } + return NULL; +} + +const unsigned cgit_snapshot_format_bit(const struct cgit_snapshot_format *f) +{ + return BIT(f - &cgit_snapshot_formats[0]); +} + +static int make_snapshot(const struct cgit_snapshot_format *format, + const char *hex, const char *prefix, + const char *filename) +{ + struct object_id oid; + + if (get_oid(hex, &oid)) { + cgit_print_error_page(404, "Not found", + "Bad object id: %s", hex); + return 1; + } + if (!lookup_commit_reference(the_repository, &oid)) { + cgit_print_error_page(400, "Bad request", + "Not a commit reference: %s", hex); + return 1; + } + ctx.page.etag = oid_to_hex(&oid); + ctx.page.mimetype = xstrdup(format->mimetype); + ctx.page.filename = xstrdup(filename); + cgit_print_http_headers(); + init_archivers(); + format->write_func(hex, prefix); + return 0; +} + +static int write_sig(const struct cgit_snapshot_format *format, + const char *hex, const char *archive, + const char *filename) +{ + const struct object_id *note = cgit_snapshot_get_sig(hex, format); + enum object_type type; + unsigned long size; + char *buf; + + if (!note) { + cgit_print_error_page(404, "Not found", + "No signature for %s", archive); + return 0; + } + + buf = read_object_file(note, &type, &size); + if (!buf) { + cgit_print_error_page(404, "Not found", "Not found"); + return 0; + } + + html("X-Content-Type-Options: nosniff\n"); + html("Content-Security-Policy: default-src 'none'\n"); + ctx.page.etag = oid_to_hex(note); + ctx.page.mimetype = xstrdup("application/pgp-signature"); + ctx.page.filename = xstrdup(filename); + cgit_print_http_headers(); + + html_raw(buf, size); + free(buf); + return 0; +} + +/* Try to guess the requested revision from the requested snapshot name. + * First the format extension is stripped, e.g. "cgit-0.7.2.tar.gz" become + * "cgit-0.7.2". If this is a valid commit object name we've got a winner. + * Otherwise, if the snapshot name has a prefix matching the result from + * repo_basename(), we strip the basename and any following '-' and '_' + * characters ("cgit-0.7.2" -> "0.7.2") and check the resulting name once + * more. If this still isn't a valid commit object name, we check if pre- + * pending a 'v' or a 'V' to the remaining snapshot name ("0.7.2" -> + * "v0.7.2") gives us something valid. + */ +static const char *get_ref_from_filename(const struct cgit_repo *repo, + const char *filename, + const struct cgit_snapshot_format *format) +{ + const char *reponame; + struct object_id oid; + struct strbuf snapshot = STRBUF_INIT; + int result = 1; + + strbuf_addstr(&snapshot, filename); + strbuf_setlen(&snapshot, snapshot.len - strlen(format->suffix)); + + if (get_oid(snapshot.buf, &oid) == 0) + goto out; + + reponame = cgit_snapshot_prefix(repo); + if (starts_with(snapshot.buf, reponame)) { + const char *new_start = snapshot.buf; + new_start += strlen(reponame); + while (new_start && (*new_start == '-' || *new_start == '_')) + new_start++; + strbuf_splice(&snapshot, 0, new_start - snapshot.buf, "", 0); + } + + if (get_oid(snapshot.buf, &oid) == 0) + goto out; + + strbuf_insert(&snapshot, 0, "v", 1); + if (get_oid(snapshot.buf, &oid) == 0) + goto out; + + strbuf_splice(&snapshot, 0, 1, "V", 1); + if (get_oid(snapshot.buf, &oid) == 0) + goto out; + + result = 0; + strbuf_release(&snapshot); + +out: + return result ? strbuf_detach(&snapshot, NULL) : NULL; +} + +void cgit_print_snapshot(const char *head, const char *hex, + const char *filename, int dwim) +{ + const struct cgit_snapshot_format* f; + const char *sig_filename = NULL; + char *adj_filename = NULL; + char *prefix = NULL; + + if (!filename) { + cgit_print_error_page(400, "Bad request", + "No snapshot name specified"); + return; + } + + if (ends_with(filename, ".asc")) { + sig_filename = filename; + + /* Strip ".asc" from filename for common format processing */ + adj_filename = xstrdup(filename); + adj_filename[strlen(adj_filename) - 4] = '\0'; + filename = adj_filename; + } + + f = get_format(filename); + if (!f || (!sig_filename && !(ctx.repo->snapshots & cgit_snapshot_format_bit(f)))) { + cgit_print_error_page(400, "Bad request", + "Unsupported snapshot format: %s", filename); + return; + } + + if (!hex && dwim) { + hex = get_ref_from_filename(ctx.repo, filename, f); + if (hex == NULL) { + cgit_print_error_page(404, "Not found", "Not found"); + return; + } + prefix = xstrdup(filename); + prefix[strlen(filename) - strlen(f->suffix)] = '\0'; + } + + if (!hex) + hex = head; + + if (!prefix) + prefix = xstrdup(cgit_snapshot_prefix(ctx.repo)); + + if (sig_filename) + write_sig(f, hex, filename, sig_filename); + else + make_snapshot(f, hex, prefix, filename); + + free(prefix); + free(adj_filename); +} diff --git a/www/git.causal.agency/cgit/ui-snapshot.h b/www/git.causal.agency/cgit/ui-snapshot.h new file mode 100644 index 00000000..a8deec36 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-snapshot.h @@ -0,0 +1,7 @@ +#ifndef UI_SNAPSHOT_H +#define UI_SNAPSHOT_H + +extern void cgit_print_snapshot(const char *head, const char *hex, + const char *filename, int dwim); + +#endif /* UI_SNAPSHOT_H */ diff --git a/www/git.causal.agency/cgit/ui-ssdiff.c b/www/git.causal.agency/cgit/ui-ssdiff.c new file mode 100644 index 00000000..af8bc9e0 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-ssdiff.c @@ -0,0 +1,420 @@ +#include "cgit.h" +#include "ui-ssdiff.h" +#include "html.h" +#include "ui-shared.h" +#include "ui-diff.h" + +extern int use_ssdiff; + +static int current_old_line, current_new_line; +static int **L = NULL; + +struct deferred_lines { + int line_no; + char *line; + struct deferred_lines *next; +}; + +static struct deferred_lines *deferred_old, *deferred_old_last; +static struct deferred_lines *deferred_new, *deferred_new_last; + +static void create_or_reset_lcs_table(void) +{ + int i; + + if (L != NULL) { + memset(*L, 0, sizeof(int) * MAX_SSDIFF_SIZE); + return; + } + + // xcalloc will die if we ran out of memory; + // not very helpful for debugging + L = (int**)xcalloc(MAX_SSDIFF_M, sizeof(int *)); + *L = (int*)xcalloc(MAX_SSDIFF_SIZE, sizeof(int)); + + for (i = 1; i < MAX_SSDIFF_M; i++) { + L[i] = *L + i * MAX_SSDIFF_N; + } +} + +static char *longest_common_subsequence(char *A, char *B) +{ + int i, j, ri; + int m = strlen(A); + int n = strlen(B); + int tmp1, tmp2; + int lcs_length; + char *result; + + // We bail if the lines are too long + if (m >= MAX_SSDIFF_M || n >= MAX_SSDIFF_N) + return NULL; + + create_or_reset_lcs_table(); + + for (i = m; i >= 0; i--) { + for (j = n; j >= 0; j--) { + if (A[i] == '\0' || B[j] == '\0') { + L[i][j] = 0; + } else if (A[i] == B[j]) { + L[i][j] = 1 + L[i + 1][j + 1]; + } else { + tmp1 = L[i + 1][j]; + tmp2 = L[i][j + 1]; + L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2); + } + } + } + + lcs_length = L[0][0]; + result = xmalloc(lcs_length + 2); + memset(result, 0, sizeof(*result) * (lcs_length + 2)); + + ri = 0; + i = 0; + j = 0; + while (i < m && j < n) { + if (A[i] == B[j]) { + result[ri] = A[i]; + ri += 1; + i += 1; + j += 1; + } else if (L[i + 1][j] >= L[i][j + 1]) { + i += 1; + } else { + j += 1; + } + } + + return result; +} + +static int line_from_hunk(char *line, char type) +{ + char *buf1, *buf2; + int len, res; + + buf1 = strchr(line, type); + if (buf1 == NULL) + return 0; + buf1 += 1; + buf2 = strchr(buf1, ','); + if (buf2 == NULL) + return 0; + len = buf2 - buf1; + buf2 = xmalloc(len + 1); + strlcpy(buf2, buf1, len + 1); + res = atoi(buf2); + free(buf2); + return res; +} + +static char *replace_tabs(char *line) +{ + char *prev_buf = line; + char *cur_buf; + size_t linelen = strlen(line); + int n_tabs = 0; + int i; + char *result; + size_t result_len; + + if (linelen == 0) { + result = xmalloc(1); + result[0] = '\0'; + return result; + } + + for (i = 0; i < linelen; i++) { + if (line[i] == '\t') + n_tabs += 1; + } + result_len = linelen + n_tabs * 8; + result = xmalloc(result_len + 1); + result[0] = '\0'; + + for (;;) { + cur_buf = strchr(prev_buf, '\t'); + if (!cur_buf) { + linelen = strlen(result); + strlcpy(&result[linelen], prev_buf, result_len - linelen + 1); + break; + } else { + linelen = strlen(result); + strlcpy(&result[linelen], prev_buf, cur_buf - prev_buf + 1); + linelen = strlen(result); + memset(&result[linelen], ' ', 8 - (linelen % 8)); + result[linelen + 8 - (linelen % 8)] = '\0'; + } + prev_buf = cur_buf + 1; + } + return result; +} + +static int calc_deferred_lines(struct deferred_lines *start) +{ + struct deferred_lines *item = start; + int result = 0; + while (item) { + result += 1; + item = item->next; + } + return result; +} + +static void deferred_old_add(char *line, int line_no) +{ + struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); + item->line = xstrdup(line); + item->line_no = line_no; + item->next = NULL; + if (deferred_old) { + deferred_old_last->next = item; + deferred_old_last = item; + } else { + deferred_old = deferred_old_last = item; + } +} + +static void deferred_new_add(char *line, int line_no) +{ + struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); + item->line = xstrdup(line); + item->line_no = line_no; + item->next = NULL; + if (deferred_new) { + deferred_new_last->next = item; + deferred_new_last = item; + } else { + deferred_new = deferred_new_last = item; + } +} + +static void print_part_with_lcs(char *class, char *line, char *lcs) +{ + int line_len = strlen(line); + int i, j; + char c[2] = " "; + int same = 1; + + j = 0; + for (i = 0; i < line_len; i++) { + c[0] = line[i]; + if (same) { + if (line[i] == lcs[j]) + j += 1; + else { + same = 0; + htmlf("<span class='%s'>", class); + } + } else if (line[i] == lcs[j]) { + same = 1; + html("</span>"); + j += 1; + } + html_txt(c); + } + if (!same) + html("</span>"); +} + +static void print_ssdiff_line(char *class, + int old_line_no, + char *old_line, + int new_line_no, + char *new_line, int individual_chars) +{ + char *lcs = NULL; + + if (old_line) + old_line = replace_tabs(old_line + 1); + if (new_line) + new_line = replace_tabs(new_line + 1); + if (individual_chars && old_line && new_line) + lcs = longest_common_subsequence(old_line, new_line); + html("<tr>\n"); + if (old_line_no > 0) { + struct diff_filespec *old_file = cgit_get_current_old_file(); + char *lineno_str = fmt("n%d", old_line_no); + char *id_str = fmt("id=%s#%s", is_null_oid(&old_file->oid)?"HEAD":oid_to_hex(old_rev_oid), lineno_str); + char *fileurl = cgit_fileurl(ctx.repo->url, "tree", old_file->path, id_str); + html("<td class='lineno'><a href='"); + html(fileurl); + htmlf("'>%s</a>", lineno_str + 1); + html("</td>"); + htmlf("<td class='%s'>", class); + free(fileurl); + } else if (old_line) + htmlf("<td class='lineno'></td><td class='%s'>", class); + else + htmlf("<td class='lineno'></td><td class='%s_dark'>", class); + if (old_line) { + if (lcs) + print_part_with_lcs("del", old_line, lcs); + else + html_txt(old_line); + } + + html("</td>\n"); + if (new_line_no > 0) { + struct diff_filespec *new_file = cgit_get_current_new_file(); + char *lineno_str = fmt("n%d", new_line_no); + char *id_str = fmt("id=%s#%s", is_null_oid(&new_file->oid)?"HEAD":oid_to_hex(new_rev_oid), lineno_str); + char *fileurl = cgit_fileurl(ctx.repo->url, "tree", new_file->path, id_str); + html("<td class='lineno'><a href='"); + html(fileurl); + htmlf("'>%s</a>", lineno_str + 1); + html("</td>"); + htmlf("<td class='%s'>", class); + free(fileurl); + } else if (new_line) + htmlf("<td class='lineno'></td><td class='%s'>", class); + else + htmlf("<td class='lineno'></td><td class='%s_dark'>", class); + if (new_line) { + if (lcs) + print_part_with_lcs("add", new_line, lcs); + else + html_txt(new_line); + } + + html("</td></tr>"); + if (lcs) + free(lcs); + if (new_line) + free(new_line); + if (old_line) + free(old_line); +} + +static void print_deferred_old_lines(void) +{ + struct deferred_lines *iter_old, *tmp; + iter_old = deferred_old; + while (iter_old) { + print_ssdiff_line("del", iter_old->line_no, + iter_old->line, -1, NULL, 0); + tmp = iter_old->next; + free(iter_old); + iter_old = tmp; + } +} + +static void print_deferred_new_lines(void) +{ + struct deferred_lines *iter_new, *tmp; + iter_new = deferred_new; + while (iter_new) { + print_ssdiff_line("add", -1, NULL, + iter_new->line_no, iter_new->line, 0); + tmp = iter_new->next; + free(iter_new); + iter_new = tmp; + } +} + +static void print_deferred_changed_lines(void) +{ + struct deferred_lines *iter_old, *iter_new, *tmp; + int n_old_lines = calc_deferred_lines(deferred_old); + int n_new_lines = calc_deferred_lines(deferred_new); + int individual_chars = (n_old_lines == n_new_lines ? 1 : 0); + + iter_old = deferred_old; + iter_new = deferred_new; + while (iter_old || iter_new) { + if (iter_old && iter_new) + print_ssdiff_line("changed", iter_old->line_no, + iter_old->line, + iter_new->line_no, iter_new->line, + individual_chars); + else if (iter_old) + print_ssdiff_line("changed", iter_old->line_no, + iter_old->line, -1, NULL, 0); + else if (iter_new) + print_ssdiff_line("changed", -1, NULL, + iter_new->line_no, iter_new->line, 0); + if (iter_old) { + tmp = iter_old->next; + free(iter_old); + iter_old = tmp; + } + + if (iter_new) { + tmp = iter_new->next; + free(iter_new); + iter_new = tmp; + } + } +} + +void cgit_ssdiff_print_deferred_lines(void) +{ + if (!deferred_old && !deferred_new) + return; + if (deferred_old && !deferred_new) + print_deferred_old_lines(); + else if (!deferred_old && deferred_new) + print_deferred_new_lines(); + else + print_deferred_changed_lines(); + deferred_old = deferred_old_last = NULL; + deferred_new = deferred_new_last = NULL; +} + +/* + * print a single line returned from xdiff + */ +void cgit_ssdiff_line_cb(char *line, int len) +{ + char c = line[len - 1]; + line[len - 1] = '\0'; + if (line[0] == '@') { + current_old_line = line_from_hunk(line, '-'); + current_new_line = line_from_hunk(line, '+'); + } + + if (line[0] == ' ') { + if (deferred_old || deferred_new) + cgit_ssdiff_print_deferred_lines(); + print_ssdiff_line("ctx", current_old_line, line, + current_new_line, line, 0); + current_old_line += 1; + current_new_line += 1; + } else if (line[0] == '+') { + deferred_new_add(line, current_new_line); + current_new_line += 1; + } else if (line[0] == '-') { + deferred_old_add(line, current_old_line); + current_old_line += 1; + } else if (line[0] == '@') { + html("<tr><td colspan='4' class='hunk'>"); + html_txt(line); + html("</td></tr>"); + } else { + html("<tr><td colspan='4' class='ctx'>"); + html_txt(line); + html("</td></tr>"); + } + line[len - 1] = c; +} + +void cgit_ssdiff_header_begin(void) +{ + current_old_line = -1; + current_new_line = -1; + html("<tr><td class='space' colspan='4'><div></div></td></tr>"); + html("<tr><td class='head' colspan='4'>"); +} + +void cgit_ssdiff_header_end(void) +{ + html("</td></tr>"); +} + +void cgit_ssdiff_footer(void) +{ + if (deferred_old || deferred_new) + cgit_ssdiff_print_deferred_lines(); + html("<tr><td class='foot' colspan='4'></td></tr>"); +} diff --git a/www/git.causal.agency/cgit/ui-ssdiff.h b/www/git.causal.agency/cgit/ui-ssdiff.h new file mode 100644 index 00000000..11f27144 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-ssdiff.h @@ -0,0 +1,25 @@ +#ifndef UI_SSDIFF_H +#define UI_SSDIFF_H + +/* + * ssdiff line limits + */ +#ifndef MAX_SSDIFF_M +#define MAX_SSDIFF_M 128 +#endif + +#ifndef MAX_SSDIFF_N +#define MAX_SSDIFF_N 128 +#endif +#define MAX_SSDIFF_SIZE ((MAX_SSDIFF_M) * (MAX_SSDIFF_N)) + +extern void cgit_ssdiff_print_deferred_lines(void); + +extern void cgit_ssdiff_line_cb(char *line, int len); + +extern void cgit_ssdiff_header_begin(void); +extern void cgit_ssdiff_header_end(void); + +extern void cgit_ssdiff_footer(void); + +#endif /* UI_SSDIFF_H */ diff --git a/www/git.causal.agency/cgit/ui-stats.c b/www/git.causal.agency/cgit/ui-stats.c new file mode 100644 index 00000000..09b3625e --- /dev/null +++ b/www/git.causal.agency/cgit/ui-stats.c @@ -0,0 +1,426 @@ +#include "cgit.h" +#include "ui-stats.h" +#include "html.h" +#include "ui-shared.h" + +struct authorstat { + long total; + struct string_list list; +}; + +#define DAY_SECS (60 * 60 * 24) +#define WEEK_SECS (DAY_SECS * 7) + +static void trunc_week(struct tm *tm) +{ + time_t t = timegm(tm); + t -= ((tm->tm_wday + 6) % 7) * DAY_SECS; + gmtime_r(&t, tm); +} + +static void dec_week(struct tm *tm) +{ + time_t t = timegm(tm); + t -= WEEK_SECS; + gmtime_r(&t, tm); +} + +static void inc_week(struct tm *tm) +{ + time_t t = timegm(tm); + t += WEEK_SECS; + gmtime_r(&t, tm); +} + +static char *pretty_week(struct tm *tm) +{ + static char buf[10]; + + strftime(buf, sizeof(buf), "W%V %G", tm); + return buf; +} + +static void trunc_month(struct tm *tm) +{ + tm->tm_mday = 1; +} + +static void dec_month(struct tm *tm) +{ + tm->tm_mon--; + if (tm->tm_mon < 0) { + tm->tm_year--; + tm->tm_mon = 11; + } +} + +static void inc_month(struct tm *tm) +{ + tm->tm_mon++; + if (tm->tm_mon > 11) { + tm->tm_year++; + tm->tm_mon = 0; + } +} + +static char *pretty_month(struct tm *tm) +{ + static const char *months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + return fmt("%s %d", months[tm->tm_mon], tm->tm_year + 1900); +} + +static void trunc_quarter(struct tm *tm) +{ + trunc_month(tm); + while (tm->tm_mon % 3 != 0) + dec_month(tm); +} + +static void dec_quarter(struct tm *tm) +{ + dec_month(tm); + dec_month(tm); + dec_month(tm); +} + +static void inc_quarter(struct tm *tm) +{ + inc_month(tm); + inc_month(tm); + inc_month(tm); +} + +static char *pretty_quarter(struct tm *tm) +{ + return fmt("Q%d %d", tm->tm_mon / 3 + 1, tm->tm_year + 1900); +} + +static void trunc_year(struct tm *tm) +{ + trunc_month(tm); + tm->tm_mon = 0; +} + +static void dec_year(struct tm *tm) +{ + tm->tm_year--; +} + +static void inc_year(struct tm *tm) +{ + tm->tm_year++; +} + +static char *pretty_year(struct tm *tm) +{ + return fmt("%d", tm->tm_year + 1900); +} + +static const struct cgit_period periods[] = { + {'w', "week", 12, 4, trunc_week, dec_week, inc_week, pretty_week}, + {'m', "month", 12, 4, trunc_month, dec_month, inc_month, pretty_month}, + {'q', "quarter", 12, 4, trunc_quarter, dec_quarter, inc_quarter, pretty_quarter}, + {'y', "year", 12, 4, trunc_year, dec_year, inc_year, pretty_year}, +}; + +/* Given a period code or name, return a period index (1, 2, 3 or 4) + * and update the period pointer to the correcsponding struct. + * If no matching code is found, return 0. + */ +int cgit_find_stats_period(const char *expr, const struct cgit_period **period) +{ + int i; + char code = '\0'; + + if (!expr) + return 0; + + if (strlen(expr) == 1) + code = expr[0]; + + for (i = 0; i < sizeof(periods) / sizeof(periods[0]); i++) + if (periods[i].code == code || !strcmp(periods[i].name, expr)) { + if (period) + *period = &periods[i]; + return i + 1; + } + return 0; +} + +const char *cgit_find_stats_periodname(int idx) +{ + if (idx > 0 && idx < 4) + return periods[idx - 1].name; + else + return ""; +} + +static void add_commit(struct string_list *authors, struct commit *commit, + const struct cgit_period *period) +{ + struct commitinfo *info; + struct string_list_item *author, *item; + struct authorstat *authorstat; + struct string_list *items; + char *tmp; + struct tm date; + time_t t; + uintptr_t *counter; + + info = cgit_parse_commit(commit); + tmp = xstrdup(info->author); + author = string_list_insert(authors, tmp); + if (!author->util) + author->util = xcalloc(1, sizeof(struct authorstat)); + else + free(tmp); + authorstat = author->util; + items = &authorstat->list; + t = info->committer_date; + gmtime_r(&t, &date); + period->trunc(&date); + tmp = xstrdup(period->pretty(&date)); + item = string_list_insert(items, tmp); + counter = (uintptr_t *)&item->util; + if (*counter) + free(tmp); + (*counter)++; + + authorstat->total++; + cgit_free_commitinfo(info); +} + +static int cmp_total_commits(const void *a1, const void *a2) +{ + const struct string_list_item *i1 = a1; + const struct string_list_item *i2 = a2; + const struct authorstat *auth1 = i1->util; + const struct authorstat *auth2 = i2->util; + + return auth2->total - auth1->total; +} + +/* Walk the commit DAG and collect number of commits per author per + * timeperiod into a nested string_list collection. + */ +static struct string_list collect_stats(const struct cgit_period *period) +{ + struct string_list authors; + struct rev_info rev; + struct commit *commit; + const char *argv[] = {NULL, ctx.qry.head, NULL, NULL, NULL, NULL}; + int argc = 3; + time_t now; + long i; + struct tm tm; + char tmp[11]; + + time(&now); + gmtime_r(&now, &tm); + period->trunc(&tm); + for (i = 1; i < period->count; i++) + period->dec(&tm); + strftime(tmp, sizeof(tmp), "%Y-%m-%d", &tm); + argv[2] = xstrdup(fmt("--since=%s", tmp)); + if (ctx.qry.path) { + argv[3] = "--"; + argv[4] = ctx.qry.path; + argc += 2; + } + init_revisions(&rev, NULL); + rev.abbrev = DEFAULT_ABBREV; + rev.commit_format = CMIT_FMT_DEFAULT; + rev.max_parents = 1; + rev.verbose_header = 1; + rev.show_root_diff = 0; + setup_revisions(argc, argv, &rev, NULL); + prepare_revision_walk(&rev); + memset(&authors, 0, sizeof(authors)); + while ((commit = get_revision(&rev)) != NULL) { + add_commit(&authors, commit, period); + free_commit_buffer(the_repository->parsed_objects, commit); + free_commit_list(commit->parents); + commit->parents = NULL; + } + return authors; +} + +static void print_combined_authorrow(struct string_list *authors, int from, + int to, const char *name, + const char *leftclass, + const char *centerclass, + const char *rightclass, + const struct cgit_period *period) +{ + struct string_list_item *author; + struct authorstat *authorstat; + struct string_list *items; + struct string_list_item *date; + time_t now; + long i, j, total, subtotal; + struct tm tm; + char *tmp; + + time(&now); + gmtime_r(&now, &tm); + period->trunc(&tm); + for (i = 1; i < period->count; i++) + period->dec(&tm); + + total = 0; + htmlf("<tr><td class='%s'>%s</td>", leftclass, + fmt(name, to - from + 1)); + for (j = 0; j < period->count; j++) { + tmp = period->pretty(&tm); + period->inc(&tm); + subtotal = 0; + for (i = from; i <= to; i++) { + author = &authors->items[i]; + authorstat = author->util; + items = &authorstat->list; + date = string_list_lookup(items, tmp); + if (date) + subtotal += (uintptr_t)date->util; + } + htmlf("<td class='%s'>%ld</td>", centerclass, subtotal); + total += subtotal; + } + htmlf("<td class='%s'>%ld</td></tr>", rightclass, total); +} + +static void print_authors(struct string_list *authors, int top, + const struct cgit_period *period) +{ + struct string_list_item *author; + struct authorstat *authorstat; + struct string_list *items; + struct string_list_item *date; + time_t now; + long i, j, total; + struct tm tm; + char *tmp; + + time(&now); + gmtime_r(&now, &tm); + period->trunc(&tm); + for (i = 1; i < period->count; i++) + period->dec(&tm); + + html("<table class='stats'><tr><th>Author</th>"); + for (j = 0; j < period->count; j++) { + tmp = period->pretty(&tm); + htmlf("<th>%s</th>", tmp); + period->inc(&tm); + } + html("<th>Total</th></tr>\n"); + + if (top <= 0 || top > authors->nr) + top = authors->nr; + + for (i = 0; i < top; i++) { + author = &authors->items[i]; + html("<tr><td class='left'>"); + html_txt(author->string); + html("</td>"); + authorstat = author->util; + items = &authorstat->list; + total = 0; + for (j = 0; j < period->count; j++) + period->dec(&tm); + for (j = 0; j < period->count; j++) { + tmp = period->pretty(&tm); + period->inc(&tm); + date = string_list_lookup(items, tmp); + if (!date) + html("<td>0</td>"); + else { + htmlf("<td>%lu</td>", (uintptr_t)date->util); + total += (uintptr_t)date->util; + } + } + htmlf("<td class='sum'>%ld</td></tr>", total); + } + + if (top < authors->nr) + print_combined_authorrow(authors, top, authors->nr - 1, + "Others (%ld)", "left", "", "sum", period); + + print_combined_authorrow(authors, 0, authors->nr - 1, "Total", + "total", "sum", "sum", period); + html("</table>"); +} + +/* Create a sorted string_list with one entry per author. The util-field + * for each author is another string_list which is used to calculate the + * number of commits per time-interval. + */ +void cgit_show_stats(void) +{ + struct string_list authors; + const struct cgit_period *period; + int top, i; + const char *code = "w"; + + if (ctx.qry.period) + code = ctx.qry.period; + + i = cgit_find_stats_period(code, &period); + if (!i) { + cgit_print_error_page(404, "Not found", + "Unknown statistics type: %c", code[0]); + return; + } + if (i > ctx.repo->max_stats) { + cgit_print_error_page(400, "Bad request", + "Statistics type disabled: %s", period->name); + return; + } + authors = collect_stats(period); + qsort(authors.items, authors.nr, sizeof(struct string_list_item), + cmp_total_commits); + + top = ctx.qry.ofs; + if (!top) + top = 10; + + cgit_print_layout_start(); + html("<div class='cgit-panel'>"); + html("<b>stat options</b>"); + html("<form method='get'>"); + cgit_add_hidden_formfields(1, 0, "stats"); + html("<table><tr><td colspan='2'/></tr>"); + if (ctx.repo->max_stats > 1) { + html("<tr><td class='label'>Period:</td>"); + html("<td class='ctrl'><select name='period' onchange='this.form.submit();'>"); + for (i = 0; i < ctx.repo->max_stats; i++) + html_option(fmt("%c", periods[i].code), + periods[i].name, fmt("%c", period->code)); + html("</select></td></tr>"); + } + html("<tr><td class='label'>Authors:</td>"); + html("<td class='ctrl'><select name='ofs' onchange='this.form.submit();'>"); + html_intoption(10, "10", top); + html_intoption(25, "25", top); + html_intoption(50, "50", top); + html_intoption(100, "100", top); + html_intoption(-1, "all", top); + html("</select></td></tr>"); + html("<tr><td/><td class='ctrl'>"); + html("<noscript><input type='submit' value='Reload'/></noscript>"); + html("</td></tr></table>"); + html("</form>"); + html("</div>"); + htmlf("<h2>Commits per author per %s", period->name); + if (ctx.qry.path) { + html(" (path '"); + html_txt(ctx.qry.path); + html("')"); + } + html("</h2>"); + print_authors(&authors, top, period); + cgit_print_layout_end(); +} + diff --git a/www/git.causal.agency/cgit/ui-stats.h b/www/git.causal.agency/cgit/ui-stats.h new file mode 100644 index 00000000..0e61b03d --- /dev/null +++ b/www/git.causal.agency/cgit/ui-stats.h @@ -0,0 +1,28 @@ +#ifndef UI_STATS_H +#define UI_STATS_H + +#include "cgit.h" + +struct cgit_period { + const char code; + const char *name; + int max_periods; + int count; + + /* Convert a tm value to the first day in the period */ + void (*trunc)(struct tm *tm); + + /* Update tm value to start of next/previous period */ + void (*dec)(struct tm *tm); + void (*inc)(struct tm *tm); + + /* Pretty-print a tm value */ + char *(*pretty)(struct tm *tm); +}; + +extern int cgit_find_stats_period(const char *expr, const struct cgit_period **period); +extern const char *cgit_find_stats_periodname(int idx); + +extern void cgit_show_stats(void); + +#endif /* UI_STATS_H */ diff --git a/www/git.causal.agency/cgit/ui-summary.c b/www/git.causal.agency/cgit/ui-summary.c new file mode 100644 index 00000000..947812a8 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-summary.c @@ -0,0 +1,148 @@ +/* ui-summary.c: functions for generating repo summary page + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-summary.h" +#include "html.h" +#include "ui-blob.h" +#include "ui-log.h" +#include "ui-plain.h" +#include "ui-refs.h" +#include "ui-shared.h" + +static int urls; + +static void print_url(const char *url) +{ + int columns = 3; + + if (ctx.repo->enable_log_filecount) + columns++; + if (ctx.repo->enable_log_linecount) + columns++; + + if (urls++ == 0) { + htmlf("<tr class='nohover'><td colspan='%d'> </td></tr>", columns); + htmlf("<tr class='nohover'><th class='left' colspan='%d'>Clone</th></tr>\n", columns); + } + + htmlf("<tr><td colspan='%d'><a rel='vcs-git' href='", columns); + html_url_path(url); + html("' title='"); + html_attr(ctx.repo->name); + html(" Git repository'>"); + html_txt(url); + html("</a></td></tr>\n"); +} + +void cgit_print_summary(void) +{ + int columns = 3; + + if (ctx.repo->enable_log_filecount) + columns++; + if (ctx.repo->enable_log_linecount) + columns++; + + cgit_print_layout_start(); + html("<table summary='repository info' class='list nowrap'>"); + cgit_print_branches(ctx.cfg.summary_branches); + htmlf("<tr class='nohover'><td colspan='%d'> </td></tr>", columns); + cgit_print_tags(ctx.cfg.summary_tags); + if (ctx.cfg.summary_log > 0) { + htmlf("<tr class='nohover'><td colspan='%d'> </td></tr>", columns); + cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL, + NULL, NULL, 0, 0, 0); + } + urls = 0; + cgit_add_clone_urls(print_url); + html("</table>"); + cgit_print_layout_end(); +} + +/* The caller must free the return value. */ +static char* append_readme_path(const char *filename, const char *ref, const char *path) +{ + char *file, *base_dir, *full_path, *resolved_base = NULL, *resolved_full = NULL; + /* If a subpath is specified for the about page, make it relative + * to the directory containing the configured readme. */ + + file = xstrdup(filename); + base_dir = dirname(file); + if (!strcmp(base_dir, ".") || !strcmp(base_dir, "..")) { + if (!ref) { + free(file); + return NULL; + } + full_path = xstrdup(path); + } else + full_path = fmtalloc("%s/%s", base_dir, path); + + if (!ref) { + resolved_base = realpath(base_dir, NULL); + resolved_full = realpath(full_path, NULL); + if (!resolved_base || !resolved_full || !starts_with(resolved_full, resolved_base)) { + free(full_path); + full_path = NULL; + } + } + + free(file); + free(resolved_base); + free(resolved_full); + + return full_path; +} + +void cgit_print_repo_readme(const char *path) +{ + char *filename, *ref, *mimetype; + int free_filename = 0; + + mimetype = get_mimetype_for_filename(path); + if (mimetype && (!strncmp(mimetype, "image/", 6) || !strncmp(mimetype, "video/", 6))) { + ctx.page.mimetype = mimetype; + ctx.page.charset = NULL; + cgit_print_plain(); + free(mimetype); + return; + } + free(mimetype); + + cgit_print_layout_start(); + if (ctx.repo->readme.nr == 0) + goto done; + + filename = ctx.repo->readme.items[0].string; + ref = ctx.repo->readme.items[0].util; + + if (path) { + free_filename = 1; + filename = append_readme_path(filename, ref, path); + if (!filename) + goto done; + } + + /* Print the calculated readme, either from the git repo or from the + * filesystem, while applying the about-filter. + */ + html("<div id='summary'>"); + cgit_open_filter(ctx.repo->about_filter, filename); + if (ref) + cgit_print_file(filename, ref, 1); + else + html_include(filename); + cgit_close_filter(ctx.repo->about_filter); + + html("</div>"); + if (free_filename) + free(filename); + +done: + cgit_print_layout_end(); +} diff --git a/www/git.causal.agency/cgit/ui-summary.h b/www/git.causal.agency/cgit/ui-summary.h new file mode 100644 index 00000000..cba696af --- /dev/null +++ b/www/git.causal.agency/cgit/ui-summary.h @@ -0,0 +1,7 @@ +#ifndef UI_SUMMARY_H +#define UI_SUMMARY_H + +extern void cgit_print_summary(void); +extern void cgit_print_repo_readme(const char *path); + +#endif /* UI_SUMMARY_H */ diff --git a/www/git.causal.agency/cgit/ui-tag.c b/www/git.causal.agency/cgit/ui-tag.c new file mode 100644 index 00000000..05952429 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-tag.c @@ -0,0 +1,120 @@ +/* ui-tag.c: display a tag + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-tag.h" +#include "html.h" +#include "ui-shared.h" + +static void print_tag_content(char *buf) +{ + char *p; + + if (!buf) + return; + + html("<div class='commit-subject'>"); + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + html_txt(buf); + html("</div>"); + if (p) { + html("<pre class='commit-msg'>"); + html_txt(++p); + html("</pre>"); + } +} + +static void print_download_links(char *revname) +{ + html("<tr><th>download</th><td class='oid'>"); + cgit_print_snapshot_links(ctx.repo, revname, "<br/>"); + html("</td></tr>"); +} + +void cgit_print_tag(char *revname) +{ + struct strbuf fullref = STRBUF_INIT; + struct object_id oid; + struct object *obj; + + if (!revname) + revname = ctx.qry.head; + + strbuf_addf(&fullref, "refs/tags/%s", revname); + if (get_oid(fullref.buf, &oid)) { + cgit_print_error_page(404, "Not found", + "Bad tag reference: %s", revname); + goto cleanup; + } + obj = parse_object(the_repository, &oid); + if (!obj) { + cgit_print_error_page(500, "Internal server error", + "Bad object id: %s", oid_to_hex(&oid)); + goto cleanup; + } + if (obj->type == OBJ_TAG) { + struct tag *tag; + struct taginfo *info; + + tag = lookup_tag(the_repository, &oid); + if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) { + cgit_print_error_page(500, "Internal server error", + "Bad tag object: %s", revname); + goto cleanup; + } + cgit_print_layout_start(); + html("<table class='commit-info'>\n"); + html("<tr><td>tag name</td><td>"); + html_txt(revname); + htmlf(" (%s)</td></tr>\n", oid_to_hex(&oid)); + if (info->tagger_date > 0) { + html("<tr><td>tag date</td><td>"); + html_txt(show_date(info->tagger_date, info->tagger_tz, + cgit_date_mode(DATE_ISO8601))); + html("</td></tr>\n"); + } + if (info->tagger) { + html("<tr><td>tagged by</td><td>"); + cgit_open_filter(ctx.repo->email_filter, info->tagger_email, "tag"); + html_txt(info->tagger); + if (info->tagger_email && !ctx.cfg.noplainemail) { + html(" "); + html_txt(info->tagger_email); + } + cgit_close_filter(ctx.repo->email_filter); + html("</td></tr>\n"); + } + html("<tr><td>tagged object</td><td class='oid'>"); + cgit_object_link(tag->tagged); + html("</td></tr>\n"); + if (ctx.repo->snapshots) + print_download_links(revname); + html("</table>\n"); + print_tag_content(info->msg); + cgit_print_layout_end(); + cgit_free_taginfo(info); + } else { + cgit_print_layout_start(); + html("<table class='commit-info'>\n"); + html("<tr><td>tag name</td><td>"); + html_txt(revname); + html("</td></tr>\n"); + html("<tr><td>tagged object</td><td class='oid'>"); + cgit_object_link(obj); + html("</td></tr>\n"); + if (ctx.repo->snapshots) + print_download_links(revname); + html("</table>\n"); + cgit_print_layout_end(); + } + +cleanup: + strbuf_release(&fullref); +} diff --git a/www/git.causal.agency/cgit/ui-tag.h b/www/git.causal.agency/cgit/ui-tag.h new file mode 100644 index 00000000..d295cdcd --- /dev/null +++ b/www/git.causal.agency/cgit/ui-tag.h @@ -0,0 +1,6 @@ +#ifndef UI_TAG_H +#define UI_TAG_H + +extern void cgit_print_tag(char *revname); + +#endif /* UI_TAG_H */ diff --git a/www/git.causal.agency/cgit/ui-tree.c b/www/git.causal.agency/cgit/ui-tree.c new file mode 100644 index 00000000..21e0b884 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-tree.c @@ -0,0 +1,411 @@ +/* ui-tree.c: functions for tree output + * + * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-tree.h" +#include "html.h" +#include "ui-shared.h" + +struct walk_tree_context { + char *curr_rev; + char *match_path; + int state; +}; + +static void print_text_buffer(const char *name, char *buf, unsigned long size) +{ + unsigned long lineno, idx; + const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n"; + + html("<table summary='blob content' class='blob'>\n"); + + if (ctx.cfg.enable_tree_linenumbers) { + html("<tr><td class='linenumbers'><pre>"); + idx = 0; + lineno = 0; + + if (size) { + htmlf(numberfmt, ++lineno); + while (idx < size - 1) { // skip absolute last newline + if (buf[idx] == '\n') + htmlf(numberfmt, ++lineno); + idx++; + } + } + html("</pre></td>\n"); + } + else { + html("<tr>\n"); + } + + if (ctx.repo->source_filter) { + char *filter_arg = xstrdup(name); + html("<td class='lines'><pre><code>"); + cgit_open_filter(ctx.repo->source_filter, filter_arg); + html_raw(buf, size); + cgit_close_filter(ctx.repo->source_filter); + free(filter_arg); + html("</code></pre></td></tr></table>\n"); + return; + } + + html("<td class='lines'><pre><code>"); + html_txt(buf); + html("</code></pre></td></tr></table>\n"); +} + +#define ROWLEN 32 + +static void print_binary_buffer(char *buf, unsigned long size) +{ + unsigned long ofs, idx; + static char ascii[ROWLEN + 1]; + + html("<table summary='blob content' class='bin-blob'>\n"); + html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); + for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { + htmlf("<tr><td class='right'>%04lx</td><td class='hex'>", ofs); + for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) + htmlf("%*s%02x", + idx == 16 ? 4 : 1, "", + buf[idx] & 0xff); + html(" </td><td class='hex'>"); + for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) + ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; + ascii[idx] = '\0'; + html_txt(ascii); + html("</td></tr>\n"); + } + html("</table>\n"); +} + +static void print_object(const struct object_id *oid, const char *path, const char *basename, const char *rev) +{ + enum object_type type; + char *buf; + unsigned long size; + int is_binary; + + type = oid_object_info(the_repository, oid, &size); + if (type == OBJ_BAD) { + cgit_print_error_page(404, "Not found", + "Bad object name: %s", oid_to_hex(oid)); + return; + } + + buf = read_object_file(oid, &type, &size); + if (!buf) { + cgit_print_error_page(500, "Internal server error", + "Error reading object %s", oid_to_hex(oid)); + return; + } + is_binary = buffer_is_binary(buf, size); + + cgit_set_title_from_path(path); + + cgit_print_layout_start(); + htmlf("blob: %s (", oid_to_hex(oid)); + cgit_plain_link("plain", NULL, NULL, ctx.qry.head, + rev, path); + if (ctx.repo->enable_blame && !is_binary) { + html(") ("); + cgit_blame_link("blame", NULL, NULL, ctx.qry.head, + rev, path); + } + html(")\n"); + + if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) { + htmlf("<div class='error'>blob size (%ldKB) exceeds display size limit (%dKB).</div>", + size / 1024, ctx.cfg.max_blob_size); + return; + } + + if (is_binary) + print_binary_buffer(buf, size); + else + print_text_buffer(basename, buf, size); + + free(buf); +} + +struct single_tree_ctx { + struct strbuf *path; + struct object_id oid; + char *name; + size_t count; +}; + +static int single_tree_cb(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, void *cbdata) +{ + struct single_tree_ctx *ctx = cbdata; + + if (++ctx->count > 1) + return -1; + + if (!S_ISDIR(mode)) { + ctx->count = 2; + return -1; + } + + ctx->name = xstrdup(pathname); + oidcpy(&ctx->oid, oid); + strbuf_addf(ctx->path, "/%s", pathname); + return 0; +} + +static void write_tree_link(const struct object_id *oid, char *name, + char *rev, struct strbuf *fullpath) +{ + size_t initial_length = fullpath->len; + struct tree *tree; + struct single_tree_ctx tree_ctx = { + .path = fullpath, + .count = 1, + }; + struct pathspec paths = { + .nr = 0 + }; + + oidcpy(&tree_ctx.oid, oid); + + while (tree_ctx.count == 1) { + cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, rev, + fullpath->buf); + + tree = lookup_tree(the_repository, &tree_ctx.oid); + if (!tree) + return; + + free(tree_ctx.name); + tree_ctx.name = NULL; + tree_ctx.count = 0; + + read_tree(the_repository, tree, &paths, single_tree_cb, &tree_ctx); + + if (tree_ctx.count != 1) + break; + + html(" / "); + name = tree_ctx.name; + } + + strbuf_setlen(fullpath, initial_length); +} + +static int ls_item(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, void *cbdata) +{ + struct walk_tree_context *walk_tree_ctx = cbdata; + char *name; + struct strbuf fullpath = STRBUF_INIT; + struct strbuf linkpath = STRBUF_INIT; + struct strbuf class = STRBUF_INIT; + enum object_type type; + unsigned long size = 0; + char *buf; + + name = xstrdup(pathname); + strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "", + ctx.qry.path ? "/" : "", name); + + if (!S_ISGITLINK(mode)) { + type = oid_object_info(the_repository, oid, &size); + if (type == OBJ_BAD) { + htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", + name, + oid_to_hex(oid)); + goto cleanup; + } + } + + html("<tr><td class='ls-mode'>"); + cgit_print_filemode(mode); + html("</td><td>"); + if (S_ISGITLINK(mode)) { + cgit_submodule_link("ls-mod", fullpath.buf, oid_to_hex(oid)); + } else if (S_ISDIR(mode)) { + write_tree_link(oid, name, walk_tree_ctx->curr_rev, + &fullpath); + } else { + char *ext = strrchr(name, '.'); + strbuf_addstr(&class, "ls-blob"); + if (ext) + strbuf_addf(&class, " %s", ext + 1); + cgit_tree_link(name, NULL, class.buf, ctx.qry.head, + walk_tree_ctx->curr_rev, fullpath.buf); + } + if (S_ISLNK(mode)) { + html(" -> "); + buf = read_object_file(oid, &type, &size); + if (!buf) { + htmlf("Error reading object: %s", oid_to_hex(oid)); + goto cleanup; + } + strbuf_addbuf(&linkpath, &fullpath); + strbuf_addf(&linkpath, "/../%s", buf); + strbuf_normalize_path(&linkpath); + cgit_tree_link(buf, NULL, class.buf, ctx.qry.head, + walk_tree_ctx->curr_rev, linkpath.buf); + free(buf); + strbuf_release(&linkpath); + } + htmlf("</td><td class='ls-size'>%li</td>", size); + + html("<td>"); + cgit_log_link("log", NULL, "button", ctx.qry.head, + walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL, + ctx.qry.showmsg, 0); + if (ctx.repo->max_stats) { + html(" "); + cgit_stats_link("stats", NULL, "button", ctx.qry.head, + fullpath.buf); + } + if (!S_ISGITLINK(mode)) { + html(" "); + cgit_plain_link("plain", NULL, "button", ctx.qry.head, + walk_tree_ctx->curr_rev, fullpath.buf); + } + if (!S_ISDIR(mode) && ctx.repo->enable_blame) { + html(" "); + cgit_blame_link("blame", NULL, "button", ctx.qry.head, + walk_tree_ctx->curr_rev, fullpath.buf); + } + html("</td></tr>\n"); + +cleanup: + free(name); + strbuf_release(&fullpath); + strbuf_release(&class); + return 0; +} + +static void ls_head(void) +{ + cgit_print_layout_start(); + html("<table summary='tree listing' class='list'>\n"); + html("<tr class='nohover'>"); + html("<th class='left'>Mode</th>"); + html("<th class='left'>Name</th>"); + html("<th class='right'>Size</th>"); + html("<th/>"); + html("</tr>\n"); +} + +static void ls_tail(void) +{ + html("</table>\n"); + cgit_print_layout_end(); +} + +static void ls_tree(const struct object_id *oid, const char *path, struct walk_tree_context *walk_tree_ctx) +{ + struct tree *tree; + struct pathspec paths = { + .nr = 0 + }; + + tree = parse_tree_indirect(oid); + if (!tree) { + cgit_print_error_page(404, "Not found", + "Not a tree object: %s", oid_to_hex(oid)); + return; + } + + ls_head(); + read_tree(the_repository, tree, &paths, ls_item, walk_tree_ctx); + ls_tail(); +} + + +static int walk_tree(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, void *cbdata) +{ + struct walk_tree_context *walk_tree_ctx = cbdata; + + if (walk_tree_ctx->state == 0) { + struct strbuf buffer = STRBUF_INIT; + + strbuf_addbuf(&buffer, base); + strbuf_addstr(&buffer, pathname); + if (strcmp(walk_tree_ctx->match_path, buffer.buf)) + return READ_TREE_RECURSIVE; + + if (S_ISDIR(mode)) { + walk_tree_ctx->state = 1; + cgit_set_title_from_path(buffer.buf); + strbuf_release(&buffer); + ls_head(); + return READ_TREE_RECURSIVE; + } else { + walk_tree_ctx->state = 2; + print_object(oid, buffer.buf, pathname, walk_tree_ctx->curr_rev); + strbuf_release(&buffer); + return 0; + } + } + ls_item(oid, base, pathname, mode, walk_tree_ctx); + return 0; +} + +/* + * Show a tree or a blob + * rev: the commit pointing at the root tree object + * path: path to tree or blob + */ +void cgit_print_tree(const char *rev, char *path) +{ + struct object_id oid; + struct commit *commit; + struct pathspec_item path_items = { + .match = path, + .len = path ? strlen(path) : 0 + }; + struct pathspec paths = { + .nr = path ? 1 : 0, + .items = &path_items + }; + struct walk_tree_context walk_tree_ctx = { + .match_path = path, + .state = 0 + }; + + if (!rev) + rev = ctx.qry.head; + + if (get_oid(rev, &oid)) { + cgit_print_error_page(404, "Not found", + "Invalid revision name: %s", rev); + return; + } + commit = lookup_commit_reference(the_repository, &oid); + if (!commit || parse_commit(commit)) { + cgit_print_error_page(404, "Not found", + "Invalid commit reference: %s", rev); + return; + } + + walk_tree_ctx.curr_rev = xstrdup(rev); + + if (path == NULL) { + ls_tree(get_commit_tree_oid(commit), NULL, &walk_tree_ctx); + goto cleanup; + } + + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &paths, walk_tree, &walk_tree_ctx); + if (walk_tree_ctx.state == 1) + ls_tail(); + else if (walk_tree_ctx.state == 2) + cgit_print_layout_end(); + else + cgit_print_error_page(404, "Not found", "Path not found"); + +cleanup: + free(walk_tree_ctx.curr_rev); +} diff --git a/www/git.causal.agency/cgit/ui-tree.h b/www/git.causal.agency/cgit/ui-tree.h new file mode 100644 index 00000000..bbd34e35 --- /dev/null +++ b/www/git.causal.agency/cgit/ui-tree.h @@ -0,0 +1,6 @@ +#ifndef UI_TREE_H +#define UI_TREE_H + +extern void cgit_print_tree(const char *rev, char *path); + +#endif /* UI_TREE_H */ diff --git a/www/git.causal.agency/cgitrc b/www/git.causal.agency/cgitrc new file mode 100644 index 00000000..8ccd7c72 --- /dev/null +++ b/www/git.causal.agency/cgitrc @@ -0,0 +1,29 @@ +root-title=causal agency +root-desc=“I think some people from the Gentoo project are behind this.” +logo= + +clone-url=https://$HTTP_HOST/$CGIT_REPO_URL +snapshots=tar.gz zip + +enable-blame=1 +enable-commit-graph=1 +enable-subject-links=1 +enable-follow-links=1 +enable-index-owner=0 +repository-sort=age +branch-sort=age + +css=/custom.css +email-filter=/usr/local/libexec/cgit-email +about-filter=/usr/local/libexec/about-filter +source-filter=/usr/local/libexec/source-filter +owner-filter=/usr/local/libexec/owner-filter + +readme=:README.7 +readme=:README + +remove-suffix=1 +enable-git-config=1 +scan-path=/home/june/pub + +cache-size=1024 diff --git a/www/git.causal.agency/custom.css b/www/git.causal.agency/custom.css new file mode 100644 index 00000000..3bc61c90 --- /dev/null +++ b/www/git.causal.agency/custom.css @@ -0,0 +1,86 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +@import url("cgit.css"); + +* { line-height: 1.25em; } + +div#cgit { + max-width: 117ch; + margin: auto; + font-family: monospace; + -moz-tab-size: 4; + tab-size: 4; +} + +div#cgit table#header td.sub { + border-top: none; +} +div#cgit table#header td.sub.right { + padding-right: 1em; +} +div#cgit table.tabs { + border-bottom: none; +} +div#cgit div.content { + border-bottom: none; +} +div#cgit table.list th a { + color: inherit; +} +div#cgit table.list tr:nth-child(even) { + background: inherit; +} +div#cgit table.list tr:hover { + background: inherit; +} +div#cgit table.list tr.nohover-highlight:hover:nth-child(even) { + background: inherit; +} + +div#cgit table.blob td.linenumbers a:target { + color: goldenrod; + text-decoration: underline; + outline: none; +} + +div#cgit div#summary { + max-width: 80ch; +} + +/* for hilex(1) */ +div#cgit pre .Ke { color: dimgray; } +div#cgit pre .Ma { color: green; } +div#cgit pre .Co { color: navy; } +div#cgit pre .St { color: teal; } +div#cgit pre .Fo { color: teal; font-weight: bold; } +div#cgit pre .Su { color: olive; } + +/* for htagml(1) */ +div#cgit pre a.tag { color: inherit; text-decoration: underline; } +div#cgit pre a.tag:target { color: goldenrod; outline: none; } + +/* for mandoc(1) */ +table.head, table.foot { width: 100%; } +td.head-rtitle, td.foot-os { text-align: right; } +td.head-vol { text-align: center; } +div.Pp { margin: 1ex 0ex; } +div.Nd, div.Bf, div.Op { display: inline; } +span.Pa, span.Ad { font-style: italic; } +span.Ms { font-weight: bold; } +dl.Bl-diag > dt { font-weight: bold; } +code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn, +code.Cd { font-weight: bold; font-family: inherit; } + +h1.Sh { font-size: 1.5em; } +table.Nm td:first-child { padding-right: 1ch; } +code.Fl { white-space: nowrap; } +span.RsT { font-style: italic; } +dl.Bl-tag:not(.Bl-compact) dt { margin-top: 1em; } +ul.Bl-bullet:not(.Bl-compact) li { margin-top: 1em; } +div.Bd-indent { margin-left: 4ch; } +table.Bl-column { width: 100%; } +table.foot { margin-top: 1em; } + +div#cgit a.permalink { color: inherit; } diff --git a/www/git.causal.agency/owner-filter.sh b/www/git.causal.agency/owner-filter.sh new file mode 100644 index 00000000..18e74cf1 --- /dev/null +++ b/www/git.causal.agency/owner-filter.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -eu + +cat <<EOF +<a href="https://liberapay.com/june/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a> +EOF diff --git a/www/git.causal.agency/source-filter.sh b/www/git.causal.agency/source-filter.sh new file mode 100644 index 00000000..514272db --- /dev/null +++ b/www/git.causal.agency/source-filter.sh @@ -0,0 +1,25 @@ +#!/bin/sh +set -eu + +ctags=/usr/bin/ctags +mtags=/usr/local/libexec/mtags +hilex=/usr/local/libexec/hilex +htagml=/usr/local/libexec/htagml + +case "$1" in + (*.[chlmy]|Makefile|*.mk|*.[1-9]|.profile|.shrc|*.sh) + tmp=$(mktemp -d -t source-filter) + trap 'rm -fr "${tmp}"' EXIT + cd "${tmp}" + cat >"$1" + touch tags + case "$1" in + (*.[chlmy]) $ctags -w "$1";; + (*) $mtags "$1";; + esac + $hilex -f html "$1" | $htagml -i "$1" + ;; + (*) + exec $hilex -t -n "$1" -f html + ;; +esac diff --git a/www/temp.causal.agency/.gitignore b/www/temp.causal.agency/.gitignore new file mode 100644 index 00000000..e31ee94e --- /dev/null +++ b/www/temp.causal.agency/.gitignore @@ -0,0 +1 @@ +up diff --git a/www/temp.causal.agency/Makefile b/www/temp.causal.agency/Makefile new file mode 100644 index 00000000..3e908305 --- /dev/null +++ b/www/temp.causal.agency/Makefile @@ -0,0 +1,16 @@ +WEBROOT = /usr/local/www/temp.causal.agency + +CFLAGS += -std=c11 -Wall -Wextra -Wpedantic -I/usr/local/include +LDFLAGS += -static -L/usr/local/lib +LDLIBS = -lkcgihtml -lkcgi -lz -lmd + +up: + +clean: + rm -f up + +install: up + install -m 700 up ${WEBROOT}/up + +uninstall: + rm -f ${WEBROOT}/up diff --git a/www/temp.causal.agency/up.c b/www/temp.causal.agency/up.c new file mode 100644 index 00000000..885b8acd --- /dev/null +++ b/www/temp.causal.agency/up.c @@ -0,0 +1,156 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/capsicum.h> +#include <sys/types.h> +#include <sysexits.h> +#include <time.h> +#include <unistd.h> + +#include <kcgi.h> +#include <kcgihtml.h> + +static int cwd = -1; + +static const struct kvalid Key = { NULL, "file" }; + +static enum kcgi_err head(struct kreq *req, enum khttp http, enum kmime mime) { + return khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[http]) + || khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[mime]); +} + +static enum kcgi_err fail(struct kreq *req, enum khttp http) { + return head(req, http, KMIME_TEXT_PLAIN) + || khttp_body(req) + || khttp_printf(req, "%s\n", khttps[http]); +} + +static enum kcgi_err handle(struct kreq *req) { + if (req->page) return fail(req, KHTTP_404); + + if (req->method == KMETHOD_GET) { + struct khtmlreq html; + struct khtmlreq *h = &html; + return head(req, KHTTP_200, KMIME_TEXT_HTML) + || khttp_body(req) + || khtml_open(h, req, 0) + || khtml_elem(h, KELEM_DOCTYPE) + || khtml_elem(h, KELEM_TITLE) + || khtml_puts(h, "Upload") + || khtml_closeelem(h, 1) + || khtml_attr( + h, KELEM_FORM, + KATTR_METHOD, "post", + KATTR_ACTION, "", + KATTR_ENCTYPE, "multipart/form-data", + KATTR__MAX + ) + || khtml_attr( + h, KELEM_INPUT, + KATTR_TYPE, "file", + KATTR_NAME, Key.name, + KATTR__MAX + ) + || khtml_attr( + h, KELEM_INPUT, + KATTR_TYPE, "submit", + KATTR_VALUE, "Upload", + KATTR__MAX + ) + || khtml_close(h); + + } else if (req->method == KMETHOD_POST) { + struct kpair *field = req->fieldmap[0]; + if (!field || !field->valsz) return fail(req, KHTTP_400); + + char name[256]; + const char *ext = strrchr(field->file, '.'); + if (!ext) ext = ""; + snprintf( + name, sizeof(name), "%jx%08x%s", + (intmax_t)time(NULL), arc4random(), ext + ); + + int fd = openat(cwd, name, O_CREAT | O_EXCL | O_WRONLY, 0644); + if (fd < 0) { + warn("openat"); + return fail(req, KHTTP_507); + } + ssize_t len = write(fd, field->val, field->valsz); + int error = close(fd); + if (len < 0 || error) { + warn("write"); + return fail(req, KHTTP_507); + } + + return head(req, KHTTP_303, KMIME_TEXT_PLAIN) + || khttp_head(req, kresps[KRESP_LOCATION], "/%s", name) + || khttp_body(req) + || khttp_puts(req, name); + + } else { + return fail(req, KHTTP_405); + } +} + +static void sandbox(void) { + cwd = open(".", O_DIRECTORY); + if (cwd < 0) err(EX_CONFIG, "."); + + int error = cap_enter(); + if (error) err(EX_OSERR, "cap_enter"); + + cap_rights_t rights; + cap_rights_init(&rights, CAP_LOOKUP, CAP_CREATE, CAP_PWRITE); + error = cap_rights_limit(cwd, &rights); + if (error) err(EX_OSERR, "cap_rights_limit"); +} + +int main(void) { + const char *page = "up"; + if (khttp_fcgi_test()) { + struct kfcgi *fcgi; + enum kcgi_err error = khttp_fcgi_init(&fcgi, &Key, 1, &page, 1, 0); + if (error) errx(EX_CONFIG, "khttp_fcgi_init: %s", kcgi_strerror(error)); + sandbox(); + for ( + struct kreq req; + KCGI_OK == (error = khttp_fcgi_parse(fcgi, &req)); + khttp_free(&req) + ) { + error = handle(&req); + if (error && error != KCGI_HUP) break; + } + if (error != KCGI_EXIT) { + errx(EX_PROTOCOL, "khttp_fcgi_parse: %s", kcgi_strerror(error)); + } + khttp_fcgi_free(fcgi); + } else { + struct kreq req; + enum kcgi_err error = khttp_parse(&req, &Key, 1, &page, 1, 0); + if (error) errx(EX_PROTOCOL, "khttp_parse: %s", kcgi_strerror(error)); + error = handle(&req); + if (error) errx(EX_PROTOCOL, "%s", kcgi_strerror(error)); + khttp_free(&req); + } +} diff --git a/www/text.causal.agency/.gitignore b/www/text.causal.agency/.gitignore new file mode 100644 index 00000000..8fe3acc9 --- /dev/null +++ b/www/text.causal.agency/.gitignore @@ -0,0 +1,3 @@ +*.txt +feed.atom +igp diff --git a/www/text.causal.agency/001-make.7 b/www/text.causal.agency/001-make.7 new file mode 100644 index 00000000..b4805729 --- /dev/null +++ b/www/text.causal.agency/001-make.7 @@ -0,0 +1,159 @@ +.Dd September 17, 2018 +.Dt MAKE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Using Make +.Nd writing less Makefile +. +.Sh DESCRIPTION +Let's talk about +.Xr make 1 . +I think an important thing to know about +.Xr make 1 +is that you don't need to write a +.Pa Makefile +to use it. +There are default rules +for C, C++ and probably Fortran. +To build +.Pa foo +from +.Pa foo.c , +just run: +. +.Pp +.Dl make foo +. +.Pp +The default rule for C files uses the +.Ev CFLAGS +variable, +so you can set that in the environment +to pass flags to the C compiler: +. +.Pp +.Dl CFLAGS=-Wall make foo +. +.Pp +It also uses +.Ev LDLIBS +for linking, +so you can add libraries with: +. +.Pp +.Dl LDLIBS=-lcurses make foo +. +.Pp +Obviously writing this every time +would become tedious, +so it might be time to write a +.Pa Makefile . +But it really doesn't need much: +. +.Bd -literal -offset indent +CFLAGS += -Wall -Wextra +LDLIBS = -lcurses + +foo: +.Ed +. +.Pp +Assigning +.Ev CFLAGS +with +.Ql += +preserves the system default +or anything passed in the environment. +Declaring +.Pa foo +as the first rule +makes it the default when +.Ql make +is run without a target. +Note that the rule doesn't need a definition; +the default will still be used. +. +.Pp +If +.Pa foo +is built from serveral source files, +unfortunately a rule definition is required: +. +.Bd -literal -offset indent +OBJS = foo.o bar.o baz.o + +foo: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@ +.Ed +. +.Pp +This rule uses +.Ev LDFLAGS +for passing linker flags, +which is what the default rule does. +The +.Ql $@ +variable here expands to +.Ql foo , +so this rule can be copied easily +for other binary targets. +. +.Pp +If some sources depend on a header file, +they can be automatically rebuilt +when the header changes +by declaring a dependency rule: +. +.Pp +.Dl foo.o bar.o: foo.h +. +.Pp +Note that several files can appear +either side of the +.Ql ":" . +. +.Pp +Lastly, +it's always nice to add a +.Cm clean +target: +. +.Bd -literal -offset indent +clean: + rm -f $(OBJS) foo +.Ed +. +.Pp +I hope this helps getting started with +.Xr make 1 +without writing too much +.Pa Makefile ! +. +.Sh EXAMPLES +The example +.Pa Makefile +in its entirety: +. +.Bd -literal -offset indent +CFLAGS += -Wall -Wextra +LDLIBS = -lcurses +OBJS = foo.o bar.o baz.o + +foo: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@ + +foo.o bar.o: foo.h + +clean: + rm -f $(OBJS) foo +.Ed +. +.Sh AUTHORS +.An Mt june@causal.agency +. +.Pp +This document is produced from +.Xr mdoc 7 +source available from +.Lk https://git.causal.agency/src/tree/www/text.causal.agency diff --git a/www/text.causal.agency/002-writing-mdoc.7 b/www/text.causal.agency/002-writing-mdoc.7 new file mode 100644 index 00000000..b377d364 --- /dev/null +++ b/www/text.causal.agency/002-writing-mdoc.7 @@ -0,0 +1,138 @@ +.Dd September 27, 2018 +.Dt WRITING-MDOC 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Writing mdoc +.Nd semantic markup +. +.Sh DESCRIPTION +I recently learned how to write man pages +so that I could document +a bunch of little programs I've written. +Modern man pages are written in +.Xr mdoc 7 , +whose documentation is also available from +.Lk http://mandoc.bsd.lv . +. +.Pp +.Xr mdoc 7 +differs from many other markup languages +by providing +.Dq semantic markup +rather than just +.Dq physical markup. +What this means is that +the markup indicates what something is, +not how to format it. +For example, +the +.Ql \&Ar +macro is used to indicate +command-line arguments +rather than one of the macros +for bold, italic or underline. +This frees each author of having to choose +and enables consistent presentation +across different man pages. +. +.Pp +Another advantage of semantic markup +is that information can be extracted from it. +For example, +.Xr makewhatis 8 +can easily extract the name and short description +from each man page +thanks to the +.Ql \&Nm +and +.Ql \&Nd +macros. +I use the same information +to generate an Atom feed for these documents, +though in admittedly a much less robust way than +.Xr mandoc 1 . +. +.Pp +When it comes to actually writing +.Xr mdoc 7 , +it can take some getting used to. +The language is of +.Xr roff 7 +lineage +so its syntax is very particular. +Macros cannot appear inline, +but must start on new lines +beginning with +.Ql \&. . +Sentences should likewise +always start on a new line. +Since I'm in the habit of writing with +semantic line breaks, +I actually find these requirements +fit in well. +. +.Pp +The more frustrating syntax limitation to me +is the rule against empty lines. +Without them, +it can be quite difficult to edit a lengthy document. +Thankfully, +lines with only a +.Ql \&. +on them are allowed, +but this still causes visual noise. +To alleviate that, +I have a +.Xr vim 1 +syntax file for +.Xr mdoc 7 +which conceals the lone dots: +. +.Bd -literal -offset indent +if exists("b:current_syntax") + finish +endif + +runtime! syntax/nroff.vim +unlet! b:current_syntax + +setlocal sections+=ShSs +syntax match mdocBlank /^\\.$/ conceal +setlocal conceallevel=2 + +let b:current_syntax = "mdoc" +.Ed +. +.Pp +It also adds the +.Xr mdoc 7 +section header and subsection header macros to the +.Cm sections +option to make +.Xr vim 1 Ap s +.Ic { +and +.Ic } +motions +aware of them. +. +.Pp +With that, +I've found writing man pages pleasant and rewarding. +I've started writing other documents with +.Xr mdoc 7 +as well, +as you can see here. +. +.Sh SEE ALSO +.Lk http://rhodesmill.org/brandon/2012/one-sentence-per-line/ "Semantic Linefeeds" +. +.Sh AUTHORS +.An Mt june@causal.agency +. +.Pp +This document is produced from +.Xr mdoc 7 +source available from +.Lk https://git.causal.agency/src/tree/www/text.causal.agency diff --git a/www/text.causal.agency/003-pleasant-c.7 b/www/text.causal.agency/003-pleasant-c.7 new file mode 100644 index 00000000..16030b7e --- /dev/null +++ b/www/text.causal.agency/003-pleasant-c.7 @@ -0,0 +1,120 @@ +.Dd September 30, 2018 +.Dt PLEASANT-C 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Pleasant C +.Nd it's good, actually +. +.Sh DESCRIPTION +I've been writing a lot of C lately +and actually find it very pleasant. +I want to talk about some of its ergonomic features. +These are C99 features unless otherwise noted. +. +.Ss Initializer syntax +Struct and union initializer syntax +is well generalized. +Designators can be chained, +making initializing nested structs easy, +and all uninitialized fields are zeroed. +. +.Bd -literal -offset indent +struct { + struct pollfd fds[2]; +} loop = { + .fds[0].fd = STDIN_FILENO, + .fds[1].fd = STDOUT_FILENO, + .fds[0].events = POLLIN, + .fds[1].events = POLLOUT, +}; +.Ed +. +.Ss Variable-length arrays +VLAs can be multi-dimensional, +which can avoid manual stride multiplications +needed to index a flat +.Xr malloc 3 Ap d +array. +. +.Bd -literal -offset indent +uint8_t glyphs[len][height][width]; +fread(glyphs, height * width, len, stdin); +.Ed +. +.Ss Incomplete array types +The last field of a struct can be an +.Dq incomplete +array type, +which means it doesn't have a length. +A variable amount of space for the struct can be +.Xr malloc 3 Ap d , +or the struct can be used as +a sort of pointer with fields. +. +.Bd -literal -offset indent +struct Line { + enum Filter type; + uint8_t data[]; +} *line = &png.data[1 + lineSize()]; +.Ed +. +.Ss Anonymous struct and union fields (C11) +Members of structs or unions +which are themselves structs or unions +can be unnamed. +In that case, +each of the inner fields +is treated as a member of the outer struct or union. +This makes working with tagged unions nicer. +. +.Bd -literal -offset indent +struct Message { + enum { Foo, Bar } type; + union { + uint8_t foo; + uint32_t bar; + }; +} msg = { .type = Foo, .foo = 0xFF }; +.Ed +. +.Ss Static assert (C11) +Assertions can be made at compile time. +Most useful for checking sizes of structs. +. +.Bd -literal -offset indent +static_assert(13 == sizeof(struct PNGHeader), "PNG IHDR size"); +.Ed +. +.Ss Leading-break switch +This one is just an odd style choice +I came across that C happens to allow. +To prevent accidental fall-through +in switch statements, +you can put breaks before the case labels. +. +.Bd -literal -offset indent +while (0 < (opt = getopt(argc, argv, "h:w:"))) { + switch (opt) { + break; case 'h': height = optarg; + break; case 'w': width = optarg; + break; default: return EX_USAGE; + } +} +.Ed +. +.Sh AUTHORS +.An Mt june@causal.agency +. +.Pp +This document is produced from +.Xr mdoc 7 +source available from +.Lk https://git.causal.agency/src/tree/www/text.causal.agency +. +.Sh CAVEATS +This isn't meant to be advice. +It's just how I like to write C, +and I don't +.Dq ship +software in C. diff --git a/www/text.causal.agency/004-uloc.7 b/www/text.causal.agency/004-uloc.7 new file mode 100644 index 00000000..edd78d80 --- /dev/null +++ b/www/text.causal.agency/004-uloc.7 @@ -0,0 +1,64 @@ +.Dd December 14, 2018 +.Dt ULOC 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm ULOC +.Nd unique lines of code +. +.Sh DESCRIPTION +There are many tools available +which measure SLOC: source lines of code. +These tools are strangely complex +for what they intend to do, +which is to estimate the relative sizes of projects. +They perform some amount of parsing +in order to discount comments in various languages, +and for reasons unknown each format their ouput +in some oddly encumbered way. +. +.Pp +I propose a much simpler method +of estimating relative sizes of projects: +unique lines of code. +ULOC can be calculated with standard tools as follows: +. +.Bd -literal -offset indent +sort -u *.h *.c | wc -l +.Ed +. +.Pp +In my opinion, +the number this produces +should be a better estimate of +the complexity of a project. +Compared to SLOC, +not only are blank lines discounted, +but so are close-brace lines +and other repetitive code +such as common includes. +On the other hand, +ULOC counts comments, +which require just as much maintenance +as the code around them does, +while avoiding inflating the result +with license headers which appear in every file, +for example. +. +.Pp +It can also be amusing +to read all of your code sorted alphabetically. +. +.Sh AUTHORS +.An Mt june@causal.agency +. +.Pp +This document is produced from +.Xr mdoc 7 +source available from +.Lk https://git.causal.agency/src/tree/www/text.causal.agency +. +.Sh CAVEATS +Estimates such as these +should not be used for decision making +as if they were data. diff --git a/www/text.causal.agency/005-testing-c.7 b/www/text.causal.agency/005-testing-c.7 new file mode 100644 index 00000000..d0c636ff --- /dev/null +++ b/www/text.causal.agency/005-testing-c.7 @@ -0,0 +1,73 @@ +.Dd December 21, 2018 +.Dt TESTING-C 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Testing C +.Nd a simple unit testing setup +. +.Sh DESCRIPTION +This is a simple approach +to unit testing in C +that I've used in a couple projects. +At the bottom of a C file +with some code I want to test, +I add: +. +.Bd -literal -offset indent +#ifdef TEST +#include <assert.h> + +int main(void) { + assert(...); + assert(...); +} + +#endif +.Ed +. +.Pp +This file normally produces a +.Pa .o +to be linked into the main binary. +For testing, +I produce separate binaries +and run them with +.Xr make 1 : +. +.Bd -literal -offset indent +TESTS = foo.t bar.t + +\&.SUFFIXES: .t + +\&.c.t: + $(CC) $(CFLAGS) -DTEST $(LDFLAGS) $< $(LDLIBS) -o $@ + +test: $(TESTS) + set -e; $(TESTS:%=./%;) +.Ed +. +.Pp +Note that the test binaries +aren't linked with the rest of the code, +so there is potential for simple stubbing or mocking. +. +.Pp +To get the best output +from C's simple +.Xr assert 3 , +it's best to assert the result +of a helper function +which takes the expected output +and the test input, +rather than calling +.Xr assert 3 +inside the helper function. +This way, +the message printed by the assert failure +contains a useful line number +and the expected output +rather than just variable names. +. +.Sh AUTHORS +.An Mt june@causal.agency diff --git a/www/text.causal.agency/006-some-libs.7 b/www/text.causal.agency/006-some-libs.7 new file mode 100644 index 00000000..5af65404 --- /dev/null +++ b/www/text.causal.agency/006-some-libs.7 @@ -0,0 +1,96 @@ +.Dd December 11, 2019 +.Dt SOME-LIBS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Some Libraries +.Nd good ones +. +.Sh DESCRIPTION +This is a little list of C libraries +I've had good experiences using. +. +.Bl -tag -width Ds +.It Fl lcurl +The library behind the +.Xr curl 1 +command. +It downloads or uploads things on the internet +through a number of protocols, +not just HTTP. +It has an easy-to-use library API, +appropriately named +.Xr libcurl-easy 3 . +I've used it to implement a +.Lk https://causal.agency/bin/title.html "page title fetcher" . +. +.It Fl lcurses +Okay so this one really isn't great. +Its interfaces can seem archaic +and its documentation is often poor. +However, it gets the job done +and is commonly available pretty much everywhere. +Interesting to note that +.Nx +uses its own implementation of curses +that is not GNU ncurses, +unlike +.Fx . +. +.It Fl ledit +This is a BSD line editing library, +similar to GNU readline. +It supports right-aligned prompts, +which I prefer for variable-length +information in shells. +. +.It Fl lkcgi +A CGI and FastCGI library +for web applications in C. +Don't worry, +it isolates HTTP parsing and input validation +from application logic +in sandboxed processes. +I think it's an excellent example +of how to design an API for C. +I used it to implement the +.Lk https://ascii.town/explore.html "torus web viewer" . +. +.It Fl lsqlite3 +An embedded relational database engine. +It's amazing what you can do with this, +and it's super easy to use! +My one gripe with it is that the library and SQL documentation +are not available as +.Xr man 1 +pages. +I'm currently working on a project using SQLite, +but it hasn't gotten very far yet. +. +.It Fl ltls +This is a new library in LibreSSL +which provides a much simpler interface for TLS sockets +compared to +.Fl lssl . +It's much more like what you'd expect +from other TLS socket wrappers, +with calls like +.Xr tls_connect 3 , +.Xr tls_read 3 +and +.Xr tls_write 3 . +I've used this for IRC clients, bouncers and bots. +. +.It Fl lz +An implementation of the DEFLATE compression algorithm +and gzip format. +It's all documented in comments in +.In zlib.h , +which isn't bad, +but for my own use I copied the docs into +.Lk https://code.causal.agency/june/zlib-man-pages "manual pages" . +I've used this for decoding and encoding PNG images. +.El +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/007-cgit-setup.7 b/www/text.causal.agency/007-cgit-setup.7 new file mode 100644 index 00000000..44fb436a --- /dev/null +++ b/www/text.causal.agency/007-cgit-setup.7 @@ -0,0 +1,271 @@ +.Dd December 15, 2019 +.Dt CGIT-SETUP 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm cgit setup +.Nd configuration notes +. +.Sh DESCRIPTION +I just set up cgit on +.Lk https://git.causal.agency +to replace an instance of gitea. +After 30 days of uptime, +gitea had accumulated over 11 hours of CPU time +and was using hundreds of megabytes of memory. +cgit is much more lightweight +and much more in line with my aesthetic. +I'm documenting how I set it up here +mostly to remind myself in the future. +. +.Ss slowcgi +cgit is CGI software, +but +.Xr nginx 8 +only supports FastCGI. +I used +.Xr slowcgi 8 +as a compatibility layer +by adding the following to +.Pa /etc/rc.conf : +.Bd -literal -offset indent +slowcgi_enable="YES" +slowcgi_flags="-p / -s /var/run/slowcgi.sock" +.Ed +. +.Ss nginx +I added the following in a new +.Cm server +block to +.Pa /usr/local/etc/nginx/nginx.conf : +.Bd -literal -offset indent +root /usr/local/www/cgit; +location / { + try_files $uri @cgit; +} +location @cgit { + fastcgi_pass unix:/var/run/slowcgi.sock; + fastcgi_param SCRIPT_FILENAME $document_root/cgit.cgi; + fastcgi_param SCRIPT_NAME /; + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $query_string; + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param CONTENT_TYPE $content_type; + fastcgi_param CONTENT_LENGTH $content_length; + fastcgi_param HTTPS $https if_not_empty; + fastcgi_param SERVER_PORT $server_port; + fastcgi_param SERVER_NAME $server_name; +} +.Ed +. +.Pp +The +.Cm try_files +directive causes +.Xr nginx 8 +to first try to serve static files from +.Pa /usr/local/www/cgit +before passing anything else on to FastCGI. +. +.Pp +The +.Va SCRIPT_FILENAME +parameter tells +.Xr slowcgi 8 +the path of the CGI binary to run. +Setting +.Va SCRIPT_NAME +to +.Pa / +tells cgit its root URL +and avoids it using query strings for everything. +. +.Ss cgit +cgit doesn't provide any configuration to start from, +so you have to just read +.Xr cgitrc 5 . +I added the following to +.Pa /usr/local/etc/cgitrc : +.Bd -literal -offset indent +cache-size=1024 +clone-url=https://$HTTP_HOST/$CGIT_REPO_URL +snapshots=tar.gz zip +remove-suffix=1 +enable-git-config=1 +scan-path=/home/june/pub +.Ed +. +.Pp +The +.Cm cache-size +option enables caching, +which by default is stored in +.Pa /var/cache/cgit , +so I made sure that directory exists +and is writable by the +.Sy www +user. +The +.Cm clone-url +option sets the clone URL to advertise. +cgit will automatically serve git over HTTP. +The +.Cm snapshots +option makes tarballs available for tags and commits. +. +.Pp +The +.Cm scan-path +option causes cgit to scan the given path +for git repositories. +I'm putting mine in +.Pa ~/pub . +The +.Cm remove-suffix +option causes cgit to remove the +.Pa .git +suffix from the URLs it uses +for the repositories it finds, +so that +.Pa ~/pub/pounce.git +is served at +.Pa /pounce . +The +.Cm enable-git-config +option allows controlling some cgit options +from the +.Xr git-config 1 +of each repository. +See +.Sx git +below. +. +.Pp +I also set up a filter to render +.Xr mdoc 7 +files +and do syntax highlighting +by adding the following to +.Pa cgitrc : +.Bd -literal -offset indent +readme=:README.7 +readme=:README +about-filter=/usr/local/libexec/cgit-filter +source-filter=/usr/local/libexec/cgit-filter +.Ed +. +.Pp +The +.Cm readme +options tell cgit which files to look for +to render the +.Dq about +page. +The colon prefix causes it to look for them +in the git tree. +The +.Pa /usr/local/libexec/cgit-filter +script contains the following: +.Bd -literal -offset indent +#!/bin/sh +case "$1" in + (*.[1-9]) + /usr/bin/mandoc -T utf8 | /usr/local/libexec/ttpre + ;; + (*) + exec /usr/local/libexec/hi -t -n "$1" -f html -o anchor + ;; +esac +.Ed +. +.Pp +Filter scripts are run with the filename as their first argument +and the contents of the file on standard input. +The +.Xr ttpre 1 +command is my own utility to convert +.Xr man 1 +output to HTML. +The +.Xr hi 1 +command is my own +.Lk https://causal.agency/bin/hi.html "syntax highlighter" . +. +.Ss git +I create my repositories in +.Pa ~/pub +with +.Ql git init --bare +and use +.Pa git.causal.agency:pub/example.git +locally as the remote. +Descriptions are set by editing the +.Pa description +file in each repository. +The section and homepage can be set with +.Xr git-config 1 +through the keys +.Cm cgit.section +and +.Cm cgit.homepage , +respectively, +thanks to the +.Cm enable-git-config +option above. +. +.Ss Redirects +I added the following to the +.Cm server +block that used to serve gitea in +.Pa nginx.conf : +.Bd -literal -offset indent +location ~* /june/([^.]+)[.]git(.*) { + return 301 https://git.causal.agency/$1$2?$query_string; +} +location ~* /june/([^/]+) { + return 301 https://git.causal.agency/$1; +} +location / { + return 301 https://git.causal.agency; +} +.Ed +. +.Pp +This redirects any links to my gitea repos +to the corresponding repo in cgit. +The first +.Sy location +block also redirects gitea HTTP clone URLs to cgit +so that +.Xr git-pull 1 +continues to work on existing clones. +. +.Ss Update: fast HTTPS clones +Someone pointed out that cloning my repos +over HTTPS was incredibly slow, +and this is because cgit only implements the +.Dq dumb +HTTP git transport. +To speed up cloning, +I send the URLs used by the +.Dq smart +HTTP transport to +.Xr git-http-backend 1 +instead: +.Bd -literal -offset indent +location ~ /.+/(info/refs|git-upload-pack) { + fastcgi_pass unix:/var/run/slowcgi.sock; + fastcgi_param SCRIPT_NAME /usr/local/libexec/git-core/git-http-backend; + fastcgi_param GIT_HTTP_EXPORT_ALL 1; + fastcgi_param GIT_PROJECT_ROOT /home/june/pub; + include fastcgi_params; +} +.Ed +. +.Pp +I factored out the FastCGI parameters +I'm using with cgit +to be included here as well. +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/008-how-irc.7 b/www/text.causal.agency/008-how-irc.7 new file mode 100644 index 00000000..aba1bbf9 --- /dev/null +++ b/www/text.causal.agency/008-how-irc.7 @@ -0,0 +1,193 @@ +.Dd March 8, 2020 +.Dt HOW-IRC 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm How I Relay Chat +.Nd in code +. +.Sh DESCRIPTION +I've been writing a lot of IRC software lately +.Pq Sx SEE ALSO , +and developed some nice code patterns +that I've been reusing. +Here they are. +. +.Ss Parsing +I use fixed size buffers almost everywhere, +so it's necessary to know IRC's size limits. +A traditional IRC message is a maximum of 512 bytes, +but the IRCv3 message-tags spec adds +(unreasonably, in my opinion) +8191 bytes for tags. +IRC messages also have a maximum of 15 command parameters. +.Bd -literal -offset indent +enum { MessageCap = 8191 + 512 }; +enum { ParamCap = 15 }; +.Ed +. +.Pp +If I'm using tags, +I'll use X macros +to declare the set I care about. +X macros are a way of maintaining parallel arrays, +or in this case an enum and an array. +.Bd -literal -offset indent +#define ENUM_TAG \e + X("msgid", TagMsgid) \e + X("time", TagTime) + +enum Tag { +#define X(name, id) id, + ENUM_TAG +#undef X + TagCap, +}; + +static const char *TagNames[TagCap] = { +#define X(name, id) [id] = name, + ENUM_TAG +#undef X +}; +.Ed +. +.Pp +The TagNames array is used by the parsing function +to assign tag values into the message structure, +which looks like this: +.Bd -literal -offset indent +struct Message { + char *tags[TagCap]; + char *nick; + char *user; + char *host; + char *cmd; + char *params[ParamCap]; +}; +.Ed +. +.Pp +I'm a fan of using +.Xr strsep 3 +for simple parsing. +Although it modifies its input +(replacing delimiters with NUL terminators), +since the raw message is in a static buffer, +it is ideal for so-called zero-copy parsing. +I'm not going to include the whole parsing function here, +but I will at least include the part that many get wrong, +which is dealing with the colon-prefixed trailing parameter: +.Bd -literal -offset indent +msg.cmd = strsep(&line, " "); +for (int i = 0; line && i < ParamCap; ++i) { + if (line[0] == ':') { + msg.params[i] = &line[1]; + break; + } + msg.params[i] = strsep(&line, " "); +} +.Ed +. +.Ss Handling +To handle IRC commands and replies +I add handler functions to a big array. +I usually have some form of helper as well +to check the number of expected parameters. +.Bd -literal -offset indent +typedef void HandlerFn(struct Message *msg); + +static const struct Handler { + const char *cmd; + HandlerFn *fn; +} Handlers[] = { + { "001", handleReplyWelcome }, + { "PING", handlePing }, + { "PRIVMSG", handlePrivmsg }, +}; +.Ed +. +.Pp +Since I keep these arrays sorted anyway, +I started using the standard +.Xr bsearch 3 +function, +but a basic for loop probably works just as well. +I do wish I could compile-time assert +that the array really is sorted, though. +.Bd -literal -offset indent +static int compar(const void *cmd, const void *_handler) { + const struct Handler *handler = _handler; + return strcmp(cmd, handler->cmd); +} + +void handle(struct Message msg) { + if (!msg.cmd) return; + const struct Handler *handler = bsearch( + msg.cmd, + Handlers, ARRAY_LEN(Handlers), + sizeof(*handler), compar + ); + if (handler) handler->fn(&msg); +} +.Ed +. +.Ss Capabilities +For IRCv3 capabilties +I use X macros again, +this time with another handy macro +for declaring bit flag enums. +.Bd -literal -offset indent +#define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit + +#define ENUM_CAP \e + X("message-tags", CapMessageTags) \e + X("sasl", CapSASL) \e + X("server-time", CapServerTime) + +enum Cap { +#define X(name, id) BIT(id), + ENUM_CAP +#undef X +}; + +static const char *CapNames[] = { +#define X(name, id) [id##Bit] = name, + ENUM_CAP +#undef X +}; +.Ed +. +.Pp +The +.Fn BIT +macro declares, for example, +.Dv CapSASL +as the bit flag and +.Dv CapSASLBit +as the corresponding index. +The +.Vt "enum Cap" +is used as a set, +for example checking if SASL is enabled with +.Ql caps & CapSASL . +. +.Pp +These patterns are serving my IRC software well, +and my IRC projects are serving me well. +It is immensely satisfying +to be (near) constantly using software +that I wrote myself and am happy with, +regardless of how niche it may be. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://git.causal.agency/pounce/about "IRC bouncer" +.It +.Lk https://git.causal.agency/litterbox/about "IRC logger" +.It +.Lk https://git.causal.agency/catgirl/about "IRC client" +.El +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/009-casual-update.7 b/www/text.causal.agency/009-casual-update.7 new file mode 100644 index 00000000..0548436a --- /dev/null +++ b/www/text.causal.agency/009-casual-update.7 @@ -0,0 +1,127 @@ +.Dd May 6, 2020 +.Dt CASUAL-UPDATE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm casual update +.Nd software developments +. +.Sh DESCRIPTION +I've been figuring out more of IMAP +and Internet Messages in general +while working on a new project +so I've revisited some older ones. +I've copied my somewhat more proper +IMAP parsing code into them, +so they should be more robust. +. +.Pp +.Xr imbox 1 +is my tool to export messages +in mboxrd format directly from IMAP. +It's mostly for applying patches sent by email +without having any kind of local mail setup. +For that, +it includes the +.Xr git-fetch-email 1 +wrapper which works very similarly to +.Xr git-send-email 1 . +I learned by reading the source of +.Xr git-subtree 1 +that +.Xr git-rev-parse 1 +can be used by shell scripts +to parse long options, +so I added those. +I also added the +.Fl Fl apply +flag to automatically pipe to +.Xr git-am 1 +with the right flags for mboxrd. +. +.Pp +.Xr notemap 1 +is a tool for mirroring text files +to an IMAP Notes mailbox, +which is used by FastMail's web UI +and the macOS/iOS Notes app. +Its original parsing code +was particularly ad-hoc. +Since I've now learned +how UTF-8 headers are encoded, +I updated it to properly encode +the file name in the Subject line. +. +.Pp +I also got distracted by +a conversation about UNIX-domain sockets +where I was comparing the macOS and FreeBSD +.Xr unix 4 +pages and the Linux +.Xr unix 7 +page. +This lead me to make +.Xr exman 1 , +a tool to locally install and read +manual pages for Linux, POSIX, +.Fx , +.Nx +and +.Ox . +I've already gotten quite a bit of use out of it. +. +.Pp +In yet another IRC distraction, +I was talking about some further plans for my IRC software, +and realized it might be time to write +my future projects list down. +I opened a +.Pa .plan +file, +immediately wondered how anyone can write plain text, +then switched to a +.Pa plan.7 +file. +There's nothing I won't use +.Xr mdoc 7 +for. +After a little setup, +I can now be fingered, +and make jokes about this silly little protocol +from the days of old. +.Xr finger 1 Ap s +default output fills me with joy: +.Bd -unfilled -offset indent +No Mail. +No Plan. +.Ed +. +.Pp +And speaking of IRC and plans, +I've been meaning to tag +.Xr catgirl 1 +version 1.0 for a while now. +I've been using it as my main client +and my commits to it have really slowed down. +When I do tag it, +I'm planning on writing another post +about my whole +.Dq suite +of IRC software +and how the parts work together. +Watch this space. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://git.causal.agency/imbox "imbox" +.It +.Lk https://git.causal.agency/notemap "notemap" +.It +.Lk https://git.causal.agency/exman "exman" +.It +.Lk https://git.causal.agency/catgirl "catgirl" +.El +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/010-irc-suite.7 b/www/text.causal.agency/010-irc-suite.7 new file mode 100644 index 00000000..515a30ab --- /dev/null +++ b/www/text.causal.agency/010-irc-suite.7 @@ -0,0 +1,409 @@ +.Dd June 19, 2020 +.Dt IRC-SUITE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm IRC suite +.Nd my own IRC software +. +.Sh DESCRIPTION +Over the past months +.Po +eight of them, according to +.Xr git-log 1 +.Pc +I developed a new +.Dq suite +of IRC software +that I now use full-time, +consisting of a bouncer, +a new logging and search solution, +and a terminal client. +These new programs share some characteristics: +they are all TLS-only +and use the libtls API from LibreSSL, +they can all be entirely configured from the command line +or with equivalent configuration files, +they are all designed as +a one process to one IRC connection mapping, +and they all take advantage of IRCv3 features. +. +.Pp +For context, +I was previously running +the znc IRC bouncer +and using the Textual IRC client +with its plain text logs. +I also continue to use +the Palaver IRC client for iOS. +. +.Ss Background +A bouncer is a piece of server software +that stays connected to IRC at all times +and acts as a relay +between your client and the IRC server. +When the client is disconnected, +the bouncer buffers incoming messages +to send to the client when it reconnects. +. +.Pp +Aside from this, +bouncers have another advantage: +client multiplexing. +Several clients, +for instance on different computers +or a phone, +should be able to connect to the same bouncer, +and send and receive messages under the same nick. +Unfortunately, +znc does not handle this use-case well at all. +Out of the box it offers two options: +either any client connection totally clears the buffer, +causing other clients to miss chat history; +or the buffer is never cleared, +causing every client connection +to be repeatedly spammed with redundant history. +There is also a znc wiki page +that suggests one way to solve this issue +is to connect znc to itself multiple times over. +Yikes. +. +.Ss pounce +My dissatisfaction with +connecting multiple clients to znc +directly motivated me to start working +on a new multi-client-focused IRC bouncer. +The result is +.Xr pounce 1 , +based on a rather straightforward +single-producer (the IRC server) +multiple-consumer (each IRC client) +ring buffer. +Each client has its own +independent position in the buffer +so can properly be brought up to date +whenever it connects. +. +.Pp +Additionally, +by assuming support for the IRCv3 +.Sy server-time +extension, +all IRC events can be accurately +relayed to clients at any time, +and the internals of +.Xr pounce 1 +can be kept very simple. +In fact, +it completely avoids parsing most IRC messages, +simply pushing them into the buffer +with an associated timestamp. +. +.Pp +The usernames sent by clients during registration +are used as opaque identifiers for buffer consumers. +This was chosen since most clients +can be configured to send an arbitrary username, +and those that don't often default +to the name of the client itself, +making it an appropriate identifier. +. +.Pp +Later, +I added a way for clients +to be informed of their own buffer positions +using a vendor-specific IRCv3 capability. +This means a client +can save the position +of the last message it actually received, +and request to set its position +when it reconnects, +ensuring no messages are lost +to network issues +or software crashes. +. +.Ss calico +Due to the simple design of mapping +one process to one IRC (server) connection, +it is necessary to run several instances of +.Xr pounce 1 . +Initially I simply used different ports for each, +but as I connected to more networks +and even ran some instances for friends, +it became less feasible. +. +.Pp +The solution I came up with +was to dispatch incoming connections +using Server Name Indication, or SNI. +This way, +multiple domains pointing to the same host +could be used with only one port +to connect to different instances of +.Xr pounce 1 . +For example, +I use a +.Li *.irc.causal.agency +wildcard DNS entry +and a subdomain for each IRC network, +all on port 6697. +. +.Pp +The +.Xr calico 1 +daemon included with pounce +accomplishes this dispatch +using the +.Dv MSG_PEEK +flag of +.Xr recvmsg(2) +on incoming connections. +Since SNI is immediately sent by TLS clients +as part of the ClientHello message in clear-text, +it can be processed +without doing any actual TLS. +The connection itself is then +sent to the corresponding +.Xr pounce 1 +instance +over UNIX-domain socket, +which handles TLS as normal. +This means that +.Xr calico 1 +and +.Xr pounce 1 +operate entirely independently of each other. +. +.Ss litterbox +Based on the multiple-consumer ring buffer design, +I realized it would be easy +to implement additional functionality +as independent purpose-built clients +which connect to +.Xr pounce 1 +alongside regular clients. +This could allow dedicated OTR or DCC software +to operate in parallel with a basic client, +or for more passive software +to provide notifications +or dedicated logging. +. +.Pp +For the latter, +I wanted to do better than +plain text log files. +.Xr grep 1 +over files works fine, +but search could be faster and smarter, +and the text format is +more lossy and less structured +than I'd like it to be. +Conveniently, +SQLite provides an extension +(actually two) +for full-text search. +. +.Pp +The litterbox project +is my dedicated logging solution +using SQLite FTS5. +It consists of three tools: +the +.Xr litterbox 1 +daemon itself which connects to pounce +and logs messages to SQLite, +the +.Xr scoop 1 +command line query tool, +and the +.Xr unscoop 1 +plain text import tool. +The +.Xr scoop 1 +tool constructs SQL queries +and formats the results for viewing, +with coloured nicks +and piped to a pager +by default. +. +.Pp +The +.Xr litterbox 1 +daemon +can also provide a simple +.Dq online +.Pq over IRC +search query interface +to other connected clients. +The simplest way to allow different +.Xr pounce 1 +clients to talk to each other +was to route private messages to self +internally without sending them to the IRC server. +So from any client +I can simply message myself +a full-text search query +and +.Xr litterbox 1 +responds with the results. +. +.Pp +Along with routing self-messages, +.Xr pounce 1 +also provides a vendor-specific IRCv3 capability +for passive clients such as +.Xr litterbox 1 +to indicate that they should not influence +the automatic away status, +which is normally only set +when no clients are connected. +. +.Pp +An advantage of this architecture +of dedicated clients +rather than bouncer modules +is that they need not run +on the same host. +I run my bouncers on a VPS, +but I'd rather not store my private logs there, +so +.Xr litterbox 1 +runs instead on a Raspberry Pi +in my apartment. +Also, +since it is essentially +just a regular IRC bot, +it could be used independently +for keeping public logs for a channel. +. +.Ss catgirl +There's not really that much to say +about the client, +.Xr catgirl 1 . +Of the three projects +it contains the most code +but is also the least interesting, +in my opinion. +It just does what I want a client to do, +and gets the details right. +. +.Pp +Tab complete is ordered by most recently seen or used, +and completing several nicks +inserts commas between them +as well as the colon following the final nick. +In the input line, +the prompt is updated +to reflect whether the input +will be interpreted as a command or as a message. +Messages are automatically scanned for URLs, +which can be opened or copied with commands +specifying the nick or a substring of the URL. +. +.Pp +Scrolling in a window creates a split view, +keeping the latest messages visible. +Nick colours are based instead on usernames, +keeping them more stable across renames, +and mentions in messages are coloured +to make the conversation easier to follow. +The visibility of ignored messages +can be toggled at any time. +Channels can be muted +so their activity is hidden +from the status line +unless you are pinged. +. +.Pp +.Xr catgirl 1 +is configured entirely on the command line +or in equivalent simple configuration files. +There's no dynamic manipulation of state +using complex +.Ql / +commands like in some other clients. +. +.Pp +The major caveat is that +.Xr catgirl 1 +connects to only one network at a time. +This keeps the configuration, the interface +and the code much simpler. +.Xr tmux 1 , +.Xr screen 1 +or a tabbed terminal emulator +are good options to run several instances. +. +.Pp +If you're interested in giving +.Xr catgirl 1 +a quick (and necessarily limited) try, +you can +.Li ssh chat@ascii.town . +. +.Ss Future +I think I'm done with IRC software for now. +As mentioned above, +there are a few more pieces +that could fit in to this setup, +but I don't really want or need them right now. +One thing I definitely want to try +at some point +is adding a litterbox component +to index the contents of URLs +to make finding previously shared links easier. +. +.Pp +If you try any of this software +and have feedback, +let me know in +.Li #ascii.town +on tilde.chat +or by email. +And of course, +patches are always welcome. +. +.Ss Update: scooper +Somehow I had the motivation +to create a web interface for litterbox: +.Xr scooper 1 . +It can be used either as CGI +or as a FastCGI worker, +and I used the excellent +.Xr kcgi 3 +library for it. +. +.Pp +The main advantage of this interface +is that you can click on a search result +to be brought to its context in the log viewer. +I also added an option to +.Xr litterbox 1 +to provide a corresponding scooper link +in response to its query interface. +. +.Pp +A small demo of scooper is hosted at +.Aq Lk "https://causal.agency/scooper/" . +It publicly logs the +.Li #litterbox +channel on tilde.chat. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk "https://git.causal.agency/pounce" pounce +.It +.Lk "https://git.causal.agency/litterbox" litterbox +.It +.Lk "https://git.causal.agency/catgirl" catgirl +.It +.Lk "https://www.sqlite.org/fts5.html" "SQLite FTS5 Extension" +.It +.Lk "https://git.causal.agency/scooper" scooper +.It +.Lk "https://kristaps.bsd.lv/kcgi/" kcgi +.El +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/011-libretls.7 b/www/text.causal.agency/011-libretls.7 new file mode 100644 index 00000000..c29c325e --- /dev/null +++ b/www/text.causal.agency/011-libretls.7 @@ -0,0 +1,220 @@ +.Dd August 9, 2020 +.Dt LIBRETLS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm LibreTLS +.Nd libtls for OpenSSL +. +.Sh DESCRIPTION +This is a sort of announcement post about LibreTLS, +my port of libtls from LibreSSL to OpenSSL. +If you've wanted to try any of my software +but have been unable to because of LibreSSL, +LibreTLS is an option that will likely work for you. +I'm including instructions +for building it and my IRC software +on Debian as an example, +since manually installing libraries +is less straightforward than it could be. +. +.Pp +libtls is +.Do +a new TLS library, +designed to make it easier to write foolproof applications +.Dc . +It was developed as part of LibreSSL, +.Ox Ap s +fork of OpenSSL, +and is implemented against their version of libssl. +It provides a nice high-level API +for TLS sockets, +with functions like +.Xr tls_connect 3 , +.Xr tls_read 3 +and +.Xr tls_write 3 . +This is a vast improvement over libssl's +confusing mess of an API! +Its relative obscurity is a real shame +for C programmers. +. +.Pp +An obvious cause of its obscurity +is that it is tied to LibreSSL. +Although LibreSSL is available +for platforms other than +.Ox , +it conflicts with OpenSSL +so is difficult to install alongside it +and is often not packaged at all. +Additionally, +even if a user manually installs LibreSSL, +libtls is likely not to work on some distros +due to its hardcoded CA bundle file path. +. +.Pp +Since libtls is implemented against libssl, +which originates in OpenSSL, +it should be possible to use libtls with it. +This is what I set out to do in LibreTLS. +I started by importing the sources +from a LibreSSL-portable release, +then worked on porting the portions +that were incompatible with OpenSSL. +. +.Pp +The simpler changes just involved +replacing internal struct field accesses +with public APIs. +libtls accesses libssl internals +using a hack to get the header files +to declare private struct fields, +and for basically no reason. +The bigger changes involved +reimplementing some functions +which only exist in LibreSSL, +but these were still quite small. +I also imported the necessary compatibility functions +from LibreSSL's libcrypto +and adapated the autotools build files +to produce only a libtls +which depends on OpenSSL. +. +.Pp +Along the way +I decided to make one small behavioural change +in order for LibreTLS to be more likely +to work for everyone. +I removed the hardcoded CA file path +and changed the default configuration +to use OpenSSL's default CA paths, +which include a CA directory. +This seems to be the preferred CA source +on systems such as Debian, +where the default CA file path doesn't exist. +. +.Pp +I think the reason LibreSSL +wants to avoid using a CA directory +is so that it can fully load the CA file +once before being sandboxed. +However, +using OpenSSL's default configuration, +the CA file will still be loaded immediately +if it exists. +If it doesn't exist, +sandboxed applications +will fail when trying to +load certificates from the directory, +but unsandboxed applications +will work just fine. +Since LibreSSL's libtls +would fail either way, +I think the new behaviour +is an improvement. +. +.Pp +Another advantage of separating libtls from LibreSSL +is that it is unencumbered by OpenSSL's +awkward double-license, +both of which are incompatible with the GPL. +libtls is all new ISC-licensed code, +and future versions of OpenSSL (3.0) +will be released under the Apache 2.0 license, +which is compatible with GPLv3. +In the future, +GPL software will be able to link with +libtls and OpenSSL without additional permissions. +. +.Pp +It's also worth noting that LibreSSL +likely will not be able to import any code +from future versions of OpenSSL, +since Apache 2.0 is on +.Ox Ap s +license shitlist. +LLVM is also slowly changing their license +to Apache 2.0, +so it'll be interesting to see what +.Ox +does. +. +.Ss Installing Manually +To install LibreTLS on Debian, +for example, +fetch a release tarball from +.Lk https://causal.agency/libretls/ +and install the build dependencies: +.Bd -literal -offset indent +sudo apt-get install build-essential libssl-dev pkgconf +.Ed +. +.Pp +.Xr pkgconf 1 +isn't a dependency of LibreTLS itself, +but it's how my software +configures its build +for a dependency on libtls. +The usual build steps +will install the library: +.Bd -literal -offset indent +\&./configure +make all +sudo make install +.Ed +. +.Pp +The library will be installed in +.Pa /usr/local/lib +by default, +and you need to make sure +the dynamic linker +will be able to find it there. +On Debian, +.Pa /usr/local/lib +already appears in +.Pa /etc/ld.so.conf.d/libc.conf , +but on other systems +you'll probably need to add it to either +.Pa /etc/ld.so.conf +or a new file such as +.Pa /etc/ld.so.conf.d/local.conf . +Once the library is installed +and the path is configured, +the linker cache needs to be refreshed: +.Bd -literal -offset indent +sudo ldconfig +.Ed +. +.Pp +You'll probably also need to set +.Ev PKG_CONFIG_PATH +for the configure scripts +of my software: +.Bd -literal -offset indent +PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure +.Ed +. +.Pp +On +.Fx , +LibreTLS and some of my IRC software +can be installed from my own +.Lk https://git.causal.agency/ports/ "ports tree" +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://git.causal.agency/libretls/about LibreTLS +.It +.Lk https://man.openbsd.org/tls_init.3 "libtls API documentation" +.El +. +.Pp +Another alternative libtls implementation, +.Lk https://sr.ht/~mcf/libtls-bearssl/ "libtls-bearssl" +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/012-inability.7 b/www/text.causal.agency/012-inability.7 new file mode 100644 index 00000000..d352143b --- /dev/null +++ b/www/text.causal.agency/012-inability.7 @@ -0,0 +1,39 @@ +.Dd November 26, 2020 +.Dt INABILITY 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Inability +.Nd losing the ability to create +. +.Sh DESCRIPTION +For often weeks, sometimes months at a time, +I lose the ability to write new code. +I can still make fixes +and little cleanups +in my existing projects, +but if I try to work on something new, +nothing happens. +I can't get anything done. +. +.Pp +I think it's now been +over 3 months +since I've created anything. +I don't know what to do about it. +In the past I've eventually +regained the ability to code, +but it's unclear to me how or why. +I also don't know what +I should be doing instead. +Writing code is the only hobby +I've ever really developed, +so without it I basically +don't do anything. +. +.Pp +Does this happen to anyone else? +How do you cope? +. +.Sh AUTHORS +.Mt june@causal.agency diff --git a/www/text.causal.agency/013-hot-tips.7 b/www/text.causal.agency/013-hot-tips.7 new file mode 100644 index 00000000..63b6e353 --- /dev/null +++ b/www/text.causal.agency/013-hot-tips.7 @@ -0,0 +1,156 @@ +.Dd December 2, 2020 +.Dt HOT-TIPS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm hot tips +.Nd from my files +. +.Sh DESCRIPTION +This is a short list of tips +from my configuration files and code +that might be useful. +. +.Ss Shell +.Bl -tag -width Ds +.It CDPATH=:~ +This is useful if you sometimes type, +for example, +.Ql cd src/bin +wanting to go to +.Pa ~/src/bin +but you aren't in +.Pa ~ . +If the path doesn't exist +in the current directory, +.Ic cd +will try it in +.Pa ~ +as well. +. +.It alias ls='LC_COLLATE=C ls' +This makes it so that +.Xr ls 1 +lists files in ASCIIbetical order, +which puts capitalized names like +.Pa README +and +.Pa Makefile +first. +. +.It git config --global commit.verbose true +Not shell but close enough. +This makes it so the entire diff is shown +below the usual summary +in the editor for a +.Xr git-commit(1) +message. +Useful for doing a quick review +of what you're committing. +.El +. +.Ss (neo)vim +.Bl -tag -width Ds +.It set inccommand=nosplit +This is the only +.Xr nvim 1 +feature I really care about +aside from the improved defaults. +This provides a live preview of what a +.Ic :s +substitution command will do. +It makes it much easier to +write complex substitutions. +. +.It nmap <leader>s vip:sort<CR> +This mapping sorts the lines of a paragraph, +or block of text separated by blank lines. +I use this a lot to sort +#include directives. +. +.It nmap <leader>S $vi{:sort<CR> +Similar to the last mapping, +this one sorts lines inside braces. +I use this to sort +switch statement cases +or array initializers. +. +.It nmap <leader>a m':0/^#include <<CR>:nohlsearch<CR>O#include < +I use this mapping to add new +#include directives, +usually followed by +.Ic <leader>s +and +.Ic '' +to sort them +and return to where I was. +. +.It nmap <leader>d :0delete<CR>:0read !date +'.Dd \e%B \e%e, \e%Y'<CR> +I use this to replace the first line of +.Xr mdoc 7 +files with the current date. +.El +. +.Ss C +.Bl -tag -width Ds +.It #define Q(...) #__VA_ARGS__ +This is what I've started using +to quote things like SQL statements +or HTML fragments in C. +Anything that happens to be valid C tokens, +which is most code, +can be quoted this way. +Macros are not expanded +inside the quoted part. +You can embed (matched) quotes +without having to escape them. +Whitespace gets collapsed, +so you can write nicely formatted multi-line SQL +that doesn't mess up your debug logging, +for example. +.Bd -literal -offset indent +const char *sql = Q( + INSERT OR IGNORE INTO names (nick, user, host) + VALUES (:nick, :user, :host); +); +.Ed +. +.It #define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit +I use this macro to declare bitflag enums. +It takes advantage of +auto-incrementing enum items +so you don't need to set the values manually. +You also get constants +for both the bit index +and the flag value +for each item. +.Bd -literal -offset indent +enum Attr { + BIT(Bold), + BIT(Reverse), + BIT(Italic), + BIT(Underline), +}; +.Ed +.Pp +For example, +defines +.Sy ItalicBit = 2 +and +.Sy Italic = 1 << 2 . +Ignore the extraneous constants. +. +.It typedef int FnType(const char *str, size_t len); +You can just typedef function types! +It annoys me more than it probably should +that everyone writes ugly +function pointer typedefs. +Just stick +.Sy typedef +on the front of a function declaration +and use +.Vt FnType * . +.El +. +.Sh AUTHORS +.Mt june@causal.agency diff --git a/www/text.causal.agency/014-using-vi.7 b/www/text.causal.agency/014-using-vi.7 new file mode 100644 index 00000000..e6a6a7a0 --- /dev/null +++ b/www/text.causal.agency/014-using-vi.7 @@ -0,0 +1,135 @@ +.Dd January 11, 2021 +.Dt USING-VI 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Using vi +.Nd simpler tools +. +.Sh DESCRIPTION +Happy new year +and hello from +.Xr vi 1 ! +I'm in the mood to post something +but not in the mood for +.Dq social +media. +This one will probably be short. +. +.Pp +Yesterday I was trying to work on sandboxing +.Xr catgirl 1 +(that's the IRC client I work on) +with +.Xr pledge 2 +and +.Xr unveil 2 +on +.Ox , +as suggested by the maintainer of its port. +I've done similar things before, +but only on server software +rather than user software. +. +.Pp +Anyway I was in +.Xr ssh 1 +to my +.Ox +VM +.Po +sadly I don't currently have any hardware to run +.Ox +on +.Pc +using my usual editor, +which is +.Xr nvim 1 . +I'm honestly not very thrilled +with what neovim is doing lately, +but the cleaned up defaults +make my configuration files happier. +. +.Pp +The real problem with +.Xr nvim 1 , +though, +is that it's laggy as hell on +.Ox . +There is significant delay +on every single keystroke, +as if I'm typing remotely to a server +on the other side of the world, +but this is on a local VM! +. +.Pp +So I did the only reasonable thing: +I typed +.Sy :qa +followed by +.Sy vi . +The difference was astonishing. +Typing and editing suddenly felt +.Em physical +again. +(I put that in italics even though I know it won't render.) +Not only was it a vast improvement over +.Xr nvim 1 +in +.Xr ssh 1 +in a VM, +it was a marked improvement over +.Xr nvim 1 +running locally and natively. +. +.Pp +Now obviously +.Xr vi 1 +doesn't have all the bells and whistles +of newer editors, +but of course the core editing model +that makes +.Xr vim 1 +and +.Xr nvim 1 +so good is there, +and in purer form, +I think. +The +.Xr vi 1 +manual page +is feasible to just sit down and read, +and learn everything there is to know about the editor. +I set up a basic configuration +and got coding. +.Bd -literal -offset indent +export EXINIT='set ai ic sm sw=4 ts=4' +.Ed +. +.Pp +After I finished my +.Xr pledge 2 +and +.Xr unveil 2 +patch, +I was so pleased with +.Xr vi 1 +that I kept on using it +yesterday and today +for other work, +and obviously to write this post. +Despite the lack of editor amenities, +its responsiveness and simplicity +are enough to make using it +.Em comfortable +and perhaps +.Em cosy . +I'm not sure I'll ever use +.Xr vi 1 +full-time, +but for now I am much less likely +to launch +.Xr nvim 1 . +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/015-reusing-tags.7 b/www/text.causal.agency/015-reusing-tags.7 new file mode 100644 index 00000000..19546496 --- /dev/null +++ b/www/text.causal.agency/015-reusing-tags.7 @@ -0,0 +1,155 @@ +.Dd January 17, 2021 +.Dt REUSING-TAGS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm reusing tags +.Nd beyond ctags +. +.Sh DESCRIPTION +I've tried to start writing this post a couple times now +and I keep getting bogged down in explanations, +so I'm just going to tell you +about some cool things I did +and hope they make sense. +. +.Pp +When I wrote my first syntax highlighter, +I decided that function definitions +should have anchor links, +because line number anchor links +are entirely useless +if you expect the file to change at all. +Since the syntax highlighter +was somewhat deliberately just a big pile of regex, +I hacked in more regex to try +to identify function and type definitions. +It wasn't elegant and it didn't always work well. +It did work though, +and I found the links very useful. +. +.Pp +Recently I was thinking about +the lexer generator +.Xr lex 1 +and decided to +rewrite the syntax highlighter +using it. +Really syntax highlighting +is no different than lexical analysis. +I ran into a problem though, +trying to preserve my anchor link function, +because really that should involve +some amount of parsing. +Trying to port my regex hacks to +.Xr lex 1 +made the lexers way more complicated +and less reliable, +so I gave up on it for a while. +. +.Pp +And then, +probably in the shower, +I realized I was approaching it +completely from the wrong direction. +There's already a tool that does what I want, +and I already use it: +.Xr ctags 1 . +All I need to do is use its output +to insert anchor links +into my syntax highlighter output. +In an afternoon I wrote +.Xr htagml 1 , +which loads tag definitions for its input file, +then scans through the input for where they match. +It can either HTML-escape +the input as it goes, +or use already formatted HTML +being piped into it from a syntax highlighter. +. +.Pp +The result is three simple tools +working together to accomplish +what a more complex tool +couldn't reliably achieve. +I'm very pleased with it, +and I've updated my site and cgit +to use the new +.Xr lex 1 Ns -based +highlighter, +.Xr ctags 1 +and +.Xr htagml 1 . +I'm currently missing a lexer for +.Xr sh 1 , +but I plan to write it eventually. +I also want to write a tool +to generate tags for +.Xr make 1 , +.Xr mdoc 7 +and perhaps +.Xr sh 1 . +The cool thing about generating more kinds of tags +is that they'll not only improve +the HTML output, +they'll also be usable in my editor. +. +.Pp +Speaking of generating different kinds of tags, +I also wrote some scripts not too long ago +for reading IETF RFCs offline. +The plain text files are available to +.Xr rsync 1 , +but they're hard to navigate on their own. +By scanning the files for headings +and generating tags, +it allows jumping to sections using +.Ic :ta +or +.Ic ^] +in +.Xr vi 1 . +For +.Xr nvim 1 +I also added an +.Ic :RFC +command to open an RFC by number +and set up +.Ic ^] +to work optimally for them. +. +.Pp +I'm still using +.Xr vi 1 +for most of my editing, +by the way. +And of course +.Xr ctags 1 +was made to work with it! +Simple old tools +are really doing it for me lately. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://causal.agency/bin/htagml.html htagml +.It +.Lk https://causal.agency/bin/hilex.html hilex +.It +.Lk https://git.causal.agency/src/tree/doc/rfc rfctags +.El +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Sh ADDENDUM +.Xr catgirl 1 , +.Xr pounce 1 , +.Xr litterbox 1 +and +.Xr scooper 1 +all have new releases, +if you're using any of them. +Also, this space is now +available over gopher, +if that's your sort of thing. diff --git a/www/text.causal.agency/016-using-openbsd.7 b/www/text.causal.agency/016-using-openbsd.7 new file mode 100644 index 00000000..b843e3c3 --- /dev/null +++ b/www/text.causal.agency/016-using-openbsd.7 @@ -0,0 +1,505 @@ +.Dd February 14, 2021 +.Dt USING-OPENBSD 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Using OpenBSD +.Nd for real +. +.Sh DESCRIPTION +Hello from +.Ox ! +After wishing one too many times +that I had a real BSD +on a physical machine, +I finally got around to +just installing one on my +mid-2014 MacBook Pro. +I hadn't done it sooner +because I didn't realize +how easy it would be. +It helped that I already had a +.Dq Boot Camp +partition with a disused Windows 8 install +that I could replace. +. +.Pp +I roughly followed an old jcs gist +along with the +.Ox +Disk Setup guide. +I'm once again happy +that I bought a printer\(em +they're very useful for instructions +to install an operating system +on your only usable computer. +I set up encrypted softraid +and the operating system +installed smoothly. +. +.Pp +Next I had to install rEFInd, +since the default Mac boot manager +is really not keen on booting much. +Installing it requires using the +macOS recovery partition these days. +But there was a problem +with my new boot menu: +I was promised a picture of Puffy, +and instead I just got some abstract coloured circles! +Turns out a bunch of OS icons +got removed from rEFInd at some point, +and I had to rescue Puffy +from the git history. +. +.Pp +So I could happily boot +.Ox +by selecting Puffy, +but I had no networking. +I thought the wifi chip might be supported by +.Xr bwfm 4 , +but I got unlucky and it's a BCM4360, +which everything hates. +Based on the jcs gist, +I checked the list of hardware +supported by the +.Xr urtwn 4 +driver for a wifi dongle to order. +Just having a clear list +in the driver manual is wonderful. +I went with the Edimax EW-7811Un v2, +which I could get for around $20. +It's nice and tiny, +though it has a piercing blue LED +(destroy all blue LEDs) +which I had to cover with electrical tape. +. +.Pp +I had to do one other thing +before I could get it all working, though. +When I had checked the +.Xr urtwn 4 +hardware list, +I had been looking at +.Ox Ns -current , +but I had installed +.Ox 6.8 , +and support for the v2 hardware +I had bought was added after that release. +So I downloaded a snapshot +.Pa bsd.rd +along with the +.Xr urtwn 4 +firmware file +to a USB drive +and upgraded the system. +. +.Pp +Connecting to wifi with +.Xr ifconfig 8 +is a breeze, by the way, +and then you just write the same thing to a +.Xr hostname.if 5 +file to make it automatic. +I wanted to use +.Ox +for exactly this reason: +simple, consistent, cohesive, well-documented tools. +. +.Pp +Finally, I got to configuring. +The console is configured with +.Xr wsconsctl 8 , +and similarly you can put the commands in +.Xr wsconsctl.conf 5 +to have them run at boot. +I added +.Li display.brightness=50% +to tone down the brightness, +which is initially 100%, +and +.Li keyboard.backlight=0% +to turn off those annoying lights. +.Xr wsconsctl.conf 5 +is also where you can set +trackpad settings if you're not using +.Xr synaptics 4 . +I ended up using: +.Bd -literal -offset indent +mouse1.tp.tapping=1 +mouse1.tp.scaling=0.2 +mouse1.reverse_scrolling=1 +.Ed +.Pp +This enables tapping with several fingers +to simulate different mouse buttons, +makes the cursor move at a reasonable speed +and scrolling move in the right direction. +I also set up my usual modified QWERTY layout. +. +.Pp +For +.Xr X 7 +I had enabled +.Xr xenodm 1 , +which seems quite nice. +It automatically prompts you to add your +.Xr ssh 1 +keys to +.Xr ssh-agent 1 +when you log in. +One of the reasons I had not wanted +to set up another graphical system +is that I thought +I would have to make too many choices, +and that I would have to choose least bad options +rather than actually good options, +but +.Ox +already includes reasonable choices. +I wanted to use +.Xr cwm 1 , +so I started a basic +.Pa .xsession +file: +.Bd -literal -offset indent +\&. ~/.profile +export LC_CTYPE=en_US.UTF-8 +xset r rate 175 m 5/4 0 +xmodmap ~/.config/X/modmap +xrdb -load ~/.config/X/resources +exec cwm -c ~/.config/cwm/cwmrc +.Ed +. +.Pp +The +.Xr xset 1 +command sets keyboard repeat rate +and mouse acceleration. +I spent some time going through +.Xr cwm 1 Ap s +functions and writing up bindings +that would get me something close enough +to what I'm used to in macOS. +Most importantly, +putting everything on the 4 modifier (command key). +. +.Pp +I also added key bindings on F1 and F2 +to adjust the brightness with +.Xr xbacklight 1 , +and on F10, F11 and F12 +to adjust volume with +.Xr sndioctl 1 . +I'm not sure why the F keys +just send regular F1, F2, etc.\& +regardless of the Fn key. +I don't use F keys for anything else though, +so I'm not too concerned. +Once again, +.Xr sndioctl 1 +is such an easy straightforward tool: +.Bd -literal -offset indent +bind-key F10 "sndioctl output.mute=!" +bind-key F11 "sndioctl output.level=-0.05" +bind-key F12 "sndioctl output.level=+0.05" +.Ed +. +.Pp +For aesthetic configuration, +I added a new output to my +.Xr scheme 1 +colour scheme tool for +.Xr X 7 Ns -style +RGB and +.Xr xterm 1 +resources. +Normally I use the +.Em Go Mono +font, +but since +.Ox +already includes +.Em Luxi Mono , +which +.Em Go Mono +is based on, +I used that. +The most important configuration +to make anything readable on a high-DPI display is: +.Bd -literal -offset indent +Xft.dpi: 144 +Xft.antialias: true +Xft.hinting: false +.Ed +. +.Pp +I'm annoyed that I haven't found +where these resources are actually documented. +I would hope they'd be in +.Xr Xft 3 +or something, +but they're not. +Anyway, +turning off hinting +seems absolutely necessary +to prevent text from looking like garbage. +. +.Pp +It seems that to get a reasonably sized cursor +I need to install +.Sy xcursor-dmz . +I'd prefer if there wasn't this one +extra package that I needed +for a reasonable setup. +Tangentially, +I've never understood why +the black versions of dmz cursors +are called +.Dq aa +when it seems like that +would stand for antialiasing +or something. +.Bd -literal -offset indent +Xcursor.size: 64 +Xcursor.theme: dmz-aa +.Ed +. +.Pp +For a desktop background, +I found a cute bitmap (little picture) +of snowflakes already in the system +and used colours from my usual scheme: +.Bd -literal -offset indent +xsetroot -bitmap /usr/X11R6/include/X11/bitmaps/xsnow \e + -bg rgb:14/13/0E -fg rgb:7A/49/55 +.Ed +. +.Pp +Since I'd rather not install anything +I don't have to, +I went with the default +.Xr xterm 1 . +It seems more than adequate, honestly. +I read through its RESOURCES +section to configure it how I like. +The important settings are +.Sy XTerm*utf8 +and +.Sy XTerm*metaSendsEscape . +Since I'm used to copying and pasting on macOS, +I added equivalent +.Dq translations : +.Bd -literal -offset indent +XTerm*VT100*translations: #override \en\e + Super <Key>C: copy-selection(CLIPBOARD) \en\e + Super <Key>V: insert-selection(CLIPBOARD) +.Ed +. +.Pp +The next thing I needed +was a clock and battery indicator. +I actually had my battery die on me +while I was doing all this, +which reminded me. +.Xr xclock 1 +would be perfect, +but then I'd need something else +for battery. +There are a couple basic battery indicators +for X in ports, +but they're terribly ugly. +I wanted something as simple as +.Xr xclock 1 , +but that I could add some other text to. +Then I realized I could just use +.Xr xterm 1 +for that. +To my +.Pa xsession +I added: +.Bd -literal -offset indent +xterm -name clock -geometry 14x1-0+0 -sl 0 -e clock & +.Ed +.Pp +This places a little terminal +in the top-right corner of the screen +with no scrollback lines, +running a script called +.Pa clock . +To have +.Xr cwm 1 +treat it like a +.Dq panel +and show it on every desktop, +I added this to my +.Pa cwmrc : +.Bd -literal -offset indent +ignore clock +autogroup 0 clock,XTerm +.Ed +.Pp +The +.Pa clock +script simply uses +.Xr date 1 +and +.Xr apm 8 +to print the time and battery charge +every minute: +.Bd -literal -offset indent +tput civis +sleep=$(( 60 - $(date +'%S' | sed 's/^0//') )) +while :; do + if [ $(apm -a) -eq 1 ]; then + printf '%3s%%' "$(apm -l)" + else + test $(apm -b) -eq 2 && tput setaf 1 bold + printf '%3.3sm' "$(apm -m)" + tput sgr0 + fi + printf ' %s\r' "$(date +'%a %H:%M')" + sleep $sleep + sleep=60 +done +.Ed +.Pp +The initial setting of +.Va sleep +is to align the updates +with the minute ticking over. +I made the battery output +a bit fancier by showing +percentage while charging, +minutes left while discharging, +and highlighting in red +when the battery is +.Dq critical . +. +.Pp +Now is a good time to mention adding +.Ql apmd_flags=-A +to +.Pa /etc/rc.conf.local +to enable +.Dq automatic performance adjustment , +or not running your battery flat +as fast as possible mode. +It seems like I can get up to 3 hours +of battery life depending on the screen brightness, +but this is quite an old battery by now. +. +.Pp +The other thing I needed +was something to tone down +that awful, evil blue light from the screen. +I asked around and someone told me about +.Xr sct 1 , +originally written by tedu. +The package also includes a little +.Xr sctd 1 +script that you can add to your +.Pa .xsession +to have it automatically adjust +the colour temperature throughout the day. +My eyes are no longer being assaulted. +. +.Pp +While I was doing all this, +I of course needed to talk about it on IRC, +and it was very nice to be able to +install my own IRC client with: +.Bd -literal -offset indent +doas pkg_add catgirl +.Ed +.Pp +I don't plan to do +general Web Browsing on +.Ox , +and there is definitely +no good choice for browser, +so I just installed +.Xr imv 1 , +.Xr mpv 1 , +.Xr youtube-dl 1 +and +.Xr w3m 1 . +I wrote a script +to open images by piping +.Xr curl 1 +into +.Xr imv 1 , +videos with +.Xr mpv 1 , +and everything else with +.Xr w3m 1 +in a new +.Xr xterm 1 . +Annoyingly, +.Xr mpv 1 +seems incapable of exiting +without segfaulting. +That's quality. +. +.Pp +One thing I am still missing +is automatic brightness adjustment +based on ambient light +like macOS can do. +I can read the sensor with +.Xr sysctl 8 +.Cm hw.sensors.asmc0.illuminance0 , +which is measured in lux. +I tried doing something with it in a script, +but it seems tricky to map its value +to brightness adjustments +and to play nice with manual brightness changes, +so I'll just keep doing it manually for now. +. +.Pp +Update: +prx sent mail to let me know about +.Aq Lk https://github.com/jcs/xdimmer . +I should've guessed jcs had written something. +. +.Pp +And that's my current +.Ox +setup after a week of using it. +I'm quite enjoying it, +and still being pleasantly surprised +by the quality-of-life from +.Ox +tools and documentation. +For a small example, +I can jump to sections +or flag definitions in +.Xr man 1 +using +.Ic :t . +Systems without basic usability like that +should be ashamed. +. +.Pp +I would post a screenshot, +but this is +.Li text.causal.agency +;) +. +.Sh SEE ALSO +.Lk https://gist.github.com/jcs/5573685 +.Pp +My full configurations are in +.Aq Lk https://git.causal.agency/src . +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Sh BUGS +There's a red LED +inside the headphone jack +that is always on +and I have no idea how to turn off. +If anyone knows +please send me an email. diff --git a/www/text.causal.agency/017-unpasswords.7 b/www/text.causal.agency/017-unpasswords.7 new file mode 100644 index 00000000..f9643f2f --- /dev/null +++ b/www/text.causal.agency/017-unpasswords.7 @@ -0,0 +1,153 @@ +.Dd February 20, 2021 +.Dt UNPASSWORDS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Unpasswords +.Nd password anti-management +. +.Sh DESCRIPTION +Right away I want to say +that I'm not trying to tell anyone +how to manage their online authentication. +This is just how I do it, +and I haven't seen anyone else write about it. +. +.Pp +I don't use a password manager. +It's not a type of software +I want to deal with. +For the small handful of sites +that I use regularly +and that actually matter, +I use strong passwords +(stored in my noggin) +and TOTP. +For everything else, +I simply do not know the password, +and neither does any software. +. +.Pp +I think I started doing this one time +when I had legitimately forgotten +the password to some old account. +I clicked on +.Dq forgot my password +and opened the email, +but I didn't want to +come up with a new password +I would just forget again. +Instead I set a random one +.Po +I usually use +.Ql openssl rand -base64 33 +for this +.Pc +and immediately used that to log in +while it was still in my clipboard. +Next time I wanted to log in, +I could use +.Dq forgot my password +again. +. +.Pp +Thinking about it, +I realized that any web authentication +with an email password reset flow +is only ever as strong as +the authentication for your email account. +So what is the point of having +all these passwords set on different sites? +They all answer to your email account, +and storing them in a password manager +seems to add another potential point of failure. +May as well have no other passwords at all, +or as close as possible. +.Po +Shout out to sites like Liberapay +and asciienema +which let me not set a password at all. +.Pc +. +.Pp +So I started doing that for any site +that I don't regularly log in to. +Going through the password reset flow +can be a bit slow, +but it doesn't need to be done often. +And I can do it from anywhere +I have access to my email, +which I feel is more easily reliable +than syncing password management databases. +It's quite stress-free. +. +.Pp +After doing this manually for years, +this week I finally got around to +writing some automation for it. +A while ago I had written +.Xr imbox 1 , +a tool to directly export mail +in mboxrd format from IMAP, +along with +.Xr git-fetch-email 1 , +a wrapper which offloads configuration to +.Xr git-config 1 . +It can match emails by +Subject, From, To and Cc. +This week I added a flag +to use IMAP IDLE +to wait for a matching message +if there isn't one already, +and a flag to move matching messages +(for example to Trash) +after exporting them. +. +.Pp +With those two new flags, +I started writing some shell scripts +to automate the password reset flow +using +.Xr curl 1 +to submit forms and +.Xr git-fetch-email 1 +with +.Xr sed 1 +to pull the reset tokens +from my inbox. +At the end of the script, +the random password it set +is copied to the clipboard +and the login page for the site is opened. +So now logging in is as simple +as running a command, +waiting for the login page to open, +and pasting. +. +.Pp +The script isn't sophisticated, +but I don't think it needs to be. +I've written functions +for a couple different sites already, +and they all work in mostly the same way. +Writing a new one is just a matter +of identifying the form URLs and fields +along with where the token is in the email. +I'm not going to turn this automation +into any kind of generally usable project, +because I don't want to have to +maintain functions for tonnes of different services. +If you're interested in this idea, +I encourage you to use my script as a template +and implement the functions for services you use. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://git.causal.agency/imbox +.It +.Lk https://causal.agency/bin/sup.html +.El +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/018-operating-systems.7 b/www/text.causal.agency/018-operating-systems.7 new file mode 100644 index 00000000..691102e2 --- /dev/null +++ b/www/text.causal.agency/018-operating-systems.7 @@ -0,0 +1,86 @@ +.Dd February 22, 2021 +.Dt OPERATING-SYSTEMS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Operating systems +.Nd criteria +. +.Sh DESCRIPTION +Sometimes in conversation +I use the term +.Dq real operating system +which people, +perhaps rightfully, +take as inflammatory. +But I have actually thought about +what I mean when I say +.Dq real operating system +and come up with +this list of criteria. +. +.Pp +An operating system should be... +.Bl -bullet +.It +Consistent and cohesive: +all parts of the system should have similar +usage, configuration, documentation and so on. +Parts of the system should naturally work together, +because they were designed to do so. +. +.It +Documented: +the system should include its own documentation. +A user should not have to +search some external wiki +to learn how the system works. +It should be obvious +where to find documentation +on a particular topic. +. +.It +Programmable: +the system should provide +a way to program the computer. +A computer which cannot be programmed +is not a computer at all. +Usually this takes the form +of a C compiler +and the tools that go with it. +In earlier times, +it might have been +a BASIC interpreter. +. +.It +Examinable and modifiable: +the full source tree +for the system should be included, +or easily obtainable +through official means. +A user should have no trouble +finding the corresponding source +for a part of the system. +Together with the previous point, +the source tree should be +compiled by the included toolchain, +allowing local modification. +.El +. +.Pp +Some things that may be parts +of real operating systems, +but are not themselves operating systems: +a kernel, +a package manager, +a collection of packages. +. +.Pp +I will leave it as an +.Dq exercise for the reader +to guess which operating systems +meet these criteria +and which don't. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/019-mailing-list.7 b/www/text.causal.agency/019-mailing-list.7 new file mode 100644 index 00000000..b3490a94 --- /dev/null +++ b/www/text.causal.agency/019-mailing-list.7 @@ -0,0 +1,286 @@ +.Dd March 4, 2021 +.Dt MAILING-LIST 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Mailing List +.Nd a small-scale approach +. +.Sh DESCRIPTION +When I initially published +some software I expected +other people to use, +I just asked that patches +be mailed directly to me, +but I figured that +if more people were interested, +it would be better +to have a mailing list. +Unfortunately +email software, +mailing list options in particular, +are quite daunting. +I wanted a light-weight option +that would require me to host +as little software as possible. +. +.Pp +My regular email is hosted by Fastmail, +and I poked around its settings +to see what I could do. +It turns out Fastmail lets you +configure address aliases to +.Dq also send to all contacts in +a contacts group. +That's a mailing list! +I created a group called +.Dq List +and an alias called +.Mt list@causal.agency +configured to deliver to that group. +So it's really just an alias +for my regular address +that happens to also +deliver to another group of people. +. +.Pp +It's easier to just configure +and manage one mailing list, +so what I do is ask patches and feedback +to be sent to +.Mt list+catgirl@causal.agency , +for example. +Fastmail treats any +.Ar +suffix +the same as the base address, +but the full address can be used +by subscribers to filter mail by topic +if they wish. +. +.Pp +To subscribe someone to the list, +I add their contact to the group. +For a long time I was planning +to write some software +to manage these subscriptions. +It should be possible +to process subscription requests from IMAP +and manipulate the contact group with CardDAV. +When I went to start implementing this, +however, +I found CardDAV (and WebDAV in general) +completely inscrutable. +It's the kind of protocol +that is split across like 20 +different RFCs +and you can't understand anything +by just reading +the one you actually care about. +So I've given up on that +and will keep manually subscribing people +on request. +. +.Pp +The only thing missing, then, +is a way for people to read +mail sent to the list +while they aren't subscribed. +All the existing +mailing list archive software +I know of +expects to have the mail locally, +but I'd rather keep all my mail in IMAP. +First, +in order to make sure +I keep a complete archive +of the mailing list in IMAP, +I added a small amount +of Sieve code +to my Fastmail filters configuration: +.Bd -literal -offset indent +if address :matches ["To", "Cc"] "list*@causal.agency" { + fileinto :copy :flags "\e\eSeen" "INBOX.List"; +} +.Ed +. +.Pp +Sieve is a small standard language +specifically for filtering mail. +This bit of code matches +anything sent to the list +and adds a copy of it +(the original is going into my inbox) +to the +.Dq List +folder +and marks the copy as read. +. +.Pp +With a pristine IMAP mailbox +to export from, +I wrote a new archive generator. +It's called +.Xr bubger 1 +kirg (have it in a way). +My goal was to render directly from IMAP +and produce only static files as output, +making it not only easy to serve, +but also to run in one place +and copy the files elsewhere. +That's important to me +because it has access to my email, +so I'd rather run it +on my local network and +.Xr rsync 1 +its output into The Cloud. +The static files are in +HTML, Atom and mboxrd formats. +. +.Pp +The architecture of +.Xr bubger 1 +is that for each piece of mail, +identified by its UID in the mailbox, +HTML and Atom fragments +are exported along with the mboxrd. +Those fragments are then stitched together +using the IMAP SORT and THREAD extensions +to make full pages and feeds +for each thread. +The fragments act as a cache +for subsequent runs. +. +.Pp +I admit I did some +pretty questionable things +to achieve this. +Namely, +I wrote a small string templating engine in C. +I use it to produce the HTML +and XML for Atom, +as well as to generate URLs +and paths. +I'm really happy with how it works, actually. +This is also where +I really started using +one of my favourite C hacks: +.Bd -literal -offset indent +#define Q(...) #__VA_ARGS__ +.Ed +. +.Pp +I quote all my HTML/XML templates +with this and it's lovely. +. +.Pp +I've been working on +.Xr bubger 1 +on and off for almost a year now, +and it's been interesting. +I learned a lot about how email +works from having to deal with +all the ways a message can be. +Thankfully a lot of that dealing +is done by the IMAP server. +. +.Pp +As for running it, +I initially just ran it with +.Xr cron 8 , +and that's still a good way to go. +To hook it up to +.Xr rsync 1 , +pipe it like so: +.Bd -literal -offset indent +bubger -C list [...] | rsync -a --files-from=- list remote:list +.Ed +. +.Pp +Later, +I got a little annoyed +with having to wait +for the next run +if I wanted to link +to some mail I just received. +I added an option +to use IMAP IDLE +to wait for new mail continuously +and I started running it +under my process supervisor, +.Xr catsitd 8 . +. +.Pp +The setup is a little more complex +to feed the list of updated files to +.Xr rsync 1 . +I added the +.Xr catsit-watch 1 +utility to run a command +when a file changes, +and in my +.Xr catsit.conf 5 +I have the following: +.Bd -literal -offset indent +bubger ~/.local/libexec/bubger +rsync catsit-watch -i -f ~/list/UIDNEXT ~/.local/libexec/rsync +.Ed +. +.Pp +The +.Pa ~/.local/libexec/bubger +script runs +.Xr bubger 1 , +writing the list of updated paths to +.Pa ~/list/FILES : +.Bd -literal -offset indent +exec bubger -i -C ~/list [...] >~/list/FILES +.Ed +. +.Pp +And the +.Pa ~/.local/libexec/rsync +script gets run each time a +.Xr bubger 1 +update completes +.Po +.Pa UIDNEXT +is always the last file written +.Pc +and copies the listed files +to the remote host: +.Bd -literal -offset indent +exec rsync -a --files-from=$HOME/list/FILES ~/list remote:list +.Ed +. +.Pp +I haven't tagged any +.Xr bubger 1 +releases yet +because it hasn't gotten +a huge amount of testing, +and I'm not sure anyone but me +would even want to use it. +But I'm happy +with how it's working right now, +so I might tag 1.0 soon +just for fun. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://causal.agency/list/ +.It +.Lk https://git.causal.agency/bubger/about +.It +.Lk https://git.causal.agency/catsit/about +.El +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Sh BUGS +Almost every time +I try to type +.Dq mailing list +I instead type +.Dq mailist list . diff --git a/www/text.causal.agency/020-c-style.7 b/www/text.causal.agency/020-c-style.7 new file mode 100644 index 00000000..9816dbc3 --- /dev/null +++ b/www/text.causal.agency/020-c-style.7 @@ -0,0 +1,172 @@ +.Dd March 16, 2021 +.Dt C-STYLE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm C Style +.Nd a rough description +. +.Sh DESCRIPTION +This is a rough description +of the style in which I write C, +since it's uncommon +but some people seem to like it. +I don't have any hard rules, +it just needs to look right. +. +.Ss Superficialities +I use tabs +and they're set to 4 characters wide +in my editor. +I keep my lines shorter than 80 columns, +which I enforce by +not resizing my terminal's width. +I use block indentation only, +meaning I write long function calls +like this: +.Bd -literal -offset indent +fprintf( + imap.w, "%s UID THREAD %s UTF-8 %s\er\en", + Atoms[thread], algo, search +); +.Ed +.Pp +Anything that can be sorted +should be sorted, +with trailing commas where possible. +This and block indentation +make for simpler diffs. +.Pp +I either write single-line ifs +or always use braces. +I put parentheses +around ternary expressions. +I use camelCase +for functions and variables, +and PascalCase for types and constants. +When an acronym appears +in an identifier, +it's in either all lower case +or all upper case. +The despicable SCREAMING_SNAKE_CASE +is reserved for macros. +I don't set globals or statics to zero +since that is already the default. +I don't compare against zero or NULL +unnecessarily. +. +.Ss \&No side-effects in control flow +I never write a function call +with side-effects +inside the condition of an if statement. +I find this makes following the +.Dq happy path +through functions +much easier. +I write things like this: +.Bd -literal -offset indent +pidFile = open(pidPath, O_WRONLY | O_CREAT | O_CLOEXEC, 0600); +if (pidFile < 0) err(EX_CANTCREAT, "%s", pidPath); + +error = flock(pidFile, LOCK_EX | LOCK_NB); +if (error && errno != EWOULDBLOCK) err(EX_IOERR, "%s", pidPath); +if (error) errx(EX_CANTCREAT, "%s: file is locked", pidPath); +.Ed +.Pp +I do write side-effects +inside for and while statement heads, +since that's generally expected. +For some reason +I like to write the constant first +if I'm comparing the result of an assignment +with a side-effect. +.Bd -literal -offset indent +for (ssize_t len; 0 <= (len = getline(&buf, &cap, file)); ++line) +.Ed +. +.Ss Paragraphs +I leave blank lines +between logical chunks of +.Dq things happening . +This is usually between side-effects +with their related error handling, +or between groups of closely related side-effects. +I try to keep variable declarations +glued to the top of the bit of code +they're used in. +. +.Ss Leading break +I've mentioned this previously. +I write my switch statement breaks +before each case label. +Doing this aligns nicely, +and being in the habit +means I always avoid +accidental fallthrough. +.Bd -literal -offset indent +switch (opt) { + break; case 'a': append = 1; + break; case 'd': delay = strtol(optarg, NULL, 10); + break; case 'f': watch(kq, optarg); + break; case 'i': init = 1; + break; default: return EX_USAGE; +} +.Ed +. +.Ss Function type definitions +Function types are always typedef'd, +and it's the function type itself +that is defined, +not a function pointer type! +I put the typedef above any functions +that are supposed to be of that type +so it's clear what the pattern is. +.Bd -literal -offset indent +typedef void Action(struct Service *service); +Action *fn = NULL; +.Ed +. +.Ss Constants +I prefer enums over #defines +for integer constants, +and static const strings over #defines +unless I want to do concatenation. +.Bd -literal -offset indent +enum { Cap = 1024 }; +.Ed +.Pp +I avoid the preprocessor +wherever possible, +with the notable exception of X macros, +which I've talked about previously. +Doing things in the actual language +makes for easier debugging. +. +.Ss Organization +I usually use only one header file +in each project. +The dependency is easy to declare +and the complete rebuild +when the header changes +isn't a problem for small projects. +Unless it's a single-file program, +I name the file which contains main +something generic, +since the name of the project +isn't relevant to its function. +I name functions like +.Ar nounVerb , +and all the functions for a +.Ar noun +are defined in +.Pa noun.c . +Not really to do with C, +but I always put a FILES section +in my README pages +to briefly describe +the layout of the code +for anyone looking to +read or make changes to it. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/021-time-machine.7 b/www/text.causal.agency/021-time-machine.7 new file mode 100644 index 00000000..93d35c1e --- /dev/null +++ b/www/text.causal.agency/021-time-machine.7 @@ -0,0 +1,144 @@ +.Dd April 25, 2021 +.Dt TIME-MACHINE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Time Machine +.Nd an awful one +. +.Sh DESCRIPTION +If, like me, +you have a Raspberry Pi 3 at home +that you've just upgraded to +.Fx 13.0 +which has a hard drive +from an old laptop +attached to it by USB adapter +with ZFS on it +and you want to +use that as a Time Machine +backup destination +over SMB using +.Xr samba 8 , +despite +.Xr samba 8 +being awful software +and using ZFS on a system +with only 1 GB of RAM +being a terrible idea, +this is how to do it. +. +.Pp +In +.Pa /usr/local/etc/smb4.conf : +.Bd -literal -offset indent +[global] +vfs objects = zfsacl catia fruit streams_xattr +fruit:metadata = stream +fruit:model = Macmini + +[TimeMachine] +read only = no +path = /media/zhdd/backup/TimeMachine +fruit:time machine = yes +fruit:time machine max size = 250G +.Ed +. +.Pp +The important thing here is +.Sy zfsacl +in the vfs objects list. +Most pages will tell you about the others, +but without +.Sy zfsacl +Time Machine will just fail to +create the backup +and not provide any useful error. +I'm not actually sure if the +.Sy fruit:metadata +setting is required, +but a bunch of pages recommend it. +The +.Sy fruit:model +just makes it look nice in Finder. +The rest creates an SMB share called +.Dq TimeMachine +that macOS will be willing to use. +You can limit the size of the share that +.Xr samba 8 +reports so that Time Machine +doesn't fill up the whole drive. +. +.Pp +The other important thing to do +is to create some swap space. +When I first tried backing up +to this share, +it stopped after a while +because +.Xr smbd 8 +got killed +when there was nowhere to swap pages to. +A wiki page told me to +create swap on ZFS like this: +.Bd -literal -offset indent +zfs create -V 2G \e + -o org.freebsd:swap=on \e + -o checksum=off \e + -o compression=off \e + -o dedup=off \e + -o sync=disabled \e + -o primarycache=none \e + zhdd/swap +swapon /dev/zvol/zhdd/swap +.Ed +. +.Pp +To be fair to +.Xr samba 8 , +most of the memory +is being used by the ZFS ARC +.Po +which you can see in +.Xr top 1 +.Pc , +but +.Xr smbd 8 +still seems to be using +far more memory than is reasonable. +It's interesting seeing processes +with 0 RES in +.Xr htop 1 +because they're all being swapped out +while the ARC takes half the available RAM. +And having to wait for my shell +to be paged back in when I quit +.Xr htop 1 . +. +.Pp +Anyway, +as expected this whole thing +is terribly slow. +On my initial backup, +I'm currently at 26.49 GB +of 104.22 GB +with an estimate of 8 hours remaining. +Normally transfer time estimates +are wildly inaccurate, +but I think in this case it's right. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Sh BUGS +.Fx +doesn't seem to want to mount +the ZFS volumes on the hard-drive-over-USB +automatically at boot. +I have to +.Xr zpool-import 8 +the drive manually each time. +I don't know if there's a workaround for this, +but I don't have anything essential +to the system on the drive, +and it doesn't need to reboot often. diff --git a/www/text.causal.agency/022-swans-are-dead.7 b/www/text.causal.agency/022-swans-are-dead.7 new file mode 100644 index 00000000..8664e886 --- /dev/null +++ b/www/text.causal.agency/022-swans-are-dead.7 @@ -0,0 +1,164 @@ +.Dd May 5, 2021 +.Dt SWANS-ARE-DEAD 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Swans Are Dead +.Nd album by Swans +. +.Sh DESCRIPTION +Swans Are Dead +is the best Swans album. +Among my favourites are +Soundtracks for the Blind, +To Be Kind +and Love of Life, +but Swans Are Dead +is the one I come back to +most consistently. +I'm always in the mood +to listen to these tunes. +. +.Pp +It's interesting to me +that I enjoy it so much, +I think because I had the expectation +that live albums +are not of the same quality +as studio albums, +but that's just completely untrue +in the case of Swans. +The performances are excellent +and the recording is +for the most part perfect. +The album feels live, +without any distracting deficiencies +of live recording +that would take you out +of just enjoying the music. +. +.Bl -ohang +.It Dq Feel Happiness +This track feels kind of special +since it's the only song on the album +that was never released +as part of another project. +I absolutely love this format of song. +It's like 10 minutes of build +before any lyrics happen, +which you only get after +the wave of the first part +of the song collapses. +It bookends the first disc nicely with +.Dq Blood Promise, +I think, +which is sort of the reverse. +. +.It Dq Blood On Yr Hands +This is such a great start +to the Jarboe-focused +section of the black disc. +A cappella apart from the hum +of the equipment on stage, +I love this vocal performance. +I sing this song, +terribly, +in the shower. +The lack of instrumental +seems to make it stick in my mind even more. +. +.It Dq I Crawled +This is another great vocal performance +by Jarboe. +It's so much more dynamic and intense +than the version of this song +released much earlier on Young God +with Gira's vocals. +I remember seeing a bad comment +somewhere online +from someone who couldn't stand +any Swans song Jarboe sang on. +They must have never heard +this version of +.Dq I Crawled. +Incredible. +. +.It Dq Blood Promise +My favourite track on +Swans Are Dead, +by far. +I had actually never heard of +.Dq The Whiffenpoof Song +until I looked up +the recording they use +to introduce this song +and indicate it's the last of the show. +Anyway, +this track highlights +what makes Swans live albums +so interesting. +This performance of the song +has evolved so much +from the studio recording on +The Great Annihilator. +They share the same lyrics, +but the earlier version is only 4:15, +to the live version's fifteen and a half minutes! +And it sucks me in the whole time. +As the song winds down +you can hear an audience member yell, +.Dq Don't stop! +and I agree. +. +.It Dq The Sound +One of my all-time favourite songs. +It's the one that got me to listen to +Soundtracks for the Blind, +and might've gotten me into Swans altogether. +I don't quite remember +what order I started listening to things in. +This version of it is great. +I don't think I could choose +between this and the studio recording. +There are just +two ways to enjoy it. +I love how frantic the guitars get +at the height of this track. +. +.It Dq I See Them All Lined Up +This version of the song +is way more harsh +than the version on Soundtracks. +It loses some contrast +between the verses +and the explosions of sound +punctuating them, +it just hits hard +the whole time. +. +.It Dq Yum Yab +An absolute banger. +The drums sound so good on this +and they really get me moving. +The whole thing is delightfully nasty and fun. +.El +. +.Pp +Everything else on the album +is good too, +of course, +I just don't have as much to say. +There's almost two and a half hours of music +on this thing! +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +I want to try writing +about different kinds of things here, +and this is my first attempt +at doing so. +There's more music +I want to write about, +and maybe some other +non-computer topics. diff --git a/www/text.causal.agency/023-sparse-checkout.7 b/www/text.causal.agency/023-sparse-checkout.7 new file mode 100644 index 00000000..925bc043 --- /dev/null +++ b/www/text.causal.agency/023-sparse-checkout.7 @@ -0,0 +1,144 @@ +.Dd June 9, 2021 +.Dt SPARSE-CHECKOUT 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Sparse Checkout +.Nd a cool git feature +. +.Sh DESCRIPTION +I was going to write a post about +.Xr git-subtree 1 +(and I still plan to!) +but while talking about it +with a friend +I came across another command: +.Xr git-sparse-checkout 1 . +I got pretty excited because +I already had a use case for it. +. +.Pp +.Xr git-sparse-checkout 1 +does pretty much what it sounds like. +It lets you only have +a subset of files in the repository actually +.Dq checked out . +This is really useful +for huge respositories +where you are only interested in +some part of it. +Any operation touching the working tree +is much faster because +it can skip all the files you don't care about. +. +.Pp +My use case is with the +.Fx +.Xr ports 7 +tree, +which recently moved to git +and contains almost 14 thousand files. +Working with the whole repository +was super painful. +.Xr git-status 1 , +which I run as a habit +when my shell is idle, +would take dozens of seconds +to check the whole working tree +and report back. +(I didn't get any real time measurements +before enabling +.Xr git-sparse-checkout 1 , +and I'm not about to disable it now, +since it'd have to check out +all those files again.) +I'm only actually working on +a small handful of ports, +so all that work is wasted. +Time to turn on sparse checkout: +.Bd -literal -offset indent +git sparse-checkout init --cone +.Ed +. +.Pp +The +.Fl \-cone +option here +(which I keep reading as +.Dq clone +because it's git) +restricts the kinds of patterns +you can use to select files to check out, +but makes the calculation more efficient. +Basically it means you can only select +paths along with everything below them, +which I think is pretty much +always what you want anyway. +Enabling sparse checkout +can take quite a while +because it has to do a lot of un-checking-out. +I should mention +that you can pass +.Fl \-sparse +to +.Xr git-clone 1 +to avoid ever checking out +the whole tree. +. +.Pp +The default selection when you run +.Cm init +is to check out all the files +at the root of the repository, +but none of the subdirectories. +For +.Xr ports 7 , +I also want to check out +the shared scripts and Makefiles: +.Bd -literal -offset indent +git sparse-checkout add Keywords Mk Templates Tools +.Ed +. +.Pp +And then I can selectively check out +just the ports I'm working on: +.Bd -literal -offset indent +git sparse-checkout add irc/catgirl irc/pounce +.Ed +. +.Pp +After enabling sparse checkout, +.Xr git-status 1 +takes what I'd call +a normal amount of time. +I also did this on +a couple-weeks-out-of-date copy of the +.Xr ports 7 +tree, +and when I ran +.Xr git-pull 1 +it was also really quick, +because it didn't have to bother +updating all those files +I'm not interested in. +It still downloads all the git objects, +of course, +and you can just add any new paths you need +to the sparse checkout list. +My disk usage also went down +by about a gigabyte. +. +.Pp +I'm super pleased to discover this part of git, +because it makes working with huge +and/or monorepo-style repositories +so much more feasible! +You can see how I came across it, +since +.Xr git-subtree 1 +is also a useful tool for monorepos. +Stay tuned for that post, +I guess :) +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/024-seprintf.7 b/www/text.causal.agency/024-seprintf.7 new file mode 100644 index 00000000..d1af2e1a --- /dev/null +++ b/www/text.causal.agency/024-seprintf.7 @@ -0,0 +1,137 @@ +.Dd June 12, 2021 +.Dt SEPRINTF 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm seprintf +.Nd an snprintf alternative +. +.Sh SYNOPSIS +.Ft "char *" +.Fn seprintf "char *ptr" "char *end" "const char *fmt" "..." +. +.Sh DESCRIPTION +While discussing string building in C recently, +mcf pointed out +.Xr seprint 2 +from Plan 9, +and it kind of blew my mind. +I had implemented my own function in +.Xr catgirl 1 +for building up strings using +.Xr snprintf 3 +and a struct containing +pointer, length and capacity, +but it felt out of place. +.Fn seprintf +(I add the +.Dq f , +Plan 9 doesn't) +is a much simpler +and more +.Dq C-like +interface with really nice usage patterns. +. +.Pp +The obvious difference from +.Xr snprintf 3 +is that +.Fn seprintf +takes an +.Fa end +pointer +rather than a size. +This means you need only calculate it +once for each buffer, +rather than subtracting +the running length from the buffer size. +.Fn seprintf Ap s +return value is a pointer +to the terminating null +of the string it wrote, +so you can pass that back in +to continue appending +to the same buffer. +. +.Pp +I'm not sure of the exact behaviour on Plan 9, +but my implementation indicates truncation occurred +by returning the +.Fa end +pointer. +That makes it both easy to check, +and perfectly fine to keep calling +.Fn seprintf +anyway. +It just won't write anything if +.Fa ptr +== +.Fa end . +. +.Pp +In the case of formatting failure +(which should be prevented by +compile-time format string checking, +but should still be considered), +.Fn seprintf +returns +.Dv NULL . +I'm again not sure if this matches Plan 9. +I like this a lot better than +.Xr snprintf 3 +returning -1, +because an unchecked +.Dv NULL +is likely to quickly cause a crash, +while blindly adding +.Xr snprintf 3 Ap s +return value +to your running length +is a non-obvious logic error. +. +.Sh EXAMPLES +Here's an example of what some code using +.Fn seprintf +might look like: +.Bd -literal -offset indent +char buf[4096]; +char *ptr = buf, *end = &buf[sizeof(buf)]; +ptr = seprintf(ptr, end, "argv: "); +for (int i = 1; i < argc; ++i) { + ptr = seprintf( + ptr, end, "%s%s", + (i > 1 ? ", " : ""), argv[i] + ); +} +if (ptr == end) errx(1, "truncation occurred :("); +.Ed +. +.Pp +And here is the very short implementation of it against +.Xr vsnprintf 3 +which I copy into my project header files: +.Bd -literal -offset indent +#include <stdarg.h> +#include <stdio.h> +static inline char * +seprintf(char *ptr, char *end, const char *fmt, ...) + __attribute__((format(printf, 3, 4))); +static inline char * +seprintf(char *ptr, char *end, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int n = vsnprintf(ptr, end - ptr, fmt, ap); + va_end(ap); + if (n < 0) return NULL; + if (n > end - ptr) return end; + return ptr + n; +} +.Ed +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +Another short one before +.Xr git-subtree 1 . +I just think this function +is really neat. diff --git a/www/text.causal.agency/Makefile b/www/text.causal.agency/Makefile new file mode 100644 index 00000000..a9330045 --- /dev/null +++ b/www/text.causal.agency/Makefile @@ -0,0 +1,49 @@ +WEBROOT ?= /usr/local/www/text.causal.agency +LIBEXEC ?= /usr/local/libexec + +CFLAGS += -Wall -Wextra + +TXTS += 001-make.txt +TXTS += 002-writing-mdoc.txt +TXTS += 003-pleasant-c.txt +TXTS += 004-uloc.txt +TXTS += 005-testing-c.txt +TXTS += 006-some-libs.txt +TXTS += 007-cgit-setup.txt +TXTS += 008-how-irc.txt +TXTS += 009-casual-update.txt +TXTS += 010-irc-suite.txt +TXTS += 011-libretls.txt +TXTS += 012-inability.txt +TXTS += 013-hot-tips.txt +TXTS += 014-using-vi.txt +TXTS += 015-reusing-tags.txt +TXTS += 016-using-openbsd.txt +TXTS += 017-unpasswords.txt +TXTS += 018-operating-systems.txt +TXTS += 019-mailing-list.txt +TXTS += 020-c-style.txt +TXTS += 021-time-machine.txt +TXTS += 022-swans-are-dead.txt +TXTS += 023-sparse-checkout.txt +TXTS += 024-seprintf.txt + +all: ${TXTS} + +.SUFFIXES: .7 .txt + +.7.txt: + mandoc -T utf8 $< | col -bx > $@ + +feed.atom: feed.sh ${TXTS} + sh feed.sh > feed.atom + +clean: + rm -f ${TXTS} feed.atom igp + +install: ${TXTS} feed.atom + install -p -m 644 ${TXTS} feed.atom ${WEBROOT} + +install-igp: igp + install igp ${LIBEXEC} + install -p -m 644 igp.c ${WEBROOT} diff --git a/www/text.causal.agency/feed.sh b/www/text.causal.agency/feed.sh new file mode 100644 index 00000000..f45bd326 --- /dev/null +++ b/www/text.causal.agency/feed.sh @@ -0,0 +1,55 @@ +#!/bin/sh +set -eu + +readonly Root='https://text.causal.agency' + +updated=$(date -u '+%FT%TZ') +cat <<-EOF + <?xml version="1.0" encoding="utf-8"?> + <feed xmlns="http://www.w3.org/2005/Atom"> + <title>Causal Agency</title> + <author><name>June</name><email>june@causal.agency</email></author> + <link href="${Root}"/> + <link rel="self" href="${Root}/feed.atom"/> + <id>${Root}/</id> + <updated>${updated}</updated> +EOF + +encode() { + sed ' + s/&/\&/g + s/</\</g + s/"/\"/g + ' "$@" +} + +for txt in *.txt; do + entry="${txt%.txt}.7" + date=$(grep '^[.]Dd' "$entry" | cut -c 5-) + title=$(grep '^[.]Nm' "$entry" | cut -c 5- | encode) + summary=$(grep '^[.]Nd' "$entry" | cut -c 5- | encode) + published=$(date -ju -f '%B %d, %Y %T' "${date} 00:00:00" '+%FT%TZ') + mtime=$(stat -f '%m' "$entry") + updated=$(date -ju -f '%s' "$mtime" '+%FT%TZ') + cat <<-EOF + <entry> + <title>${title}</title> + <summary>${summary}</summary> + <link href="${Root}/${txt}"/> + <id>${Root}/${txt}</id> + <published>${published}</published> + <updated>${updated}</updated> + <content type="xhtml"> + <div xmlns="http://www.w3.org/1999/xhtml"> + EOF + printf '<pre>' + encode "$txt" + cat <<-EOF + </pre> + </div> + </content> + </entry> + EOF +done + +echo '</feed>' diff --git a/www/text.causal.agency/igp.1 b/www/text.causal.agency/igp.1 new file mode 100644 index 00000000..ccfaeaa6 --- /dev/null +++ b/www/text.causal.agency/igp.1 @@ -0,0 +1,49 @@ +.Dd January 14, 2021 +.Dt IGP 1 +.Os +. +.Sh NAME +.Nm igp +.Nd insane gopher posse +. +.Sh SYNOPSIS +.Nm +.Op Fl h Ar host +.Op Fl p Ar port +.Ar directory +. +.Sh DESCRIPTION +The +.Nm +utility is a simple +Internet Gopher server +which can be started by +.Xr inetd 8 . +It serves directory listings +and files under +.Ar directory . +. +.Pp +The arguments are as follows: +.Bl -tag -width Ds +.It Fl h Ar host +Set the host used in directory listings. +The default is the local host name. +.It Fl p Ar port +Set the port used in directory listeings. +The default is port 70. +.El +. +.Sh STANDARDS +.Rs +.%A B. Alberti +.%A F. Anklesaria +.%A D. Johnson +.%A P. Lindner +.%A M. McCahill +.%A D. Torrey +.%T The Internet Gopher Protocol +.%I IETF +.%R RFC 1436 +.%D March 1993 +.Re diff --git a/www/text.causal.agency/igp.c b/www/text.causal.agency/igp.c new file mode 100644 index 00000000..d7db2b28 --- /dev/null +++ b/www/text.causal.agency/igp.c @@ -0,0 +1,143 @@ +/* Copyright (C) 2021 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sysexits.h> +#include <time.h> +#include <unistd.h> + +#ifdef __FreeBSD__ +#include <sys/capsicum.h> +#endif + +static int compar(const void *_a, const void *_b) { + const struct dirent *a = _a; + const struct dirent *b = _b; + if (a->d_type != b->d_type) { + return (a->d_type > b->d_type) - (a->d_type < b->d_type); + } + return strcmp(a->d_name, b->d_name); +} + +int main(int argc, char *argv[]) { + int error; + const char *host = NULL; + const char *port = "70"; + for (int opt; 0 < (opt = getopt(argc, argv, "h:p:"));) { + switch (opt) { + break; case 'h': host = optarg; + break; case 'p': port = optarg; + break; default: return EX_USAGE; + } + } + if (optind == argc) return EX_USAGE; + if (!host) { + static char buf[256]; + error = gethostname(buf, sizeof(buf)); + if (error) abort(); + host = buf; + } + + const char *path = argv[optind]; + int root = open(path, O_RDONLY | O_DIRECTORY); + if (root < 0) err(EX_NOINPUT, "/"); + +#ifdef __FreeBSD__ + cap_rights_t cap; + error = cap_enter() + || cap_rights_limit(STDIN_FILENO, cap_rights_init(&cap, CAP_READ)) + || cap_rights_limit(STDOUT_FILENO, cap_rights_init(&cap, CAP_WRITE)) + || cap_rights_limit(STDERR_FILENO, &cap) + || cap_rights_limit( + root, cap_rights_init(&cap, CAP_PREAD, CAP_FSTATAT, CAP_FSTATFS) + ); + if (error) abort(); +#else +#warning "This is completely insecure without capsicum(4)!" +#endif + + char buf[1024]; + if (!fgets(buf, sizeof(buf), stdin)) return EX_PROTOCOL; + char *ptr = buf; + char *sel = strsep(&ptr, "\t\r\n"); + if (sel[0] == '/') sel++; + + int fd = (sel[0] ? openat(root, sel, O_RDONLY) : root); + if (fd < 0) err(EX_NOINPUT, "%s", sel); + + struct stat stat; + error = fstat(fd, &stat); + if (error) err(EX_IOERR, "%s", sel); + if (!(stat.st_mode & (S_IFREG | S_IFDIR))) { + errx(EX_NOINPUT, "%s: Not a file or directory", sel); + } + + if (stat.st_mode & S_IFREG) { +#ifdef __FreeBSD__ + error = sendfile(fd, STDOUT_FILENO, 0, 0, NULL, NULL, 0); + if (!error) return EX_OK; +#endif + char buf[4096]; + for (ssize_t len; 0 < (len = read(fd, buf, sizeof(buf)));) { + fwrite(buf, len, 1, stdout); + } + return EX_OK; + } + + DIR *dir = fdopendir(fd); + if (!dir) err(EX_IOERR, "%s", sel); + + size_t len = 0; + size_t width = 0; + static struct dirent ents[4096]; + for (struct dirent *ent; len < 4096 && (ent = readdir(dir));) { + if (ent->d_name[0] == '.') continue; + if (ent->d_type != DT_REG && ent->d_type != DT_DIR) continue; + if (ent->d_namlen > width) width = ent->d_namlen; + ents[len++] = *ent; + } + + qsort(ents, len, sizeof(ents[0]), compar); + for (size_t i = 0; i < len; ++i) { + char mtime[26] = ""; + if (ents[i].d_type == DT_REG) { + error = fstatat(fd, ents[i].d_name, &stat, 0); + if (error) err(EX_IOERR, "%s/%s", sel, ents[i].d_name); + ctime_r(&stat.st_mtime, mtime); + mtime[24] = '\0'; + } + printf( + "%c%-*s %s\t%s%s%s\t%s\t%s\r\n", + (ents[i].d_type == DT_DIR ? '1' : '0'), + (int)width, ents[i].d_name, mtime, + sel, (sel[0] ? "/" : ""), ents[i].d_name, host, port + ); + } + + printf("i-- \t\t%s\t%s\r\n", host, port); + printf("0Served by IGP (AGPLv3)\tigp.c\ttext.causal.agency\t70\r\n"); + printf(".\r\n"); +} |