mu-0.9.18/0000755000175000017500000000000013021065721007230 500000000000000mu-0.9.18/COPYING0000644000175000017500000010451312605152236010214 00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . mu-0.9.18/TODO0000644000175000017500000001405012605152236007645 00000000000000#+STARTUP: showall * TODO (fixes, ideas, etc.) ** Future stuff *** mu - put threading information in the database, and enable getting the complete threads when searching - refactor fill_database function in test cases - don't show duplicate e-mails (i.e.. for Gmail); check the message-id *** mu-guile - move contact export to separate scm - fix logging *** mu4e - special-case replying to messages sent by self - identities (see Jacek's 'mu4e: From field in replies' mail) ==> [ workaround available, using mu4e-pre-compose-hook, dynamic folders ] - new-mail warning ==> [ workaround available, using mu4e-index-updated-hook ] - custom header fields in headers-view, message-view - show maildirs as a tree, not a list in speed bar - review emacs menus - re-factor / separate window/buffer management - enable keeping message view buffers around - better naming for draft/view buffers - header updating interferes with marks (when updating for 'mark as read', when reading a marked message) - set/unset flag editing command - handling of database upgrades - restore point after rerunning a search - make the mu4e-bookmarks format similar to the other ones - refresh current query after update? - fix mu4e-mark-set to work from the view buffer as well - open links to mails through headers-mode somehow (i.e.., mu4e-view-message-with-msgid) - improve mouse interaction (i.e., cursor vs point) - show counts of messages in searches (in main view) - show flush only if there's something to flush (and # of flushables) - fix unsafe temp-file handling - make copy paste name/address in mu4e-view possible * Done (0.9.9.x) - mu4e: scroll down –> go to next message - mu: add contact: as a shortcut for matching from/to/cc/bcc: - guile integration - statistics - 'human' dates in the headers view - :tags in headers, message view * Done :PROPERTIES: :VISIBILITY: folded :END: ** Done (0.9.9) - make contacts in the view clickable (toggle long/short display, compose message) - opening urls is too eager (now use M-RET for opening url at point, not just RET, which conflicted with using RET for scrolling) - document quoting of queries - use mu-error - tooltips in header labels - tooltip for flags field - remove --summary option (for mu find, mu view); use --summary-len instead - add sort buttons to header labels (and do the sorting) - cleanup mu-cmd-find - implement --after for mu find, to only show message files changed after a certain time (mtime) - add mu:timestamp for guile (referring to the message file's mtime) - guile automated tests - add 'mu verify' - automated tests - handle verbose/quiet/normal output 'mu verify' - check gmime 2.4 does not break - hook up mu4e with 'mu verify' - add 'help' command - refactor mu-msg-part - move widgets/ into toys/mug2, remove toys/mug/, rename toys/mug2 -> toys/mug - add guile mu:count - don't show GPG/PKCS7 sigs as attachments - fix address completion (quote names) - add support for X-Keywords (in addition to X-Label) - guile: add stats test cases - fixed iso-2022-jp (japanese) decoding - make address completion case-insensitive - recognize '*' in urls - handle exception 'The revision being read has been discarded - you should call Xapian::Database::reopen() and retry the operation' - handle passwords from get-mail shell command - support fancy (non-ascii) chars for header flags, thread prefix strings - improve performance of getting the list of maildirs - fix setting wrapped/hide state in viewer - fix ' realpath() failed for...' stuff - allow for fancy chars (> ascii), make it configurable (mu4e-use-fancy-chars) - don't user `error' for user-errors - better echo-area reporting - improve help feedback for user (command line) - handling of encrypted messages - improved checked for gmime-2.6 crypto funcs - handling of command line options / help - fix / add support for :size - mu4e~view-wrap-lines (use visual-line-mode? see Jacek's mu4e~view-wrap-lines mail) - better help - threading optimizations - actions for /all/ headers, actions for /all/ attachment - handle attached messages with attachments ** Done (0.8.9.5) - make next/prev header respect prefix argument (Jacek's patch) - make search results a stack (well, multiple stacks) - optionally keep cc with user's email - enable setting/unsetting 'Flagged' on messages - allow narrowing of search results - interactive split-view control (Jacek) - view images inline - *FIX* slow maildirs when there are many - *FIX* ignore unrecognized maildir flag letters - *FIX*: reply-to does not make it to the frontend - *FIX* wrong buffer deleted after sending (see '(non mu) buffer is killed') - rich text composing (with org-mode) - let message-mode deal with burying/killing compose buffers - *FIX* add runtime check for imagemagick - *FIX* no error note if target message already exists (when moving) - sorting + show / hide threads - *FIX* having multiple header views visible - *FIX* fix for strings where len (g_utf8_strdown (str)) > len (str) - make sure marks correspond to the *current* message in message view (see https://github.com/djcb/mu/issues/26) - *FIX* don't remove unknown message flags when moving - make guile/gtk/webkit dependency optional - improve fringe marks (see https://github.com/djcb/mu/issues/21) - mark message, decide what to do with them later (i.e.. 'deferred marking') - custom predicate functions for marking - make mu4e buffer killing less aggressive (i.e.., DWIM) - about mu4e - hide some headers when composing - fix sorting subjects with ':' (but not 'Re:' or 'Fwd:') - strip signature from original when replying - make refresh after changing sort, threads the default - contact completion (see Jacek's 'mu4e: using' mail) - *FIX* emacs23 mailto: handling - *FIX* message interference - *FIX* emacs23.2+ auto-completion # Local Variables: # mode: org # End: mu-0.9.18/Makefile.in0000644000175000017500000007373013021065705011231 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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 = : build_triplet = @build@ host_triplet = @host@ subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/perlmod.m4 $(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 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; }; \ } am__installdirs = "$(DESTDIR)$(docdir)" DATA = $(doc_DATA) 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 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 = m4 man lib guile mu mu4e contrib toys am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(top_srcdir)/gtest.mk AUTHORS COPYING ChangeLog INSTALL NEWS \ README TODO compile config.guess config.sub depcomp install-sh \ ltmain.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 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@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMACS = @EMACS@ EMACSLOADPATH = @EMACSLOADPATH@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GMIME_CFLAGS = @GMIME_CFLAGS@ GMIME_LIBS = @GMIME_LIBS@ GREP = @GREP@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ GUILE_BINARY = @GUILE_BINARY@ GUILE_CFLAGS = @GUILE_CFLAGS@ GUILE_LIBS = @GUILE_LIBS@ GUILE_SITEDIR = @GUILE_SITEDIR@ GUILE_SNARF = @GUILE_SNARF@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MU_DOC_DIR = @MU_DOC_DIR@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PMCCABE = @PMCCABE@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SORT = @SORT@ STRIP = @STRIP@ VERSION = @VERSION@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ XAPIAN_CONFIG = @XAPIAN_CONFIG@ XAPIAN_CXXFLAGS = @XAPIAN_CXXFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ 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 = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_makeinfo = @have_makeinfo@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ lispdir = @lispdir@ 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@ TEST_PROGS = @BUILD_GUILE_FALSE@guile = @BUILD_GUILE_TRUE@guile = guile @BUILD_MU4E_FALSE@mu4e = @BUILD_MU4E_TRUE@mu4e = mu4e @BUILD_PERL_FALSE@perl = @BUILD_PERL_TRUE@perl = perl SUBDIRS = m4 man lib $(guile) mu $(mu4e) contrib toys # $(perl) ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = \ TODO \ HACKING \ gtest.mk \ NEWS \ NEWS.org \ autogen.sh doc_DATA = \ NEWS.org all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtest.mk $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu 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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtest.mk $(am__empty): $(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 mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt install-docDATA: $(doc_DATA) @$(NORMAL_INSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ done uninstall-docDATA: @$(NORMAL_UNINSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) # 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 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: $(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-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 ;;\ 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) dvi \ && $(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 $(DATA) config.h installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(docdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done 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 clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr \ distclean-libtool distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-docDATA 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 mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-docDATA .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 \ clean-libtool cscope cscopelist-am ctags ctags-am dist \ dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ dist-xz dist-zip distcheck distclean distclean-generic \ distclean-hdr distclean-libtool distclean-tags distcleancheck \ distdir distuninstallcheck dvi dvi-am html html-am info \ info-am install install-am install-data install-data-am \ install-docDATA 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 \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-docDATA .PRECIOUS: Makefile # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof # so we can say 'make test' check: test cleanupnote cleanupnote: @echo -e "\nNote: you can remove the mu-test- dir in your tempdir" @echo "after 'make check' has finished." tags: gtags # this warns about function that have a cyclomatic complexity of > 10, # which is a sign that it needs some refactoring. requires the pmccabe # tool. If all is fine, it outputs nothing cc10: @$(PMCCABE) `find . -name '*.c' -o -name '*.cc'` \ | grep -v mu-str-normalize.c \ | grep -v mu_str_subject_normalize \ | grep -v tests \ | sort -nr | awk '($$1 > 10)' # this warns about functions that are over 35 non-comment lines long, which is a # sign that they need some refactoring. requires the pmccabe tool. if # all is fine, it outputs nothing # note, some functions are exempted from this rule. line35: @$(PMCCABE) -c `find . -name '*.c' -o -name '*.cc'` \ | grep -v mu-str-normalize.c \ | grep -v mu_str_subject_normalize \ | grep -v config_options_group_find \ | grep -v SCM_DEFINE \ | grep -v tests \ | awk '($$5 > 35)' # get all todo/fixme messages fixme: @grep -i 'FIXME\|TODO' `find src -type f` # check whether we can run make distcheck from the repo version gitcheck: cd `mktemp -d`; \ git clone git://github.com/djcb/mu.git ; \ cd mu; \ autoreconf -i ; \ ./configure ; \ make distcheck # 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: mu-0.9.18/guile/0000755000175000017500000000000013021065721010335 500000000000000mu-0.9.18/guile/texinfo.tex0000644000175000017500000116703612605152261012474 00000000000000% texinfo.tex -- TeX macros to handle Texinfo files. % % Load plain if necessary, i.e., if running under initex. \expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi % \def\texinfoversion{2013-02-01.11} % % Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, % 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, % 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc. % % This texinfo.tex file is free software: you can redistribute it and/or % modify it under the terms of the GNU General Public License as % published by the Free Software Foundation, either version 3 of the % License, or (at your option) any later version. % % This texinfo.tex file is distributed in the hope that it will be % useful, but WITHOUT ANY WARRANTY; without even the implied warranty % of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU % General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program. If not, see . % % As a special exception, when this file is read by TeX when processing % a Texinfo source document, you may use the result without % restriction. This Exception is an additional permission under section 7 % of the GNU General Public License, version 3 ("GPLv3"). % % Please try the latest version of texinfo.tex before submitting bug % reports; you can get the latest version from: % http://ftp.gnu.org/gnu/texinfo/ (the Texinfo release area), or % http://ftpmirror.gnu.org/texinfo/ (same, via a mirror), or % http://www.gnu.org/software/texinfo/ (the Texinfo home page) % The texinfo.tex in any given distribution could well be out % of date, so if that's what you're using, please check. % % Send bug reports to bug-texinfo@gnu.org. Please include including a % complete document in each bug report with which we can reproduce the % problem. Patches are, of course, greatly appreciated. % % To process a Texinfo manual with TeX, it's most reliable to use the % texi2dvi shell script that comes with the distribution. For a simple % manual foo.texi, however, you can get away with this: % tex foo.texi % texindex foo.?? % tex foo.texi % tex foo.texi % dvips foo.dvi -o # or whatever; this makes foo.ps. % The extra TeX runs get the cross-reference information correct. % Sometimes one run after texindex suffices, and sometimes you need more % than two; texi2dvi does it as many times as necessary. % % It is possible to adapt texinfo.tex for other languages, to some % extent. You can get the existing language-specific files from the % full Texinfo distribution. % % The GNU Texinfo home page is http://www.gnu.org/software/texinfo. \message{Loading texinfo [version \texinfoversion]:} % If in a .fmt file, print the version number % and turn on active characters that we couldn't do earlier because % they might have appeared in the input file name. \everyjob{\message{[Texinfo version \texinfoversion]}% \catcode`+=\active \catcode`\_=\active} \chardef\other=12 % We never want plain's \outer definition of \+ in Texinfo. % For @tex, we can use \tabalign. \let\+ = \relax % Save some plain tex macros whose names we will redefine. \let\ptexb=\b \let\ptexbullet=\bullet \let\ptexc=\c \let\ptexcomma=\, \let\ptexdot=\. \let\ptexdots=\dots \let\ptexend=\end \let\ptexequiv=\equiv \let\ptexexclam=\! \let\ptexfootnote=\footnote \let\ptexgtr=> \let\ptexhat=^ \let\ptexi=\i \let\ptexindent=\indent \let\ptexinsert=\insert \let\ptexlbrace=\{ \let\ptexless=< \let\ptexnewwrite\newwrite \let\ptexnoindent=\noindent \let\ptexplus=+ \let\ptexraggedright=\raggedright \let\ptexrbrace=\} \let\ptexslash=\/ \let\ptexstar=\* \let\ptext=\t \let\ptextop=\top {\catcode`\'=\active \global\let\ptexquoteright'}% active in plain's math mode % If this character appears in an error message or help string, it % starts a new line in the output. \newlinechar = `^^J % Use TeX 3.0's \inputlineno to get the line number, for better error % messages, but if we're using an old version of TeX, don't do anything. % \ifx\inputlineno\thisisundefined \let\linenumber = \empty % Pre-3.0. \else \def\linenumber{l.\the\inputlineno:\space} \fi % Set up fixed words for English if not already set. \ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi \ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi \ifx\putworderror\undefined \gdef\putworderror{error}\fi \ifx\putwordfile\undefined \gdef\putwordfile{file}\fi \ifx\putwordin\undefined \gdef\putwordin{in}\fi \ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi \ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi \ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi \ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi \ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi \ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi \ifx\putwordof\undefined \gdef\putwordof{of}\fi \ifx\putwordon\undefined \gdef\putwordon{on}\fi \ifx\putwordpage\undefined \gdef\putwordpage{page}\fi \ifx\putwordsection\undefined \gdef\putwordsection{section}\fi \ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi \ifx\putwordsee\undefined \gdef\putwordsee{see}\fi \ifx\putwordSee\undefined \gdef\putwordSee{See}\fi \ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi \ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi % \ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi \ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi \ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi \ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi \ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi \ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi \ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi \ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi \ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi \ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi \ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi \ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi % \ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi \ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi \ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi \ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi \ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi % Since the category of space is not known, we have to be careful. \chardef\spacecat = 10 \def\spaceisspace{\catcode`\ =\spacecat} % sometimes characters are active, so we need control sequences. \chardef\ampChar = `\& \chardef\colonChar = `\: \chardef\commaChar = `\, \chardef\dashChar = `\- \chardef\dotChar = `\. \chardef\exclamChar= `\! \chardef\hashChar = `\# \chardef\lquoteChar= `\` \chardef\questChar = `\? \chardef\rquoteChar= `\' \chardef\semiChar = `\; \chardef\slashChar = `\/ \chardef\underChar = `\_ % Ignore a token. % \def\gobble#1{} % The following is used inside several \edef's. \def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} % Hyphenation fixes. \hyphenation{ Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script ap-pen-dix bit-map bit-maps data-base data-bases eshell fall-ing half-way long-est man-u-script man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces spell-ing spell-ings stand-alone strong-est time-stamp time-stamps which-ever white-space wide-spread wrap-around } % Margin to add to right of even pages, to left of odd pages. \newdimen\bindingoffset \newdimen\normaloffset \newdimen\pagewidth \newdimen\pageheight % For a final copy, take out the rectangles % that mark overfull boxes (in case you have decided % that the text looks ok even though it passes the margin). % \def\finalout{\overfullrule=0pt } % Sometimes it is convenient to have everything in the transcript file % and nothing on the terminal. We don't just call \tracingall here, % since that produces some useless output on the terminal. We also make % some effort to order the tracing commands to reduce output in the log % file; cf. trace.sty in LaTeX. % \def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% \def\loggingall{% \tracingstats2 \tracingpages1 \tracinglostchars2 % 2 gives us more in etex \tracingparagraphs1 \tracingoutput1 \tracingmacros2 \tracingrestores1 \showboxbreadth\maxdimen \showboxdepth\maxdimen \ifx\eTeXversion\thisisundefined\else % etex gives us more logging \tracingscantokens1 \tracingifs1 \tracinggroups1 \tracingnesting2 \tracingassigns1 \fi \tracingcommands3 % 3 gives us more in etex \errorcontextlines16 }% % @errormsg{MSG}. Do the index-like expansions on MSG, but if things % aren't perfect, it's not the end of the world, being an error message, % after all. % \def\errormsg{\begingroup \indexnofonts \doerrormsg} \def\doerrormsg#1{\errmessage{#1}} % add check for \lastpenalty to plain's definitions. If the last thing % we did was a \nobreak, we don't want to insert more space. % \def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount \removelastskip\penalty-50\smallskip\fi\fi} \def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount \removelastskip\penalty-100\medskip\fi\fi} \def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount \removelastskip\penalty-200\bigskip\fi\fi} % Do @cropmarks to get crop marks. % \newif\ifcropmarks \let\cropmarks = \cropmarkstrue % % Dimensions to add cropmarks at corners. % Added by P. A. MacKay, 12 Nov. 1986 % \newdimen\outerhsize \newdimen\outervsize % set by the paper size routines \newdimen\cornerlong \cornerlong=1pc \newdimen\cornerthick \cornerthick=.3pt \newdimen\topandbottommargin \topandbottommargin=.75in % Output a mark which sets \thischapter, \thissection and \thiscolor. % We dump everything together because we only have one kind of mark. % This works because we only use \botmark / \topmark, not \firstmark. % % A mark contains a subexpression of the \ifcase ... \fi construct. % \get*marks macros below extract the needed part using \ifcase. % % Another complication is to let the user choose whether \thischapter % (\thissection) refers to the chapter (section) in effect at the top % of a page, or that at the bottom of a page. The solution is % described on page 260 of The TeXbook. It involves outputting two % marks for the sectioning macros, one before the section break, and % one after. I won't pretend I can describe this better than DEK... \def\domark{% \toks0=\expandafter{\lastchapterdefs}% \toks2=\expandafter{\lastsectiondefs}% \toks4=\expandafter{\prevchapterdefs}% \toks6=\expandafter{\prevsectiondefs}% \toks8=\expandafter{\lastcolordefs}% \mark{% \the\toks0 \the\toks2 \noexpand\or \the\toks4 \the\toks6 \noexpand\else \the\toks8 }% } % \topmark doesn't work for the very first chapter (after the title % page or the contents), so we use \firstmark there -- this gets us % the mark with the chapter defs, unless the user sneaks in, e.g., % @setcolor (or @url, or @link, etc.) between @contents and the very % first @chapter. \def\gettopheadingmarks{% \ifcase0\topmark\fi \ifx\thischapter\empty \ifcase0\firstmark\fi \fi } \def\getbottomheadingmarks{\ifcase1\botmark\fi} \def\getcolormarks{\ifcase2\topmark\fi} % Avoid "undefined control sequence" errors. \def\lastchapterdefs{} \def\lastsectiondefs{} \def\prevchapterdefs{} \def\prevsectiondefs{} \def\lastcolordefs{} % Main output routine. \chardef\PAGE = 255 \output = {\onepageout{\pagecontents\PAGE}} \newbox\headlinebox \newbox\footlinebox % \onepageout takes a vbox as an argument. Note that \pagecontents % does insertions, but you have to call it yourself. \def\onepageout#1{% \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi % \ifodd\pageno \advance\hoffset by \bindingoffset \else \advance\hoffset by -\bindingoffset\fi % % Do this outside of the \shipout so @code etc. will be expanded in % the headline as they should be, not taken literally (outputting ''code). \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}% \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}% % {% % Have to do this stuff outside the \shipout because we want it to % take effect in \write's, yet the group defined by the \vbox ends % before the \shipout runs. % \indexdummies % don't expand commands in the output. \normalturnoffactive % \ in index entries must not stay \, e.g., if % the page break happens to be in the middle of an example. % We don't want .vr (or whatever) entries like this: % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}} % "\acronym" won't work when it's read back in; % it needs to be % {\code {{\tt \backslashcurfont }acronym} \shipout\vbox{% % Do this early so pdf references go to the beginning of the page. \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi % \ifcropmarks \vbox to \outervsize\bgroup \hsize = \outerhsize \vskip-\topandbottommargin \vtop to0pt{% \line{\ewtop\hfil\ewtop}% \nointerlineskip \line{% \vbox{\moveleft\cornerthick\nstop}% \hfill \vbox{\moveright\cornerthick\nstop}% }% \vss}% \vskip\topandbottommargin \line\bgroup \hfil % center the page within the outer (page) hsize. \ifodd\pageno\hskip\bindingoffset\fi \vbox\bgroup \fi % \unvbox\headlinebox \pagebody{#1}% \ifdim\ht\footlinebox > 0pt % Only leave this space if the footline is nonempty. % (We lessened \vsize for it in \oddfootingyyy.) % The \baselineskip=24pt in plain's \makefootline has no effect. \vskip 24pt \unvbox\footlinebox \fi % \ifcropmarks \egroup % end of \vbox\bgroup \hfil\egroup % end of (centering) \line\bgroup \vskip\topandbottommargin plus1fill minus1fill \boxmaxdepth = \cornerthick \vbox to0pt{\vss \line{% \vbox{\moveleft\cornerthick\nsbot}% \hfill \vbox{\moveright\cornerthick\nsbot}% }% \nointerlineskip \line{\ewbot\hfil\ewbot}% }% \egroup % \vbox from first cropmarks clause \fi }% end of \shipout\vbox }% end of group with \indexdummies \advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi } \newinsert\margin \dimen\margin=\maxdimen \def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} {\catcode`\@ =11 \gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi % marginal hacks, juha@viisa.uucp (Juha Takala) \ifvoid\margin\else % marginal info is present \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi \dimen@=\dp#1\relax \unvbox#1\relax \ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi \ifr@ggedbottom \kern-\dimen@ \vfil \fi} } % Here are the rules for the cropmarks. Note that they are % offset so that the space between them is truly \outerhsize or \outervsize % (P. A. MacKay, 12 November, 1986) % \def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} \def\nstop{\vbox {\hrule height\cornerthick depth\cornerlong width\cornerthick}} \def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} \def\nsbot{\vbox {\hrule height\cornerlong depth\cornerthick width\cornerthick}} % Parse an argument, then pass it to #1. The argument is the rest of % the input line (except we remove a trailing comment). #1 should be a % macro which expects an ordinary undelimited TeX argument. % \def\parsearg{\parseargusing{}} \def\parseargusing#1#2{% \def\argtorun{#2}% \begingroup \obeylines \spaceisspace #1% \parseargline\empty% Insert the \empty token, see \finishparsearg below. } {\obeylines % \gdef\parseargline#1^^M{% \endgroup % End of the group started in \parsearg. \argremovecomment #1\comment\ArgTerm% }% } % First remove any @comment, then any @c comment. \def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} \def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} % Each occurrence of `\^^M' or `\^^M' is replaced by a single space. % % \argremovec might leave us with trailing space, e.g., % @end itemize @c foo % This space token undergoes the same procedure and is eventually removed % by \finishparsearg. % \def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} \def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} \def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% \def\temp{#3}% \ifx\temp\empty % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: \let\temp\finishparsearg \else \let\temp\argcheckspaces \fi % Put the space token in: \temp#1 #3\ArgTerm } % If a _delimited_ argument is enclosed in braces, they get stripped; so % to get _exactly_ the rest of the line, we had to prevent such situation. % We prepended an \empty token at the very beginning and we expand it now, % just before passing the control to \argtorun. % (Similarly, we have to think about #3 of \argcheckspacesY above: it is % either the null string, or it ends with \^^M---thus there is no danger % that a pair of braces would be stripped. % % But first, we have to remove the trailing space token. % \def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} % \parseargdef\foo{...} % is roughly equivalent to % \def\foo{\parsearg\Xfoo} % \def\Xfoo#1{...} % % Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my % favourite TeX trick. --kasal, 16nov03 \def\parseargdef#1{% \expandafter \doparseargdef \csname\string#1\endcsname #1% } \def\doparseargdef#1#2{% \def#2{\parsearg#1}% \def#1##1% } % Several utility definitions with active space: { \obeyspaces \gdef\obeyedspace{ } % Make each space character in the input produce a normal interword % space in the output. Don't allow a line break at this space, as this % is used only in environments like @example, where each line of input % should produce a line of output anyway. % \gdef\sepspaces{\obeyspaces\let =\tie} % If an index command is used in an @example environment, any spaces % therein should become regular spaces in the raw index file, not the % expansion of \tie (\leavevmode \penalty \@M \ ). \gdef\unsepspaces{\let =\space} } \def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} % Define the framework for environments in texinfo.tex. It's used like this: % % \envdef\foo{...} % \def\Efoo{...} % % It's the responsibility of \envdef to insert \begingroup before the % actual body; @end closes the group after calling \Efoo. \envdef also % defines \thisenv, so the current environment is known; @end checks % whether the environment name matches. The \checkenv macro can also be % used to check whether the current environment is the one expected. % % Non-false conditionals (@iftex, @ifset) don't fit into this, so they % are not treated as environments; they don't open a group. (The % implementation of @end takes care not to call \endgroup in this % special case.) % At run-time, environments start with this: \def\startenvironment#1{\begingroup\def\thisenv{#1}} % initialize \let\thisenv\empty % ... but they get defined via ``\envdef\foo{...}'': \long\def\envdef#1#2{\def#1{\startenvironment#1#2}} \def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} % Check whether we're in the right environment: \def\checkenv#1{% \def\temp{#1}% \ifx\thisenv\temp \else \badenverr \fi } % Environment mismatch, #1 expected: \def\badenverr{% \errhelp = \EMsimple \errmessage{This command can appear only \inenvironment\temp, not \inenvironment\thisenv}% } \def\inenvironment#1{% \ifx#1\empty outside of any environment% \else in environment \expandafter\string#1% \fi } % @end foo executes the definition of \Efoo. % But first, it executes a specialized version of \checkenv % \parseargdef\end{% \if 1\csname iscond.#1\endcsname \else % The general wording of \badenverr may not be ideal. \expandafter\checkenv\csname#1\endcsname \csname E#1\endcsname \endgroup \fi } \newhelp\EMsimple{Press RETURN to continue.} % Be sure we're in horizontal mode when doing a tie, since we make space % equivalent to this in @example-like environments. Otherwise, a space % at the beginning of a line will start with \penalty -- and % since \penalty is valid in vertical mode, we'd end up putting the % penalty on the vertical list instead of in the new paragraph. {\catcode`@ = 11 % Avoid using \@M directly, because that causes trouble % if the definition is written into an index file. \global\let\tiepenalty = \@M \gdef\tie{\leavevmode\penalty\tiepenalty\ } } % @: forces normal size whitespace following. \def\:{\spacefactor=1000 } % @* forces a line break. \def\*{\unskip\hfil\break\hbox{}\ignorespaces} % @/ allows a line break. \let\/=\allowbreak % @. is an end-of-sentence period. \def\.{.\spacefactor=\endofsentencespacefactor\space} % @! is an end-of-sentence bang. \def\!{!\spacefactor=\endofsentencespacefactor\space} % @? is an end-of-sentence query. \def\?{?\spacefactor=\endofsentencespacefactor\space} % @frenchspacing on|off says whether to put extra space after punctuation. % \def\onword{on} \def\offword{off} % \parseargdef\frenchspacing{% \def\temp{#1}% \ifx\temp\onword \plainfrenchspacing \else\ifx\temp\offword \plainnonfrenchspacing \else \errhelp = \EMsimple \errmessage{Unknown @frenchspacing option `\temp', must be on|off}% \fi\fi } % @w prevents a word break. Without the \leavevmode, @w at the % beginning of a paragraph, when TeX is still in vertical mode, would % produce a whole line of output instead of starting the paragraph. \def\w#1{\leavevmode\hbox{#1}} % @group ... @end group forces ... to be all on one page, by enclosing % it in a TeX vbox. We use \vtop instead of \vbox to construct the box % to keep its height that of a normal line. According to the rules for % \topskip (p.114 of the TeXbook), the glue inserted is % max (\topskip - \ht (first item), 0). If that height is large, % therefore, no glue is inserted, and the space between the headline and % the text is small, which looks bad. % % Another complication is that the group might be very large. This can % cause the glue on the previous page to be unduly stretched, because it % does not have much material. In this case, it's better to add an % explicit \vfill so that the extra space is at the bottom. The % threshold for doing this is if the group is more than \vfilllimit % percent of a page (\vfilllimit can be changed inside of @tex). % \newbox\groupbox \def\vfilllimit{0.7} % \envdef\group{% \ifnum\catcode`\^^M=\active \else \errhelp = \groupinvalidhelp \errmessage{@group invalid in context where filling is enabled}% \fi \startsavinginserts % \setbox\groupbox = \vtop\bgroup % Do @comment since we are called inside an environment such as % @example, where each end-of-line in the input causes an % end-of-line in the output. We don't want the end-of-line after % the `@group' to put extra space in the output. Since @group % should appear on a line by itself (according to the Texinfo % manual), we don't worry about eating any user text. \comment } % % The \vtop produces a box with normal height and large depth; thus, TeX puts % \baselineskip glue before it, and (when the next line of text is done) % \lineskip glue after it. Thus, space below is not quite equal to space % above. But it's pretty close. \def\Egroup{% % To get correct interline space between the last line of the group % and the first line afterwards, we have to propagate \prevdepth. \endgraf % Not \par, as it may have been set to \lisppar. \global\dimen1 = \prevdepth \egroup % End the \vtop. % \dimen0 is the vertical size of the group's box. \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox % \dimen2 is how much space is left on the page (more or less). \dimen2 = \pageheight \advance\dimen2 by -\pagetotal % if the group doesn't fit on the current page, and it's a big big % group, force a page break. \ifdim \dimen0 > \dimen2 \ifdim \pagetotal < \vfilllimit\pageheight \page \fi \fi \box\groupbox \prevdepth = \dimen1 \checkinserts } % % TeX puts in an \escapechar (i.e., `@') at the beginning of the help % message, so this ends up printing `@group can only ...'. % \newhelp\groupinvalidhelp{% group can only be used in environments such as @example,^^J% where each line of input produces a line of output.} % @need space-in-mils % forces a page break if there is not space-in-mils remaining. \newdimen\mil \mil=0.001in \parseargdef\need{% % Ensure vertical mode, so we don't make a big box in the middle of a % paragraph. \par % % If the @need value is less than one line space, it's useless. \dimen0 = #1\mil \dimen2 = \ht\strutbox \advance\dimen2 by \dp\strutbox \ifdim\dimen0 > \dimen2 % % Do a \strut just to make the height of this box be normal, so the % normal leading is inserted relative to the preceding line. % And a page break here is fine. \vtop to #1\mil{\strut\vfil}% % % TeX does not even consider page breaks if a penalty added to the % main vertical list is 10000 or more. But in order to see if the % empty box we just added fits on the page, we must make it consider % page breaks. On the other hand, we don't want to actually break the % page after the empty box. So we use a penalty of 9999. % % There is an extremely small chance that TeX will actually break the % page at this \penalty, if there are no other feasible breakpoints in % sight. (If the user is using lots of big @group commands, which % almost-but-not-quite fill up a page, TeX will have a hard time doing % good page breaking, for example.) However, I could not construct an % example where a page broke at this \penalty; if it happens in a real % document, then we can reconsider our strategy. \penalty9999 % % Back up by the size of the box, whether we did a page break or not. \kern -#1\mil % % Do not allow a page break right after this kern. \nobreak \fi } % @br forces paragraph break (and is undocumented). \let\br = \par % @page forces the start of a new page. % \def\page{\par\vfill\supereject} % @exdent text.... % outputs text on separate line in roman font, starting at standard page margin % This records the amount of indent in the innermost environment. % That's how much \exdent should take out. \newskip\exdentamount % This defn is used inside fill environments such as @defun. \parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} % This defn is used inside nofill environments such as @example. \parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount \leftline{\hskip\leftskip{\rm#1}}}} % @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current % paragraph. For more general purposes, use the \margin insertion % class. WHICH is `l' or `r'. Not documented, written for gawk manual. % \newskip\inmarginspacing \inmarginspacing=1cm \def\strutdepth{\dp\strutbox} % \def\doinmargin#1#2{\strut\vadjust{% \nobreak \kern-\strutdepth \vtop to \strutdepth{% \baselineskip=\strutdepth \vss % if you have multiple lines of stuff to put here, you'll need to % make the vbox yourself of the appropriate size. \ifx#1l% \llap{\ignorespaces #2\hskip\inmarginspacing}% \else \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% \fi \null }% }} \def\inleftmargin{\doinmargin l} \def\inrightmargin{\doinmargin r} % % @inmargin{TEXT [, RIGHT-TEXT]} % (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; % else use TEXT for both). % \def\inmargin#1{\parseinmargin #1,,\finish} \def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0 > 0pt \def\lefttext{#1}% have both texts \def\righttext{#2}% \else \def\lefttext{#1}% have only one text \def\righttext{#1}% \fi % \ifodd\pageno \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin \else \def\temp{\inleftmargin\lefttext}% \fi \temp } % @| inserts a changebar to the left of the current line. It should % surround any changed text. This approach does *not* work if the % change spans more than two lines of output. To handle that, we would % have adopt a much more difficult approach (putting marks into the main % vertical list for the beginning and end of each change). This command % is not documented, not supported, and doesn't work. % \def\|{% % \vadjust can only be used in horizontal mode. \leavevmode % % Append this vertical mode material after the current line in the output. \vadjust{% % We want to insert a rule with the height and depth of the current % leading; that is exactly what \strutbox is supposed to record. \vskip-\baselineskip % % \vadjust-items are inserted at the left edge of the type. So % the \llap here moves out into the left-hand margin. \llap{% % % For a thicker or thinner bar, change the `1pt'. \vrule height\baselineskip width1pt % % This is the space between the bar and the text. \hskip 12pt }% }% } % @include FILE -- \input text of FILE. % \def\include{\parseargusing\filenamecatcodes\includezzz} \def\includezzz#1{% \pushthisfilestack \def\thisfile{#1}% {% \makevalueexpandable % we want to expand any @value in FILE. \turnoffactive % and allow special characters in the expansion \indexnofonts % Allow `@@' and other weird things in file names. \wlog{texinfo.tex: doing @include of #1^^J}% \edef\temp{\noexpand\input #1 }% % % This trickery is to read FILE outside of a group, in case it makes % definitions, etc. \expandafter }\temp \popthisfilestack } \def\filenamecatcodes{% \catcode`\\=\other \catcode`~=\other \catcode`^=\other \catcode`_=\other \catcode`|=\other \catcode`<=\other \catcode`>=\other \catcode`+=\other \catcode`-=\other \catcode`\`=\other \catcode`\'=\other } \def\pushthisfilestack{% \expandafter\pushthisfilestackX\popthisfilestack\StackTerm } \def\pushthisfilestackX{% \expandafter\pushthisfilestackY\thisfile\StackTerm } \def\pushthisfilestackY #1\StackTerm #2\StackTerm {% \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% } \def\popthisfilestack{\errthisfilestackempty} \def\errthisfilestackempty{\errmessage{Internal error: the stack of filenames is empty.}} % \def\thisfile{} % @center line % outputs that line, centered. % \parseargdef\center{% \ifhmode \let\centersub\centerH \else \let\centersub\centerV \fi \centersub{\hfil \ignorespaces#1\unskip \hfil}% \let\centersub\relax % don't let the definition persist, just in case } \def\centerH#1{{% \hfil\break \advance\hsize by -\leftskip \advance\hsize by -\rightskip \line{#1}% \break }} % \newcount\centerpenalty \def\centerV#1{% % The idea here is the same as in \startdefun, \cartouche, etc.: if % @center is the first thing after a section heading, we need to wipe % out the negative parskip inserted by \sectionheading, but still % prevent a page break here. \centerpenalty = \lastpenalty \ifnum\centerpenalty>10000 \vskip\parskip \fi \ifnum\centerpenalty>9999 \penalty\centerpenalty \fi \line{\kern\leftskip #1\kern\rightskip}% } % @sp n outputs n lines of vertical space % \parseargdef\sp{\vskip #1\baselineskip} % @comment ...line which is ignored... % @c is the same as @comment % @ignore ... @end ignore is another way to write a comment % \def\comment{\begingroup \catcode`\^^M=\other% \catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% \commentxxx} {\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}} % \let\c=\comment % @paragraphindent NCHARS % We'll use ems for NCHARS, close enough. % NCHARS can also be the word `asis' or `none'. % We cannot feasibly implement @paragraphindent asis, though. % \def\asisword{asis} % no translation, these are keywords \def\noneword{none} % \parseargdef\paragraphindent{% \def\temp{#1}% \ifx\temp\asisword \else \ifx\temp\noneword \defaultparindent = 0pt \else \defaultparindent = #1em \fi \fi \parindent = \defaultparindent } % @exampleindent NCHARS % We'll use ems for NCHARS like @paragraphindent. % It seems @exampleindent asis isn't necessary, but % I preserve it to make it similar to @paragraphindent. \parseargdef\exampleindent{% \def\temp{#1}% \ifx\temp\asisword \else \ifx\temp\noneword \lispnarrowing = 0pt \else \lispnarrowing = #1em \fi \fi } % @firstparagraphindent WORD % If WORD is `none', then suppress indentation of the first paragraph % after a section heading. If WORD is `insert', then do indent at such % paragraphs. % % The paragraph indentation is suppressed or not by calling % \suppressfirstparagraphindent, which the sectioning commands do. % We switch the definition of this back and forth according to WORD. % By default, we suppress indentation. % \def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} \def\insertword{insert} % \parseargdef\firstparagraphindent{% \def\temp{#1}% \ifx\temp\noneword \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent \else\ifx\temp\insertword \let\suppressfirstparagraphindent = \relax \else \errhelp = \EMsimple \errmessage{Unknown @firstparagraphindent option `\temp'}% \fi\fi } % Here is how we actually suppress indentation. Redefine \everypar to % \kern backwards by \parindent, and then reset itself to empty. % % We also make \indent itself not actually do anything until the next % paragraph. % \gdef\dosuppressfirstparagraphindent{% \gdef\indent{% \restorefirstparagraphindent \indent }% \gdef\noindent{% \restorefirstparagraphindent \noindent }% \global\everypar = {% \kern -\parindent \restorefirstparagraphindent }% } \gdef\restorefirstparagraphindent{% \global \let \indent = \ptexindent \global \let \noindent = \ptexnoindent \global \everypar = {}% } % @refill is a no-op. \let\refill=\relax % If working on a large document in chapters, it is convenient to % be able to disable indexing, cross-referencing, and contents, for test runs. % This is done with @novalidate (before @setfilename). % \newif\iflinks \linkstrue % by default we want the aux files. \let\novalidate = \linksfalse % @setfilename is done at the beginning of every texinfo file. % So open here the files we need to have open while reading the input. % This makes it possible to make a .fmt file for texinfo. \def\setfilename{% \fixbackslash % Turn off hack to swallow `\input texinfo'. \iflinks \tryauxfile % Open the new aux file. TeX will close it automatically at exit. \immediate\openout\auxfile=\jobname.aux \fi % \openindices needs to do some work in any case. \openindices \let\setfilename=\comment % Ignore extra @setfilename cmds. % % If texinfo.cnf is present on the system, read it. % Useful for site-wide @afourpaper, etc. \openin 1 texinfo.cnf \ifeof 1 \else \input texinfo.cnf \fi \closein 1 % \comment % Ignore the actual filename. } % Called from \setfilename. % \def\openindices{% \newindex{cp}% \newcodeindex{fn}% \newcodeindex{vr}% \newcodeindex{tp}% \newcodeindex{ky}% \newcodeindex{pg}% } % @bye. \outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} \message{pdf,} % adobe `portable' document format \newcount\tempnum \newcount\lnkcount \newtoks\filename \newcount\filenamelength \newcount\pgn \newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD \newbox\boxA \newcount\countA \newif\ifpdf \newif\ifpdfmakepagedest % when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 % can be set). So we test for \relax and 0 as well as being undefined. \ifx\pdfoutput\thisisundefined \else \ifx\pdfoutput\relax \else \ifcase\pdfoutput \else \pdftrue \fi \fi \fi % PDF uses PostScript string constants for the names of xref targets, % for display in the outlines, and in other places. Thus, we have to % double any backslashes. Otherwise, a name like "\node" will be % interpreted as a newline (\n), followed by o, d, e. Not good. % % See http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html and % related messages. The final outcome is that it is up to the TeX user % to double the backslashes and otherwise make the string valid, so % that's what we do. pdftex 1.30.0 (ca.2005) introduced a primitive to % do this reliably, so we use it. % #1 is a control sequence in which to do the replacements, % which we \xdef. \def\txiescapepdf#1{% \ifx\pdfescapestring\thisisundefined % No primitive available; should we give a warning or log? % Many times it won't matter. \else % The expandable \pdfescapestring primitive escapes parentheses, % backslashes, and other special chars. \xdef#1{\pdfescapestring{#1}}% \fi } \newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images with PDF output, and none of those formats could be found. (.eps cannot be supported due to the design of the PDF format; use regular TeX (DVI output) for that.)} \ifpdf % % Color manipulation macros based on pdfcolor.tex, % except using rgb instead of cmyk; the latter is said to render as a % very dark gray on-screen and a very dark halftone in print, instead % of actual black. \def\rgbDarkRed{0.50 0.09 0.12} \def\rgbBlack{0 0 0} % % k sets the color for filling (usual text, etc.); % K sets the color for stroking (thin rules, e.g., normal _'s). \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}} % % Set color, and create a mark which defines \thiscolor accordingly, % so that \makeheadline knows which color to restore. \def\setcolor#1{% \xdef\lastcolordefs{\gdef\noexpand\thiscolor{#1}}% \domark \pdfsetcolor{#1}% } % \def\maincolor{\rgbBlack} \pdfsetcolor{\maincolor} \edef\thiscolor{\maincolor} \def\lastcolordefs{} % \def\makefootline{% \baselineskip24pt \line{\pdfsetcolor{\maincolor}\the\footline}% } % \def\makeheadline{% \vbox to 0pt{% \vskip-22.5pt \line{% \vbox to8.5pt{}% % Extract \thiscolor definition from the marks. \getcolormarks % Typeset the headline with \maincolor, then restore the color. \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% }% \vss }% \nointerlineskip } % % \pdfcatalog{/PageMode /UseOutlines} % % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). \def\dopdfimage#1#2#3{% \def\pdfimagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% \def\pdfimageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% % % pdftex (and the PDF format) support .pdf, .png, .jpg (among % others). Let's try in that order, PDF first since if % someone has a scalable image, presumably better to use that than a % bitmap. \let\pdfimgext=\empty \begingroup \openin 1 #1.pdf \ifeof 1 \openin 1 #1.PDF \ifeof 1 \openin 1 #1.png \ifeof 1 \openin 1 #1.jpg \ifeof 1 \openin 1 #1.jpeg \ifeof 1 \openin 1 #1.JPG \ifeof 1 \errhelp = \nopdfimagehelp \errmessage{Could not find image file #1 for pdf}% \else \gdef\pdfimgext{JPG}% \fi \else \gdef\pdfimgext{jpeg}% \fi \else \gdef\pdfimgext{jpg}% \fi \else \gdef\pdfimgext{png}% \fi \else \gdef\pdfimgext{PDF}% \fi \else \gdef\pdfimgext{pdf}% \fi \closein 1 \endgroup % % without \immediate, ancient pdftex seg faults when the same image is % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) \ifnum\pdftexversion < 14 \immediate\pdfimage \else \immediate\pdfximage \fi \ifdim \wd0 >0pt width \pdfimagewidth \fi \ifdim \wd2 >0pt height \pdfimageheight \fi \ifnum\pdftexversion<13 #1.\pdfimgext \else {#1.\pdfimgext}% \fi \ifnum\pdftexversion < 14 \else \pdfrefximage \pdflastximage \fi} % \def\pdfmkdest#1{{% % We have to set dummies so commands such as @code, and characters % such as \, aren't expanded when present in a section title. \indexnofonts \turnoffactive \makevalueexpandable \def\pdfdestname{#1}% \txiescapepdf\pdfdestname \safewhatsit{\pdfdest name{\pdfdestname} xyz}% }} % % used to mark target names; must be expandable. \def\pdfmkpgn#1{#1} % % by default, use a color that is dark enough to print on paper as % nearly black, but still distinguishable for online viewing. \def\urlcolor{\rgbDarkRed} \def\linkcolor{\rgbDarkRed} \def\endlink{\setcolor{\maincolor}\pdfendlink} % % Adding outlines to PDF; macros for calculating structure of outlines % come from Petr Olsak \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% \else \csname#1\endcsname \fi} \def\advancenumber#1{\tempnum=\expnumber{#1}\relax \advance\tempnum by 1 \expandafter\xdef\csname#1\endcsname{\the\tempnum}} % % #1 is the section text, which is what will be displayed in the % outline by the pdf viewer. #2 is the pdf expression for the number % of subentries (or empty, for subsubsections). #3 is the node text, % which might be empty if this toc entry had no corresponding node. % #4 is the page number % \def\dopdfoutline#1#2#3#4{% % Generate a link to the node text if that exists; else, use the % page number. We could generate a destination for the section % text in the case where a section has no node, but it doesn't % seem worth the trouble, since most documents are normally structured. \edef\pdfoutlinedest{#3}% \ifx\pdfoutlinedest\empty \def\pdfoutlinedest{#4}% \else \txiescapepdf\pdfoutlinedest \fi % % Also escape PDF chars in the display string. \edef\pdfoutlinetext{#1}% \txiescapepdf\pdfoutlinetext % \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}% } % \def\pdfmakeoutlines{% \begingroup % Read toc silently, to get counts of subentries for \pdfoutline. \def\partentry##1##2##3##4{}% ignore parts in the outlines \def\numchapentry##1##2##3##4{% \def\thischapnum{##2}% \def\thissecnum{0}% \def\thissubsecnum{0}% }% \def\numsecentry##1##2##3##4{% \advancenumber{chap\thischapnum}% \def\thissecnum{##2}% \def\thissubsecnum{0}% }% \def\numsubsecentry##1##2##3##4{% \advancenumber{sec\thissecnum}% \def\thissubsecnum{##2}% }% \def\numsubsubsecentry##1##2##3##4{% \advancenumber{subsec\thissubsecnum}% }% \def\thischapnum{0}% \def\thissecnum{0}% \def\thissubsecnum{0}% % % use \def rather than \let here because we redefine \chapentry et % al. a second time, below. \def\appentry{\numchapentry}% \def\appsecentry{\numsecentry}% \def\appsubsecentry{\numsubsecentry}% \def\appsubsubsecentry{\numsubsubsecentry}% \def\unnchapentry{\numchapentry}% \def\unnsecentry{\numsecentry}% \def\unnsubsecentry{\numsubsecentry}% \def\unnsubsubsecentry{\numsubsubsecentry}% \readdatafile{toc}% % % Read toc second time, this time actually producing the outlines. % The `-' means take the \expnumber as the absolute number of % subentries, which we calculated on our first read of the .toc above. % % We use the node names as the destinations. \def\numchapentry##1##2##3##4{% \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% \def\numsecentry##1##2##3##4{% \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% \def\numsubsecentry##1##2##3##4{% \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% \def\numsubsubsecentry##1##2##3##4{% count is always zero \dopdfoutline{##1}{}{##3}{##4}}% % % PDF outlines are displayed using system fonts, instead of % document fonts. Therefore we cannot use special characters, % since the encoding is unknown. For example, the eogonek from % Latin 2 (0xea) gets translated to a | character. Info from % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. % % TODO this right, we have to translate 8-bit characters to % their "best" equivalent, based on the @documentencoding. Too % much work for too little return. Just use the ASCII equivalents % we use for the index sort strings. % \indexnofonts \setupdatafile % We can have normal brace characters in the PDF outlines, unlike % Texinfo index files. So set that up. \def\{{\lbracecharliteral}% \def\}{\rbracecharliteral}% \catcode`\\=\active \otherbackslash \input \tocreadfilename \endgroup } {\catcode`[=1 \catcode`]=2 \catcode`{=\other \catcode`}=\other \gdef\lbracecharliteral[{]% \gdef\rbracecharliteral[}]% ] % \def\skipspaces#1{\def\PP{#1}\def\D{|}% \ifx\PP\D\let\nextsp\relax \else\let\nextsp\skipspaces \addtokens{\filename}{\PP}% \advance\filenamelength by 1 \fi \nextsp} \def\getfilename#1{% \filenamelength=0 % If we don't expand the argument now, \skipspaces will get % snagged on things like "@value{foo}". \edef\temp{#1}% \expandafter\skipspaces\temp|\relax } \ifnum\pdftexversion < 14 \let \startlink \pdfannotlink \else \let \startlink \pdfstartlink \fi % make a live url in pdf output. \def\pdfurl#1{% \begingroup % it seems we really need yet another set of dummies; have not % tried to figure out what each command should do in the context % of @url. for now, just make @/ a no-op, that's the only one % people have actually reported a problem with. % \normalturnoffactive \def\@{@}% \let\/=\empty \makevalueexpandable % do we want to go so far as to use \indexnofonts instead of just % special-casing \var here? \def\var##1{##1}% % \leavevmode\setcolor{\urlcolor}% \startlink attr{/Border [0 0 0]}% user{/Subtype /Link /A << /S /URI /URI (#1) >>}% \endgroup} \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} \def\maketoks{% \expandafter\poptoks\the\toksA|ENDTOKS|\relax \ifx\first0\adn0 \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 \else \ifnum0=\countA\else\makelink\fi \ifx\first.\let\next=\done\else \let\next=\maketoks \addtokens{\toksB}{\the\toksD} \ifx\first,\addtokens{\toksB}{\space}\fi \fi \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \next} \def\makelink{\addtokens{\toksB}% {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} \def\pdflink#1{% \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} \setcolor{\linkcolor}#1\endlink} \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} \else % non-pdf mode \let\pdfmkdest = \gobble \let\pdfurl = \gobble \let\endlink = \relax \let\setcolor = \gobble \let\pdfsetcolor = \gobble \let\pdfmakeoutlines = \relax \fi % \ifx\pdfoutput \message{fonts,} % Change the current font style to #1, remembering it in \curfontstyle. % For now, we do not accumulate font styles: @b{@i{foo}} prints foo in % italics, not bold italics. % \def\setfontstyle#1{% \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. \csname ten#1\endcsname % change the current font } % Select #1 fonts with the current style. % \def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname} \def\rm{\fam=0 \setfontstyle{rm}} \def\it{\fam=\itfam \setfontstyle{it}} \def\sl{\fam=\slfam \setfontstyle{sl}} \def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} \def\tt{\fam=\ttfam \setfontstyle{tt}} % Unfortunately, we have to override this for titles and the like, since % in those cases "rm" is bold. Sigh. \def\rmisbold{\rm\def\curfontstyle{bf}} % Texinfo sort of supports the sans serif font style, which plain TeX does not. % So we set up a \sf. \newfam\sffam \def\sf{\fam=\sffam \setfontstyle{sf}} \let\li = \sf % Sometimes we call it \li, not \sf. % We don't need math for this font style. \def\ttsl{\setfontstyle{ttsl}} % Set the baselineskip to #1, and the lineskip and strut size % correspondingly. There is no deep meaning behind these magic numbers % used as factors; they just match (closely enough) what Knuth defined. % \def\lineskipfactor{.08333} \def\strutheightpercent{.70833} \def\strutdepthpercent {.29167} % % can get a sort of poor man's double spacing by redefining this. \def\baselinefactor{1} % \newdimen\textleading \def\setleading#1{% \dimen0 = #1\relax \normalbaselineskip = \baselinefactor\dimen0 \normallineskip = \lineskipfactor\normalbaselineskip \normalbaselines \setbox\strutbox =\hbox{% \vrule width0pt height\strutheightpercent\baselineskip depth \strutdepthpercent \baselineskip }% } % PDF CMaps. See also LaTeX's t1.cmap. % % do nothing with this by default. \expandafter\let\csname cmapOT1\endcsname\gobble \expandafter\let\csname cmapOT1IT\endcsname\gobble \expandafter\let\csname cmapOT1TT\endcsname\gobble % if we are producing pdf, and we have \pdffontattr, then define cmaps. % (\pdffontattr was introduced many years ago, but people still run % older pdftex's; it's easy to conditionalize, so we do.) \ifpdf \ifx\pdffontattr\thisisundefined \else \begingroup \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap %%DocumentNeededResources: ProcSet (CIDInit) %%IncludeResource: ProcSet (CIDInit) %%BeginResource: CMap (TeX-OT1-0) %%Title: (TeX-OT1-0 TeX OT1 0) %%Version: 1.000 %%EndComments /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (TeX) /Ordering (OT1) /Supplement 0 >> def /CMapName /TeX-OT1-0 def /CMapType 2 def 1 begincodespacerange <00> <7F> endcodespacerange 8 beginbfrange <00> <01> <0393> <09> <0A> <03A8> <23> <26> <0023> <28> <3B> <0028> <3F> <5B> <003F> <5D> <5E> <005D> <61> <7A> <0061> <7B> <7C> <2013> endbfrange 40 beginbfchar <02> <0398> <03> <039B> <04> <039E> <05> <03A0> <06> <03A3> <07> <03D2> <08> <03A6> <0B> <00660066> <0C> <00660069> <0D> <0066006C> <0E> <006600660069> <0F> <00660066006C> <10> <0131> <11> <0237> <12> <0060> <13> <00B4> <14> <02C7> <15> <02D8> <16> <00AF> <17> <02DA> <18> <00B8> <19> <00DF> <1A> <00E6> <1B> <0153> <1C> <00F8> <1D> <00C6> <1E> <0152> <1F> <00D8> <21> <0021> <22> <201D> <27> <2019> <3C> <00A1> <3D> <003D> <3E> <00BF> <5C> <201C> <5F> <02D9> <60> <2018> <7D> <02DD> <7E> <007E> <7F> <00A8> endbfchar endcmap CMapName currentdict /CMap defineresource pop end end %%EndResource %%EOF }\endgroup \expandafter\edef\csname cmapOT1\endcsname#1{% \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% }% % % \cmapOT1IT \begingroup \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap %%DocumentNeededResources: ProcSet (CIDInit) %%IncludeResource: ProcSet (CIDInit) %%BeginResource: CMap (TeX-OT1IT-0) %%Title: (TeX-OT1IT-0 TeX OT1IT 0) %%Version: 1.000 %%EndComments /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (TeX) /Ordering (OT1IT) /Supplement 0 >> def /CMapName /TeX-OT1IT-0 def /CMapType 2 def 1 begincodespacerange <00> <7F> endcodespacerange 8 beginbfrange <00> <01> <0393> <09> <0A> <03A8> <25> <26> <0025> <28> <3B> <0028> <3F> <5B> <003F> <5D> <5E> <005D> <61> <7A> <0061> <7B> <7C> <2013> endbfrange 42 beginbfchar <02> <0398> <03> <039B> <04> <039E> <05> <03A0> <06> <03A3> <07> <03D2> <08> <03A6> <0B> <00660066> <0C> <00660069> <0D> <0066006C> <0E> <006600660069> <0F> <00660066006C> <10> <0131> <11> <0237> <12> <0060> <13> <00B4> <14> <02C7> <15> <02D8> <16> <00AF> <17> <02DA> <18> <00B8> <19> <00DF> <1A> <00E6> <1B> <0153> <1C> <00F8> <1D> <00C6> <1E> <0152> <1F> <00D8> <21> <0021> <22> <201D> <23> <0023> <24> <00A3> <27> <2019> <3C> <00A1> <3D> <003D> <3E> <00BF> <5C> <201C> <5F> <02D9> <60> <2018> <7D> <02DD> <7E> <007E> <7F> <00A8> endbfchar endcmap CMapName currentdict /CMap defineresource pop end end %%EndResource %%EOF }\endgroup \expandafter\edef\csname cmapOT1IT\endcsname#1{% \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% }% % % \cmapOT1TT \begingroup \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap %%DocumentNeededResources: ProcSet (CIDInit) %%IncludeResource: ProcSet (CIDInit) %%BeginResource: CMap (TeX-OT1TT-0) %%Title: (TeX-OT1TT-0 TeX OT1TT 0) %%Version: 1.000 %%EndComments /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (TeX) /Ordering (OT1TT) /Supplement 0 >> def /CMapName /TeX-OT1TT-0 def /CMapType 2 def 1 begincodespacerange <00> <7F> endcodespacerange 5 beginbfrange <00> <01> <0393> <09> <0A> <03A8> <21> <26> <0021> <28> <5F> <0028> <61> <7E> <0061> endbfrange 32 beginbfchar <02> <0398> <03> <039B> <04> <039E> <05> <03A0> <06> <03A3> <07> <03D2> <08> <03A6> <0B> <2191> <0C> <2193> <0D> <0027> <0E> <00A1> <0F> <00BF> <10> <0131> <11> <0237> <12> <0060> <13> <00B4> <14> <02C7> <15> <02D8> <16> <00AF> <17> <02DA> <18> <00B8> <19> <00DF> <1A> <00E6> <1B> <0153> <1C> <00F8> <1D> <00C6> <1E> <0152> <1F> <00D8> <20> <2423> <27> <2019> <60> <2018> <7F> <00A8> endbfchar endcmap CMapName currentdict /CMap defineresource pop end end %%EndResource %%EOF }\endgroup \expandafter\edef\csname cmapOT1TT\endcsname#1{% \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% }% \fi\fi % Set the font macro #1 to the font named \fontprefix#2. % #3 is the font's design size, #4 is a scale factor, #5 is the CMap % encoding (only OT1, OT1IT and OT1TT are allowed, or empty to omit). % Example: % #1 = \textrm % #2 = \rmshape % #3 = 10 % #4 = \mainmagstep % #5 = OT1 % \def\setfont#1#2#3#4#5{% \font#1=\fontprefix#2#3 scaled #4 \csname cmap#5\endcsname#1% } % This is what gets called when #5 of \setfont is empty. \let\cmap\gobble % % (end of cmaps) % Use cm as the default font prefix. % To specify the font prefix, you must define \fontprefix % before you read in texinfo.tex. \ifx\fontprefix\thisisundefined \def\fontprefix{cm} \fi % Support font families that don't use the same naming scheme as CM. \def\rmshape{r} \def\rmbshape{bx} % where the normal face is bold \def\bfshape{b} \def\bxshape{bx} \def\ttshape{tt} \def\ttbshape{tt} \def\ttslshape{sltt} \def\itshape{ti} \def\itbshape{bxti} \def\slshape{sl} \def\slbshape{bxsl} \def\sfshape{ss} \def\sfbshape{ss} \def\scshape{csc} \def\scbshape{csc} % Definitions for a main text size of 11pt. (The default in Texinfo.) % \def\definetextfontsizexi{% % Text fonts (11.2pt, magstep1). \def\textnominalsize{11pt} \edef\mainmagstep{\magstephalf} \setfont\textrm\rmshape{10}{\mainmagstep}{OT1} \setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} \setfont\textbf\bfshape{10}{\mainmagstep}{OT1} \setfont\textit\itshape{10}{\mainmagstep}{OT1IT} \setfont\textsl\slshape{10}{\mainmagstep}{OT1} \setfont\textsf\sfshape{10}{\mainmagstep}{OT1} \setfont\textsc\scshape{10}{\mainmagstep}{OT1} \setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} \font\texti=cmmi10 scaled \mainmagstep \font\textsy=cmsy10 scaled \mainmagstep \def\textecsize{1095} % A few fonts for @defun names and args. \setfont\defbf\bfshape{10}{\magstep1}{OT1} \setfont\deftt\ttshape{10}{\magstep1}{OT1TT} \setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT} \def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} % Fonts for indices, footnotes, small examples (9pt). \def\smallnominalsize{9pt} \setfont\smallrm\rmshape{9}{1000}{OT1} \setfont\smalltt\ttshape{9}{1000}{OT1TT} \setfont\smallbf\bfshape{10}{900}{OT1} \setfont\smallit\itshape{9}{1000}{OT1IT} \setfont\smallsl\slshape{9}{1000}{OT1} \setfont\smallsf\sfshape{9}{1000}{OT1} \setfont\smallsc\scshape{10}{900}{OT1} \setfont\smallttsl\ttslshape{10}{900}{OT1TT} \font\smalli=cmmi9 \font\smallsy=cmsy9 \def\smallecsize{0900} % Fonts for small examples (8pt). \def\smallernominalsize{8pt} \setfont\smallerrm\rmshape{8}{1000}{OT1} \setfont\smallertt\ttshape{8}{1000}{OT1TT} \setfont\smallerbf\bfshape{10}{800}{OT1} \setfont\smallerit\itshape{8}{1000}{OT1IT} \setfont\smallersl\slshape{8}{1000}{OT1} \setfont\smallersf\sfshape{8}{1000}{OT1} \setfont\smallersc\scshape{10}{800}{OT1} \setfont\smallerttsl\ttslshape{10}{800}{OT1TT} \font\smalleri=cmmi8 \font\smallersy=cmsy8 \def\smallerecsize{0800} % Fonts for title page (20.4pt): \def\titlenominalsize{20pt} \setfont\titlerm\rmbshape{12}{\magstep3}{OT1} \setfont\titleit\itbshape{10}{\magstep4}{OT1IT} \setfont\titlesl\slbshape{10}{\magstep4}{OT1} \setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} \setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} \setfont\titlesf\sfbshape{17}{\magstep1}{OT1} \let\titlebf=\titlerm \setfont\titlesc\scbshape{10}{\magstep4}{OT1} \font\titlei=cmmi12 scaled \magstep3 \font\titlesy=cmsy10 scaled \magstep4 \def\titleecsize{2074} % Chapter (and unnumbered) fonts (17.28pt). \def\chapnominalsize{17pt} \setfont\chaprm\rmbshape{12}{\magstep2}{OT1} \setfont\chapit\itbshape{10}{\magstep3}{OT1IT} \setfont\chapsl\slbshape{10}{\magstep3}{OT1} \setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT} \setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT} \setfont\chapsf\sfbshape{17}{1000}{OT1} \let\chapbf=\chaprm \setfont\chapsc\scbshape{10}{\magstep3}{OT1} \font\chapi=cmmi12 scaled \magstep2 \font\chapsy=cmsy10 scaled \magstep3 \def\chapecsize{1728} % Section fonts (14.4pt). \def\secnominalsize{14pt} \setfont\secrm\rmbshape{12}{\magstep1}{OT1} \setfont\secit\itbshape{10}{\magstep2}{OT1IT} \setfont\secsl\slbshape{10}{\magstep2}{OT1} \setfont\sectt\ttbshape{12}{\magstep1}{OT1TT} \setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT} \setfont\secsf\sfbshape{12}{\magstep1}{OT1} \let\secbf\secrm \setfont\secsc\scbshape{10}{\magstep2}{OT1} \font\seci=cmmi12 scaled \magstep1 \font\secsy=cmsy10 scaled \magstep2 \def\sececsize{1440} % Subsection fonts (13.15pt). \def\ssecnominalsize{13pt} \setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1} \setfont\ssecit\itbshape{10}{1315}{OT1IT} \setfont\ssecsl\slbshape{10}{1315}{OT1} \setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT} \setfont\ssecttsl\ttslshape{10}{1315}{OT1TT} \setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1} \let\ssecbf\ssecrm \setfont\ssecsc\scbshape{10}{1315}{OT1} \font\sseci=cmmi12 scaled \magstephalf \font\ssecsy=cmsy10 scaled 1315 \def\ssececsize{1200} % Reduced fonts for @acro in text (10pt). \def\reducednominalsize{10pt} \setfont\reducedrm\rmshape{10}{1000}{OT1} \setfont\reducedtt\ttshape{10}{1000}{OT1TT} \setfont\reducedbf\bfshape{10}{1000}{OT1} \setfont\reducedit\itshape{10}{1000}{OT1IT} \setfont\reducedsl\slshape{10}{1000}{OT1} \setfont\reducedsf\sfshape{10}{1000}{OT1} \setfont\reducedsc\scshape{10}{1000}{OT1} \setfont\reducedttsl\ttslshape{10}{1000}{OT1TT} \font\reducedi=cmmi10 \font\reducedsy=cmsy10 \def\reducedecsize{1000} \textleading = 13.2pt % line spacing for 11pt CM \textfonts % reset the current fonts \rm } % end of 11pt text font size definitions, \definetextfontsizexi % Definitions to make the main text be 10pt Computer Modern, with % section, chapter, etc., sizes following suit. This is for the GNU % Press printing of the Emacs 22 manual. Maybe other manuals in the % future. Used with @smallbook, which sets the leading to 12pt. % \def\definetextfontsizex{% % Text fonts (10pt). \def\textnominalsize{10pt} \edef\mainmagstep{1000} \setfont\textrm\rmshape{10}{\mainmagstep}{OT1} \setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} \setfont\textbf\bfshape{10}{\mainmagstep}{OT1} \setfont\textit\itshape{10}{\mainmagstep}{OT1IT} \setfont\textsl\slshape{10}{\mainmagstep}{OT1} \setfont\textsf\sfshape{10}{\mainmagstep}{OT1} \setfont\textsc\scshape{10}{\mainmagstep}{OT1} \setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} \font\texti=cmmi10 scaled \mainmagstep \font\textsy=cmsy10 scaled \mainmagstep \def\textecsize{1000} % A few fonts for @defun names and args. \setfont\defbf\bfshape{10}{\magstephalf}{OT1} \setfont\deftt\ttshape{10}{\magstephalf}{OT1TT} \setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT} \def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} % Fonts for indices, footnotes, small examples (9pt). \def\smallnominalsize{9pt} \setfont\smallrm\rmshape{9}{1000}{OT1} \setfont\smalltt\ttshape{9}{1000}{OT1TT} \setfont\smallbf\bfshape{10}{900}{OT1} \setfont\smallit\itshape{9}{1000}{OT1IT} \setfont\smallsl\slshape{9}{1000}{OT1} \setfont\smallsf\sfshape{9}{1000}{OT1} \setfont\smallsc\scshape{10}{900}{OT1} \setfont\smallttsl\ttslshape{10}{900}{OT1TT} \font\smalli=cmmi9 \font\smallsy=cmsy9 \def\smallecsize{0900} % Fonts for small examples (8pt). \def\smallernominalsize{8pt} \setfont\smallerrm\rmshape{8}{1000}{OT1} \setfont\smallertt\ttshape{8}{1000}{OT1TT} \setfont\smallerbf\bfshape{10}{800}{OT1} \setfont\smallerit\itshape{8}{1000}{OT1IT} \setfont\smallersl\slshape{8}{1000}{OT1} \setfont\smallersf\sfshape{8}{1000}{OT1} \setfont\smallersc\scshape{10}{800}{OT1} \setfont\smallerttsl\ttslshape{10}{800}{OT1TT} \font\smalleri=cmmi8 \font\smallersy=cmsy8 \def\smallerecsize{0800} % Fonts for title page (20.4pt): \def\titlenominalsize{20pt} \setfont\titlerm\rmbshape{12}{\magstep3}{OT1} \setfont\titleit\itbshape{10}{\magstep4}{OT1IT} \setfont\titlesl\slbshape{10}{\magstep4}{OT1} \setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} \setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} \setfont\titlesf\sfbshape{17}{\magstep1}{OT1} \let\titlebf=\titlerm \setfont\titlesc\scbshape{10}{\magstep4}{OT1} \font\titlei=cmmi12 scaled \magstep3 \font\titlesy=cmsy10 scaled \magstep4 \def\titleecsize{2074} % Chapter fonts (14.4pt). \def\chapnominalsize{14pt} \setfont\chaprm\rmbshape{12}{\magstep1}{OT1} \setfont\chapit\itbshape{10}{\magstep2}{OT1IT} \setfont\chapsl\slbshape{10}{\magstep2}{OT1} \setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT} \setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT} \setfont\chapsf\sfbshape{12}{\magstep1}{OT1} \let\chapbf\chaprm \setfont\chapsc\scbshape{10}{\magstep2}{OT1} \font\chapi=cmmi12 scaled \magstep1 \font\chapsy=cmsy10 scaled \magstep2 \def\chapecsize{1440} % Section fonts (12pt). \def\secnominalsize{12pt} \setfont\secrm\rmbshape{12}{1000}{OT1} \setfont\secit\itbshape{10}{\magstep1}{OT1IT} \setfont\secsl\slbshape{10}{\magstep1}{OT1} \setfont\sectt\ttbshape{12}{1000}{OT1TT} \setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT} \setfont\secsf\sfbshape{12}{1000}{OT1} \let\secbf\secrm \setfont\secsc\scbshape{10}{\magstep1}{OT1} \font\seci=cmmi12 \font\secsy=cmsy10 scaled \magstep1 \def\sececsize{1200} % Subsection fonts (10pt). \def\ssecnominalsize{10pt} \setfont\ssecrm\rmbshape{10}{1000}{OT1} \setfont\ssecit\itbshape{10}{1000}{OT1IT} \setfont\ssecsl\slbshape{10}{1000}{OT1} \setfont\ssectt\ttbshape{10}{1000}{OT1TT} \setfont\ssecttsl\ttslshape{10}{1000}{OT1TT} \setfont\ssecsf\sfbshape{10}{1000}{OT1} \let\ssecbf\ssecrm \setfont\ssecsc\scbshape{10}{1000}{OT1} \font\sseci=cmmi10 \font\ssecsy=cmsy10 \def\ssececsize{1000} % Reduced fonts for @acro in text (9pt). \def\reducednominalsize{9pt} \setfont\reducedrm\rmshape{9}{1000}{OT1} \setfont\reducedtt\ttshape{9}{1000}{OT1TT} \setfont\reducedbf\bfshape{10}{900}{OT1} \setfont\reducedit\itshape{9}{1000}{OT1IT} \setfont\reducedsl\slshape{9}{1000}{OT1} \setfont\reducedsf\sfshape{9}{1000}{OT1} \setfont\reducedsc\scshape{10}{900}{OT1} \setfont\reducedttsl\ttslshape{10}{900}{OT1TT} \font\reducedi=cmmi9 \font\reducedsy=cmsy9 \def\reducedecsize{0900} \divide\parskip by 2 % reduce space between paragraphs \textleading = 12pt % line spacing for 10pt CM \textfonts % reset the current fonts \rm } % end of 10pt text font size definitions, \definetextfontsizex % We provide the user-level command % @fonttextsize 10 % (or 11) to redefine the text font size. pt is assumed. % \def\xiword{11} \def\xword{10} \def\xwordpt{10pt} % \parseargdef\fonttextsize{% \def\textsizearg{#1}% %\wlog{doing @fonttextsize \textsizearg}% % % Set \globaldefs so that documents can use this inside @tex, since % makeinfo 4.8 does not support it, but we need it nonetheless. % \begingroup \globaldefs=1 \ifx\textsizearg\xword \definetextfontsizex \else \ifx\textsizearg\xiword \definetextfontsizexi \else \errhelp=\EMsimple \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} \fi\fi \endgroup } % In order for the font changes to affect most math symbols and letters, % we have to define the \textfont of the standard families. Since % texinfo doesn't allow for producing subscripts and superscripts except % in the main text, we don't bother to reset \scriptfont and % \scriptscriptfont (which would also require loading a lot more fonts). % \def\resetmathfonts{% \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf \textfont\ttfam=\tentt \textfont\sffam=\tensf } % The font-changing commands redefine the meanings of \tenSTYLE, instead % of just \STYLE. We do this because \STYLE needs to also set the % current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire % \tenSTYLE to set the current font. % % Each font-changing command also sets the names \lsize (one size lower) % and \lllsize (three sizes lower). These relative commands are used in % the LaTeX logo and acronyms. % % This all needs generalizing, badly. % \def\textfonts{% \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy \let\tenttsl=\textttsl \def\curfontsize{text}% \def\lsize{reduced}\def\lllsize{smaller}% \resetmathfonts \setleading{\textleading}} \def\titlefonts{% \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy \let\tenttsl=\titlettsl \def\curfontsize{title}% \def\lsize{chap}\def\lllsize{subsec}% \resetmathfonts \setleading{27pt}} \def\titlefont#1{{\titlefonts\rmisbold #1}} \def\chapfonts{% \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy \let\tenttsl=\chapttsl \def\curfontsize{chap}% \def\lsize{sec}\def\lllsize{text}% \resetmathfonts \setleading{19pt}} \def\secfonts{% \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy \let\tenttsl=\secttsl \def\curfontsize{sec}% \def\lsize{subsec}\def\lllsize{reduced}% \resetmathfonts \setleading{16pt}} \def\subsecfonts{% \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy \let\tenttsl=\ssecttsl \def\curfontsize{ssec}% \def\lsize{text}\def\lllsize{small}% \resetmathfonts \setleading{15pt}} \let\subsubsecfonts = \subsecfonts \def\reducedfonts{% \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy \let\tenttsl=\reducedttsl \def\curfontsize{reduced}% \def\lsize{small}\def\lllsize{smaller}% \resetmathfonts \setleading{10.5pt}} \def\smallfonts{% \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy \let\tenttsl=\smallttsl \def\curfontsize{small}% \def\lsize{smaller}\def\lllsize{smaller}% \resetmathfonts \setleading{10.5pt}} \def\smallerfonts{% \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy \let\tenttsl=\smallerttsl \def\curfontsize{smaller}% \def\lsize{smaller}\def\lllsize{smaller}% \resetmathfonts \setleading{9.5pt}} % Fonts for short table of contents. \setfont\shortcontrm\rmshape{12}{1000}{OT1} \setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12 \setfont\shortcontsl\slshape{12}{1000}{OT1} \setfont\shortconttt\ttshape{12}{1000}{OT1TT} % Define these just so they can be easily changed for other fonts. \def\angleleft{$\langle$} \def\angleright{$\rangle$} % Set the fonts to use with the @small... environments. \let\smallexamplefonts = \smallfonts % About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample % can fit this many characters: % 8.5x11=86 smallbook=72 a4=90 a5=69 % If we use \scriptfonts (8pt), then we can fit this many characters: % 8.5x11=90+ smallbook=80 a4=90+ a5=77 % For me, subjectively, the few extra characters that fit aren't worth % the additional smallness of 8pt. So I'm making the default 9pt. % % By the way, for comparison, here's what fits with @example (10pt): % 8.5x11=71 smallbook=60 a4=75 a5=58 % --karl, 24jan03. % Set up the default fonts, so we can use them for creating boxes. % \definetextfontsizexi \message{markup,} % Check if we are currently using a typewriter font. Since all the % Computer Modern typewriter fonts have zero interword stretch (and % shrink), and it is reasonable to expect all typewriter fonts to have % this property, we can check that font parameter. % \def\ifmonospace{\ifdim\fontdimen3\font=0pt } % Markup style infrastructure. \defmarkupstylesetup\INITMACRO will % define and register \INITMACRO to be called on markup style changes. % \INITMACRO can check \currentmarkupstyle for the innermost % style and the set of \ifmarkupSTYLE switches for all styles % currently in effect. \newif\ifmarkupvar \newif\ifmarkupsamp \newif\ifmarkupkey %\newif\ifmarkupfile % @file == @samp. %\newif\ifmarkupoption % @option == @samp. \newif\ifmarkupcode \newif\ifmarkupkbd %\newif\ifmarkupenv % @env == @code. %\newif\ifmarkupcommand % @command == @code. \newif\ifmarkuptex % @tex (and part of @math, for now). \newif\ifmarkupexample \newif\ifmarkupverb \newif\ifmarkupverbatim \let\currentmarkupstyle\empty \def\setupmarkupstyle#1{% \csname markup#1true\endcsname \def\currentmarkupstyle{#1}% \markupstylesetup } \let\markupstylesetup\empty \def\defmarkupstylesetup#1{% \expandafter\def\expandafter\markupstylesetup \expandafter{\markupstylesetup #1}% \def#1% } % Markup style setup for left and right quotes. \defmarkupstylesetup\markupsetuplq{% \expandafter\let\expandafter \temp \csname markupsetuplq\currentmarkupstyle\endcsname \ifx\temp\relax \markupsetuplqdefault \else \temp \fi } \defmarkupstylesetup\markupsetuprq{% \expandafter\let\expandafter \temp \csname markupsetuprq\currentmarkupstyle\endcsname \ifx\temp\relax \markupsetuprqdefault \else \temp \fi } { \catcode`\'=\active \catcode`\`=\active \gdef\markupsetuplqdefault{\let`\lq} \gdef\markupsetuprqdefault{\let'\rq} \gdef\markupsetcodequoteleft{\let`\codequoteleft} \gdef\markupsetcodequoteright{\let'\codequoteright} } \let\markupsetuplqcode \markupsetcodequoteleft \let\markupsetuprqcode \markupsetcodequoteright % \let\markupsetuplqexample \markupsetcodequoteleft \let\markupsetuprqexample \markupsetcodequoteright % \let\markupsetuplqkbd \markupsetcodequoteleft \let\markupsetuprqkbd \markupsetcodequoteright % \let\markupsetuplqsamp \markupsetcodequoteleft \let\markupsetuprqsamp \markupsetcodequoteright % \let\markupsetuplqverb \markupsetcodequoteleft \let\markupsetuprqverb \markupsetcodequoteright % \let\markupsetuplqverbatim \markupsetcodequoteleft \let\markupsetuprqverbatim \markupsetcodequoteright % Allow an option to not use regular directed right quote/apostrophe % (char 0x27), but instead the undirected quote from cmtt (char 0x0d). % The undirected quote is ugly, so don't make it the default, but it % works for pasting with more pdf viewers (at least evince), the % lilypond developers report. xpdf does work with the regular 0x27. % \def\codequoteright{% \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax '% \else \char'15 \fi \else \char'15 \fi } % % and a similar option for the left quote char vs. a grave accent. % Modern fonts display ASCII 0x60 as a grave accent, so some people like % the code environments to do likewise. % \def\codequoteleft{% \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax % [Knuth] pp. 380,381,391 % \relax disables Spanish ligatures ?` and !` of \tt font. \relax`% \else \char'22 \fi \else \char'22 \fi } % Commands to set the quote options. % \parseargdef\codequoteundirected{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETtxicodequoteundirected\endcsname = t% \else\ifx\temp\offword \expandafter\let\csname SETtxicodequoteundirected\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @codequoteundirected value `\temp', must be on|off}% \fi\fi } % \parseargdef\codequotebacktick{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETtxicodequotebacktick\endcsname = t% \else\ifx\temp\offword \expandafter\let\csname SETtxicodequotebacktick\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @codequotebacktick value `\temp', must be on|off}% \fi\fi } % [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font. \def\noligaturesquoteleft{\relax\lq} % Count depth in font-changes, for error checks \newcount\fontdepth \fontdepth=0 % Font commands. % #1 is the font command (\sl or \it), #2 is the text to slant. % If we are in a monospaced environment, however, 1) always use \ttsl, % and 2) do not add an italic correction. \def\dosmartslant#1#2{% \ifusingtt {{\ttsl #2}\let\next=\relax}% {\def\next{{#1#2}\futurelet\next\smartitaliccorrection}}% \next } \def\smartslanted{\dosmartslant\sl} \def\smartitalic{\dosmartslant\it} % Output an italic correction unless \next (presumed to be the following % character) is such as not to need one. \def\smartitaliccorrection{% \ifx\next,% \else\ifx\next-% \else\ifx\next.% \else\ptexslash \fi\fi\fi \aftersmartic } % Unconditional use \ttsl, and no ic. @var is set to this for defuns. \def\ttslanted#1{{\ttsl #1}} % @cite is like \smartslanted except unconditionally use \sl. We never want % ttsl for book titles, do we? \def\cite#1{{\sl #1}\futurelet\next\smartitaliccorrection} \def\aftersmartic{} \def\var#1{% \let\saveaftersmartic = \aftersmartic \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}% \smartslanted{#1}% } \let\i=\smartitalic \let\slanted=\smartslanted \let\dfn=\smartslanted \let\emph=\smartitalic % Explicit font changes: @r, @sc, undocumented @ii. \def\r#1{{\rm #1}} % roman font \def\sc#1{{\smallcaps#1}} % smallcaps font \def\ii#1{{\it #1}} % italic font % @b, explicit bold. Also @strong. \def\b#1{{\bf #1}} \let\strong=\b % @sansserif, explicit sans. \def\sansserif#1{{\sf #1}} % We can't just use \exhyphenpenalty, because that only has effect at % the end of a paragraph. Restore normal hyphenation at the end of the % group within which \nohyphenation is presumably called. % \def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} \def\restorehyphenation{\hyphenchar\font = `- } % Set sfcode to normal for the chars that usually have another value. % Can't use plain's \frenchspacing because it uses the `\x notation, and % sometimes \x has an active definition that messes things up. % \catcode`@=11 \def\plainfrenchspacing{% \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m \def\endofsentencespacefactor{1000}% for @. and friends } \def\plainnonfrenchspacing{% \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 \def\endofsentencespacefactor{3000}% for @. and friends } \catcode`@=\other \def\endofsentencespacefactor{3000}% default % @t, explicit typewriter. \def\t#1{% {\tt \rawbackslash \plainfrenchspacing #1}% \null } % @samp. \def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}} % @indicateurl is \samp, that is, with quotes. \let\indicateurl=\samp % @code (and similar) prints in typewriter, but with spaces the same % size as normal in the surrounding text, without hyphenation, etc. % This is a subroutine for that. \def\tclose#1{% {% % Change normal interword space to be same as for the current font. \spaceskip = \fontdimen2\font % % Switch to typewriter. \tt % % But `\ ' produces the large typewriter interword space. \def\ {{\spaceskip = 0pt{} }}% % % Turn off hyphenation. \nohyphenation % \rawbackslash \plainfrenchspacing #1% }% \null % reset spacefactor to 1000 } % We *must* turn on hyphenation at `-' and `_' in @code. % Otherwise, it is too hard to avoid overfull hboxes % in the Emacs manual, the Library manual, etc. % % Unfortunately, TeX uses one parameter (\hyphenchar) to control % both hyphenation at - and hyphenation within words. % We must therefore turn them both off (\tclose does that) % and arrange explicitly to hyphenate at a dash. % -- rms. { \catcode`\-=\active \catcode`\_=\active \catcode`\'=\active \catcode`\`=\active \global\let'=\rq \global\let`=\lq % default definitions % \global\def\code{\begingroup \setupmarkupstyle{code}% % The following should really be moved into \setupmarkupstyle handlers. \catcode\dashChar=\active \catcode\underChar=\active \ifallowcodebreaks \let-\codedash \let_\codeunder \else \let-\normaldash \let_\realunder \fi \codex } } \def\codex #1{\tclose{#1}\endgroup} \def\normaldash{-} \def\codedash{-\discretionary{}{}{}} \def\codeunder{% % this is all so @math{@code{var_name}+1} can work. In math mode, _ % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) % will therefore expand the active definition of _, which is us % (inside @code that is), therefore an endless loop. \ifusingtt{\ifmmode \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. \else\normalunderscore \fi \discretionary{}{}{}}% {\_}% } % An additional complication: the above will allow breaks after, e.g., % each of the four underscores in __typeof__. This is bad. % @allowcodebreaks provides a document-level way to turn breaking at - % and _ on and off. % \newif\ifallowcodebreaks \allowcodebreakstrue \def\keywordtrue{true} \def\keywordfalse{false} \parseargdef\allowcodebreaks{% \def\txiarg{#1}% \ifx\txiarg\keywordtrue \allowcodebreakstrue \else\ifx\txiarg\keywordfalse \allowcodebreaksfalse \else \errhelp = \EMsimple \errmessage{Unknown @allowcodebreaks option `\txiarg', must be true|false}% \fi\fi } % For @command, @env, @file, @option quotes seem unnecessary, % so use \code rather than \samp. \let\command=\code \let\env=\code \let\file=\code \let\option=\code % @uref (abbreviation for `urlref') takes an optional (comma-separated) % second argument specifying the text to display and an optional third % arg as text to display instead of (rather than in addition to) the url % itself. First (mandatory) arg is the url. % (This \urefnobreak definition isn't used now, leaving it for a while % for comparison.) \def\urefnobreak#1{\dourefnobreak #1,,,\finish} \def\dourefnobreak#1,#2,#3,#4\finish{\begingroup \unsepspaces \pdfurl{#1}% \setbox0 = \hbox{\ignorespaces #3}% \ifdim\wd0 > 0pt \unhbox0 % third arg given, show only that \else \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0 > 0pt \ifpdf \unhbox0 % PDF: 2nd arg given, show only it \else \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url \fi \else \code{#1}% only url given, so show it \fi \fi \endlink \endgroup} % This \urefbreak definition is the active one. \def\urefbreak{\begingroup \urefcatcodes \dourefbreak} \let\uref=\urefbreak \def\dourefbreak#1{\urefbreakfinish #1,,,\finish} \def\urefbreakfinish#1,#2,#3,#4\finish{% doesn't work in @example \unsepspaces \pdfurl{#1}% \setbox0 = \hbox{\ignorespaces #3}% \ifdim\wd0 > 0pt \unhbox0 % third arg given, show only that \else \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0 > 0pt \ifpdf \unhbox0 % PDF: 2nd arg given, show only it \else \unhbox0\ (\urefcode{#1})% DVI: 2nd arg given, show both it and url \fi \else \urefcode{#1}% only url given, so show it \fi \fi \endlink \endgroup} % Allow line breaks around only a few characters (only). \def\urefcatcodes{% \catcode\ampChar=\active \catcode\dotChar=\active \catcode\hashChar=\active \catcode\questChar=\active \catcode\slashChar=\active } { \urefcatcodes % \global\def\urefcode{\begingroup \setupmarkupstyle{code}% \urefcatcodes \let&\urefcodeamp \let.\urefcodedot \let#\urefcodehash \let?\urefcodequest \let/\urefcodeslash \codex } % % By default, they are just regular characters. \global\def&{\normalamp} \global\def.{\normaldot} \global\def#{\normalhash} \global\def?{\normalquest} \global\def/{\normalslash} } % we put a little stretch before and after the breakable chars, to help % line breaking of long url's. The unequal skips make look better in % cmtt at least, especially for dots. \def\urefprestretch{\urefprebreak \hskip0pt plus.13em } \def\urefpoststretch{\urefpostbreak \hskip0pt plus.1em } % \def\urefcodeamp{\urefprestretch \&\urefpoststretch} \def\urefcodedot{\urefprestretch .\urefpoststretch} \def\urefcodehash{\urefprestretch \#\urefpoststretch} \def\urefcodequest{\urefprestretch ?\urefpoststretch} \def\urefcodeslash{\futurelet\next\urefcodeslashfinish} { \catcode`\/=\active \global\def\urefcodeslashfinish{% \urefprestretch \slashChar % Allow line break only after the final / in a sequence of % slashes, to avoid line break between the slashes in http://. \ifx\next/\else \urefpoststretch \fi } } % One more complication: by default we'll break after the special % characters, but some people like to break before the special chars, so % allow that. Also allow no breaking at all, for manual control. % \parseargdef\urefbreakstyle{% \def\txiarg{#1}% \ifx\txiarg\wordnone \def\urefprebreak{\nobreak}\def\urefpostbreak{\nobreak} \else\ifx\txiarg\wordbefore \def\urefprebreak{\allowbreak}\def\urefpostbreak{\nobreak} \else\ifx\txiarg\wordafter \def\urefprebreak{\nobreak}\def\urefpostbreak{\allowbreak} \else \errhelp = \EMsimple \errmessage{Unknown @urefbreakstyle setting `\txiarg'}% \fi\fi\fi } \def\wordafter{after} \def\wordbefore{before} \def\wordnone{none} \urefbreakstyle after % @url synonym for @uref, since that's how everyone uses it. % \let\url=\uref % rms does not like angle brackets --karl, 17may97. % So now @email is just like @uref, unless we are pdf. % %\def\email#1{\angleleft{\tt #1}\angleright} \ifpdf \def\email#1{\doemail#1,,\finish} \def\doemail#1,#2,#3\finish{\begingroup \unsepspaces \pdfurl{mailto:#1}% \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi \endlink \endgroup} \else \let\email=\uref \fi % @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), % `example' (@kbd uses ttsl only inside of @example and friends), % or `code' (@kbd uses normal tty font always). \parseargdef\kbdinputstyle{% \def\txiarg{#1}% \ifx\txiarg\worddistinct \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% \else\ifx\txiarg\wordexample \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% \else\ifx\txiarg\wordcode \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% \else \errhelp = \EMsimple \errmessage{Unknown @kbdinputstyle setting `\txiarg'}% \fi\fi\fi } \def\worddistinct{distinct} \def\wordexample{example} \def\wordcode{code} % Default is `distinct'. \kbdinputstyle distinct % @kbd is like @code, except that if the argument is just one @key command, % then @kbd has no effect. \def\kbd#1{{\def\look{#1}\expandafter\kbdsub\look??\par}} \def\xkey{\key} \def\kbdsub#1#2#3\par{% \def\one{#1}\def\three{#3}\def\threex{??}% \ifx\one\xkey\ifx\threex\three \key{#2}% \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi } % definition of @key that produces a lozenge. Doesn't adjust to text size. %\setfont\keyrm\rmshape{8}{1000}{OT1} %\font\keysy=cmsy9 %\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% % \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% % \vbox{\hrule\kern-0.4pt % \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% % \kern-0.4pt\hrule}% % \kern-.06em\raise0.4pt\hbox{\angleright}}}} % definition of @key with no lozenge. If the current font is already % monospace, don't change it; that way, we respect @kbdinputstyle. But % if it isn't monospace, then use \tt. % \def\key#1{{\setupmarkupstyle{key}% \nohyphenation \ifmonospace\else\tt\fi #1}\null} % @clicksequence{File @click{} Open ...} \def\clicksequence#1{\begingroup #1\endgroup} % @clickstyle @arrow (by default) \parseargdef\clickstyle{\def\click{#1}} \def\click{\arrow} % Typeset a dimension, e.g., `in' or `pt'. The only reason for the % argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. % \def\dmn#1{\thinspace #1} % @l was never documented to mean ``switch to the Lisp font'', % and it is not used as such in any manual I can find. We need it for % Polish suppressed-l. --karl, 22sep96. %\def\l#1{{\li #1}\null} % @acronym for "FBI", "NATO", and the like. % We print this one point size smaller, since it's intended for % all-uppercase. % \def\acronym#1{\doacronym #1,,\finish} \def\doacronym#1,#2,#3\finish{% {\selectfonts\lsize #1}% \def\temp{#2}% \ifx\temp\empty \else \space ({\unsepspaces \ignorespaces \temp \unskip})% \fi \null % reset \spacefactor=1000 } % @abbr for "Comput. J." and the like. % No font change, but don't do end-of-sentence spacing. % \def\abbr#1{\doabbr #1,,\finish} \def\doabbr#1,#2,#3\finish{% {\plainfrenchspacing #1}% \def\temp{#2}% \ifx\temp\empty \else \space ({\unsepspaces \ignorespaces \temp \unskip})% \fi \null % reset \spacefactor=1000 } % @asis just yields its argument. Used with @table, for example. % \def\asis#1{#1} % @math outputs its argument in math mode. % % One complication: _ usually means subscripts, but it could also mean % an actual _ character, as in @math{@var{some_variable} + 1}. So make % _ active, and distinguish by seeing if the current family is \slfam, % which is what @var uses. { \catcode`\_ = \active \gdef\mathunderscore{% \catcode`\_=\active \def_{\ifnum\fam=\slfam \_\else\sb\fi}% } } % Another complication: we want \\ (and @\) to output a math (or tt) \. % FYI, plain.tex uses \\ as a temporary control sequence (for no % particular reason), but this is not advertised and we don't care. % % The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. \def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} % \def\math{% \tex \mathunderscore \let\\ = \mathbackslash \mathactive % make the texinfo accent commands work in math mode \let\"=\ddot \let\'=\acute \let\==\bar \let\^=\hat \let\`=\grave \let\u=\breve \let\v=\check \let\~=\tilde \let\dotaccent=\dot $\finishmath } \def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. % Some active characters (such as <) are spaced differently in math. % We have to reset their definitions in case the @math was an argument % to a command which sets the catcodes (such as @item or @section). % { \catcode`^ = \active \catcode`< = \active \catcode`> = \active \catcode`+ = \active \catcode`' = \active \gdef\mathactive{% \let^ = \ptexhat \let< = \ptexless \let> = \ptexgtr \let+ = \ptexplus \let' = \ptexquoteright } } % ctrl is no longer a Texinfo command, but leave this definition for fun. \def\ctrl #1{{\tt \rawbackslash \hat}#1} % @inlinefmt{FMTNAME,PROCESSED-TEXT} and @inlineraw{FMTNAME,RAW-TEXT}. % Ignore unless FMTNAME == tex; then it is like @iftex and @tex, % except specified as a normal braced arg, so no newlines to worry about. % \def\outfmtnametex{tex} % \long\def\inlinefmt#1{\doinlinefmt #1,\finish} \long\def\doinlinefmt#1,#2,\finish{% \def\inlinefmtname{#1}% \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\fi } % For raw, must switch into @tex before parsing the argument, to avoid % setting catcodes prematurely. Doing it this way means that, for % example, @inlineraw{html, foo{bar} gets a parse error instead of being % ignored. But this isn't important because if people want a literal % *right* brace they would have to use a command anyway, so they may as % well use a command to get a left brace too. We could re-use the % delimiter character idea from \verb, but it seems like overkill. % \long\def\inlineraw{\tex \doinlineraw} \long\def\doinlineraw#1{\doinlinerawtwo #1,\finish} \def\doinlinerawtwo#1,#2,\finish{% \def\inlinerawname{#1}% \ifx\inlinerawname\outfmtnametex \ignorespaces #2\fi \endgroup % close group opened by \tex. } \message{glyphs,} % and logos. % @@ prints an @, as does @atchar{}. \def\@{\char64 } \let\atchar=\@ % @{ @} @lbracechar{} @rbracechar{} all generate brace characters. % Unless we're in typewriter, use \ecfont because the CM text fonts do % not have braces, and we don't want to switch into math. \def\mylbrace{{\ifmonospace\else\ecfont\fi \char123}} \def\myrbrace{{\ifmonospace\else\ecfont\fi \char125}} \let\{=\mylbrace \let\lbracechar=\{ \let\}=\myrbrace \let\rbracechar=\} \begingroup % Definitions to produce \{ and \} commands for indices, % and @{ and @} for the aux/toc files. \catcode`\{ = \other \catcode`\} = \other \catcode`\[ = 1 \catcode`\] = 2 \catcode`\! = 0 \catcode`\\ = \other !gdef!lbracecmd[\{]% !gdef!rbracecmd[\}]% !gdef!lbraceatcmd[@{]% !gdef!rbraceatcmd[@}]% !endgroup % @comma{} to avoid , parsing problems. \let\comma = , % Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent % Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. \let\, = \ptexc \let\dotaccent = \ptexdot \def\ringaccent#1{{\accent23 #1}} \let\tieaccent = \ptext \let\ubaraccent = \ptexb \let\udotaccent = \d % Other special characters: @questiondown @exclamdown @ordf @ordm % Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. \def\questiondown{?`} \def\exclamdown{!`} \def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}} \def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}} % Dotless i and dotless j, used for accents. \def\imacro{i} \def\jmacro{j} \def\dotless#1{% \def\temp{#1}% \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi \else \errmessage{@dotless can be used only with i or j}% \fi\fi } % The \TeX{} logo, as in plain, but resetting the spacing so that a % period following counts as ending a sentence. (Idea found in latex.) % \edef\TeX{\TeX \spacefactor=1000 } % @LaTeX{} logo. Not quite the same results as the definition in % latex.ltx, since we use a different font for the raised A; it's most % convenient for us to use an explicitly smaller font, rather than using % the \scriptstyle font (since we don't reset \scriptstyle and % \scriptscriptstyle). % \def\LaTeX{% L\kern-.36em {\setbox0=\hbox{T}% \vbox to \ht0{\hbox{% \ifx\textnominalsize\xwordpt % for 10pt running text, \lllsize (8pt) is too small for the A in LaTeX. % Revert to plain's \scriptsize, which is 7pt. \count255=\the\fam $\fam\count255 \scriptstyle A$% \else % For 11pt, we can use our lllsize. \selectfonts\lllsize A% \fi }% \vss }}% \kern-.15em \TeX } % Some math mode symbols. \def\bullet{$\ptexbullet$} \def\geq{\ifmmode \ge\else $\ge$\fi} \def\leq{\ifmmode \le\else $\le$\fi} \def\minus{\ifmmode -\else $-$\fi} % @dots{} outputs an ellipsis using the current font. % We do .5em per period so that it has the same spacing in the cm % typewriter fonts as three actual period characters; on the other hand, % in other typewriter fonts three periods are wider than 1.5em. So do % whichever is larger. % \def\dots{% \leavevmode \setbox0=\hbox{...}% get width of three periods \ifdim\wd0 > 1.5em \dimen0 = \wd0 \else \dimen0 = 1.5em \fi \hbox to \dimen0{% \hskip 0pt plus.25fil .\hskip 0pt plus1fil .\hskip 0pt plus1fil .\hskip 0pt plus.5fil }% } % @enddots{} is an end-of-sentence ellipsis. % \def\enddots{% \dots \spacefactor=\endofsentencespacefactor } % @point{}, @result{}, @expansion{}, @print{}, @equiv{}. % % Since these characters are used in examples, they should be an even number of % \tt widths. Each \tt character is 1en, so two makes it 1em. % \def\point{$\star$} \def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}} \def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} \def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}} \def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} \def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}} % The @error{} command. % Adapted from the TeXbook's \boxit. % \newbox\errorbox % {\tentt \global\dimen0 = 3em}% Width of the box. \dimen2 = .55pt % Thickness of rules % The text. (`r' is open on the right, `e' somewhat less so on the left.) \setbox0 = \hbox{\kern-.75pt \reducedsf \putworderror\kern-1.5pt} % \setbox\errorbox=\hbox to \dimen0{\hfil \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. \advance\hsize by -2\dimen2 % Rules. \vbox{% \hrule height\dimen2 \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. \kern3pt\vrule width\dimen2}% Space to right. \hrule height\dimen2} \hfil} % \def\error{\leavevmode\lower.7ex\copy\errorbox} % @pounds{} is a sterling sign, which Knuth put in the CM italic font. % \def\pounds{{\it\$}} % @euro{} comes from a separate font, depending on the current style. % We use the free feym* fonts from the eurosym package by Henrik % Theiling, which support regular, slanted, bold and bold slanted (and % "outlined" (blackboard board, sort of) versions, which we don't need). % It is available from http://www.ctan.org/tex-archive/fonts/eurosym. % % Although only regular is the truly official Euro symbol, we ignore % that. The Euro is designed to be slightly taller than the regular % font height. % % feymr - regular % feymo - slanted % feybr - bold % feybo - bold slanted % % There is no good (free) typewriter version, to my knowledge. % A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. % Hmm. % % Also doesn't work in math. Do we need to do math with euro symbols? % Hope not. % % \def\euro{{\eurofont e}} \def\eurofont{% % We set the font at each command, rather than predefining it in % \textfonts and the other font-switching commands, so that % installations which never need the symbol don't have to have the % font installed. % % There is only one designed size (nominal 10pt), so we always scale % that to the current nominal size. % % By the way, simply using "at 1em" works for cmr10 and the like, but % does not work for cmbx10 and other extended/shrunken fonts. % \def\eurosize{\csname\curfontsize nominalsize\endcsname}% % \ifx\curfontstyle\bfstylename % bold: \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize \else % regular: \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize \fi \thiseurofont } % Glyphs from the EC fonts. We don't use \let for the aliases, because % sometimes we redefine the original macro, and the alias should reflect % the redefinition. % % Use LaTeX names for the Icelandic letters. \def\DH{{\ecfont \char"D0}} % Eth \def\dh{{\ecfont \char"F0}} % eth \def\TH{{\ecfont \char"DE}} % Thorn \def\th{{\ecfont \char"FE}} % thorn % \def\guillemetleft{{\ecfont \char"13}} \def\guillemotleft{\guillemetleft} \def\guillemetright{{\ecfont \char"14}} \def\guillemotright{\guillemetright} \def\guilsinglleft{{\ecfont \char"0E}} \def\guilsinglright{{\ecfont \char"0F}} \def\quotedblbase{{\ecfont \char"12}} \def\quotesinglbase{{\ecfont \char"0D}} % % This positioning is not perfect (see the ogonek LaTeX package), but % we have the precomposed glyphs for the most common cases. We put the % tests to use those glyphs in the single \ogonek macro so we have fewer % dummy definitions to worry about for index entries, etc. % % ogonek is also used with other letters in Lithuanian (IOU), but using % the precomposed glyphs for those is not so easy since they aren't in % the same EC font. \def\ogonek#1{{% \def\temp{#1}% \ifx\temp\macrocharA\Aogonek \else\ifx\temp\macrochara\aogonek \else\ifx\temp\macrocharE\Eogonek \else\ifx\temp\macrochare\eogonek \else \ecfont \setbox0=\hbox{#1}% \ifdim\ht0=1ex\accent"0C #1% \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}% \fi \fi\fi\fi\fi }% } \def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A} \def\aogonek{{\ecfont \char"A1}}\def\macrochara{a} \def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E} \def\eogonek{{\ecfont \char"A6}}\def\macrochare{e} % % Use the ec* fonts (cm-super in outline format) for non-CM glyphs. \def\ecfont{% % We can't distinguish serif/sans and italic/slanted, but this % is used for crude hacks anyway (like adding French and German % quotes to documents typeset with CM, where we lose kerning), so % hopefully nobody will notice/care. \edef\ecsize{\csname\curfontsize ecsize\endcsname}% \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}% \ifmonospace % typewriter: \font\thisecfont = ectt\ecsize \space at \nominalsize \else \ifx\curfontstyle\bfstylename % bold: \font\thisecfont = ecb\ifusingit{i}{x}\ecsize \space at \nominalsize \else % regular: \font\thisecfont = ec\ifusingit{ti}{rm}\ecsize \space at \nominalsize \fi \fi \thisecfont } % @registeredsymbol - R in a circle. The font for the R should really % be smaller yet, but lllsize is the best we can do for now. % Adapted from the plain.tex definition of \copyright. % \def\registeredsymbol{% $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}% \hfil\crcr\Orb}}% }$% } % @textdegree - the normal degrees sign. % \def\textdegree{$^\circ$} % Laurent Siebenmann reports \Orb undefined with: % Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 % so we'll define it if necessary. % \ifx\Orb\thisisundefined \def\Orb{\mathhexbox20D} \fi % Quotes. \chardef\quotedblleft="5C \chardef\quotedblright=`\" \chardef\quoteleft=`\` \chardef\quoteright=`\' \message{page headings,} \newskip\titlepagetopglue \titlepagetopglue = 1.5in \newskip\titlepagebottomglue \titlepagebottomglue = 2pc % First the title page. Must do @settitle before @titlepage. \newif\ifseenauthor \newif\iffinishedtitlepage % Do an implicit @contents or @shortcontents after @end titlepage if the % user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. % \newif\ifsetcontentsaftertitlepage \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue \newif\ifsetshortcontentsaftertitlepage \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue \parseargdef\shorttitlepage{% \begingroup \hbox{}\vskip 1.5in \chaprm \centerline{#1}% \endgroup\page\hbox{}\page} \envdef\titlepage{% % Open one extra group, as we want to close it in the middle of \Etitlepage. \begingroup \parindent=0pt \textfonts % Leave some space at the very top of the page. \vglue\titlepagetopglue % No rule at page bottom unless we print one at the top with @title. \finishedtitlepagetrue % % Most title ``pages'' are actually two pages long, with space % at the top of the second. We don't want the ragged left on the second. \let\oldpage = \page \def\page{% \iffinishedtitlepage\else \finishtitlepage \fi \let\page = \oldpage \page \null }% } \def\Etitlepage{% \iffinishedtitlepage\else \finishtitlepage \fi % It is important to do the page break before ending the group, % because the headline and footline are only empty inside the group. % If we use the new definition of \page, we always get a blank page % after the title page, which we certainly don't want. \oldpage \endgroup % % Need this before the \...aftertitlepage checks so that if they are % in effect the toc pages will come out with page numbers. \HEADINGSon % % If they want short, they certainly want long too. \ifsetshortcontentsaftertitlepage \shortcontents \contents \global\let\shortcontents = \relax \global\let\contents = \relax \fi % \ifsetcontentsaftertitlepage \contents \global\let\contents = \relax \global\let\shortcontents = \relax \fi } \def\finishtitlepage{% \vskip4pt \hrule height 2pt width \hsize \vskip\titlepagebottomglue \finishedtitlepagetrue } % Settings used for typesetting titles: no hyphenation, no indentation, % don't worry much about spacing, ragged right. This should be used % inside a \vbox, and fonts need to be set appropriately first. Because % it is always used for titles, nothing else, we call \rmisbold. \par % should be specified before the end of the \vbox, since a vbox is a group. % \def\raggedtitlesettings{% \rmisbold \hyphenpenalty=10000 \parindent=0pt \tolerance=5000 \ptexraggedright } % Macros to be used within @titlepage: \let\subtitlerm=\tenrm \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} \parseargdef\title{% \checkenv\titlepage \vbox{\titlefonts \raggedtitlesettings #1\par}% % print a rule at the page bottom also. \finishedtitlepagefalse \vskip4pt \hrule height 4pt width \hsize \vskip4pt } \parseargdef\subtitle{% \checkenv\titlepage {\subtitlefont \rightline{#1}}% } % @author should come last, but may come many times. % It can also be used inside @quotation. % \parseargdef\author{% \def\temp{\quotation}% \ifx\thisenv\temp \def\quotationauthor{#1}% printed in \Equotation. \else \checkenv\titlepage \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi {\secfonts\rmisbold \leftline{#1}}% \fi } % Set up page headings and footings. \let\thispage=\folio \newtoks\evenheadline % headline on even pages \newtoks\oddheadline % headline on odd pages \newtoks\evenfootline % footline on even pages \newtoks\oddfootline % footline on odd pages % Now make TeX use those variables \headline={{\textfonts\rm \ifodd\pageno \the\oddheadline \else \the\evenheadline \fi}} \footline={{\textfonts\rm \ifodd\pageno \the\oddfootline \else \the\evenfootline \fi}\HEADINGShook} \let\HEADINGShook=\relax % Commands to set those variables. % For example, this is what @headings on does % @evenheading @thistitle|@thispage|@thischapter % @oddheading @thischapter|@thispage|@thistitle % @evenfooting @thisfile|| % @oddfooting ||@thisfile \def\evenheading{\parsearg\evenheadingxxx} \def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} \def\evenheadingyyy #1\|#2\|#3\|#4\finish{% \global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \def\oddheading{\parsearg\oddheadingxxx} \def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} \def\oddheadingyyy #1\|#2\|#3\|#4\finish{% \global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% \def\evenfooting{\parsearg\evenfootingxxx} \def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} \def\evenfootingyyy #1\|#2\|#3\|#4\finish{% \global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \def\oddfooting{\parsearg\oddfootingxxx} \def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} \def\oddfootingyyy #1\|#2\|#3\|#4\finish{% \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% % % Leave some space for the footline. Hopefully ok to assume % @evenfooting will not be used by itself. \global\advance\pageheight by -12pt \global\advance\vsize by -12pt } \parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} % @evenheadingmarks top \thischapter <- chapter at the top of a page % @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page % % The same set of arguments for: % % @oddheadingmarks % @evenfootingmarks % @oddfootingmarks % @everyheadingmarks % @everyfootingmarks \def\evenheadingmarks{\headingmarks{even}{heading}} \def\oddheadingmarks{\headingmarks{odd}{heading}} \def\evenfootingmarks{\headingmarks{even}{footing}} \def\oddfootingmarks{\headingmarks{odd}{footing}} \def\everyheadingmarks#1 {\headingmarks{even}{heading}{#1} \headingmarks{odd}{heading}{#1} } \def\everyfootingmarks#1 {\headingmarks{even}{footing}{#1} \headingmarks{odd}{footing}{#1} } % #1 = even/odd, #2 = heading/footing, #3 = top/bottom. \def\headingmarks#1#2#3 {% \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname \global\expandafter\let\csname get#1#2marks\endcsname \temp } \everyheadingmarks bottom \everyfootingmarks bottom % @headings double turns headings on for double-sided printing. % @headings single turns headings on for single-sided printing. % @headings off turns them off. % @headings on same as @headings double, retained for compatibility. % @headings after turns on double-sided headings after this page. % @headings doubleafter turns on double-sided headings after this page. % @headings singleafter turns on single-sided headings after this page. % By default, they are off at the start of a document, % and turned `on' after @end titlepage. \def\headings #1 {\csname HEADINGS#1\endcsname} \def\headingsoff{% non-global headings elimination \evenheadline={\hfil}\evenfootline={\hfil}% \oddheadline={\hfil}\oddfootline={\hfil}% } \def\HEADINGSoff{{\globaldefs=1 \headingsoff}} % global setting \HEADINGSoff % it's the default % When we turn headings on, set the page number to 1. % For double-sided printing, put current file name in lower left corner, % chapter name on inside top of right hand pages, document % title on inside top of left hand pages, and page numbers on outside top % edge of all pages. \def\HEADINGSdouble{% \global\pageno=1 \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\folio\hfil\thistitle}} \global\oddheadline={\line{\thischapter\hfil\folio}} \global\let\contentsalignmacro = \chapoddpage } \let\contentsalignmacro = \chappager % For single-sided printing, chapter title goes across top left of page, % page number on top right. \def\HEADINGSsingle{% \global\pageno=1 \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\thischapter\hfil\folio}} \global\oddheadline={\line{\thischapter\hfil\folio}} \global\let\contentsalignmacro = \chappager } \def\HEADINGSon{\HEADINGSdouble} \def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} \let\HEADINGSdoubleafter=\HEADINGSafter \def\HEADINGSdoublex{% \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\folio\hfil\thistitle}} \global\oddheadline={\line{\thischapter\hfil\folio}} \global\let\contentsalignmacro = \chapoddpage } \def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} \def\HEADINGSsinglex{% \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\thischapter\hfil\folio}} \global\oddheadline={\line{\thischapter\hfil\folio}} \global\let\contentsalignmacro = \chappager } % Subroutines used in generating headings % This produces Day Month Year style of output. % Only define if not already defined, in case a txi-??.tex file has set % up a different format (e.g., txi-cs.tex does this). \ifx\today\thisisundefined \def\today{% \number\day\space \ifcase\month \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec \fi \space\number\year} \fi % @settitle line... specifies the title of the document, for headings. % It generates no output of its own. \def\thistitle{\putwordNoTitle} \def\settitle{\parsearg{\gdef\thistitle}} \message{tables,} % Tables -- @table, @ftable, @vtable, @item(x). % default indentation of table text \newdimen\tableindent \tableindent=.8in % default indentation of @itemize and @enumerate text \newdimen\itemindent \itemindent=.3in % margin between end of table item and start of table text. \newdimen\itemmargin \itemmargin=.1in % used internally for \itemindent minus \itemmargin \newdimen\itemmax % Note @table, @ftable, and @vtable define @item, @itemx, etc., with % these defs. % They also define \itemindex % to index the item name in whatever manner is desired (perhaps none). \newif\ifitemxneedsnegativevskip \def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} \def\internalBitem{\smallbreak \parsearg\itemzzz} \def\internalBitemx{\itemxpar \parsearg\itemzzz} \def\itemzzz #1{\begingroup % \advance\hsize by -\rightskip \advance\hsize by -\tableindent \setbox0=\hbox{\itemindicate{#1}}% \itemindex{#1}% \nobreak % This prevents a break before @itemx. % % If the item text does not fit in the space we have, put it on a line % by itself, and do not allow a page break either before or after that % line. We do not start a paragraph here because then if the next % command is, e.g., @kindex, the whatsit would get put into the % horizontal list on a line by itself, resulting in extra blank space. \ifdim \wd0>\itemmax % % Make this a paragraph so we get the \parskip glue and wrapping, % but leave it ragged-right. \begingroup \advance\leftskip by-\tableindent \advance\hsize by\tableindent \advance\rightskip by0pt plus1fil\relax \leavevmode\unhbox0\par \endgroup % % We're going to be starting a paragraph, but we don't want the % \parskip glue -- logically it's part of the @item we just started. \nobreak \vskip-\parskip % % Stop a page break at the \parskip glue coming up. However, if % what follows is an environment such as @example, there will be no % \parskip glue; then the negative vskip we just inserted would % cause the example and the item to crash together. So we use this % bizarre value of 10001 as a signal to \aboveenvbreak to insert % \parskip glue after all. Section titles are handled this way also. % \penalty 10001 \endgroup \itemxneedsnegativevskipfalse \else % The item text fits into the space. Start a paragraph, so that the % following text (if any) will end up on the same line. \noindent % Do this with kerns and \unhbox so that if there is a footnote in % the item text, it can migrate to the main vertical list and % eventually be printed. \nobreak\kern-\tableindent \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 \unhbox0 \nobreak\kern\dimen0 \endgroup \itemxneedsnegativevskiptrue \fi } \def\item{\errmessage{@item while not in a list environment}} \def\itemx{\errmessage{@itemx while not in a list environment}} % @table, @ftable, @vtable. \envdef\table{% \let\itemindex\gobble \tablecheck{table}% } \envdef\ftable{% \def\itemindex ##1{\doind {fn}{\code{##1}}}% \tablecheck{ftable}% } \envdef\vtable{% \def\itemindex ##1{\doind {vr}{\code{##1}}}% \tablecheck{vtable}% } \def\tablecheck#1{% \ifnum \the\catcode`\^^M=\active \endgroup \errmessage{This command won't work in this context; perhaps the problem is that we are \inenvironment\thisenv}% \def\next{\doignore{#1}}% \else \let\next\tablex \fi \next } \def\tablex#1{% \def\itemindicate{#1}% \parsearg\tabley } \def\tabley#1{% {% \makevalueexpandable \edef\temp{\noexpand\tablez #1\space\space\space}% \expandafter }\temp \endtablez } \def\tablez #1 #2 #3 #4\endtablez{% \aboveenvbreak \ifnum 0#1>0 \advance \leftskip by #1\mil \fi \ifnum 0#2>0 \tableindent=#2\mil \fi \ifnum 0#3>0 \advance \rightskip by #3\mil \fi \itemmax=\tableindent \advance \itemmax by -\itemmargin \advance \leftskip by \tableindent \exdentamount=\tableindent \parindent = 0pt \parskip = \smallskipamount \ifdim \parskip=0pt \parskip=2pt \fi \let\item = \internalBitem \let\itemx = \internalBitemx } \def\Etable{\endgraf\afterenvbreak} \let\Eftable\Etable \let\Evtable\Etable \let\Eitemize\Etable \let\Eenumerate\Etable % This is the counter used by @enumerate, which is really @itemize \newcount \itemno \envdef\itemize{\parsearg\doitemize} \def\doitemize#1{% \aboveenvbreak \itemmax=\itemindent \advance\itemmax by -\itemmargin \advance\leftskip by \itemindent \exdentamount=\itemindent \parindent=0pt \parskip=\smallskipamount \ifdim\parskip=0pt \parskip=2pt \fi % % Try typesetting the item mark that if the document erroneously says % something like @itemize @samp (intending @table), there's an error % right away at the @itemize. It's not the best error message in the % world, but it's better than leaving it to the @item. This means if % the user wants an empty mark, they have to say @w{} not just @w. \def\itemcontents{#1}% \setbox0 = \hbox{\itemcontents}% % % @itemize with no arg is equivalent to @itemize @bullet. \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi % \let\item=\itemizeitem } % Definition of @item while inside @itemize and @enumerate. % \def\itemizeitem{% \advance\itemno by 1 % for enumerations {\let\par=\endgraf \smallbreak}% reasonable place to break {% % If the document has an @itemize directly after a section title, a % \nobreak will be last on the list, and \sectionheading will have % done a \vskip-\parskip. In that case, we don't want to zero % parskip, or the item text will crash with the heading. On the % other hand, when there is normal text preceding the item (as there % usually is), we do want to zero parskip, or there would be too much % space. In that case, we won't have a \nobreak before. At least % that's the theory. \ifnum\lastpenalty<10000 \parskip=0in \fi \noindent \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% % \vadjust{\penalty 1200}}% not good to break after first line of item. \flushcr } % \splitoff TOKENS\endmark defines \first to be the first token in % TOKENS, and \rest to be the remainder. % \def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% % Allow an optional argument of an uppercase letter, lowercase letter, % or number, to specify the first label in the enumerated list. No % argument is the same as `1'. % \envparseargdef\enumerate{\enumeratey #1 \endenumeratey} \def\enumeratey #1 #2\endenumeratey{% % If we were given no argument, pretend we were given `1'. \def\thearg{#1}% \ifx\thearg\empty \def\thearg{1}\fi % % Detect if the argument is a single token. If so, it might be a % letter. Otherwise, the only valid thing it can be is a number. % (We will always have one token, because of the test we just made. % This is a good thing, since \splitoff doesn't work given nothing at % all -- the first parameter is undelimited.) \expandafter\splitoff\thearg\endmark \ifx\rest\empty % Only one token in the argument. It could still be anything. % A ``lowercase letter'' is one whose \lccode is nonzero. % An ``uppercase letter'' is one whose \lccode is both nonzero, and % not equal to itself. % Otherwise, we assume it's a number. % % We need the \relax at the end of the \ifnum lines to stop TeX from % continuing to look for a . % \ifnum\lccode\expandafter`\thearg=0\relax \numericenumerate % a number (we hope) \else % It's a letter. \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax \lowercaseenumerate % lowercase letter \else \uppercaseenumerate % uppercase letter \fi \fi \else % Multiple tokens in the argument. We hope it's a number. \numericenumerate \fi } % An @enumerate whose labels are integers. The starting integer is % given in \thearg. % \def\numericenumerate{% \itemno = \thearg \startenumeration{\the\itemno}% } % The starting (lowercase) letter is in \thearg. \def\lowercaseenumerate{% \itemno = \expandafter`\thearg \startenumeration{% % Be sure we're not beyond the end of the alphabet. \ifnum\itemno=0 \errmessage{No more lowercase letters in @enumerate; get a bigger alphabet}% \fi \char\lccode\itemno }% } % The starting (uppercase) letter is in \thearg. \def\uppercaseenumerate{% \itemno = \expandafter`\thearg \startenumeration{% % Be sure we're not beyond the end of the alphabet. \ifnum\itemno=0 \errmessage{No more uppercase letters in @enumerate; get a bigger alphabet} \fi \char\uccode\itemno }% } % Call \doitemize, adding a period to the first argument and supplying the % common last two arguments. Also subtract one from the initial value in % \itemno, since @item increments \itemno. % \def\startenumeration#1{% \advance\itemno by -1 \doitemize{#1.}\flushcr } % @alphaenumerate and @capsenumerate are abbreviations for giving an arg % to @enumerate. % \def\alphaenumerate{\enumerate{a}} \def\capsenumerate{\enumerate{A}} \def\Ealphaenumerate{\Eenumerate} \def\Ecapsenumerate{\Eenumerate} % @multitable macros % Amy Hendrickson, 8/18/94, 3/6/96 % % @multitable ... @end multitable will make as many columns as desired. % Contents of each column will wrap at width given in preamble. Width % can be specified either with sample text given in a template line, % or in percent of \hsize, the current width of text on page. % Table can continue over pages but will only break between lines. % To make preamble: % % Either define widths of columns in terms of percent of \hsize: % @multitable @columnfractions .25 .3 .45 % @item ... % % Numbers following @columnfractions are the percent of the total % current hsize to be used for each column. You may use as many % columns as desired. % Or use a template: % @multitable {Column 1 template} {Column 2 template} {Column 3 template} % @item ... % using the widest term desired in each column. % Each new table line starts with @item, each subsequent new column % starts with @tab. Empty columns may be produced by supplying @tab's % with nothing between them for as many times as empty columns are needed, % ie, @tab@tab@tab will produce two empty columns. % @item, @tab do not need to be on their own lines, but it will not hurt % if they are. % Sample multitable: % @multitable {Column 1 template} {Column 2 template} {Column 3 template} % @item first col stuff @tab second col stuff @tab third col % @item % first col stuff % @tab % second col stuff % @tab % third col % @item first col stuff @tab second col stuff % @tab Many paragraphs of text may be used in any column. % % They will wrap at the width determined by the template. % @item@tab@tab This will be in third column. % @end multitable % Default dimensions may be reset by user. % @multitableparskip is vertical space between paragraphs in table. % @multitableparindent is paragraph indent in table. % @multitablecolmargin is horizontal space to be left between columns. % @multitablelinespace is space to leave between table items, baseline % to baseline. % 0pt means it depends on current normal line spacing. % \newskip\multitableparskip \newskip\multitableparindent \newdimen\multitablecolspace \newskip\multitablelinespace \multitableparskip=0pt \multitableparindent=6pt \multitablecolspace=12pt \multitablelinespace=0pt % Macros used to set up halign preamble: % \let\endsetuptable\relax \def\xendsetuptable{\endsetuptable} \let\columnfractions\relax \def\xcolumnfractions{\columnfractions} \newif\ifsetpercent % #1 is the @columnfraction, usually a decimal number like .5, but might % be just 1. We just use it, whatever it is. % \def\pickupwholefraction#1 {% \global\advance\colcount by 1 \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% \setuptable } \newcount\colcount \def\setuptable#1{% \def\firstarg{#1}% \ifx\firstarg\xendsetuptable \let\go = \relax \else \ifx\firstarg\xcolumnfractions \global\setpercenttrue \else \ifsetpercent \let\go\pickupwholefraction \else \global\advance\colcount by 1 \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a % separator; typically that is always in the input, anyway. \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% \fi \fi \ifx\go\pickupwholefraction % Put the argument back for the \pickupwholefraction call, so % we'll always have a period there to be parsed. \def\go{\pickupwholefraction#1}% \else \let\go = \setuptable \fi% \fi \go } % multitable-only commands. % % @headitem starts a heading row, which we typeset in bold. % Assignments have to be global since we are inside the implicit group % of an alignment entry. \everycr resets \everytab so we don't have to % undo it ourselves. \def\headitemfont{\b}% for people to use in the template row; not changeable \def\headitem{% \checkenv\multitable \crcr \global\everytab={\bf}% can't use \headitemfont since the parsing differs \the\everytab % for the first item }% % % A \tab used to include \hskip1sp. But then the space in a template % line is not enough. That is bad. So let's go back to just `&' until % we again encounter the problem the 1sp was intended to solve. % --karl, nathan@acm.org, 20apr99. \def\tab{\checkenv\multitable &\the\everytab}% % @multitable ... @end multitable definitions: % \newtoks\everytab % insert after every tab. % \envdef\multitable{% \vskip\parskip \startsavinginserts % % @item within a multitable starts a normal row. % We use \def instead of \let so that if one of the multitable entries % contains an @itemize, we don't choke on the \item (seen as \crcr aka % \endtemplate) expanding \doitemize. \def\item{\crcr}% % \tolerance=9500 \hbadness=9500 \setmultitablespacing \parskip=\multitableparskip \parindent=\multitableparindent \overfullrule=0pt \global\colcount=0 % \everycr = {% \noalign{% \global\everytab={}% \global\colcount=0 % Reset the column counter. % Check for saved footnotes, etc. \checkinserts % Keeps underfull box messages off when table breaks over pages. %\filbreak % Maybe so, but it also creates really weird page breaks when the % table breaks over pages. Wouldn't \vfil be better? Wait until the % problem manifests itself, so it can be fixed for real --karl. }% }% % \parsearg\domultitable } \def\domultitable#1{% % To parse everything between @multitable and @item: \setuptable#1 \endsetuptable % % This preamble sets up a generic column definition, which will % be used as many times as user calls for columns. % \vtop will set a single line and will also let text wrap and % continue for many paragraphs if desired. \halign\bgroup &% \global\advance\colcount by 1 \multistrut \vtop{% % Use the current \colcount to find the correct column width: \hsize=\expandafter\csname col\the\colcount\endcsname % % In order to keep entries from bumping into each other % we will add a \leftskip of \multitablecolspace to all columns after % the first one. % % If a template has been used, we will add \multitablecolspace % to the width of each template entry. % % If the user has set preamble in terms of percent of \hsize we will % use that dimension as the width of the column, and the \leftskip % will keep entries from bumping into each other. Table will start at % left margin and final column will justify at right margin. % % Make sure we don't inherit \rightskip from the outer environment. \rightskip=0pt \ifnum\colcount=1 % The first column will be indented with the surrounding text. \advance\hsize by\leftskip \else \ifsetpercent \else % If user has not set preamble in terms of percent of \hsize % we will advance \hsize by \multitablecolspace. \advance\hsize by \multitablecolspace \fi % In either case we will make \leftskip=\multitablecolspace: \leftskip=\multitablecolspace \fi % Ignoring space at the beginning and end avoids an occasional spurious % blank line, when TeX decides to break the line at the space before the % box from the multistrut, so the strut ends up on a line by itself. % For example: % @multitable @columnfractions .11 .89 % @item @code{#} % @tab Legal holiday which is valid in major parts of the whole country. % Is automatically provided with highlighting sequences respectively % marking characters. \noindent\ignorespaces##\unskip\multistrut }\cr } \def\Emultitable{% \crcr \egroup % end the \halign \global\setpercentfalse } \def\setmultitablespacing{% \def\multistrut{\strut}% just use the standard line spacing % % Compute \multitablelinespace (if not defined by user) for use in % \multitableparskip calculation. We used define \multistrut based on % this, but (ironically) that caused the spacing to be off. % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. \ifdim\multitablelinespace=0pt \setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip \global\advance\multitablelinespace by-\ht0 \fi % Test to see if parskip is larger than space between lines of % table. If not, do nothing. % If so, set to same dimension as multitablelinespace. \ifdim\multitableparskip>\multitablelinespace \global\multitableparskip=\multitablelinespace \global\advance\multitableparskip-7pt % to keep parskip somewhat smaller % than skip between lines in the table. \fi% \ifdim\multitableparskip=0pt \global\multitableparskip=\multitablelinespace \global\advance\multitableparskip-7pt % to keep parskip somewhat smaller % than skip between lines in the table. \fi} \message{conditionals,} % @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, % @ifnotxml always succeed. They currently do nothing; we don't % attempt to check whether the conditionals are properly nested. But we % have to remember that they are conditionals, so that @end doesn't % attempt to close an environment group. % \def\makecond#1{% \expandafter\let\csname #1\endcsname = \relax \expandafter\let\csname iscond.#1\endcsname = 1 } \makecond{iftex} \makecond{ifnotdocbook} \makecond{ifnothtml} \makecond{ifnotinfo} \makecond{ifnotplaintext} \makecond{ifnotxml} % Ignore @ignore, @ifhtml, @ifinfo, and the like. % \def\direntry{\doignore{direntry}} \def\documentdescription{\doignore{documentdescription}} \def\docbook{\doignore{docbook}} \def\html{\doignore{html}} \def\ifdocbook{\doignore{ifdocbook}} \def\ifhtml{\doignore{ifhtml}} \def\ifinfo{\doignore{ifinfo}} \def\ifnottex{\doignore{ifnottex}} \def\ifplaintext{\doignore{ifplaintext}} \def\ifxml{\doignore{ifxml}} \def\ignore{\doignore{ignore}} \def\menu{\doignore{menu}} \def\xml{\doignore{xml}} % Ignore text until a line `@end #1', keeping track of nested conditionals. % % A count to remember the depth of nesting. \newcount\doignorecount \def\doignore#1{\begingroup % Scan in ``verbatim'' mode: \obeylines \catcode`\@ = \other \catcode`\{ = \other \catcode`\} = \other % % Make sure that spaces turn into tokens that match what \doignoretext wants. \spaceisspace % % Count number of #1's that we've seen. \doignorecount = 0 % % Swallow text until we reach the matching `@end #1'. \dodoignore{#1}% } { \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. \obeylines % % \gdef\dodoignore#1{% % #1 contains the command name as a string, e.g., `ifinfo'. % % Define a command to find the next `@end #1'. \long\def\doignoretext##1^^M@end #1{% \doignoretextyyy##1^^M@#1\_STOP_}% % % And this command to find another #1 command, at the beginning of a % line. (Otherwise, we would consider a line `@c @ifset', for % example, to count as an @ifset for nesting.) \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% % % And now expand that command. \doignoretext ^^M% }% } \def\doignoreyyy#1{% \def\temp{#1}% \ifx\temp\empty % Nothing found. \let\next\doignoretextzzz \else % Found a nested condition, ... \advance\doignorecount by 1 \let\next\doignoretextyyy % ..., look for another. % If we're here, #1 ends with ^^M\ifinfo (for example). \fi \next #1% the token \_STOP_ is present just after this macro. } % We have to swallow the remaining "\_STOP_". % \def\doignoretextzzz#1{% \ifnum\doignorecount = 0 % We have just found the outermost @end. \let\next\enddoignore \else % Still inside a nested condition. \advance\doignorecount by -1 \let\next\doignoretext % Look for the next @end. \fi \next } % Finish off ignored text. { \obeylines% % Ignore anything after the last `@end #1'; this matters in verbatim % environments, where otherwise the newline after an ignored conditional % would result in a blank line in the output. \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% } % @set VAR sets the variable VAR to an empty value. % @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. % % Since we want to separate VAR from REST-OF-LINE (which might be % empty), we can't just use \parsearg; we have to insert a space of our % own to delimit the rest of the line, and then take it out again if we % didn't need it. % We rely on the fact that \parsearg sets \catcode`\ =10. % \parseargdef\set{\setyyy#1 \endsetyyy} \def\setyyy#1 #2\endsetyyy{% {% \makevalueexpandable \def\temp{#2}% \edef\next{\gdef\makecsname{SET#1}}% \ifx\temp\empty \next{}% \else \setzzz#2\endsetzzz \fi }% } % Remove the trailing space \setxxx inserted. \def\setzzz#1 \endsetzzz{\next{#1}} % @clear VAR clears (i.e., unsets) the variable VAR. % \parseargdef\clear{% {% \makevalueexpandable \global\expandafter\let\csname SET#1\endcsname=\relax }% } % @value{foo} gets the text saved in variable foo. \def\value{\begingroup\makevalueexpandable\valuexxx} \def\valuexxx#1{\expandablevalue{#1}\endgroup} { \catcode`\- = \active \catcode`\_ = \active % \gdef\makevalueexpandable{% \let\value = \expandablevalue % We don't want these characters active, ... \catcode`\-=\other \catcode`\_=\other % ..., but we might end up with active ones in the argument if % we're called from @code, as @code{@value{foo-bar_}}, though. % So \let them to their normal equivalents. \let-\normaldash \let_\normalunderscore } } % We have this subroutine so that we can handle at least some @value's % properly in indexes (we call \makevalueexpandable in \indexdummies). % The command has to be fully expandable (if the variable is set), since % the result winds up in the index file. This means that if the % variable's value contains other Texinfo commands, it's almost certain % it will fail (although perhaps we could fix that with sufficient work % to do a one-level expansion on the result, instead of complete). % \def\expandablevalue#1{% \expandafter\ifx\csname SET#1\endcsname\relax {[No value for ``#1'']}% \message{Variable `#1', used in @value, is not set.}% \else \csname SET#1\endcsname \fi } % @ifset VAR ... @end ifset reads the `...' iff VAR has been defined % with @set. % % To get special treatment of `@end ifset,' call \makeond and the redefine. % \makecond{ifset} \def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} \def\doifset#1#2{% {% \makevalueexpandable \let\next=\empty \expandafter\ifx\csname SET#2\endcsname\relax #1% If not set, redefine \next. \fi \expandafter }\next } \def\ifsetfail{\doignore{ifset}} % @ifclear VAR ... @end executes the `...' iff VAR has never been % defined with @set, or has been undefined with @clear. % % The `\else' inside the `\doifset' parameter is a trick to reuse the % above code: if the variable is not set, do nothing, if it is set, % then redefine \next to \ifclearfail. % \makecond{ifclear} \def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} \def\ifclearfail{\doignore{ifclear}} % @ifcommandisdefined CMD ... @end executes the `...' if CMD (written % without the @) is in fact defined. We can only feasibly check at the % TeX level, so something like `mathcode' is going to considered % defined even though it is not a Texinfo command. % \makecond{ifcommanddefined} \def\ifcommanddefined{\parsearg{\doifcmddefined{\let\next=\ifcmddefinedfail}}} % \def\doifcmddefined#1#2{{% \makevalueexpandable \let\next=\empty \expandafter\ifx\csname #2\endcsname\relax #1% If not defined, \let\next as above. \fi \expandafter }\next } \def\ifcmddefinedfail{\doignore{ifcommanddefined}} % @ifcommandnotdefined CMD ... handled similar to @ifclear above. \makecond{ifcommandnotdefined} \def\ifcommandnotdefined{% \parsearg{\doifcmddefined{\else \let\next=\ifcmdnotdefinedfail}}} \def\ifcmdnotdefinedfail{\doignore{ifcommandnotdefined}} % Set the `txicommandconditionals' variable, so documents have a way to % test if the @ifcommand...defined conditionals are available. \set txicommandconditionals % @dircategory CATEGORY -- specify a category of the dir file % which this file should belong to. Ignore this in TeX. \let\dircategory=\comment % @defininfoenclose. \let\definfoenclose=\comment \message{indexing,} % Index generation facilities % Define \newwrite to be identical to plain tex's \newwrite % except not \outer, so it can be used within macros and \if's. \edef\newwrite{\makecsname{ptexnewwrite}} % \newindex {foo} defines an index named foo. % It automatically defines \fooindex such that % \fooindex ...rest of line... puts an entry in the index foo. % It also defines \fooindfile to be the number of the output channel for % the file that accumulates this index. The file's extension is foo. % The name of an index should be no more than 2 characters long % for the sake of vms. % \def\newindex#1{% \iflinks \expandafter\newwrite \csname#1indfile\endcsname \openout \csname#1indfile\endcsname \jobname.#1 % Open the file \fi \expandafter\xdef\csname#1index\endcsname{% % Define @#1index \noexpand\doindex{#1}} } % @defindex foo == \newindex{foo} % \def\defindex{\parsearg\newindex} % Define @defcodeindex, like @defindex except put all entries in @code. % \def\defcodeindex{\parsearg\newcodeindex} % \def\newcodeindex#1{% \iflinks \expandafter\newwrite \csname#1indfile\endcsname \openout \csname#1indfile\endcsname \jobname.#1 \fi \expandafter\xdef\csname#1index\endcsname{% \noexpand\docodeindex{#1}}% } % @synindex foo bar makes index foo feed into index bar. % Do this instead of @defindex foo if you don't want it as a separate index. % % @syncodeindex foo bar similar, but put all entries made for index foo % inside @code. % \def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} \def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} % #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), % #3 the target index (bar). \def\dosynindex#1#2#3{% % Only do \closeout if we haven't already done it, else we'll end up % closing the target index. \expandafter \ifx\csname donesynindex#2\endcsname \relax % The \closeout helps reduce unnecessary open files; the limit on the % Acorn RISC OS is a mere 16 files. \expandafter\closeout\csname#2indfile\endcsname \expandafter\let\csname donesynindex#2\endcsname = 1 \fi % redefine \fooindfile: \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname \expandafter\let\csname#2indfile\endcsname=\temp % redefine \fooindex: \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% } % Define \doindex, the driver for all \fooindex macros. % Argument #1 is generated by the calling \fooindex macro, % and it is "foo", the name of the index. % \doindex just uses \parsearg; it calls \doind for the actual work. % This is because \doind is more useful to call from other macros. % There is also \dosubind {index}{topic}{subtopic} % which makes an entry in a two-level index such as the operation index. \def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} \def\singleindexer #1{\doind{\indexname}{#1}} % like the previous two, but they put @code around the argument. \def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} \def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} % Take care of Texinfo commands that can appear in an index entry. % Since there are some commands we want to expand, and others we don't, % we have to laboriously prevent expansion for those that we don't. % \def\indexdummies{% \escapechar = `\\ % use backslash in output files. \def\@{@}% change to @@ when we switch to @ as escape char in index files. \def\ {\realbackslash\space }% % % Need these unexpandable (because we define \tt as a dummy) % definitions when @{ or @} appear in index entry text. Also, more % complicated, when \tex is in effect and \{ is a \delimiter again. % We can't use \lbracecmd and \rbracecmd because texindex assumes % braces and backslashes are used only as delimiters. Perhaps we % should define @lbrace and @rbrace commands a la @comma. \def\{{{\tt\char123}}% \def\}{{\tt\char125}}% % % I don't entirely understand this, but when an index entry is % generated from a macro call, the \endinput which \scanmacro inserts % causes processing to be prematurely terminated. This is, % apparently, because \indexsorttmp is fully expanded, and \endinput % is an expandable command. The redefinition below makes \endinput % disappear altogether for that purpose -- although logging shows that % processing continues to some further point. On the other hand, it % seems \endinput does not hurt in the printed index arg, since that % is still getting written without apparent harm. % % Sample source (mac-idx3.tex, reported by Graham Percival to % help-texinfo, 22may06): % @macro funindex {WORD} % @findex xyz % @end macro % ... % @funindex commtest % % The above is not enough to reproduce the bug, but it gives the flavor. % % Sample whatsit resulting: % .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}} % % So: \let\endinput = \empty % % Do the redefinitions. \commondummies } % For the aux and toc files, @ is the escape character. So we want to % redefine everything using @ as the escape character (instead of % \realbackslash, still used for index files). When everything uses @, % this will be simpler. % \def\atdummies{% \def\@{@@}% \def\ {@ }% \let\{ = \lbraceatcmd \let\} = \rbraceatcmd % % Do the redefinitions. \commondummies \otherbackslash } % Called from \indexdummies and \atdummies. % \def\commondummies{% % % \definedummyword defines \#1 as \string\#1\space, thus effectively % preventing its expansion. This is used only for control words, % not control letters, because the \space would be incorrect for % control characters, but is needed to separate the control word % from whatever follows. % % For control letters, we have \definedummyletter, which omits the % space. % % These can be used both for control words that take an argument and % those that do not. If it is followed by {arg} in the input, then % that will dutifully get written to the index (or wherever). % \def\definedummyword ##1{\def##1{\string##1\space}}% \def\definedummyletter##1{\def##1{\string##1}}% \let\definedummyaccent\definedummyletter % \commondummiesnofonts % \definedummyletter\_% \definedummyletter\-% % % Non-English letters. \definedummyword\AA \definedummyword\AE \definedummyword\DH \definedummyword\L \definedummyword\O \definedummyword\OE \definedummyword\TH \definedummyword\aa \definedummyword\ae \definedummyword\dh \definedummyword\exclamdown \definedummyword\l \definedummyword\o \definedummyword\oe \definedummyword\ordf \definedummyword\ordm \definedummyword\questiondown \definedummyword\ss \definedummyword\th % % Although these internal commands shouldn't show up, sometimes they do. \definedummyword\bf \definedummyword\gtr \definedummyword\hat \definedummyword\less \definedummyword\sf \definedummyword\sl \definedummyword\tclose \definedummyword\tt % \definedummyword\LaTeX \definedummyword\TeX % % Assorted special characters. \definedummyword\arrow \definedummyword\bullet \definedummyword\comma \definedummyword\copyright \definedummyword\registeredsymbol \definedummyword\dots \definedummyword\enddots \definedummyword\entrybreak \definedummyword\equiv \definedummyword\error \definedummyword\euro \definedummyword\expansion \definedummyword\geq \definedummyword\guillemetleft \definedummyword\guillemetright \definedummyword\guilsinglleft \definedummyword\guilsinglright \definedummyword\lbracechar \definedummyword\leq \definedummyword\minus \definedummyword\ogonek \definedummyword\pounds \definedummyword\point \definedummyword\print \definedummyword\quotedblbase \definedummyword\quotedblleft \definedummyword\quotedblright \definedummyword\quoteleft \definedummyword\quoteright \definedummyword\quotesinglbase \definedummyword\rbracechar \definedummyword\result \definedummyword\textdegree % % We want to disable all macros so that they are not expanded by \write. \macrolist % \normalturnoffactive % % Handle some cases of @value -- where it does not contain any % (non-fully-expandable) commands. \makevalueexpandable } % \commondummiesnofonts: common to \commondummies and \indexnofonts. % \def\commondummiesnofonts{% % Control letters and accents. \definedummyletter\!% \definedummyaccent\"% \definedummyaccent\'% \definedummyletter\*% \definedummyaccent\,% \definedummyletter\.% \definedummyletter\/% \definedummyletter\:% \definedummyaccent\=% \definedummyletter\?% \definedummyaccent\^% \definedummyaccent\`% \definedummyaccent\~% \definedummyword\u \definedummyword\v \definedummyword\H \definedummyword\dotaccent \definedummyword\ogonek \definedummyword\ringaccent \definedummyword\tieaccent \definedummyword\ubaraccent \definedummyword\udotaccent \definedummyword\dotless % % Texinfo font commands. \definedummyword\b \definedummyword\i \definedummyword\r \definedummyword\sansserif \definedummyword\sc \definedummyword\slanted \definedummyword\t % % Commands that take arguments. \definedummyword\abbr \definedummyword\acronym \definedummyword\anchor \definedummyword\cite \definedummyword\code \definedummyword\command \definedummyword\dfn \definedummyword\dmn \definedummyword\email \definedummyword\emph \definedummyword\env \definedummyword\file \definedummyword\image \definedummyword\indicateurl \definedummyword\inforef \definedummyword\kbd \definedummyword\key \definedummyword\math \definedummyword\option \definedummyword\pxref \definedummyword\ref \definedummyword\samp \definedummyword\strong \definedummyword\tie \definedummyword\uref \definedummyword\url \definedummyword\var \definedummyword\verb \definedummyword\w \definedummyword\xref } % \indexnofonts is used when outputting the strings to sort the index % by, and when constructing control sequence names. It eliminates all % control sequences and just writes whatever the best ASCII sort string % would be for a given command (usually its argument). % \def\indexnofonts{% % Accent commands should become @asis. \def\definedummyaccent##1{\let##1\asis}% % We can just ignore other control letters. \def\definedummyletter##1{\let##1\empty}% % All control words become @asis by default; overrides below. \let\definedummyword\definedummyaccent % \commondummiesnofonts % % Don't no-op \tt, since it isn't a user-level command % and is used in the definitions of the active chars like <, >, |, etc. % Likewise with the other plain tex font commands. %\let\tt=\asis % \def\ { }% \def\@{@}% \def\_{\normalunderscore}% \def\-{}% @- shouldn't affect sorting % % Unfortunately, texindex is not prepared to handle braces in the % content at all. So for index sorting, we map @{ and @} to strings % starting with |, since that ASCII character is between ASCII { and }. \def\{{|a}% \def\lbracechar{|a}% % \def\}{|b}% \def\rbracechar{|b}% % % Non-English letters. \def\AA{AA}% \def\AE{AE}% \def\DH{DZZ}% \def\L{L}% \def\OE{OE}% \def\O{O}% \def\TH{ZZZ}% \def\aa{aa}% \def\ae{ae}% \def\dh{dzz}% \def\exclamdown{!}% \def\l{l}% \def\oe{oe}% \def\ordf{a}% \def\ordm{o}% \def\o{o}% \def\questiondown{?}% \def\ss{ss}% \def\th{zzz}% % \def\LaTeX{LaTeX}% \def\TeX{TeX}% % % Assorted special characters. % (The following {} will end up in the sort string, but that's ok.) \def\arrow{->}% \def\bullet{bullet}% \def\comma{,}% \def\copyright{copyright}% \def\dots{...}% \def\enddots{...}% \def\equiv{==}% \def\error{error}% \def\euro{euro}% \def\expansion{==>}% \def\geq{>=}% \def\guillemetleft{<<}% \def\guillemetright{>>}% \def\guilsinglleft{<}% \def\guilsinglright{>}% \def\leq{<=}% \def\minus{-}% \def\point{.}% \def\pounds{pounds}% \def\print{-|}% \def\quotedblbase{"}% \def\quotedblleft{"}% \def\quotedblright{"}% \def\quoteleft{`}% \def\quoteright{'}% \def\quotesinglbase{,}% \def\registeredsymbol{R}% \def\result{=>}% \def\textdegree{o}% % \expandafter\ifx\csname SETtxiindexlquoteignore\endcsname\relax \else \indexlquoteignore \fi % % We need to get rid of all macros, leaving only the arguments (if present). % Of course this is not nearly correct, but it is the best we can do for now. % makeinfo does not expand macros in the argument to @deffn, which ends up % writing an index entry, and texindex isn't prepared for an index sort entry % that starts with \. % % Since macro invocations are followed by braces, we can just redefine them % to take a single TeX argument. The case of a macro invocation that % goes to end-of-line is not handled. % \macrolist } % Undocumented (for FSFS 2nd ed.): @set txiindexlquoteignore makes us % ignore left quotes in the sort term. {\catcode`\`=\active \gdef\indexlquoteignore{\let`=\empty}} \let\indexbackslash=0 %overridden during \printindex. \let\SETmarginindex=\relax % put index entries in margin (undocumented)? % Most index entries go through here, but \dosubind is the general case. % #1 is the index name, #2 is the entry text. \def\doind#1#2{\dosubind{#1}{#2}{}} % Workhorse for all \fooindexes. % #1 is name of index, #2 is stuff to put there, #3 is subentry -- % empty if called from \doind, as we usually are (the main exception % is with most defuns, which call us directly). % \def\dosubind#1#2#3{% \iflinks {% % Store the main index entry text (including the third arg). \toks0 = {#2}% % If third arg is present, precede it with a space. \def\thirdarg{#3}% \ifx\thirdarg\empty \else \toks0 = \expandafter{\the\toks0 \space #3}% \fi % \edef\writeto{\csname#1indfile\endcsname}% % \safewhatsit\dosubindwrite }% \fi } % Write the entry in \toks0 to the index file: % \def\dosubindwrite{% % Put the index entry in the margin if desired. \ifx\SETmarginindex\relax\else \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}% \fi % % Remember, we are within a group. \indexdummies % Must do this here, since \bf, etc expand at this stage \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now % so it will be output as is; and it will print as backslash. % % Process the index entry with all font commands turned off, to % get the string to sort by. {\indexnofonts \edef\temp{\the\toks0}% need full expansion \xdef\indexsorttmp{\temp}% }% % % Set up the complete index entry, with both the sort key and % the original text, including any font commands. We write % three arguments to \entry to the .?? file (four in the % subentry case), texindex reduces to two when writing the .??s % sorted result. \edef\temp{% \write\writeto{% \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}% }% \temp } % Take care of unwanted page breaks/skips around a whatsit: % % If a skip is the last thing on the list now, preserve it % by backing up by \lastskip, doing the \write, then inserting % the skip again. Otherwise, the whatsit generated by the % \write or \pdfdest will make \lastskip zero. The result is that % sequences like this: % @end defun % @tindex whatever % @defun ... % will have extra space inserted, because the \medbreak in the % start of the @defun won't see the skip inserted by the @end of % the previous defun. % % But don't do any of this if we're not in vertical mode. We % don't want to do a \vskip and prematurely end a paragraph. % % Avoid page breaks due to these extra skips, too. % % But wait, there is a catch there: % We'll have to check whether \lastskip is zero skip. \ifdim is not % sufficient for this purpose, as it ignores stretch and shrink parts % of the skip. The only way seems to be to check the textual % representation of the skip. % % The following is almost like \def\zeroskipmacro{0.0pt} except that % the ``p'' and ``t'' characters have catcode \other, not 11 (letter). % \edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} % \newskip\whatsitskip \newcount\whatsitpenalty % % ..., ready, GO: % \def\safewhatsit#1{\ifhmode #1% \else % \lastskip and \lastpenalty cannot both be nonzero simultaneously. \whatsitskip = \lastskip \edef\lastskipmacro{\the\lastskip}% \whatsitpenalty = \lastpenalty % % If \lastskip is nonzero, that means the last item was a % skip. And since a skip is discardable, that means this % -\whatsitskip glue we're inserting is preceded by a % non-discardable item, therefore it is not a potential % breakpoint, therefore no \nobreak needed. \ifx\lastskipmacro\zeroskipmacro \else \vskip-\whatsitskip \fi % #1% % \ifx\lastskipmacro\zeroskipmacro % If \lastskip was zero, perhaps the last item was a penalty, and % perhaps it was >=10000, e.g., a \nobreak. In that case, we want % to re-insert the same penalty (values >10000 are used for various % signals); since we just inserted a non-discardable item, any % following glue (such as a \parskip) would be a breakpoint. For example: % @deffn deffn-whatever % @vindex index-whatever % Description. % would allow a break between the index-whatever whatsit % and the "Description." paragraph. \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi \else % On the other hand, if we had a nonzero \lastskip, % this make-up glue would be preceded by a non-discardable item % (the whatsit from the \write), so we must insert a \nobreak. \nobreak\vskip\whatsitskip \fi \fi} % The index entry written in the file actually looks like % \entry {sortstring}{page}{topic} % or % \entry {sortstring}{page}{topic}{subtopic} % The texindex program reads in these files and writes files % containing these kinds of lines: % \initial {c} % before the first topic whose initial is c % \entry {topic}{pagelist} % for a topic that is used without subtopics % \primary {topic} % for the beginning of a topic that is used with subtopics % \secondary {subtopic}{pagelist} % for each subtopic. % Define the user-accessible indexing commands % @findex, @vindex, @kindex, @cindex. \def\findex {\fnindex} \def\kindex {\kyindex} \def\cindex {\cpindex} \def\vindex {\vrindex} \def\tindex {\tpindex} \def\pindex {\pgindex} \def\cindexsub {\begingroup\obeylines\cindexsub} {\obeylines % \gdef\cindexsub "#1" #2^^M{\endgroup % \dosubind{cp}{#2}{#1}}} % Define the macros used in formatting output of the sorted index material. % @printindex causes a particular index (the ??s file) to get printed. % It does not print any chapter heading (usually an @unnumbered). % \parseargdef\printindex{\begingroup \dobreak \chapheadingskip{10000}% % \smallfonts \rm \tolerance = 9500 \plainfrenchspacing \everypar = {}% don't want the \kern\-parindent from indentation suppression. % % See if the index file exists and is nonempty. % Change catcode of @ here so that if the index file contains % \initial {@} % as its first line, TeX doesn't complain about mismatched braces % (because it thinks @} is a control sequence). \catcode`\@ = 11 \openin 1 \jobname.#1s \ifeof 1 % \enddoublecolumns gets confused if there is no text in the index, % and it loses the chapter title and the aux file entries for the % index. The easiest way to prevent this problem is to make sure % there is some text. \putwordIndexNonexistent \else % % If the index file exists but is empty, then \openin leaves \ifeof % false. We have to make TeX try to read something from the file, so % it can discover if there is anything in it. \read 1 to \temp \ifeof 1 \putwordIndexIsEmpty \else % Index files are almost Texinfo source, but we use \ as the escape % character. It would be better to use @, but that's too big a change % to make right now. \def\indexbackslash{\backslashcurfont}% \catcode`\\ = 0 \escapechar = `\\ \begindoublecolumns \input \jobname.#1s \enddoublecolumns \fi \fi \closein 1 \endgroup} % These macros are used by the sorted index file itself. % Change them to control the appearance of the index. \def\initial#1{{% % Some minor font changes for the special characters. \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt % % Remove any glue we may have, we'll be inserting our own. \removelastskip % % We like breaks before the index initials, so insert a bonus. \nobreak \vskip 0pt plus 3\baselineskip \penalty 0 \vskip 0pt plus -3\baselineskip % % Typeset the initial. Making this add up to a whole number of % baselineskips increases the chance of the dots lining up from column % to column. It still won't often be perfect, because of the stretch % we need before each entry, but it's better. % % No shrink because it confuses \balancecolumns. \vskip 1.67\baselineskip plus .5\baselineskip \leftline{\secbf #1}% % Do our best not to break after the initial. \nobreak \vskip .33\baselineskip plus .1\baselineskip }} % \entry typesets a paragraph consisting of the text (#1), dot leaders, and % then page number (#2) flushed to the right margin. It is used for index % and table of contents entries. The paragraph is indented by \leftskip. % % A straightforward implementation would start like this: % \def\entry#1#2{... % But this freezes the catcodes in the argument, and can cause problems to % @code, which sets - active. This problem was fixed by a kludge--- % ``-'' was active throughout whole index, but this isn't really right. % The right solution is to prevent \entry from swallowing the whole text. % --kasal, 21nov03 \def\entry{% \begingroup % % Start a new paragraph if necessary, so our assignments below can't % affect previous text. \par % % Do not fill out the last line with white space. \parfillskip = 0in % % No extra space above this paragraph. \parskip = 0in % % Do not prefer a separate line ending with a hyphen to fewer lines. \finalhyphendemerits = 0 % % \hangindent is only relevant when the entry text and page number % don't both fit on one line. In that case, bob suggests starting the % dots pretty far over on the line. Unfortunately, a large % indentation looks wrong when the entry text itself is broken across % lines. So we use a small indentation and put up with long leaders. % % \hangafter is reset to 1 (which is the value we want) at the start % of each paragraph, so we need not do anything with that. \hangindent = 2em % % When the entry text needs to be broken, just fill out the first line % with blank space. \rightskip = 0pt plus1fil % % A bit of stretch before each entry for the benefit of balancing % columns. \vskip 0pt plus1pt % % When reading the text of entry, convert explicit line breaks % from @* into spaces. The user might give these in long section % titles, for instance. \def\*{\unskip\space\ignorespaces}% \def\entrybreak{\hfil\break}% % % Swallow the left brace of the text (first parameter): \afterassignment\doentry \let\temp = } \def\entrybreak{\unskip\space\ignorespaces}% \def\doentry{% \bgroup % Instead of the swallowed brace. \noindent \aftergroup\finishentry % And now comes the text of the entry. } \def\finishentry#1{% % #1 is the page number. % % The following is kludged to not output a line of dots in the index if % there are no page numbers. The next person who breaks this will be % cursed by a Unix daemon. \setbox\boxA = \hbox{#1}% \ifdim\wd\boxA = 0pt \ % \else % % If we must, put the page number on a line of its own, and fill out % this line with blank space. (The \hfil is overwhelmed with the % fill leaders glue in \indexdotfill if the page number does fit.) \hfil\penalty50 \null\nobreak\indexdotfill % Have leaders before the page number. % % The `\ ' here is removed by the implicit \unskip that TeX does as % part of (the primitive) \par. Without it, a spurious underfull % \hbox ensues. \ifpdf \pdfgettoks#1.% \ \the\toksA \else \ #1% \fi \fi \par \endgroup } % Like plain.tex's \dotfill, except uses up at least 1 em. \def\indexdotfill{\cleaders \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill} \def\primary #1{\line{#1\hfil}} \newskip\secondaryindent \secondaryindent=0.5cm \def\secondary#1#2{{% \parfillskip=0in \parskip=0in \hangindent=1in \hangafter=1 \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill \ifpdf \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. \else #2 \fi \par }} % Define two-column mode, which we use to typeset indexes. % Adapted from the TeXbook, page 416, which is to say, % the manmac.tex format used to print the TeXbook itself. \catcode`\@=11 \newbox\partialpage \newdimen\doublecolumnhsize \def\begindoublecolumns{\begingroup % ended by \enddoublecolumns % Grab any single-column material above us. \output = {% % % Here is a possibility not foreseen in manmac: if we accumulate a % whole lot of material, we might end up calling this \output % routine twice in a row (see the doublecol-lose test, which is % essentially a couple of indexes with @setchapternewpage off). In % that case we just ship out what is in \partialpage with the normal % output routine. Generally, \partialpage will be empty when this % runs and this will be a no-op. See the indexspread.tex test case. \ifvoid\partialpage \else \onepageout{\pagecontents\partialpage}% \fi % \global\setbox\partialpage = \vbox{% % Unvbox the main output page. \unvbox\PAGE \kern-\topskip \kern\baselineskip }% }% \eject % run that output routine to set \partialpage % % Use the double-column output routine for subsequent pages. \output = {\doublecolumnout}% % % Change the page size parameters. We could do this once outside this % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 % format, but then we repeat the same computation. Repeating a couple % of assignments once per index is clearly meaningless for the % execution time, so we may as well do it in one place. % % First we halve the line length, less a little for the gutter between % the columns. We compute the gutter based on the line length, so it % changes automatically with the paper format. The magic constant % below is chosen so that the gutter has the same value (well, +-<1pt) % as it did when we hard-coded it. % % We put the result in a separate register, \doublecolumhsize, so we % can restore it in \pagesofar, after \hsize itself has (potentially) % been clobbered. % \doublecolumnhsize = \hsize \advance\doublecolumnhsize by -.04154\hsize \divide\doublecolumnhsize by 2 \hsize = \doublecolumnhsize % % Double the \vsize as well. (We don't need a separate register here, % since nobody clobbers \vsize.) \vsize = 2\vsize } % The double-column output routine for all double-column pages except % the last. % \def\doublecolumnout{% \splittopskip=\topskip \splitmaxdepth=\maxdepth % Get the available space for the double columns -- the normal % (undoubled) page height minus any material left over from the % previous page. \dimen@ = \vsize \divide\dimen@ by 2 \advance\dimen@ by -\ht\partialpage % % box0 will be the left-hand column, box2 the right. \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ \onepageout\pagesofar \unvbox255 \penalty\outputpenalty } % % Re-output the contents of the output page -- any previous material, % followed by the two boxes we just split, in box0 and box2. \def\pagesofar{% \unvbox\partialpage % \hsize = \doublecolumnhsize \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}% } % % All done with double columns. \def\enddoublecolumns{% % The following penalty ensures that the page builder is exercised % _before_ we change the output routine. This is necessary in the % following situation: % % The last section of the index consists only of a single entry. % Before this section, \pagetotal is less than \pagegoal, so no % break occurs before the last section starts. However, the last % section, consisting of \initial and the single \entry, does not % fit on the page and has to be broken off. Without the following % penalty the page builder will not be exercised until \eject % below, and by that time we'll already have changed the output % routine to the \balancecolumns version, so the next-to-last % double-column page will be processed with \balancecolumns, which % is wrong: The two columns will go to the main vertical list, with % the broken-off section in the recent contributions. As soon as % the output routine finishes, TeX starts reconsidering the page % break. The two columns and the broken-off section both fit on the % page, because the two columns now take up only half of the page % goal. When TeX sees \eject from below which follows the final % section, it invokes the new output routine that we've set after % \balancecolumns below; \onepageout will try to fit the two columns % and the final section into the vbox of \pageheight (see % \pagebody), causing an overfull box. % % Note that glue won't work here, because glue does not exercise the % page builder, unlike penalties (see The TeXbook, pp. 280-281). \penalty0 % \output = {% % Split the last of the double-column material. Leave it on the % current page, no automatic page break. \balancecolumns % % If we end up splitting too much material for the current page, % though, there will be another page break right after this \output % invocation ends. Having called \balancecolumns once, we do not % want to call it again. Therefore, reset \output to its normal % definition right away. (We hope \balancecolumns will never be % called on to balance too much material, but if it is, this makes % the output somewhat more palatable.) \global\output = {\onepageout{\pagecontents\PAGE}}% }% \eject \endgroup % started in \begindoublecolumns % % \pagegoal was set to the doubled \vsize above, since we restarted % the current page. We're now back to normal single-column % typesetting, so reset \pagegoal to the normal \vsize (after the % \endgroup where \vsize got restored). \pagegoal = \vsize } % % Called at the end of the double column material. \def\balancecolumns{% \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. \dimen@ = \ht0 \advance\dimen@ by \topskip \advance\dimen@ by-\baselineskip \divide\dimen@ by 2 % target to split to %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}% \splittopskip = \topskip % Loop until we get a decent breakpoint. {% \vbadness = 10000 \loop \global\setbox3 = \copy0 \global\setbox1 = \vsplit3 to \dimen@ \ifdim\ht3>\dimen@ \global\advance\dimen@ by 1pt \repeat }% %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}% \setbox0=\vbox to\dimen@{\unvbox1}% \setbox2=\vbox to\dimen@{\unvbox3}% % \pagesofar } \catcode`\@ = \other \message{sectioning,} % Chapters, sections, etc. % Let's start with @part. \outer\parseargdef\part{\partzzz{#1}} \def\partzzz#1{% \chapoddpage \null \vskip.3\vsize % move it down on the page a bit \begingroup \noindent \titlefonts\rmisbold #1\par % the text \let\lastnode=\empty % no node to associate with \writetocentry{part}{#1}{}% but put it in the toc \headingsoff % no headline or footline on the part page \chapoddpage \endgroup } % \unnumberedno is an oxymoron. But we count the unnumbered % sections so that we can refer to them unambiguously in the pdf % outlines by their "section number". We avoid collisions with chapter % numbers by starting them at 10000. (If a document ever has 10000 % chapters, we're in trouble anyway, I'm sure.) \newcount\unnumberedno \unnumberedno = 10000 \newcount\chapno \newcount\secno \secno=0 \newcount\subsecno \subsecno=0 \newcount\subsubsecno \subsubsecno=0 % This counter is funny since it counts through charcodes of letters A, B, ... \newcount\appendixno \appendixno = `\@ % % \def\appendixletter{\char\the\appendixno} % We do the following ugly conditional instead of the above simple % construct for the sake of pdftex, which needs the actual % letter in the expansion, not just typeset. % \def\appendixletter{% \ifnum\appendixno=`A A% \else\ifnum\appendixno=`B B% \else\ifnum\appendixno=`C C% \else\ifnum\appendixno=`D D% \else\ifnum\appendixno=`E E% \else\ifnum\appendixno=`F F% \else\ifnum\appendixno=`G G% \else\ifnum\appendixno=`H H% \else\ifnum\appendixno=`I I% \else\ifnum\appendixno=`J J% \else\ifnum\appendixno=`K K% \else\ifnum\appendixno=`L L% \else\ifnum\appendixno=`M M% \else\ifnum\appendixno=`N N% \else\ifnum\appendixno=`O O% \else\ifnum\appendixno=`P P% \else\ifnum\appendixno=`Q Q% \else\ifnum\appendixno=`R R% \else\ifnum\appendixno=`S S% \else\ifnum\appendixno=`T T% \else\ifnum\appendixno=`U U% \else\ifnum\appendixno=`V V% \else\ifnum\appendixno=`W W% \else\ifnum\appendixno=`X X% \else\ifnum\appendixno=`Y Y% \else\ifnum\appendixno=`Z Z% % The \the is necessary, despite appearances, because \appendixletter is % expanded while writing the .toc file. \char\appendixno is not % expandable, thus it is written literally, thus all appendixes come out % with the same letter (or @) in the toc without it. \else\char\the\appendixno \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} % Each @chapter defines these (using marks) as the number+name, number % and name of the chapter. Page headings and footings can use % these. @section does likewise. \def\thischapter{} \def\thischapternum{} \def\thischaptername{} \def\thissection{} \def\thissectionnum{} \def\thissectionname{} \newcount\absseclevel % used to calculate proper heading level \newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count % @raisesections: treat @section as chapter, @subsection as section, etc. \def\raisesections{\global\advance\secbase by -1} \let\up=\raisesections % original BFox name % @lowersections: treat @chapter as section, @section as subsection, etc. \def\lowersections{\global\advance\secbase by 1} \let\down=\lowersections % original BFox name % we only have subsub. \chardef\maxseclevel = 3 % % A numbered section within an unnumbered changes to unnumbered too. % To achieve this, remember the "biggest" unnum. sec. we are currently in: \chardef\unnlevel = \maxseclevel % % Trace whether the current chapter is an appendix or not: % \chapheadtype is "N" or "A", unnumbered chapters are ignored. \def\chapheadtype{N} % Choose a heading macro % #1 is heading type % #2 is heading level % #3 is text for heading \def\genhead#1#2#3{% % Compute the abs. sec. level: \absseclevel=#2 \advance\absseclevel by \secbase % Make sure \absseclevel doesn't fall outside the range: \ifnum \absseclevel < 0 \absseclevel = 0 \else \ifnum \absseclevel > 3 \absseclevel = 3 \fi \fi % The heading type: \def\headtype{#1}% \if \headtype U% \ifnum \absseclevel < \unnlevel \chardef\unnlevel = \absseclevel \fi \else % Check for appendix sections: \ifnum \absseclevel = 0 \edef\chapheadtype{\headtype}% \else \if \headtype A\if \chapheadtype N% \errmessage{@appendix... within a non-appendix chapter}% \fi\fi \fi % Check for numbered within unnumbered: \ifnum \absseclevel > \unnlevel \def\headtype{U}% \else \chardef\unnlevel = 3 \fi \fi % Now print the heading: \if \headtype U% \ifcase\absseclevel \unnumberedzzz{#3}% \or \unnumberedseczzz{#3}% \or \unnumberedsubseczzz{#3}% \or \unnumberedsubsubseczzz{#3}% \fi \else \if \headtype A% \ifcase\absseclevel \appendixzzz{#3}% \or \appendixsectionzzz{#3}% \or \appendixsubseczzz{#3}% \or \appendixsubsubseczzz{#3}% \fi \else \ifcase\absseclevel \chapterzzz{#3}% \or \seczzz{#3}% \or \numberedsubseczzz{#3}% \or \numberedsubsubseczzz{#3}% \fi \fi \fi \suppressfirstparagraphindent } % an interface: \def\numhead{\genhead N} \def\apphead{\genhead A} \def\unnmhead{\genhead U} % @chapter, @appendix, @unnumbered. Increment top-level counter, reset % all lower-level sectioning counters to zero. % % Also set \chaplevelprefix, which we prepend to @float sequence numbers % (e.g., figures), q.v. By default (before any chapter), that is empty. \let\chaplevelprefix = \empty % \outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz \def\chapterzzz#1{% % section resetting is \global in case the chapter is in a group, such % as an @include file. \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 \global\advance\chapno by 1 % % Used for \float. \gdef\chaplevelprefix{\the\chapno.}% \resetallfloatnos % % \putwordChapter can contain complex things in translations. \toks0=\expandafter{\putwordChapter}% \message{\the\toks0 \space \the\chapno}% % % Write the actual heading. \chapmacro{#1}{Ynumbered}{\the\chapno}% % % So @section and the like are numbered underneath this chapter. \global\let\section = \numberedsec \global\let\subsection = \numberedsubsec \global\let\subsubsection = \numberedsubsubsec } \outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz % \def\appendixzzz#1{% \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 \global\advance\appendixno by 1 \gdef\chaplevelprefix{\appendixletter.}% \resetallfloatnos % % \putwordAppendix can contain complex things in translations. \toks0=\expandafter{\putwordAppendix}% \message{\the\toks0 \space \appendixletter}% % \chapmacro{#1}{Yappendix}{\appendixletter}% % \global\let\section = \appendixsec \global\let\subsection = \appendixsubsec \global\let\subsubsection = \appendixsubsubsec } % normally unnmhead0 calls unnumberedzzz: \outer\parseargdef\unnumbered{\unnmhead0{#1}} \def\unnumberedzzz#1{% \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 \global\advance\unnumberedno by 1 % % Since an unnumbered has no number, no prefix for figures. \global\let\chaplevelprefix = \empty \resetallfloatnos % % This used to be simply \message{#1}, but TeX fully expands the % argument to \message. Therefore, if #1 contained @-commands, TeX % expanded them. For example, in `@unnumbered The @cite{Book}', TeX % expanded @cite (which turns out to cause errors because \cite is meant % to be executed, not expanded). % % Anyway, we don't want the fully-expanded definition of @cite to appear % as a result of the \message, we just want `@cite' itself. We use % \the to achieve this: TeX expands \the only once, % simply yielding the contents of . (We also do this for % the toc entries.) \toks0 = {#1}% \message{(\the\toks0)}% % \chapmacro{#1}{Ynothing}{\the\unnumberedno}% % \global\let\section = \unnumberedsec \global\let\subsection = \unnumberedsubsec \global\let\subsubsection = \unnumberedsubsubsec } % @centerchap is like @unnumbered, but the heading is centered. \outer\parseargdef\centerchap{% % Well, we could do the following in a group, but that would break % an assumption that \chapmacro is called at the outermost level. % Thus we are safer this way: --kasal, 24feb04 \let\centerparametersmaybe = \centerparameters \unnmhead0{#1}% \let\centerparametersmaybe = \relax } % @top is like @unnumbered. \let\top\unnumbered % Sections. % \outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz \def\seczzz#1{% \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% } % normally calls appendixsectionzzz: \outer\parseargdef\appendixsection{\apphead1{#1}} \def\appendixsectionzzz#1{% \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% } \let\appendixsec\appendixsection % normally calls unnumberedseczzz: \outer\parseargdef\unnumberedsec{\unnmhead1{#1}} \def\unnumberedseczzz#1{% \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% } % Subsections. % % normally calls numberedsubseczzz: \outer\parseargdef\numberedsubsec{\numhead2{#1}} \def\numberedsubseczzz#1{% \global\subsubsecno=0 \global\advance\subsecno by 1 \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% } % normally calls appendixsubseczzz: \outer\parseargdef\appendixsubsec{\apphead2{#1}} \def\appendixsubseczzz#1{% \global\subsubsecno=0 \global\advance\subsecno by 1 \sectionheading{#1}{subsec}{Yappendix}% {\appendixletter.\the\secno.\the\subsecno}% } % normally calls unnumberedsubseczzz: \outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} \def\unnumberedsubseczzz#1{% \global\subsubsecno=0 \global\advance\subsecno by 1 \sectionheading{#1}{subsec}{Ynothing}% {\the\unnumberedno.\the\secno.\the\subsecno}% } % Subsubsections. % % normally numberedsubsubseczzz: \outer\parseargdef\numberedsubsubsec{\numhead3{#1}} \def\numberedsubsubseczzz#1{% \global\advance\subsubsecno by 1 \sectionheading{#1}{subsubsec}{Ynumbered}% {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% } % normally appendixsubsubseczzz: \outer\parseargdef\appendixsubsubsec{\apphead3{#1}} \def\appendixsubsubseczzz#1{% \global\advance\subsubsecno by 1 \sectionheading{#1}{subsubsec}{Yappendix}% {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% } % normally unnumberedsubsubseczzz: \outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} \def\unnumberedsubsubseczzz#1{% \global\advance\subsubsecno by 1 \sectionheading{#1}{subsubsec}{Ynothing}% {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% } % These macros control what the section commands do, according % to what kind of chapter we are in (ordinary, appendix, or unnumbered). % Define them by default for a numbered chapter. \let\section = \numberedsec \let\subsection = \numberedsubsec \let\subsubsection = \numberedsubsubsec % Define @majorheading, @heading and @subheading \def\majorheading{% {\advance\chapheadingskip by 10pt \chapbreak }% \parsearg\chapheadingzzz } \def\chapheading{\chapbreak \parsearg\chapheadingzzz} \def\chapheadingzzz#1{% \vbox{\chapfonts \raggedtitlesettings #1\par}% \nobreak\bigskip \nobreak \suppressfirstparagraphindent } % @heading, @subheading, @subsubheading. \parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} \suppressfirstparagraphindent} \parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} \suppressfirstparagraphindent} \parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} \suppressfirstparagraphindent} % These macros generate a chapter, section, etc. heading only % (including whitespace, linebreaking, etc. around it), % given all the information in convenient, parsed form. % Args are the skip and penalty (usually negative) \def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} % Parameter controlling skip before chapter headings (if needed) \newskip\chapheadingskip % Define plain chapter starts, and page on/off switching for it. \def\chapbreak{\dobreak \chapheadingskip {-4000}} \def\chappager{\par\vfill\supereject} % Because \domark is called before \chapoddpage, the filler page will % get the headings for the next chapter, which is wrong. But we don't % care -- we just disable all headings on the filler page. \def\chapoddpage{% \chappager \ifodd\pageno \else \begingroup \headingsoff \null \chappager \endgroup \fi } \def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} \def\CHAPPAGoff{% \global\let\contentsalignmacro = \chappager \global\let\pchapsepmacro=\chapbreak \global\let\pagealignmacro=\chappager} \def\CHAPPAGon{% \global\let\contentsalignmacro = \chappager \global\let\pchapsepmacro=\chappager \global\let\pagealignmacro=\chappager \global\def\HEADINGSon{\HEADINGSsingle}} \def\CHAPPAGodd{% \global\let\contentsalignmacro = \chapoddpage \global\let\pchapsepmacro=\chapoddpage \global\let\pagealignmacro=\chapoddpage \global\def\HEADINGSon{\HEADINGSdouble}} \CHAPPAGon % Chapter opening. % % #1 is the text, #2 is the section type (Ynumbered, Ynothing, % Yappendix, Yomitfromtoc), #3 the chapter number. % % To test against our argument. \def\Ynothingkeyword{Ynothing} \def\Yomitfromtockeyword{Yomitfromtoc} \def\Yappendixkeyword{Yappendix} % \def\chapmacro#1#2#3{% % Insert the first mark before the heading break (see notes for \domark). \let\prevchapterdefs=\lastchapterdefs \let\prevsectiondefs=\lastsectiondefs \gdef\lastsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}% \gdef\thissection{}}% % \def\temptype{#2}% \ifx\temptype\Ynothingkeyword \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% \gdef\thischapter{\thischaptername}}% \else\ifx\temptype\Yomitfromtockeyword \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% \gdef\thischapter{}}% \else\ifx\temptype\Yappendixkeyword \toks0={#1}% \xdef\lastchapterdefs{% \gdef\noexpand\thischaptername{\the\toks0}% \gdef\noexpand\thischapternum{\appendixletter}% % \noexpand\putwordAppendix avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thischapter{\noexpand\putwordAppendix{} \noexpand\thischapternum: \noexpand\thischaptername}% }% \else \toks0={#1}% \xdef\lastchapterdefs{% \gdef\noexpand\thischaptername{\the\toks0}% \gdef\noexpand\thischapternum{\the\chapno}% % \noexpand\putwordChapter avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thischapter{\noexpand\putwordChapter{} \noexpand\thischapternum: \noexpand\thischaptername}% }% \fi\fi\fi % % Output the mark. Pass it through \safewhatsit, to take care of % the preceding space. \safewhatsit\domark % % Insert the chapter heading break. \pchapsepmacro % % Now the second mark, after the heading break. No break points % between here and the heading. \let\prevchapterdefs=\lastchapterdefs \let\prevsectiondefs=\lastsectiondefs \domark % {% \chapfonts \rmisbold % % Have to define \lastsection before calling \donoderef, because the % xref code eventually uses it. On the other hand, it has to be called % after \pchapsepmacro, or the headline will change too soon. \gdef\lastsection{#1}% % % Only insert the separating space if we have a chapter/appendix % number, and don't print the unnumbered ``number''. \ifx\temptype\Ynothingkeyword \setbox0 = \hbox{}% \def\toctype{unnchap}% \else\ifx\temptype\Yomitfromtockeyword \setbox0 = \hbox{}% contents like unnumbered, but no toc entry \def\toctype{omit}% \else\ifx\temptype\Yappendixkeyword \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% \def\toctype{app}% \else \setbox0 = \hbox{#3\enspace}% \def\toctype{numchap}% \fi\fi\fi % % Write the toc entry for this chapter. Must come before the % \donoderef, because we include the current node name in the toc % entry, and \donoderef resets it to empty. \writetocentry{\toctype}{#1}{#3}% % % For pdftex, we have to write out the node definition (aka, make % the pdfdest) after any page break, but before the actual text has % been typeset. If the destination for the pdf outline is after the % text, then jumping from the outline may wind up with the text not % being visible, for instance under high magnification. \donoderef{#2}% % % Typeset the actual heading. \nobreak % Avoid page breaks at the interline glue. \vbox{\raggedtitlesettings \hangindent=\wd0 \centerparametersmaybe \unhbox0 #1\par}% }% \nobreak\bigskip % no page break after a chapter title \nobreak } % @centerchap -- centered and unnumbered. \let\centerparametersmaybe = \relax \def\centerparameters{% \advance\rightskip by 3\rightskip \leftskip = \rightskip \parfillskip = 0pt } % I don't think this chapter style is supported any more, so I'm not % updating it with the new noderef stuff. We'll see. --karl, 11aug03. % \def\setchapterstyle #1 {\csname CHAPF#1\endcsname} % \def\unnchfopen #1{% \chapoddpage \vbox{\chapfonts \raggedtitlesettings #1\par}% \nobreak\bigskip\nobreak } \def\chfopen #1#2{\chapoddpage {\chapfonts \vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% \par\penalty 5000 % } \def\centerchfopen #1{% \chapoddpage \vbox{\chapfonts \raggedtitlesettings \hfill #1\hfill}% \nobreak\bigskip \nobreak } \def\CHAPFopen{% \global\let\chapmacro=\chfopen \global\let\centerchapmacro=\centerchfopen} % Section titles. These macros combine the section number parts and % call the generic \sectionheading to do the printing. % \newskip\secheadingskip \def\secheadingbreak{\dobreak \secheadingskip{-1000}} % Subsection titles. \newskip\subsecheadingskip \def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} % Subsubsection titles. \def\subsubsecheadingskip{\subsecheadingskip} \def\subsubsecheadingbreak{\subsecheadingbreak} % Print any size, any type, section title. % % #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is % the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the % section number. % \def\seckeyword{sec} % \def\sectionheading#1#2#3#4{% {% \checkenv{}% should not be in an environment. % % Switch to the right set of fonts. \csname #2fonts\endcsname \rmisbold % \def\sectionlevel{#2}% \def\temptype{#3}% % % Insert first mark before the heading break (see notes for \domark). \let\prevsectiondefs=\lastsectiondefs \ifx\temptype\Ynothingkeyword \ifx\sectionlevel\seckeyword \gdef\lastsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}% \gdef\thissection{\thissectionname}}% \fi \else\ifx\temptype\Yomitfromtockeyword % Don't redefine \thissection. \else\ifx\temptype\Yappendixkeyword \ifx\sectionlevel\seckeyword \toks0={#1}% \xdef\lastsectiondefs{% \gdef\noexpand\thissectionname{\the\toks0}% \gdef\noexpand\thissectionnum{#4}% % \noexpand\putwordSection avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thissection{\noexpand\putwordSection{} \noexpand\thissectionnum: \noexpand\thissectionname}% }% \fi \else \ifx\sectionlevel\seckeyword \toks0={#1}% \xdef\lastsectiondefs{% \gdef\noexpand\thissectionname{\the\toks0}% \gdef\noexpand\thissectionnum{#4}% % \noexpand\putwordSection avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thissection{\noexpand\putwordSection{} \noexpand\thissectionnum: \noexpand\thissectionname}% }% \fi \fi\fi\fi % % Go into vertical mode. Usually we'll already be there, but we % don't want the following whatsit to end up in a preceding paragraph % if the document didn't happen to have a blank line. \par % % Output the mark. Pass it through \safewhatsit, to take care of % the preceding space. \safewhatsit\domark % % Insert space above the heading. \csname #2headingbreak\endcsname % % Now the second mark, after the heading break. No break points % between here and the heading. \let\prevsectiondefs=\lastsectiondefs \domark % % Only insert the space after the number if we have a section number. \ifx\temptype\Ynothingkeyword \setbox0 = \hbox{}% \def\toctype{unn}% \gdef\lastsection{#1}% \else\ifx\temptype\Yomitfromtockeyword % for @headings -- no section number, don't include in toc, % and don't redefine \lastsection. \setbox0 = \hbox{}% \def\toctype{omit}% \let\sectionlevel=\empty \else\ifx\temptype\Yappendixkeyword \setbox0 = \hbox{#4\enspace}% \def\toctype{app}% \gdef\lastsection{#1}% \else \setbox0 = \hbox{#4\enspace}% \def\toctype{num}% \gdef\lastsection{#1}% \fi\fi\fi % % Write the toc entry (before \donoderef). See comments in \chapmacro. \writetocentry{\toctype\sectionlevel}{#1}{#4}% % % Write the node reference (= pdf destination for pdftex). % Again, see comments in \chapmacro. \donoderef{#3}% % % Interline glue will be inserted when the vbox is completed. % That glue will be a valid breakpoint for the page, since it'll be % preceded by a whatsit (usually from the \donoderef, or from the % \writetocentry if there was no node). We don't want to allow that % break, since then the whatsits could end up on page n while the % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. \nobreak % % Output the actual section heading. \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright \hangindent=\wd0 % zero if no section number \unhbox0 #1}% }% % Add extra space after the heading -- half of whatever came above it. % Don't allow stretch, though. \kern .5 \csname #2headingskip\endcsname % % Do not let the kern be a potential breakpoint, as it would be if it % was followed by glue. \nobreak % % We'll almost certainly start a paragraph next, so don't let that % glue accumulate. (Not a breakpoint because it's preceded by a % discardable item.) However, when a paragraph is not started next % (\startdefun, \cartouche, \center, etc.), this needs to be wiped out % or the negative glue will cause weirdly wrong output, typically % obscuring the section heading with something else. \vskip-\parskip % % This is so the last item on the main vertical list is a known % \penalty > 10000, so \startdefun, etc., can recognize the situation % and do the needful. \penalty 10001 } \message{toc,} % Table of contents. \newwrite\tocfile % Write an entry to the toc file, opening it if necessary. % Called from @chapter, etc. % % Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} % We append the current node name (if any) and page number as additional % arguments for the \{chap,sec,...}entry macros which will eventually % read this. The node name is used in the pdf outlines as the % destination to jump to. % % We open the .toc file for writing here instead of at @setfilename (or % any other fixed time) so that @contents can be anywhere in the document. % But if #1 is `omit', then we don't do anything. This is used for the % table of contents chapter openings themselves. % \newif\iftocfileopened \def\omitkeyword{omit}% % \def\writetocentry#1#2#3{% \edef\writetoctype{#1}% \ifx\writetoctype\omitkeyword \else \iftocfileopened\else \immediate\openout\tocfile = \jobname.toc \global\tocfileopenedtrue \fi % \iflinks {\atdummies \edef\temp{% \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% \temp }% \fi \fi % % Tell \shipout to create a pdf destination on each page, if we're % writing pdf. These are used in the table of contents. We can't % just write one on every page because the title pages are numbered % 1 and 2 (the page numbers aren't printed), and so are the first % two pages of the document. Thus, we'd have two destinations named % `1', and two named `2'. \ifpdf \global\pdfmakepagedesttrue \fi } % These characters do not print properly in the Computer Modern roman % fonts, so we must take special care. This is more or less redundant % with the Texinfo input format setup at the end of this file. % \def\activecatcodes{% \catcode`\"=\active \catcode`\$=\active \catcode`\<=\active \catcode`\>=\active \catcode`\\=\active \catcode`\^=\active \catcode`\_=\active \catcode`\|=\active \catcode`\~=\active } % Read the toc file, which is essentially Texinfo input. \def\readtocfile{% \setupdatafile \activecatcodes \input \tocreadfilename } \newskip\contentsrightmargin \contentsrightmargin=1in \newcount\savepageno \newcount\lastnegativepageno \lastnegativepageno = -1 % Prepare to read what we've written to \tocfile. % \def\startcontents#1{% % If @setchapternewpage on, and @headings double, the contents should % start on an odd page, unlike chapters. Thus, we maintain % \contentsalignmacro in parallel with \pagealignmacro. % From: Torbjorn Granlund \contentsalignmacro \immediate\closeout\tocfile % % Don't need to put `Contents' or `Short Contents' in the headline. % It is abundantly clear what they are. \chapmacro{#1}{Yomitfromtoc}{}% % \savepageno = \pageno \begingroup % Set up to handle contents files properly. \raggedbottom % Worry more about breakpoints than the bottom. \advance\hsize by -\contentsrightmargin % Don't use the full line length. % % Roman numerals for page numbers. \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi } % redefined for the two-volume lispref. We always output on % \jobname.toc even if this is redefined. % \def\tocreadfilename{\jobname.toc} % Normal (long) toc. % \def\contents{% \startcontents{\putwordTOC}% \openin 1 \tocreadfilename\space \ifeof 1 \else \readtocfile \fi \vfill \eject \contentsalignmacro % in case @setchapternewpage odd is in effect \ifeof 1 \else \pdfmakeoutlines \fi \closein 1 \endgroup \lastnegativepageno = \pageno \global\pageno = \savepageno } % And just the chapters. \def\summarycontents{% \startcontents{\putwordShortTOC}% % \let\partentry = \shortpartentry \let\numchapentry = \shortchapentry \let\appentry = \shortchapentry \let\unnchapentry = \shortunnchapentry % We want a true roman here for the page numbers. \secfonts \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl \let\tt=\shortconttt \rm \hyphenpenalty = 10000 \advance\baselineskip by 1pt % Open it up a little. \def\numsecentry##1##2##3##4{} \let\appsecentry = \numsecentry \let\unnsecentry = \numsecentry \let\numsubsecentry = \numsecentry \let\appsubsecentry = \numsecentry \let\unnsubsecentry = \numsecentry \let\numsubsubsecentry = \numsecentry \let\appsubsubsecentry = \numsecentry \let\unnsubsubsecentry = \numsecentry \openin 1 \tocreadfilename\space \ifeof 1 \else \readtocfile \fi \closein 1 \vfill \eject \contentsalignmacro % in case @setchapternewpage odd is in effect \endgroup \lastnegativepageno = \pageno \global\pageno = \savepageno } \let\shortcontents = \summarycontents % Typeset the label for a chapter or appendix for the short contents. % The arg is, e.g., `A' for an appendix, or `3' for a chapter. % \def\shortchaplabel#1{% % This space should be enough, since a single number is .5em, and the % widest letter (M) is 1em, at least in the Computer Modern fonts. % But use \hss just in case. % (This space doesn't include the extra space that gets added after % the label; that gets put in by \shortchapentry above.) % % We'd like to right-justify chapter numbers, but that looks strange % with appendix letters. And right-justifying numbers and % left-justifying letters looks strange when there is less than 10 % chapters. Have to read the whole toc once to know how many chapters % there are before deciding ... \hbox to 1em{#1\hss}% } % These macros generate individual entries in the table of contents. % The first argument is the chapter or section name. % The last argument is the page number. % The arguments in between are the chapter number, section number, ... % Parts, in the main contents. Replace the part number, which doesn't % exist, with an empty box. Let's hope all the numbers have the same width. % Also ignore the page number, which is conventionally not printed. \def\numeralbox{\setbox0=\hbox{8}\hbox to \wd0{\hfil}} \def\partentry#1#2#3#4{\dochapentry{\numeralbox\labelspace#1}{}} % % Parts, in the short toc. \def\shortpartentry#1#2#3#4{% \penalty-300 \vskip.5\baselineskip plus.15\baselineskip minus.1\baselineskip \shortchapentry{{\bf #1}}{\numeralbox}{}{}% } % Chapters, in the main contents. \def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} % % Chapters, in the short toc. % See comments in \dochapentry re vbox and related settings. \def\shortchapentry#1#2#3#4{% \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% } % Appendices, in the main contents. % Need the word Appendix, and a fixed-size box. % \def\appendixbox#1{% % We use M since it's probably the widest letter. \setbox0 = \hbox{\putwordAppendix{} M}% \hbox to \wd0{\putwordAppendix{} #1\hss}} % \def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}} % Unnumbered chapters. \def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} \def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} % Sections. \def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} \let\appsecentry=\numsecentry \def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} % Subsections. \def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} \let\appsubsecentry=\numsubsecentry \def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} % And subsubsections. \def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} \let\appsubsubsecentry=\numsubsubsecentry \def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} % This parameter controls the indentation of the various levels. % Same as \defaultparindent. \newdimen\tocindent \tocindent = 15pt % Now for the actual typesetting. In all these, #1 is the text and #2 is the % page number. % % If the toc has to be broken over pages, we want it to be at chapters % if at all possible; hence the \penalty. \def\dochapentry#1#2{% \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip \begingroup \chapentryfonts \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup \nobreak\vskip .25\baselineskip plus.1\baselineskip } \def\dosecentry#1#2{\begingroup \secentryfonts \leftskip=\tocindent \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup} \def\dosubsecentry#1#2{\begingroup \subsecentryfonts \leftskip=2\tocindent \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup} \def\dosubsubsecentry#1#2{\begingroup \subsubsecentryfonts \leftskip=3\tocindent \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup} % We use the same \entry macro as for the index entries. \let\tocentry = \entry % Space between chapter (or whatever) number and the title. \def\labelspace{\hskip1em \relax} \def\dopageno#1{{\rm #1}} \def\doshortpageno#1{{\rm #1}} \def\chapentryfonts{\secfonts \rm} \def\secentryfonts{\textfonts} \def\subsecentryfonts{\textfonts} \def\subsubsecentryfonts{\textfonts} \message{environments,} % @foo ... @end foo. % @tex ... @end tex escapes into raw TeX temporarily. % One exception: @ is still an escape character, so that @end tex works. % But \@ or @@ will get a plain @ character. \envdef\tex{% \setupmarkupstyle{tex}% \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie \catcode `\%=14 \catcode `\+=\other \catcode `\"=\other \catcode `\|=\other \catcode `\<=\other \catcode `\>=\other \catcode`\`=\other \catcode`\'=\other \escapechar=`\\ % % ' is active in math mode (mathcode"8000). So reset it, and all our % other math active characters (just in case), to plain's definitions. \mathactive % \let\b=\ptexb \let\bullet=\ptexbullet \let\c=\ptexc \let\,=\ptexcomma \let\.=\ptexdot \let\dots=\ptexdots \let\equiv=\ptexequiv \let\!=\ptexexclam \let\i=\ptexi \let\indent=\ptexindent \let\noindent=\ptexnoindent \let\{=\ptexlbrace \let\+=\tabalign \let\}=\ptexrbrace \let\/=\ptexslash \let\*=\ptexstar \let\t=\ptext \expandafter \let\csname top\endcsname=\ptextop % outer \let\frenchspacing=\plainfrenchspacing % \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% \def\@{@}% } % There is no need to define \Etex. % Define @lisp ... @end lisp. % @lisp environment forms a group so it can rebind things, % including the definition of @end lisp (which normally is erroneous). % Amount to narrow the margins by for @lisp. \newskip\lispnarrowing \lispnarrowing=0.4in % This is the definition that ^^M gets inside @lisp, @example, and other % such environments. \null is better than a space, since it doesn't % have any width. \def\lisppar{\null\endgraf} % This space is always present above and below environments. \newskip\envskipamount \envskipamount = 0pt % Make spacing and below environment symmetrical. We use \parskip here % to help in doing that, since in @example-like environments \parskip % is reset to zero; thus the \afterenvbreak inserts no space -- but the % start of the next paragraph will insert \parskip. % \def\aboveenvbreak{{% % =10000 instead of <10000 because of a special case in \itemzzz and % \sectionheading, q.v. \ifnum \lastpenalty=10000 \else \advance\envskipamount by \parskip \endgraf \ifdim\lastskip<\envskipamount \removelastskip % it's not a good place to break if the last penalty was \nobreak % or better ... \ifnum\lastpenalty<10000 \penalty-50 \fi \vskip\envskipamount \fi \fi }} \let\afterenvbreak = \aboveenvbreak % \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will % also clear it, so that its embedded environments do the narrowing again. \let\nonarrowing=\relax % @cartouche ... @end cartouche: draw rectangle w/rounded corners around % environment contents. \font\circle=lcircle10 \newdimen\circthick \newdimen\cartouter\newdimen\cartinner \newskip\normbskip\newskip\normpskip\newskip\normlskip \circthick=\fontdimen8\circle % \def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth \def\ctr{{\hskip 6pt\circle\char'010}} \def\cbl{{\circle\char'012\hskip -6pt}} \def\cbr{{\hskip 6pt\circle\char'011}} \def\carttop{\hbox to \cartouter{\hskip\lskip \ctl\leaders\hrule height\circthick\hfil\ctr \hskip\rskip}} \def\cartbot{\hbox to \cartouter{\hskip\lskip \cbl\leaders\hrule height\circthick\hfil\cbr \hskip\rskip}} % \newskip\lskip\newskip\rskip \envdef\cartouche{% \ifhmode\par\fi % can't be in the midst of a paragraph. \startsavinginserts \lskip=\leftskip \rskip=\rightskip \leftskip=0pt\rightskip=0pt % we want these *outside*. \cartinner=\hsize \advance\cartinner by-\lskip \advance\cartinner by-\rskip \cartouter=\hsize \advance\cartouter by 18.4pt % allow for 3pt kerns on either % side, and for 6pt waste from % each corner char, and rule thickness \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip % Flag to tell @lisp, etc., not to narrow margin. \let\nonarrowing = t% % % If this cartouche directly follows a sectioning command, we need the % \parskip glue (backspaced over by default) or the cartouche can % collide with the section heading. \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi % \vbox\bgroup \baselineskip=0pt\parskip=0pt\lineskip=0pt \carttop \hbox\bgroup \hskip\lskip \vrule\kern3pt \vbox\bgroup \kern3pt \hsize=\cartinner \baselineskip=\normbskip \lineskip=\normlskip \parskip=\normpskip \vskip -\parskip \comment % For explanation, see the end of def\group. } \def\Ecartouche{% \ifhmode\par\fi \kern3pt \egroup \kern3pt\vrule \hskip\rskip \egroup \cartbot \egroup \checkinserts } % This macro is called at the beginning of all the @example variants, % inside a group. \newdimen\nonfillparindent \def\nonfillstart{% \aboveenvbreak \hfuzz = 12pt % Don't be fussy \sepspaces % Make spaces be word-separators rather than space tokens. \let\par = \lisppar % don't ignore blank lines \obeylines % each line of input is a line of output \parskip = 0pt % Turn off paragraph indentation but redefine \indent to emulate % the normal \indent. \nonfillparindent=\parindent \parindent = 0pt \let\indent\nonfillindent % \emergencystretch = 0pt % don't try to avoid overfull boxes \ifx\nonarrowing\relax \advance \leftskip by \lispnarrowing \exdentamount=\lispnarrowing \else \let\nonarrowing = \relax \fi \let\exdent=\nofillexdent } \begingroup \obeyspaces % We want to swallow spaces (but not other tokens) after the fake % @indent in our nonfill-environments, where spaces are normally % active and set to @tie, resulting in them not being ignored after % @indent. \gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}% \gdef\nonfillindentcheck{% \ifx\temp % \expandafter\nonfillindentgobble% \else% \leavevmode\nonfillindentbox% \fi% }% \endgroup \def\nonfillindentgobble#1{\nonfillindent} \def\nonfillindentbox{\hbox to \nonfillparindent{\hss}} % If you want all examples etc. small: @set dispenvsize small. % If you want even small examples the full size: @set dispenvsize nosmall. % This affects the following displayed environments: % @example, @display, @format, @lisp % \def\smallword{small} \def\nosmallword{nosmall} \let\SETdispenvsize\relax \def\setnormaldispenv{% \ifx\SETdispenvsize\smallword % end paragraph for sake of leading, in case document has no blank % line. This is redundant with what happens in \aboveenvbreak, but % we need to do it before changing the fonts, and it's inconvenient % to change the fonts afterward. \ifnum \lastpenalty=10000 \else \endgraf \fi \smallexamplefonts \rm \fi } \def\setsmalldispenv{% \ifx\SETdispenvsize\nosmallword \else \ifnum \lastpenalty=10000 \else \endgraf \fi \smallexamplefonts \rm \fi } % We often define two environments, @foo and @smallfoo. % Let's do it in one command. #1 is the env name, #2 the definition. \def\makedispenvdef#1#2{% \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}% \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}% \expandafter\let\csname E#1\endcsname \afterenvbreak \expandafter\let\csname Esmall#1\endcsname \afterenvbreak } % Define two environment synonyms (#1 and #2) for an environment. \def\maketwodispenvdef#1#2#3{% \makedispenvdef{#1}{#3}% \makedispenvdef{#2}{#3}% } % % @lisp: indented, narrowed, typewriter font; % @example: same as @lisp. % % @smallexample and @smalllisp: use smaller fonts. % Originally contributed by Pavel@xerox. % \maketwodispenvdef{lisp}{example}{% \nonfillstart \tt\setupmarkupstyle{example}% \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. \gobble % eat return } % @display/@smalldisplay: same as @lisp except keep current font. % \makedispenvdef{display}{% \nonfillstart \gobble } % @format/@smallformat: same as @display except don't narrow margins. % \makedispenvdef{format}{% \let\nonarrowing = t% \nonfillstart \gobble } % @flushleft: same as @format, but doesn't obey \SETdispenvsize. \envdef\flushleft{% \let\nonarrowing = t% \nonfillstart \gobble } \let\Eflushleft = \afterenvbreak % @flushright. % \envdef\flushright{% \let\nonarrowing = t% \nonfillstart \advance\leftskip by 0pt plus 1fill\relax \gobble } \let\Eflushright = \afterenvbreak % @raggedright does more-or-less normal line breaking but no right % justification. From plain.tex. \envdef\raggedright{% \rightskip0pt plus2em \spaceskip.3333em \xspaceskip.5em\relax } \let\Eraggedright\par \envdef\raggedleft{% \parindent=0pt \leftskip0pt plus2em \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt \hbadness=10000 % Last line will usually be underfull, so turn off % badness reporting. } \let\Eraggedleft\par \envdef\raggedcenter{% \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt \hbadness=10000 % Last line will usually be underfull, so turn off % badness reporting. } \let\Eraggedcenter\par % @quotation does normal linebreaking (hence we can't use \nonfillstart) % and narrows the margins. We keep \parskip nonzero in general, since % we're doing normal filling. So, when using \aboveenvbreak and % \afterenvbreak, temporarily make \parskip 0. % \makedispenvdef{quotation}{\quotationstart} % \def\quotationstart{% \indentedblockstart % same as \indentedblock, but increase right margin too. \ifx\nonarrowing\relax \advance\rightskip by \lispnarrowing \fi \parsearg\quotationlabel } % We have retained a nonzero parskip for the environment, since we're % doing normal filling. % \def\Equotation{% \par \ifx\quotationauthor\thisisundefined\else % indent a bit. \leftline{\kern 2\leftskip \sl ---\quotationauthor}% \fi {\parskip=0pt \afterenvbreak}% } \def\Esmallquotation{\Equotation} % If we're given an argument, typeset it in bold with a colon after. \def\quotationlabel#1{% \def\temp{#1}% \ifx\temp\empty \else {\bf #1: }% \fi } % @indentedblock is like @quotation, but indents only on the left and % has no optional argument. % \makedispenvdef{indentedblock}{\indentedblockstart} % \def\indentedblockstart{% {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip \parindent=0pt % % @cartouche defines \nonarrowing to inhibit narrowing at next level down. \ifx\nonarrowing\relax \advance\leftskip by \lispnarrowing \exdentamount = \lispnarrowing \else \let\nonarrowing = \relax \fi } % Keep a nonzero parskip for the environment, since we're doing normal filling. % \def\Eindentedblock{% \par {\parskip=0pt \afterenvbreak}% } \def\Esmallindentedblock{\Eindentedblock} % LaTeX-like @verbatim...@end verbatim and @verb{...} % If we want to allow any as delimiter, % we need the curly braces so that makeinfo sees the @verb command, eg: % `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org % % [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. % % [Knuth] p.344; only we need to do the other characters Texinfo sets % active too. Otherwise, they get lost as the first character on a % verbatim line. \def\dospecials{% \do\ \do\\\do\{\do\}\do\$\do\&% \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% \do\<\do\>\do\|\do\@\do+\do\"% % Don't do the quotes -- if we do, @set txicodequoteundirected and % @set txicodequotebacktick will not have effect on @verb and % @verbatim, and ?` and !` ligatures won't get disabled. %\do\`\do\'% } % % [Knuth] p. 380 \def\uncatcodespecials{% \def\do##1{\catcode`##1=\other}\dospecials} % % Setup for the @verb command. % % Eight spaces for a tab \begingroup \catcode`\^^I=\active \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} \endgroup % \def\setupverb{% \tt % easiest (and conventionally used) font for verbatim \def\par{\leavevmode\endgraf}% \setupmarkupstyle{verb}% \tabeightspaces % Respect line breaks, % print special symbols as themselves, and % make each space count % must do in this order: \obeylines \uncatcodespecials \sepspaces } % Setup for the @verbatim environment % % Real tab expansion. \newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount % % We typeset each line of the verbatim in an \hbox, so we can handle % tabs. The \global is in case the verbatim line starts with an accent, % or some other command that starts with a begin-group. Otherwise, the % entire \verbbox would disappear at the corresponding end-group, before % it is typeset. Meanwhile, we can't have nested verbatim commands % (can we?), so the \global won't be overwriting itself. \newbox\verbbox \def\starttabbox{\global\setbox\verbbox=\hbox\bgroup} % \begingroup \catcode`\^^I=\active \gdef\tabexpand{% \catcode`\^^I=\active \def^^I{\leavevmode\egroup \dimen\verbbox=\wd\verbbox % the width so far, or since the previous tab \divide\dimen\verbbox by\tabw \multiply\dimen\verbbox by\tabw % compute previous multiple of \tabw \advance\dimen\verbbox by\tabw % advance to next multiple of \tabw \wd\verbbox=\dimen\verbbox \box\verbbox \starttabbox }% } \endgroup % start the verbatim environment. \def\setupverbatim{% \let\nonarrowing = t% \nonfillstart \tt % easiest (and conventionally used) font for verbatim % The \leavevmode here is for blank lines. Otherwise, we would % never \starttabox and the \egroup would end verbatim mode. \def\par{\leavevmode\egroup\box\verbbox\endgraf}% \tabexpand \setupmarkupstyle{verbatim}% % Respect line breaks, % print special symbols as themselves, and % make each space count. % Must do in this order: \obeylines \uncatcodespecials \sepspaces \everypar{\starttabbox}% } % Do the @verb magic: verbatim text is quoted by unique % delimiter characters. Before first delimiter expect a % right brace, after last delimiter expect closing brace: % % \def\doverb'{'#1'}'{#1} % % [Knuth] p. 382; only eat outer {} \begingroup \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] \endgroup % \def\verb{\begingroup\setupverb\doverb} % % % Do the @verbatim magic: define the macro \doverbatim so that % the (first) argument ends when '@end verbatim' is reached, ie: % % \def\doverbatim#1@end verbatim{#1} % % For Texinfo it's a lot easier than for LaTeX, % because texinfo's \verbatim doesn't stop at '\end{verbatim}': % we need not redefine '\', '{' and '}'. % % Inspired by LaTeX's verbatim command set [latex.ltx] % \begingroup \catcode`\ =\active \obeylines % % ignore everything up to the first ^^M, that's the newline at the end % of the @verbatim input line itself. Otherwise we get an extra blank % line in the output. \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% % We really want {...\end verbatim} in the body of the macro, but % without the active space; thus we have to use \xdef and \gobble. \endgroup % \envdef\verbatim{% \setupverbatim\doverbatim } \let\Everbatim = \afterenvbreak % @verbatiminclude FILE - insert text of file in verbatim environment. % \def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} % \def\doverbatiminclude#1{% {% \makevalueexpandable \setupverbatim \indexnofonts % Allow `@@' and other weird things in file names. \wlog{texinfo.tex: doing @verbatiminclude of #1^^J}% \input #1 \afterenvbreak }% } % @copying ... @end copying. % Save the text away for @insertcopying later. % % We save the uninterpreted tokens, rather than creating a box. % Saving the text in a box would be much easier, but then all the % typesetting commands (@smallbook, font changes, etc.) have to be done % beforehand -- and a) we want @copying to be done first in the source % file; b) letting users define the frontmatter in as flexible order as % possible is very desirable. % \def\copying{\checkenv{}\begingroup\scanargctxt\docopying} \def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} % \def\insertcopying{% \begingroup \parindent = 0pt % paragraph indentation looks wrong on title page \scanexp\copyingtext \endgroup } \message{defuns,} % @defun etc. \newskip\defbodyindent \defbodyindent=.4in \newskip\defargsindent \defargsindent=50pt \newskip\deflastargmargin \deflastargmargin=18pt \newcount\defunpenalty % Start the processing of @deffn: \def\startdefun{% \ifnum\lastpenalty<10000 \medbreak \defunpenalty=10003 % Will keep this @deffn together with the % following @def command, see below. \else % If there are two @def commands in a row, we'll have a \nobreak, % which is there to keep the function description together with its % header. But if there's nothing but headers, we need to allow a % break somewhere. Check specifically for penalty 10002, inserted % by \printdefunline, instead of 10000, since the sectioning % commands also insert a nobreak penalty, and we don't want to allow % a break between a section heading and a defun. % % As a further refinement, we avoid "club" headers by signalling % with penalty of 10003 after the very first @deffn in the % sequence (see above), and penalty of 10002 after any following % @def command. \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi % % Similarly, after a section heading, do not allow a break. % But do insert the glue. \medskip % preceded by discardable penalty, so not a breakpoint \fi % \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent } \def\dodefunx#1{% % First, check whether we are in the right environment: \checkenv#1% % % As above, allow line break if we have multiple x headers in a row. % It's not a great place, though. \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi % % And now, it's time to reuse the body of the original defun: \expandafter\gobbledefun#1% } \def\gobbledefun#1\startdefun{} % \printdefunline \deffnheader{text} % \def\printdefunline#1#2{% \begingroup % call \deffnheader: #1#2 \endheader % common ending: \interlinepenalty = 10000 \advance\rightskip by 0pt plus 1fil\relax \endgraf \nobreak\vskip -\parskip \penalty\defunpenalty % signal to \startdefun and \dodefunx % Some of the @defun-type tags do not enable magic parentheses, % rendering the following check redundant. But we don't optimize. \checkparencounts \endgroup } \def\Edefun{\endgraf\medbreak} % \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; % the only thing remaining is to define \deffnheader. % \def\makedefun#1{% \expandafter\let\csname E#1\endcsname = \Edefun \edef\temp{\noexpand\domakedefun \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% \temp } % \domakedefun \deffn \deffnx \deffnheader % % Define \deffn and \deffnx, without parameters. % \deffnheader has to be defined explicitly. % \def\domakedefun#1#2#3{% \envdef#1{% \startdefun \doingtypefnfalse % distinguish typed functions from all else \parseargusing\activeparens{\printdefunline#3}% }% \def#2{\dodefunx#1}% \def#3% } \newif\ifdoingtypefn % doing typed function? \newif\ifrettypeownline % typeset return type on its own line? % @deftypefnnewline on|off says whether the return type of typed functions % are printed on their own line. This affects @deftypefn, @deftypefun, % @deftypeop, and @deftypemethod. % \parseargdef\deftypefnnewline{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETtxideftypefnnl\endcsname = \empty \else\ifx\temp\offword \expandafter\let\csname SETtxideftypefnnl\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @txideftypefnnl value `\temp', must be on|off}% \fi\fi } % Untyped functions: % @deffn category name args \makedefun{deffn}{\deffngeneral{}} % @deffn category class name args \makedefun{defop}#1 {\defopon{#1\ \putwordon}} % \defopon {category on}class name args \def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } % \deffngeneral {subind}category name args % \def\deffngeneral#1#2 #3 #4\endheader{% % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}. \dosubind{fn}{\code{#3}}{#1}% \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% } % Typed functions: % @deftypefn category type name args \makedefun{deftypefn}{\deftypefngeneral{}} % @deftypeop category class type name args \makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} % \deftypeopon {category on}class type name args \def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } % \deftypefngeneral {subind}category type name args % \def\deftypefngeneral#1#2 #3 #4 #5\endheader{% \dosubind{fn}{\code{#4}}{#1}% \doingtypefntrue \defname{#2}{#3}{#4}\defunargs{#5\unskip}% } % Typed variables: % @deftypevr category type var args \makedefun{deftypevr}{\deftypecvgeneral{}} % @deftypecv category class type var args \makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} % \deftypecvof {category of}class type var args \def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } % \deftypecvgeneral {subind}category type var args % \def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% \dosubind{vr}{\code{#4}}{#1}% \defname{#2}{#3}{#4}\defunargs{#5\unskip}% } % Untyped variables: % @defvr category var args \makedefun{defvr}#1 {\deftypevrheader{#1} {} } % @defcv category class var args \makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} % \defcvof {category of}class var args \def\defcvof#1#2 {\deftypecvof{#1}#2 {} } % Types: % @deftp category name args \makedefun{deftp}#1 #2 #3\endheader{% \doind{tp}{\code{#2}}% \defname{#1}{}{#2}\defunargs{#3\unskip}% } % Remaining @defun-like shortcuts: \makedefun{defun}{\deffnheader{\putwordDeffunc} } \makedefun{defmac}{\deffnheader{\putwordDefmac} } \makedefun{defspec}{\deffnheader{\putwordDefspec} } \makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } \makedefun{defvar}{\defvrheader{\putwordDefvar} } \makedefun{defopt}{\defvrheader{\putwordDefopt} } \makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } \makedefun{defmethod}{\defopon\putwordMethodon} \makedefun{deftypemethod}{\deftypeopon\putwordMethodon} \makedefun{defivar}{\defcvof\putwordInstanceVariableof} \makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} % \defname, which formats the name of the @def (not the args). % #1 is the category, such as "Function". % #2 is the return type, if any. % #3 is the function name. % % We are followed by (but not passed) the arguments, if any. % \def\defname#1#2#3{% \par % Get the values of \leftskip and \rightskip as they were outside the @def... \advance\leftskip by -\defbodyindent % % Determine if we are typesetting the return type of a typed function % on a line by itself. \rettypeownlinefalse \ifdoingtypefn % doing a typed function specifically? % then check user option for putting return type on its own line: \expandafter\ifx\csname SETtxideftypefnnl\endcsname\relax \else \rettypeownlinetrue \fi \fi % % How we'll format the category name. Putting it in brackets helps % distinguish it from the body text that may end up on the next line % just below it. \def\temp{#1}% \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} % % Figure out line sizes for the paragraph shape. We'll always have at % least two. \tempnum = 2 % % The first line needs space for \box0; but if \rightskip is nonzero, % we need only space for the part of \box0 which exceeds it: \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip % % If doing a return type on its own line, we'll have another line. \ifrettypeownline \advance\tempnum by 1 \def\maybeshapeline{0in \hsize}% \else \def\maybeshapeline{}% \fi % % The continuations: \dimen2=\hsize \advance\dimen2 by -\defargsindent % % The final paragraph shape: \parshape \tempnum 0in \dimen0 \maybeshapeline \defargsindent \dimen2 % % Put the category name at the right margin. \noindent \hbox to 0pt{% \hfil\box0 \kern-\hsize % \hsize has to be shortened this way: \kern\leftskip % Intentionally do not respect \rightskip, since we need the space. }% % % Allow all lines to be underfull without complaint: \tolerance=10000 \hbadness=10000 \exdentamount=\defbodyindent {% % defun fonts. We use typewriter by default (used to be bold) because: % . we're printing identifiers, they should be in tt in principle. % . in languages with many accents, such as Czech or French, it's % common to leave accents off identifiers. The result looks ok in % tt, but exceedingly strange in rm. % . we don't want -- and --- to be treated as ligatures. % . this still does not fix the ?` and !` ligatures, but so far no % one has made identifiers using them :). \df \tt \def\temp{#2}% text of the return type \ifx\temp\empty\else \tclose{\temp}% typeset the return type \ifrettypeownline % put return type on its own line; prohibit line break following: \hfil\vadjust{\nobreak}\break \else \space % type on same line, so just followed by a space \fi \fi % no return type #3% output function name }% {\rm\enskip}% hskip 0.5 em of \tenrm % \boldbrax % arguments will be output next, if any. } % Print arguments in slanted roman (not ttsl), inconsistently with using % tt for the name. This is because literal text is sometimes needed in % the argument list (groff manual), and ttsl and tt are not very % distinguishable. Prevent hyphenation at `-' chars. % \def\defunargs#1{% % use sl by default (not ttsl), % tt for the names. \df \sl \hyphenchar\font=0 % % On the other hand, if an argument has two dashes (for instance), we % want a way to get ttsl. We used to recommend @var for that, so % leave the code in, but it's strange for @var to lead to typewriter. % Nowadays we recommend @code, since the difference between a ttsl hyphen % and a tt hyphen is pretty tiny. @code also disables ?` !`. \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}% #1% \sl\hyphenchar\font=45 } % We want ()&[] to print specially on the defun line. % \def\activeparens{% \catcode`\(=\active \catcode`\)=\active \catcode`\[=\active \catcode`\]=\active \catcode`\&=\active } % Make control sequences which act like normal parenthesis chars. \let\lparen = ( \let\rparen = ) % Be sure that we always have a definition for `(', etc. For example, % if the fn name has parens in it, \boldbrax will not be in effect yet, % so TeX would otherwise complain about undefined control sequence. { \activeparens \global\let(=\lparen \global\let)=\rparen \global\let[=\lbrack \global\let]=\rbrack \global\let& = \& \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} \gdef\magicamp{\let&=\amprm} } \newcount\parencount % If we encounter &foo, then turn on ()-hacking afterwards \newif\ifampseen \def\amprm#1 {\ampseentrue{\bf\ }} \def\parenfont{% \ifampseen % At the first level, print parens in roman, % otherwise use the default font. \ifnum \parencount=1 \rm \fi \else % The \sf parens (in \boldbrax) actually are a little bolder than % the contained text. This is especially needed for [ and ] . \sf \fi } \def\infirstlevel#1{% \ifampseen \ifnum\parencount=1 #1% \fi \fi } \def\bfafterword#1 {#1 \bf} \def\opnr{% \global\advance\parencount by 1 {\parenfont(}% \infirstlevel \bfafterword } \def\clnr{% {\parenfont)}% \infirstlevel \sl \global\advance\parencount by -1 } \newcount\brackcount \def\lbrb{% \global\advance\brackcount by 1 {\bf[}% } \def\rbrb{% {\bf]}% \global\advance\brackcount by -1 } \def\checkparencounts{% \ifnum\parencount=0 \else \badparencount \fi \ifnum\brackcount=0 \else \badbrackcount \fi } % these should not use \errmessage; the glibc manual, at least, actually % has such constructs (when documenting function pointers). \def\badparencount{% \message{Warning: unbalanced parentheses in @def...}% \global\parencount=0 } \def\badbrackcount{% \message{Warning: unbalanced square brackets in @def...}% \global\brackcount=0 } \message{macros,} % @macro. % To do this right we need a feature of e-TeX, \scantokens, % which we arrange to emulate with a temporary file in ordinary TeX. \ifx\eTeXversion\thisisundefined \newwrite\macscribble \def\scantokens#1{% \toks0={#1}% \immediate\openout\macscribble=\jobname.tmp \immediate\write\macscribble{\the\toks0}% \immediate\closeout\macscribble \input \jobname.tmp } \fi \def\scanmacro#1{\begingroup \newlinechar`\^^M \let\xeatspaces\eatspaces % % Undo catcode changes of \startcontents and \doprintindex % When called from @insertcopying or (short)caption, we need active % backslash to get it printed correctly. Previously, we had % \catcode`\\=\other instead. We'll see whether a problem appears % with macro expansion. --kasal, 19aug04 \catcode`\@=0 \catcode`\\=\active \escapechar=`\@ % % ... and for \example: \spaceisspace % % The \empty here causes a following catcode 5 newline to be eaten as % part of reading whitespace after a control sequence. It does not % eat a catcode 13 newline. There's no good way to handle the two % cases (untried: maybe e-TeX's \everyeof could help, though plain TeX % would then have different behavior). See the Macro Details node in % the manual for the workaround we recommend for macros and % line-oriented commands. % \scantokens{#1\empty}% \endgroup} \def\scanexp#1{% \edef\temp{\noexpand\scanmacro{#1}}% \temp } \newcount\paramno % Count of parameters \newtoks\macname % Macro name \newif\ifrecursive % Is it recursive? % List of all defined macros in the form % \definedummyword\macro1\definedummyword\macro2... % Currently is also contains all @aliases; the list can be split % if there is a need. \def\macrolist{} % Add the macro to \macrolist \def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} \def\addtomacrolistxxx#1{% \toks0 = \expandafter{\macrolist\definedummyword#1}% \xdef\macrolist{\the\toks0}% } % Utility routines. % This does \let #1 = #2, with \csnames; that is, % \let \csname#1\endcsname = \csname#2\endcsname % (except of course we have to play expansion games). % \def\cslet#1#2{% \expandafter\let \csname#1\expandafter\endcsname \csname#2\endcsname } % Trim leading and trailing spaces off a string. % Concepts from aro-bend problem 15 (see CTAN). {\catcode`\@=11 \gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} \gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} \gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} \def\unbrace#1{#1} \unbrace{\gdef\trim@@@ #1 } #2@{#1} } % Trim a single trailing ^^M off a string. {\catcode`\^^M=\other \catcode`\Q=3% \gdef\eatcr #1{\eatcra #1Q^^MQ}% \gdef\eatcra#1^^MQ{\eatcrb#1Q}% \gdef\eatcrb#1Q#2Q{#1}% } % Macro bodies are absorbed as an argument in a context where % all characters are catcode 10, 11 or 12, except \ which is active % (as in normal texinfo). It is necessary to change the definition of \ % to recognize macro arguments; this is the job of \mbodybackslash. % % Non-ASCII encodings make 8-bit characters active, so un-activate % them to avoid their expansion. Must do this non-globally, to % confine the change to the current group. % % It's necessary to have hard CRs when the macro is executed. This is % done by making ^^M (\endlinechar) catcode 12 when reading the macro % body, and then making it the \newlinechar in \scanmacro. % \def\scanctxt{% used as subroutine \catcode`\"=\other \catcode`\+=\other \catcode`\<=\other \catcode`\>=\other \catcode`\@=\other \catcode`\^=\other \catcode`\_=\other \catcode`\|=\other \catcode`\~=\other \ifx\declaredencoding\ascii \else \setnonasciicharscatcodenonglobal\other \fi } \def\scanargctxt{% used for copying and captions, not macros. \scanctxt \catcode`\\=\other \catcode`\^^M=\other } \def\macrobodyctxt{% used for @macro definitions \scanctxt \catcode`\{=\other \catcode`\}=\other \catcode`\^^M=\other \usembodybackslash } \def\macroargctxt{% used when scanning invocations \scanctxt \catcode`\\=0 } % why catcode 0 for \ in the above? To recognize \\ \{ \} as "escapes" % for the single characters \ { }. Thus, we end up with the "commands" % that would be written @\ @{ @} in a Texinfo document. % % We already have @{ and @}. For @\, we define it here, and only for % this purpose, to produce a typewriter backslash (so, the @\ that we % define for @math can't be used with @macro calls): % \def\\{\normalbackslash}% % % We would like to do this for \, too, since that is what makeinfo does. % But it is not possible, because Texinfo already has a command @, for a % cedilla accent. Documents must use @comma{} instead. % % \anythingelse will almost certainly be an error of some kind. % \mbodybackslash is the definition of \ in @macro bodies. % It maps \foo\ => \csname macarg.foo\endcsname => #N % where N is the macro parameter number. % We define \csname macarg.\endcsname to be \realbackslash, so % \\ in macro replacement text gets you a backslash. % {\catcode`@=0 @catcode`@\=@active @gdef@usembodybackslash{@let\=@mbodybackslash} @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} } \expandafter\def\csname macarg.\endcsname{\realbackslash} \def\margbackslash#1{\char`\#1 } \def\macro{\recursivefalse\parsearg\macroxxx} \def\rmacro{\recursivetrue\parsearg\macroxxx} \def\macroxxx#1{% \getargs{#1}% now \macname is the macname and \argl the arglist \ifx\argl\empty % no arguments \paramno=0\relax \else \expandafter\parsemargdef \argl;% \if\paramno>256\relax \ifx\eTeXversion\thisisundefined \errhelp = \EMsimple \errmessage{You need eTeX to compile a file with macros with more than 256 arguments} \fi \fi \fi \if1\csname ismacro.\the\macname\endcsname \message{Warning: redefining \the\macname}% \else \expandafter\ifx\csname \the\macname\endcsname \relax \else \errmessage{Macro name \the\macname\space already defined}\fi \global\cslet{macsave.\the\macname}{\the\macname}% \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% \addtomacrolist{\the\macname}% \fi \begingroup \macrobodyctxt \ifrecursive \expandafter\parsermacbody \else \expandafter\parsemacbody \fi} \parseargdef\unmacro{% \if1\csname ismacro.#1\endcsname \global\cslet{#1}{macsave.#1}% \global\expandafter\let \csname ismacro.#1\endcsname=0% % Remove the macro name from \macrolist: \begingroup \expandafter\let\csname#1\endcsname \relax \let\definedummyword\unmacrodo \xdef\macrolist{\macrolist}% \endgroup \else \errmessage{Macro #1 not defined}% \fi } % Called by \do from \dounmacro on each macro. The idea is to omit any % macro definitions that have been changed to \relax. % \def\unmacrodo#1{% \ifx #1\relax % remove this \else \noexpand\definedummyword \noexpand#1% \fi } % This makes use of the obscure feature that if the last token of a % is #, then the preceding argument is delimited by % an opening brace, and that opening brace is not consumed. \def\getargs#1{\getargsxxx#1{}} \def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} \def\getmacname#1 #2\relax{\macname={#1}} \def\getmacargs#1{\def\argl{#1}} % For macro processing make @ a letter so that we can make Texinfo private macro names. \edef\texiatcatcode{\the\catcode`\@} \catcode `@=11\relax % Parse the optional {params} list. Set up \paramno and \paramlist % so \defmacro knows what to do. Define \macarg.BLAH for each BLAH % in the params list to some hook where the argument si to be expanded. If % there are less than 10 arguments that hook is to be replaced by ##N where N % is the position in that list, that is to say the macro arguments are to be % defined `a la TeX in the macro body. % % That gets used by \mbodybackslash (above). % % We need to get `macro parameter char #' into several definitions. % The technique used is stolen from LaTeX: let \hash be something % unexpandable, insert that wherever you need a #, and then redefine % it to # just before using the token list produced. % % The same technique is used to protect \eatspaces till just before % the macro is used. % % If there are 10 or more arguments, a different technique is used, where the % hook remains in the body, and when macro is to be expanded the body is % processed again to replace the arguments. % % In that case, the hook is \the\toks N-1, and we simply set \toks N-1 to the % argument N value and then \edef the body (nothing else will expand because of % the catcode regime underwhich the body was input). % % If you compile with TeX (not eTeX), and you have macros with 10 or more % arguments, you need that no macro has more than 256 arguments, otherwise an % error is produced. \def\parsemargdef#1;{% \paramno=0\def\paramlist{}% \let\hash\relax \let\xeatspaces\relax \parsemargdefxxx#1,;,% % In case that there are 10 or more arguments we parse again the arguments % list to set new definitions for the \macarg.BLAH macros corresponding to % each BLAH argument. It was anyhow needed to parse already once this list % in order to count the arguments, and as macros with at most 9 arguments % are by far more frequent than macro with 10 or more arguments, defining % twice the \macarg.BLAH macros does not cost too much processing power. \ifnum\paramno<10\relax\else \paramno0\relax \parsemmanyargdef@@#1,;,% 10 or more arguments \fi } \def\parsemargdefxxx#1,{% \if#1;\let\next=\relax \else \let\next=\parsemargdefxxx \advance\paramno by 1 \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname {\xeatspaces{\hash\the\paramno}}% \edef\paramlist{\paramlist\hash\the\paramno,}% \fi\next} \def\parsemmanyargdef@@#1,{% \if#1;\let\next=\relax \else \let\next=\parsemmanyargdef@@ \edef\tempb{\eatspaces{#1}}% \expandafter\def\expandafter\tempa \expandafter{\csname macarg.\tempb\endcsname}% % Note that we need some extra \noexpand\noexpand, this is because we % don't want \the to be expanded in the \parsermacbody as it uses an % \xdef . \expandafter\edef\tempa {\noexpand\noexpand\noexpand\the\toks\the\paramno}% \advance\paramno by 1\relax \fi\next} % These two commands read recursive and nonrecursive macro bodies. % (They're different since rec and nonrec macros end differently.) % \catcode `\@\texiatcatcode \long\def\parsemacbody#1@end macro% {\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% \long\def\parsermacbody#1@end rmacro% {\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% \catcode `\@=11\relax \let\endargs@\relax \let\nil@\relax \def\nilm@{\nil@}% \long\def\nillm@{\nil@}% % This macro is expanded during the Texinfo macro expansion, not during its % definition. It gets all the arguments values and assigns them to macros % macarg.ARGNAME % % #1 is the macro name % #2 is the list of argument names % #3 is the list of argument values \def\getargvals@#1#2#3{% \def\macargdeflist@{}% \def\saveparamlist@{#2}% Need to keep a copy for parameter expansion. \def\paramlist{#2,\nil@}% \def\macroname{#1}% \begingroup \macroargctxt \def\argvaluelist{#3,\nil@}% \def\@tempa{#3}% \ifx\@tempa\empty \setemptyargvalues@ \else \getargvals@@ \fi } % \def\getargvals@@{% \ifx\paramlist\nilm@ % Some sanity check needed here that \argvaluelist is also empty. \ifx\argvaluelist\nillm@ \else \errhelp = \EMsimple \errmessage{Too many arguments in macro `\macroname'!}% \fi \let\next\macargexpandinbody@ \else \ifx\argvaluelist\nillm@ % No more arguments values passed to macro. Set remaining named-arg % macros to empty. \let\next\setemptyargvalues@ \else % pop current arg name into \@tempb \def\@tempa##1{\pop@{\@tempb}{\paramlist}##1\endargs@}% \expandafter\@tempa\expandafter{\paramlist}% % pop current argument value into \@tempc \def\@tempa##1{\longpop@{\@tempc}{\argvaluelist}##1\endargs@}% \expandafter\@tempa\expandafter{\argvaluelist}% % Here \@tempb is the current arg name and \@tempc is the current arg value. % First place the new argument macro definition into \@tempd \expandafter\macname\expandafter{\@tempc}% \expandafter\let\csname macarg.\@tempb\endcsname\relax \expandafter\def\expandafter\@tempe\expandafter{% \csname macarg.\@tempb\endcsname}% \edef\@tempd{\long\def\@tempe{\the\macname}}% \push@\@tempd\macargdeflist@ \let\next\getargvals@@ \fi \fi \next } \def\push@#1#2{% \expandafter\expandafter\expandafter\def \expandafter\expandafter\expandafter#2% \expandafter\expandafter\expandafter{% \expandafter#1#2}% } % Replace arguments by their values in the macro body, and place the result % in macro \@tempa \def\macvalstoargs@{% % To do this we use the property that token registers that are \the'ed % within an \edef expand only once. So we are going to place all argument % values into respective token registers. % % First we save the token context, and initialize argument numbering. \begingroup \paramno0\relax % Then, for each argument number #N, we place the corresponding argument % value into a new token list register \toks#N \expandafter\putargsintokens@\saveparamlist@,;,% % Then, we expand the body so that argument are replaced by their % values. The trick for values not to be expanded themselves is that they % are within tokens and that tokens expand only once in an \edef . \edef\@tempc{\csname mac.\macroname .body\endcsname}% % Now we restore the token stack pointer to free the token list registers % which we have used, but we make sure that expanded body is saved after % group. \expandafter \endgroup \expandafter\def\expandafter\@tempa\expandafter{\@tempc}% } \def\macargexpandinbody@{% %% Define the named-macro outside of this group and then close this group. \expandafter \endgroup \macargdeflist@ % First the replace in body the macro arguments by their values, the result % is in \@tempa . \macvalstoargs@ % Then we point at the \norecurse or \gobble (for recursive) macro value % with \@tempb . \expandafter\let\expandafter\@tempb\csname mac.\macroname .recurse\endcsname % Depending on whether it is recursive or not, we need some tailing % \egroup . \ifx\@tempb\gobble \let\@tempc\relax \else \let\@tempc\egroup \fi % And now we do the real job: \edef\@tempd{\noexpand\@tempb{\macroname}\noexpand\scanmacro{\@tempa}\@tempc}% \@tempd } \def\putargsintokens@#1,{% \if#1;\let\next\relax \else \let\next\putargsintokens@ % First we allocate the new token list register, and give it a temporary % alias \@tempb . \toksdef\@tempb\the\paramno % Then we place the argument value into that token list register. \expandafter\let\expandafter\@tempa\csname macarg.#1\endcsname \expandafter\@tempb\expandafter{\@tempa}% \advance\paramno by 1\relax \fi \next } % Save the token stack pointer into macro #1 \def\texisavetoksstackpoint#1{\edef#1{\the\@cclvi}} % Restore the token stack pointer from number in macro #1 \def\texirestoretoksstackpoint#1{\expandafter\mathchardef\expandafter\@cclvi#1\relax} % newtoks that can be used non \outer . \def\texinonouternewtoks{\alloc@ 5\toks \toksdef \@cclvi} % Tailing missing arguments are set to empty \def\setemptyargvalues@{% \ifx\paramlist\nilm@ \let\next\macargexpandinbody@ \else \expandafter\setemptyargvaluesparser@\paramlist\endargs@ \let\next\setemptyargvalues@ \fi \next } \def\setemptyargvaluesparser@#1,#2\endargs@{% \expandafter\def\expandafter\@tempa\expandafter{% \expandafter\def\csname macarg.#1\endcsname{}}% \push@\@tempa\macargdeflist@ \def\paramlist{#2}% } % #1 is the element target macro % #2 is the list macro % #3,#4\endargs@ is the list value \def\pop@#1#2#3,#4\endargs@{% \def#1{#3}% \def#2{#4}% } \long\def\longpop@#1#2#3,#4\endargs@{% \long\def#1{#3}% \long\def#2{#4}% } % This defines a Texinfo @macro. There are eight cases: recursive and % nonrecursive macros of zero, one, up to nine, and many arguments. % Much magic with \expandafter here. % \xdef is used so that macro definitions will survive the file % they're defined in; @include reads the file inside a group. % \def\defmacro{% \let\hash=##% convert placeholders to macro parameter chars \ifrecursive \ifcase\paramno % 0 \expandafter\xdef\csname\the\macname\endcsname{% \noexpand\scanmacro{\temp}}% \or % 1 \expandafter\xdef\csname\the\macname\endcsname{% \bgroup\noexpand\macroargctxt \noexpand\braceorline \expandafter\noexpand\csname\the\macname xxx\endcsname}% \expandafter\xdef\csname\the\macname xxx\endcsname##1{% \egroup\noexpand\scanmacro{\temp}}% \else \ifnum\paramno<10\relax % at most 9 \expandafter\xdef\csname\the\macname\endcsname{% \bgroup\noexpand\macroargctxt \noexpand\csname\the\macname xx\endcsname}% \expandafter\xdef\csname\the\macname xx\endcsname##1{% \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% \expandafter\expandafter \expandafter\xdef \expandafter\expandafter \csname\the\macname xxx\endcsname \paramlist{\egroup\noexpand\scanmacro{\temp}}% \else % 10 or more \expandafter\xdef\csname\the\macname\endcsname{% \noexpand\getargvals@{\the\macname}{\argl}% }% \global\expandafter\let\csname mac.\the\macname .body\endcsname\temp \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble \fi \fi \else \ifcase\paramno % 0 \expandafter\xdef\csname\the\macname\endcsname{% \noexpand\norecurse{\the\macname}% \noexpand\scanmacro{\temp}\egroup}% \or % 1 \expandafter\xdef\csname\the\macname\endcsname{% \bgroup\noexpand\macroargctxt \noexpand\braceorline \expandafter\noexpand\csname\the\macname xxx\endcsname}% \expandafter\xdef\csname\the\macname xxx\endcsname##1{% \egroup \noexpand\norecurse{\the\macname}% \noexpand\scanmacro{\temp}\egroup}% \else % at most 9 \ifnum\paramno<10\relax \expandafter\xdef\csname\the\macname\endcsname{% \bgroup\noexpand\macroargctxt \expandafter\noexpand\csname\the\macname xx\endcsname}% \expandafter\xdef\csname\the\macname xx\endcsname##1{% \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% \expandafter\expandafter \expandafter\xdef \expandafter\expandafter \csname\the\macname xxx\endcsname \paramlist{% \egroup \noexpand\norecurse{\the\macname}% \noexpand\scanmacro{\temp}\egroup}% \else % 10 or more: \expandafter\xdef\csname\the\macname\endcsname{% \noexpand\getargvals@{\the\macname}{\argl}% }% \global\expandafter\let\csname mac.\the\macname .body\endcsname\temp \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\norecurse \fi \fi \fi} \catcode `\@\texiatcatcode\relax \def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} % \braceorline decides whether the next nonwhitespace character is a % {. If so it reads up to the closing }, if not, it reads the whole % line. Whatever was read is then fed to the next control sequence % as an argument (by \parsebrace or \parsearg). % \def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} \def\braceorlinexxx{% \ifx\nchar\bgroup\else \expandafter\parsearg \fi \macnamexxx} % @alias. % We need some trickery to remove the optional spaces around the equal % sign. Make them active and then expand them all to nothing. % \def\alias{\parseargusing\obeyspaces\aliasxxx} \def\aliasxxx #1{\aliasyyy#1\relax} \def\aliasyyy #1=#2\relax{% {% \expandafter\let\obeyedspace=\empty \addtomacrolist{#1}% \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% }% \next } \message{cross references,} \newwrite\auxfile \newif\ifhavexrefs % True if xref values are known. \newif\ifwarnedxrefs % True if we warned once that they aren't known. % @inforef is relatively simple. \def\inforef #1{\inforefzzz #1,,,,**} \def\inforefzzz #1,#2,#3,#4**{% \putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, node \samp{\ignorespaces#1{}}} % @node's only job in TeX is to define \lastnode, which is used in % cross-references. The @node line might or might not have commas, and % might or might not have spaces before the first comma, like: % @node foo , bar , ... % We don't want such trailing spaces in the node name. % \parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} % % also remove a trailing comma, in case of something like this: % @node Help-Cross, , , Cross-refs \def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} \def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}} \let\nwnode=\node \let\lastnode=\empty % Write a cross-reference definition for the current node. #1 is the % type (Ynumbered, Yappendix, Ynothing). % \def\donoderef#1{% \ifx\lastnode\empty\else \setref{\lastnode}{#1}% \global\let\lastnode=\empty \fi } % @anchor{NAME} -- define xref target at arbitrary point. % \newcount\savesfregister % \def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} \def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} \def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} % \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an % anchor), which consists of three parts: % 1) NAME-title - the current sectioning name taken from \lastsection, % or the anchor name. % 2) NAME-snt - section number and type, passed as the SNT arg, or % empty for anchors. % 3) NAME-pg - the page number. % % This is called from \donoderef, \anchor, and \dofloat. In the case of % floats, there is an additional part, which is not written here: % 4) NAME-lof - the text as it should appear in a @listoffloats. % \def\setref#1#2{% \pdfmkdest{#1}% \iflinks {% \atdummies % preserve commands, but don't expand them \edef\writexrdef##1##2{% \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef ##1}{##2}}% these are parameters of \writexrdef }% \toks0 = \expandafter{\lastsection}% \immediate \writexrdef{title}{\the\toks0 }% \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, at \shipout }% \fi } % @xrefautosectiontitle on|off says whether @section(ing) names are used % automatically in xrefs, if the third arg is not explicitly specified. % This was provided as a "secret" @set xref-automatic-section-title % variable, now it's official. % \parseargdef\xrefautomaticsectiontitle{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETxref-automatic-section-title\endcsname = \empty \else\ifx\temp\offword \expandafter\let\csname SETxref-automatic-section-title\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @xrefautomaticsectiontitle value `\temp', must be on|off}% \fi\fi } % % @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is % the node name, #2 the name of the Info cross-reference, #3 the printed % node name, #4 the name of the Info file, #5 the name of the printed % manual. All but the node name can be omitted. % \def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} \def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} \def\ref#1{\xrefX[#1,,,,,,,]} % \newbox\toprefbox \newbox\printedrefnamebox \newbox\infofilenamebox \newbox\printedmanualbox % \def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup \unsepspaces % % Get args without leading/trailing spaces. \def\printedrefname{\ignorespaces #3}% \setbox\printedrefnamebox = \hbox{\printedrefname\unskip}% % \def\infofilename{\ignorespaces #4}% \setbox\infofilenamebox = \hbox{\infofilename\unskip}% % \def\printedmanual{\ignorespaces #5}% \setbox\printedmanualbox = \hbox{\printedmanual\unskip}% % % If the printed reference name (arg #3) was not explicitly given in % the @xref, figure out what we want to use. \ifdim \wd\printedrefnamebox = 0pt % No printed node name was explicitly given. \expandafter\ifx\csname SETxref-automatic-section-title\endcsname \relax % Not auto section-title: use node name inside the square brackets. \def\printedrefname{\ignorespaces #1}% \else % Auto section-title: use chapter/section title inside % the square brackets if we have it. \ifdim \wd\printedmanualbox > 0pt % It is in another manual, so we don't have it; use node name. \def\printedrefname{\ignorespaces #1}% \else \ifhavexrefs % We (should) know the real title if we have the xref values. \def\printedrefname{\refx{#1-title}{}}% \else % Otherwise just copy the Info node name. \def\printedrefname{\ignorespaces #1}% \fi% \fi \fi \fi % % Make link in pdf output. \ifpdf {\indexnofonts \turnoffactive \makevalueexpandable % This expands tokens, so do it after making catcode changes, so _ % etc. don't get their TeX definitions. This ignores all spaces in % #4, including (wrongly) those in the middle of the filename. \getfilename{#4}% % % This (wrongly) does not take account of leading or trailing % spaces in #1, which should be ignored. \edef\pdfxrefdest{#1}% \ifx\pdfxrefdest\empty \def\pdfxrefdest{Top}% no empty targets \else \txiescapepdf\pdfxrefdest % escape PDF special chars \fi % \leavevmode \startlink attr{/Border [0 0 0]}% \ifnum\filenamelength>0 goto file{\the\filename.pdf} name{\pdfxrefdest}% \else goto name{\pdfmkpgn{\pdfxrefdest}}% \fi }% \setcolor{\linkcolor}% \fi % % Float references are printed completely differently: "Figure 1.2" % instead of "[somenode], p.3". We distinguish them by the % LABEL-title being set to a magic string. {% % Have to otherify everything special to allow the \csname to % include an _ in the xref name, etc. \indexnofonts \turnoffactive \expandafter\global\expandafter\let\expandafter\Xthisreftitle \csname XR#1-title\endcsname }% \iffloat\Xthisreftitle % If the user specified the print name (third arg) to the ref, % print it instead of our usual "Figure 1.2". \ifdim\wd\printedrefnamebox = 0pt \refx{#1-snt}{}% \else \printedrefname \fi % % If the user also gave the printed manual name (fifth arg), append % "in MANUALNAME". \ifdim \wd\printedmanualbox > 0pt \space \putwordin{} \cite{\printedmanual}% \fi \else % node/anchor (non-float) references. % % If we use \unhbox to print the node names, TeX does not insert % empty discretionaries after hyphens, which means that it will not % find a line break at a hyphen in a node names. Since some manuals % are best written with fairly long node names, containing hyphens, % this is a loss. Therefore, we give the text of the node name % again, so it is as if TeX is seeing it for the first time. % \ifdim \wd\printedmanualbox > 0pt % Cross-manual reference with a printed manual name. % \crossmanualxref{\cite{\printedmanual\unskip}}% % \else\ifdim \wd\infofilenamebox > 0pt % Cross-manual reference with only an info filename (arg 4), no % printed manual name (arg 5). This is essentially the same as % the case above; we output the filename, since we have nothing else. % \crossmanualxref{\code{\infofilename\unskip}}% % \else % Reference within this manual. % % _ (for example) has to be the character _ for the purposes of the % control sequence corresponding to the node, but it has to expand % into the usual \leavevmode...\vrule stuff for purposes of % printing. So we \turnoffactive for the \refx-snt, back on for the % printing, back off for the \refx-pg. {\turnoffactive % Only output a following space if the -snt ref is nonempty; for % @unnumbered and @anchor, it won't be. \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi }% % output the `[mynode]' via the macro below so it can be overridden. \xrefprintnodename\printedrefname % % But we always want a comma and a space: ,\space % % output the `page 3'. \turnoffactive \putwordpage\tie\refx{#1-pg}{}% \fi\fi \fi \endlink \endgroup} % Output a cross-manual xref to #1. Used just above (twice). % % Only include the text "Section ``foo'' in" if the foo is neither % missing or Top. Thus, @xref{,,,foo,The Foo Manual} outputs simply % "see The Foo Manual", the idea being to refer to the whole manual. % % But, this being TeX, we can't easily compare our node name against the % string "Top" while ignoring the possible spaces before and after in % the input. By adding the arbitrary 7sp below, we make it much less % likely that a real node name would have the same width as "Top" (e.g., % in a monospaced font). Hopefully it will never happen in practice. % % For the same basic reason, we retypeset the "Top" at every % reference, since the current font is indeterminate. % \def\crossmanualxref#1{% \setbox\toprefbox = \hbox{Top\kern7sp}% \setbox2 = \hbox{\ignorespaces \printedrefname \unskip \kern7sp}% \ifdim \wd2 > 7sp % nonempty? \ifdim \wd2 = \wd\toprefbox \else % same as Top? \putwordSection{} ``\printedrefname'' \putwordin{}\space \fi \fi #1% } % This macro is called from \xrefX for the `[nodename]' part of xref % output. It's a separate macro only so it can be changed more easily, % since square brackets don't work well in some documents. Particularly % one that Bob is working on :). % \def\xrefprintnodename#1{[#1]} % Things referred to by \setref. % \def\Ynothing{} \def\Yomitfromtoc{} \def\Ynumbered{% \ifnum\secno=0 \putwordChapter@tie \the\chapno \else \ifnum\subsecno=0 \putwordSection@tie \the\chapno.\the\secno \else \ifnum\subsubsecno=0 \putwordSection@tie \the\chapno.\the\secno.\the\subsecno \else \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno \fi\fi\fi } \def\Yappendix{% \ifnum\secno=0 \putwordAppendix@tie @char\the\appendixno{}% \else \ifnum\subsecno=0 \putwordSection@tie @char\the\appendixno.\the\secno \else \ifnum\subsubsecno=0 \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno \else \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno \fi\fi\fi } % Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. % If its value is nonempty, SUFFIX is output afterward. % \def\refx#1#2{% {% \indexnofonts \otherbackslash \expandafter\global\expandafter\let\expandafter\thisrefX \csname XR#1\endcsname }% \ifx\thisrefX\relax % If not defined, say something at least. \angleleft un\-de\-fined\angleright \iflinks \ifhavexrefs {\toks0 = {#1}% avoid expansion of possibly-complex value \message{\linenumber Undefined cross reference `\the\toks0'.}}% \else \ifwarnedxrefs\else \global\warnedxrefstrue \message{Cross reference values unknown; you must run TeX again.}% \fi \fi \fi \else % It's defined, so just use it. \thisrefX \fi #2% Output the suffix in any case. } % This is the macro invoked by entries in the aux file. Usually it's % just a \def (we prepend XR to the control sequence name to avoid % collisions). But if this is a float type, we have more work to do. % \def\xrdef#1#2{% {% The node name might contain 8-bit characters, which in our current % implementation are changed to commands like @'e. Don't let these % mess up the control sequence name. \indexnofonts \turnoffactive \xdef\safexrefname{#1}% }% % \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref % % Was that xref control sequence that we just defined for a float? \expandafter\iffloat\csname XR\safexrefname\endcsname % it was a float, and we have the (safe) float type in \iffloattype. \expandafter\let\expandafter\floatlist \csname floatlist\iffloattype\endcsname % % Is this the first time we've seen this float type? \expandafter\ifx\floatlist\relax \toks0 = {\do}% yes, so just \do \else % had it before, so preserve previous elements in list. \toks0 = \expandafter{\floatlist\do}% \fi % % Remember this xref in the control sequence \floatlistFLOATTYPE, % for later use in \listoffloats. \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0 {\safexrefname}}% \fi } % Read the last existing aux file, if any. No error if none exists. % \def\tryauxfile{% \openin 1 \jobname.aux \ifeof 1 \else \readdatafile{aux}% \global\havexrefstrue \fi \closein 1 } \def\setupdatafile{% \catcode`\^^@=\other \catcode`\^^A=\other \catcode`\^^B=\other \catcode`\^^C=\other \catcode`\^^D=\other \catcode`\^^E=\other \catcode`\^^F=\other \catcode`\^^G=\other \catcode`\^^H=\other \catcode`\^^K=\other \catcode`\^^L=\other \catcode`\^^N=\other \catcode`\^^P=\other \catcode`\^^Q=\other \catcode`\^^R=\other \catcode`\^^S=\other \catcode`\^^T=\other \catcode`\^^U=\other \catcode`\^^V=\other \catcode`\^^W=\other \catcode`\^^X=\other \catcode`\^^Z=\other \catcode`\^^[=\other \catcode`\^^\=\other \catcode`\^^]=\other \catcode`\^^^=\other \catcode`\^^_=\other % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc. % in xref tags, i.e., node names. But since ^^e4 notation isn't % supported in the main text, it doesn't seem desirable. Furthermore, % that is not enough: for node names that actually contain a ^ % character, we would end up writing a line like this: 'xrdef {'hat % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first % argument, and \hat is not an expandable control sequence. It could % all be worked out, but why? Either we support ^^ or we don't. % % The other change necessary for this was to define \auxhat: % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter % and then to call \auxhat in \setq. % \catcode`\^=\other % % Special characters. Should be turned off anyway, but... \catcode`\~=\other \catcode`\[=\other \catcode`\]=\other \catcode`\"=\other \catcode`\_=\other \catcode`\|=\other \catcode`\<=\other \catcode`\>=\other \catcode`\$=\other \catcode`\#=\other \catcode`\&=\other \catcode`\%=\other \catcode`+=\other % avoid \+ for paranoia even though we've turned it off % % This is to support \ in node names and titles, since the \ % characters end up in a \csname. It's easier than % leaving it active and making its active definition an actual \ % character. What I don't understand is why it works in the *value* % of the xrdef. Seems like it should be a catcode12 \, and that % should not typeset properly. But it works, so I'm moving on for % now. --karl, 15jan04. \catcode`\\=\other % % Make the characters 128-255 be printing characters. {% \count1=128 \def\loop{% \catcode\count1=\other \advance\count1 by 1 \ifnum \count1<256 \loop \fi }% }% % % @ is our escape character in .aux files, and we need braces. \catcode`\{=1 \catcode`\}=2 \catcode`\@=0 } \def\readdatafile#1{% \begingroup \setupdatafile \input\jobname.#1 \endgroup} \message{insertions,} % including footnotes. \newcount \footnoteno % The trailing space in the following definition for supereject is % vital for proper filling; pages come out unaligned when you do a % pagealignmacro call if that space before the closing brace is % removed. (Generally, numeric constants should always be followed by a % space to prevent strange expansion errors.) \def\supereject{\par\penalty -20000\footnoteno =0 } % @footnotestyle is meaningful for Info output only. \let\footnotestyle=\comment {\catcode `\@=11 % % Auto-number footnotes. Otherwise like plain. \gdef\footnote{% \let\indent=\ptexindent \let\noindent=\ptexnoindent \global\advance\footnoteno by \@ne \edef\thisfootno{$^{\the\footnoteno}$}% % % In case the footnote comes at the end of a sentence, preserve the % extra spacing after we do the footnote number. \let\@sf\empty \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi % % Remove inadvertent blank space before typesetting the footnote number. \unskip \thisfootno\@sf \dofootnote }% % Don't bother with the trickery in plain.tex to not require the % footnote text as a parameter. Our footnotes don't need to be so general. % % Oh yes, they do; otherwise, @ifset (and anything else that uses % \parseargline) fails inside footnotes because the tokens are fixed when % the footnote is read. --karl, 16nov96. % \gdef\dofootnote{% \insert\footins\bgroup % We want to typeset this text as a normal paragraph, even if the % footnote reference occurs in (for example) a display environment. % So reset some parameters. \hsize=\pagewidth \interlinepenalty\interfootnotelinepenalty \splittopskip\ht\strutbox % top baseline for broken footnotes \splitmaxdepth\dp\strutbox \floatingpenalty\@MM \leftskip\z@skip \rightskip\z@skip \spaceskip\z@skip \xspaceskip\z@skip \parindent\defaultparindent % \smallfonts \rm % % Because we use hanging indentation in footnotes, a @noindent appears % to exdent this text, so make it be a no-op. makeinfo does not use % hanging indentation so @noindent can still be needed within footnote % text after an @example or the like (not that this is good style). \let\noindent = \relax % % Hang the footnote text off the number. Use \everypar in case the % footnote extends for more than one paragraph. \everypar = {\hang}% \textindent{\thisfootno}% % % Don't crash into the line above the footnote text. Since this % expands into a box, it must come within the paragraph, lest it % provide a place where TeX can split the footnote. \footstrut % % Invoke rest of plain TeX footnote routine. \futurelet\next\fo@t } }%end \catcode `\@=11 % In case a @footnote appears in a vbox, save the footnote text and create % the real \insert just after the vbox finished. Otherwise, the insertion % would be lost. % Similarly, if a @footnote appears inside an alignment, save the footnote % text to a box and make the \insert when a row of the table is finished. % And the same can be done for other insert classes. --kasal, 16nov03. % Replace the \insert primitive by a cheating macro. % Deeper inside, just make sure that the saved insertions are not spilled % out prematurely. % \def\startsavinginserts{% \ifx \insert\ptexinsert \let\insert\saveinsert \else \let\checkinserts\relax \fi } % This \insert replacement works for both \insert\footins{foo} and % \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. % \def\saveinsert#1{% \edef\next{\noexpand\savetobox \makeSAVEname#1}% \afterassignment\next % swallow the left brace \let\temp = } \def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} \def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} \def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} \def\placesaveins#1{% \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname {\box#1}% } % eat @SAVE -- beware, all of them have catcode \other: { \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) \gdef\gobblesave @SAVE{} } % initialization: \def\newsaveins #1{% \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% \next } \def\newsaveinsX #1{% \csname newbox\endcsname #1% \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts \checksaveins #1}% } % initialize: \let\checkinserts\empty \newsaveins\footins \newsaveins\margin % @image. We use the macros from epsf.tex to support this. % If epsf.tex is not installed and @image is used, we complain. % % Check for and read epsf.tex up front. If we read it only at @image % time, we might be inside a group, and then its definitions would get % undone and the next image would fail. \openin 1 = epsf.tex \ifeof 1 \else % Do not bother showing banner with epsf.tex v2.7k (available in % doc/epsf.tex and on ctan). \def\epsfannounce{\toks0 = }% \input epsf.tex \fi \closein 1 % % We will only complain once about lack of epsf.tex. \newif\ifwarnednoepsf \newhelp\noepsfhelp{epsf.tex must be installed for images to work. It is also included in the Texinfo distribution, or you can get it from ftp://tug.org/tex/epsf.tex.} % \def\image#1{% \ifx\epsfbox\thisisundefined \ifwarnednoepsf \else \errhelp = \noepsfhelp \errmessage{epsf.tex not found, images will be ignored}% \global\warnednoepsftrue \fi \else \imagexxx #1,,,,,\finish \fi } % % Arguments to @image: % #1 is (mandatory) image filename; we tack on .eps extension. % #2 is (optional) width, #3 is (optional) height. % #4 is (ignored optional) html alt text. % #5 is (ignored optional) extension. % #6 is just the usual extra ignored arg for parsing stuff. \newif\ifimagevmode \def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup \catcode`\^^M = 5 % in case we're inside an example \normalturnoffactive % allow _ et al. in names % If the image is by itself, center it. \ifvmode \imagevmodetrue \else \ifx\centersub\centerV % for @center @image, we need a vbox so we can have our vertical space \imagevmodetrue \vbox\bgroup % vbox has better behavior than vtop herev \fi\fi % \ifimagevmode \nobreak\medskip % Usually we'll have text after the image which will insert % \parskip glue, so insert it here too to equalize the space % above and below. \nobreak\vskip\parskip \nobreak \fi % % Leave vertical mode so that indentation from an enclosing % environment such as @quotation is respected. % However, if we're at the top level, we don't want the % normal paragraph indentation. % On the other hand, if we are in the case of @center @image, we don't % want to start a paragraph, which will create a hsize-width box and % eradicate the centering. \ifx\centersub\centerV\else \noindent \fi % % Output the image. \ifpdf \dopdfimage{#1}{#2}{#3}% \else % \epsfbox itself resets \epsf?size at each figure. \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi \epsfbox{#1.eps}% \fi % \ifimagevmode \medskip % space after a standalone image \fi \ifx\centersub\centerV \egroup \fi \endgroup} % @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, % etc. We don't actually implement floating yet, we always include the % float "here". But it seemed the best name for the future. % \envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} % There may be a space before second and/or third parameter; delete it. \def\eatcommaspace#1, {#1,} % #1 is the optional FLOATTYPE, the text label for this float, typically % "Figure", "Table", "Example", etc. Can't contain commas. If omitted, % this float will not be numbered and cannot be referred to. % % #2 is the optional xref label. Also must be present for the float to % be referable. % % #3 is the optional positioning argument; for now, it is ignored. It % will somehow specify the positions allowed to float to (here, top, bottom). % % We keep a separate counter for each FLOATTYPE, which we reset at each % chapter-level command. \let\resetallfloatnos=\empty % \def\dofloat#1,#2,#3,#4\finish{% \let\thiscaption=\empty \let\thisshortcaption=\empty % % don't lose footnotes inside @float. % % BEWARE: when the floats start float, we have to issue warning whenever an % insert appears inside a float which could possibly float. --kasal, 26may04 % \startsavinginserts % % We can't be used inside a paragraph. \par % \vtop\bgroup \def\floattype{#1}% \def\floatlabel{#2}% \def\floatloc{#3}% we do nothing with this yet. % \ifx\floattype\empty \let\safefloattype=\empty \else {% % the floattype might have accents or other special characters, % but we need to use it in a control sequence name. \indexnofonts \turnoffactive \xdef\safefloattype{\floattype}% }% \fi % % If label is given but no type, we handle that as the empty type. \ifx\floatlabel\empty \else % We want each FLOATTYPE to be numbered separately (Figure 1, % Table 1, Figure 2, ...). (And if no label, no number.) % \expandafter\getfloatno\csname\safefloattype floatno\endcsname \global\advance\floatno by 1 % {% % This magic value for \lastsection is output by \setref as the % XREFLABEL-title value. \xrefX uses it to distinguish float % labels (which have a completely different output format) from % node and anchor labels. And \xrdef uses it to construct the % lists of floats. % \edef\lastsection{\floatmagic=\safefloattype}% \setref{\floatlabel}{Yfloat}% }% \fi % % start with \parskip glue, I guess. \vskip\parskip % % Don't suppress indentation if a float happens to start a section. \restorefirstparagraphindent } % we have these possibilities: % @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap % @float Foo,lbl & no caption: Foo 1.1 % @float Foo & @caption{Cap}: Foo: Cap % @float Foo & no caption: Foo % @float ,lbl & Caption{Cap}: 1.1: Cap % @float ,lbl & no caption: 1.1 % @float & @caption{Cap}: Cap % @float & no caption: % \def\Efloat{% \let\floatident = \empty % % In all cases, if we have a float type, it comes first. \ifx\floattype\empty \else \def\floatident{\floattype}\fi % % If we have an xref label, the number comes next. \ifx\floatlabel\empty \else \ifx\floattype\empty \else % if also had float type, need tie first. \appendtomacro\floatident{\tie}% \fi % the number. \appendtomacro\floatident{\chaplevelprefix\the\floatno}% \fi % % Start the printed caption with what we've constructed in % \floatident, but keep it separate; we need \floatident again. \let\captionline = \floatident % \ifx\thiscaption\empty \else \ifx\floatident\empty \else \appendtomacro\captionline{: }% had ident, so need a colon between \fi % % caption text. \appendtomacro\captionline{\scanexp\thiscaption}% \fi % % If we have anything to print, print it, with space before. % Eventually this needs to become an \insert. \ifx\captionline\empty \else \vskip.5\parskip \captionline % % Space below caption. \vskip\parskip \fi % % If have an xref label, write the list of floats info. Do this % after the caption, to avoid chance of it being a breakpoint. \ifx\floatlabel\empty \else % Write the text that goes in the lof to the aux file as % \floatlabel-lof. Besides \floatident, we include the short % caption if specified, else the full caption if specified, else nothing. {% \atdummies % % since we read the caption text in the macro world, where ^^M % is turned into a normal character, we have to scan it back, so % we don't write the literal three characters "^^M" into the aux file. \scanexp{% \xdef\noexpand\gtemp{% \ifx\thisshortcaption\empty \thiscaption \else \thisshortcaption \fi }% }% \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident \ifx\gtemp\empty \else : \gtemp \fi}}% }% \fi \egroup % end of \vtop % % place the captured inserts % % BEWARE: when the floats start floating, we have to issue warning % whenever an insert appears inside a float which could possibly % float. --kasal, 26may04 % \checkinserts } % Append the tokens #2 to the definition of macro #1, not expanding either. % \def\appendtomacro#1#2{% \expandafter\def\expandafter#1\expandafter{#1#2}% } % @caption, @shortcaption % \def\caption{\docaption\thiscaption} \def\shortcaption{\docaption\thisshortcaption} \def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} \def\defcaption#1#2{\egroup \def#1{#2}} % The parameter is the control sequence identifying the counter we are % going to use. Create it if it doesn't exist and assign it to \floatno. \def\getfloatno#1{% \ifx#1\relax % Haven't seen this figure type before. \csname newcount\endcsname #1% % % Remember to reset this floatno at the next chap. \expandafter\gdef\expandafter\resetallfloatnos \expandafter{\resetallfloatnos #1=0 }% \fi \let\floatno#1% } % \setref calls this to get the XREFLABEL-snt value. We want an @xref % to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we % first read the @float command. % \def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% % Magic string used for the XREFLABEL-title value, so \xrefX can % distinguish floats from other xref types. \def\floatmagic{!!float!!} % #1 is the control sequence we are passed; we expand into a conditional % which is true if #1 represents a float ref. That is, the magic % \lastsection value which we \setref above. % \def\iffloat#1{\expandafter\doiffloat#1==\finish} % % #1 is (maybe) the \floatmagic string. If so, #2 will be the % (safe) float type for this float. We set \iffloattype to #2. % \def\doiffloat#1=#2=#3\finish{% \def\temp{#1}% \def\iffloattype{#2}% \ifx\temp\floatmagic } % @listoffloats FLOATTYPE - print a list of floats like a table of contents. % \parseargdef\listoffloats{% \def\floattype{#1}% floattype {% % the floattype might have accents or other special characters, % but we need to use it in a control sequence name. \indexnofonts \turnoffactive \xdef\safefloattype{\floattype}% }% % % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax \ifhavexrefs % if the user said @listoffloats foo but never @float foo. \message{\linenumber No `\safefloattype' floats to list.}% \fi \else \begingroup \leftskip=\tocindent % indent these entries like a toc \let\do=\listoffloatsdo \csname floatlist\safefloattype\endcsname \endgroup \fi } % This is called on each entry in a list of floats. We're passed the % xref label, in the form LABEL-title, which is how we save it in the % aux file. We strip off the -title and look up \XRLABEL-lof, which % has the text we're supposed to typeset here. % % Figures without xref labels will not be included in the list (since % they won't appear in the aux file). % \def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} \def\listoffloatsdoentry#1-title\finish{{% % Can't fully expand XR#1-lof because it can contain anything. Just % pass the control sequence. On the other hand, XR#1-pg is just the % page number, and we want to fully expand that so we can get a link % in pdf output. \toksA = \expandafter{\csname XR#1-lof\endcsname}% % % use the same \entry macro we use to generate the TOC and index. \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% \writeentry }} \message{localization,} % For single-language documents, @documentlanguage is usually given very % early, just after @documentencoding. Single argument is the language % (de) or locale (de_DE) abbreviation. % { \catcode`\_ = \active \globaldefs=1 \parseargdef\documentlanguage{\begingroup \let_=\normalunderscore % normal _ character for filenames \tex % read txi-??.tex file in plain TeX. % Read the file by the name they passed if it exists. \openin 1 txi-#1.tex \ifeof 1 \documentlanguagetrywithoutunderscore{#1_\finish}% \else \globaldefs = 1 % everything in the txi-LL files needs to persist \input txi-#1.tex \fi \closein 1 \endgroup % end raw TeX \endgroup} % % If they passed de_DE, and txi-de_DE.tex doesn't exist, % try txi-de.tex. % \gdef\documentlanguagetrywithoutunderscore#1_#2\finish{% \openin 1 txi-#1.tex \ifeof 1 \errhelp = \nolanghelp \errmessage{Cannot read language file txi-#1.tex}% \else \globaldefs = 1 % everything in the txi-LL files needs to persist \input txi-#1.tex \fi \closein 1 } }% end of special _ catcode % \newhelp\nolanghelp{The given language definition file cannot be found or is empty. Maybe you need to install it? Putting it in the current directory should work if nowhere else does.} % This macro is called from txi-??.tex files; the first argument is the % \language name to set (without the "\lang@" prefix), the second and % third args are \{left,right}hyphenmin. % % The language names to pass are determined when the format is built. % See the etex.log file created at that time, e.g., % /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log. % % With TeX Live 2008, etex now includes hyphenation patterns for all % available languages. This means we can support hyphenation in % Texinfo, at least to some extent. (This still doesn't solve the % accented characters problem.) % \catcode`@=11 \def\txisetlanguage#1#2#3{% % do not set the language if the name is undefined in the current TeX. \expandafter\ifx\csname lang@#1\endcsname \relax \message{no patterns for #1}% \else \global\language = \csname lang@#1\endcsname \fi % but there is no harm in adjusting the hyphenmin values regardless. \global\lefthyphenmin = #2\relax \global\righthyphenmin = #3\relax } % Helpers for encodings. % Set the catcode of characters 128 through 255 to the specified number. % \def\setnonasciicharscatcode#1{% \count255=128 \loop\ifnum\count255<256 \global\catcode\count255=#1\relax \advance\count255 by 1 \repeat } \def\setnonasciicharscatcodenonglobal#1{% \count255=128 \loop\ifnum\count255<256 \catcode\count255=#1\relax \advance\count255 by 1 \repeat } % @documentencoding sets the definition of non-ASCII characters % according to the specified encoding. % \parseargdef\documentencoding{% % Encoding being declared for the document. \def\declaredencoding{\csname #1.enc\endcsname}% % % Supported encodings: names converted to tokens in order to be able % to compare them with \ifx. \def\ascii{\csname US-ASCII.enc\endcsname}% \def\latnine{\csname ISO-8859-15.enc\endcsname}% \def\latone{\csname ISO-8859-1.enc\endcsname}% \def\lattwo{\csname ISO-8859-2.enc\endcsname}% \def\utfeight{\csname UTF-8.enc\endcsname}% % \ifx \declaredencoding \ascii \asciichardefs % \else \ifx \declaredencoding \lattwo \setnonasciicharscatcode\active \lattwochardefs % \else \ifx \declaredencoding \latone \setnonasciicharscatcode\active \latonechardefs % \else \ifx \declaredencoding \latnine \setnonasciicharscatcode\active \latninechardefs % \else \ifx \declaredencoding \utfeight \setnonasciicharscatcode\active \utfeightchardefs % \else \message{Unknown document encoding #1, ignoring.}% % \fi % utfeight \fi % latnine \fi % latone \fi % lattwo \fi % ascii } % A message to be logged when using a character that isn't available % the default font encoding (OT1). % \def\missingcharmsg#1{\message{Character missing in OT1 encoding: #1.}} % Take account of \c (plain) vs. \, (Texinfo) difference. \def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi} % First, make active non-ASCII characters in order for them to be % correctly categorized when TeX reads the replacement text of % macros containing the character definitions. \setnonasciicharscatcode\active % % Latin1 (ISO-8859-1) character definitions. \def\latonechardefs{% \gdef^^a0{\tie} \gdef^^a1{\exclamdown} \gdef^^a2{\missingcharmsg{CENT SIGN}} \gdef^^a3{{\pounds}} \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} \gdef^^a5{\missingcharmsg{YEN SIGN}} \gdef^^a6{\missingcharmsg{BROKEN BAR}} \gdef^^a7{\S} \gdef^^a8{\"{}} \gdef^^a9{\copyright} \gdef^^aa{\ordf} \gdef^^ab{\guillemetleft} \gdef^^ac{$\lnot$} \gdef^^ad{\-} \gdef^^ae{\registeredsymbol} \gdef^^af{\={}} % \gdef^^b0{\textdegree} \gdef^^b1{$\pm$} \gdef^^b2{$^2$} \gdef^^b3{$^3$} \gdef^^b4{\'{}} \gdef^^b5{$\mu$} \gdef^^b6{\P} % \gdef^^b7{$^.$} \gdef^^b8{\cedilla\ } \gdef^^b9{$^1$} \gdef^^ba{\ordm} % \gdef^^bb{\guillemetright} \gdef^^bc{$1\over4$} \gdef^^bd{$1\over2$} \gdef^^be{$3\over4$} \gdef^^bf{\questiondown} % \gdef^^c0{\`A} \gdef^^c1{\'A} \gdef^^c2{\^A} \gdef^^c3{\~A} \gdef^^c4{\"A} \gdef^^c5{\ringaccent A} \gdef^^c6{\AE} \gdef^^c7{\cedilla C} \gdef^^c8{\`E} \gdef^^c9{\'E} \gdef^^ca{\^E} \gdef^^cb{\"E} \gdef^^cc{\`I} \gdef^^cd{\'I} \gdef^^ce{\^I} \gdef^^cf{\"I} % \gdef^^d0{\DH} \gdef^^d1{\~N} \gdef^^d2{\`O} \gdef^^d3{\'O} \gdef^^d4{\^O} \gdef^^d5{\~O} \gdef^^d6{\"O} \gdef^^d7{$\times$} \gdef^^d8{\O} \gdef^^d9{\`U} \gdef^^da{\'U} \gdef^^db{\^U} \gdef^^dc{\"U} \gdef^^dd{\'Y} \gdef^^de{\TH} \gdef^^df{\ss} % \gdef^^e0{\`a} \gdef^^e1{\'a} \gdef^^e2{\^a} \gdef^^e3{\~a} \gdef^^e4{\"a} \gdef^^e5{\ringaccent a} \gdef^^e6{\ae} \gdef^^e7{\cedilla c} \gdef^^e8{\`e} \gdef^^e9{\'e} \gdef^^ea{\^e} \gdef^^eb{\"e} \gdef^^ec{\`{\dotless i}} \gdef^^ed{\'{\dotless i}} \gdef^^ee{\^{\dotless i}} \gdef^^ef{\"{\dotless i}} % \gdef^^f0{\dh} \gdef^^f1{\~n} \gdef^^f2{\`o} \gdef^^f3{\'o} \gdef^^f4{\^o} \gdef^^f5{\~o} \gdef^^f6{\"o} \gdef^^f7{$\div$} \gdef^^f8{\o} \gdef^^f9{\`u} \gdef^^fa{\'u} \gdef^^fb{\^u} \gdef^^fc{\"u} \gdef^^fd{\'y} \gdef^^fe{\th} \gdef^^ff{\"y} } % Latin9 (ISO-8859-15) encoding character definitions. \def\latninechardefs{% % Encoding is almost identical to Latin1. \latonechardefs % \gdef^^a4{\euro} \gdef^^a6{\v S} \gdef^^a8{\v s} \gdef^^b4{\v Z} \gdef^^b8{\v z} \gdef^^bc{\OE} \gdef^^bd{\oe} \gdef^^be{\"Y} } % Latin2 (ISO-8859-2) character definitions. \def\lattwochardefs{% \gdef^^a0{\tie} \gdef^^a1{\ogonek{A}} \gdef^^a2{\u{}} \gdef^^a3{\L} \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} \gdef^^a5{\v L} \gdef^^a6{\'S} \gdef^^a7{\S} \gdef^^a8{\"{}} \gdef^^a9{\v S} \gdef^^aa{\cedilla S} \gdef^^ab{\v T} \gdef^^ac{\'Z} \gdef^^ad{\-} \gdef^^ae{\v Z} \gdef^^af{\dotaccent Z} % \gdef^^b0{\textdegree} \gdef^^b1{\ogonek{a}} \gdef^^b2{\ogonek{ }} \gdef^^b3{\l} \gdef^^b4{\'{}} \gdef^^b5{\v l} \gdef^^b6{\'s} \gdef^^b7{\v{}} \gdef^^b8{\cedilla\ } \gdef^^b9{\v s} \gdef^^ba{\cedilla s} \gdef^^bb{\v t} \gdef^^bc{\'z} \gdef^^bd{\H{}} \gdef^^be{\v z} \gdef^^bf{\dotaccent z} % \gdef^^c0{\'R} \gdef^^c1{\'A} \gdef^^c2{\^A} \gdef^^c3{\u A} \gdef^^c4{\"A} \gdef^^c5{\'L} \gdef^^c6{\'C} \gdef^^c7{\cedilla C} \gdef^^c8{\v C} \gdef^^c9{\'E} \gdef^^ca{\ogonek{E}} \gdef^^cb{\"E} \gdef^^cc{\v E} \gdef^^cd{\'I} \gdef^^ce{\^I} \gdef^^cf{\v D} % \gdef^^d0{\DH} \gdef^^d1{\'N} \gdef^^d2{\v N} \gdef^^d3{\'O} \gdef^^d4{\^O} \gdef^^d5{\H O} \gdef^^d6{\"O} \gdef^^d7{$\times$} \gdef^^d8{\v R} \gdef^^d9{\ringaccent U} \gdef^^da{\'U} \gdef^^db{\H U} \gdef^^dc{\"U} \gdef^^dd{\'Y} \gdef^^de{\cedilla T} \gdef^^df{\ss} % \gdef^^e0{\'r} \gdef^^e1{\'a} \gdef^^e2{\^a} \gdef^^e3{\u a} \gdef^^e4{\"a} \gdef^^e5{\'l} \gdef^^e6{\'c} \gdef^^e7{\cedilla c} \gdef^^e8{\v c} \gdef^^e9{\'e} \gdef^^ea{\ogonek{e}} \gdef^^eb{\"e} \gdef^^ec{\v e} \gdef^^ed{\'{\dotless{i}}} \gdef^^ee{\^{\dotless{i}}} \gdef^^ef{\v d} % \gdef^^f0{\dh} \gdef^^f1{\'n} \gdef^^f2{\v n} \gdef^^f3{\'o} \gdef^^f4{\^o} \gdef^^f5{\H o} \gdef^^f6{\"o} \gdef^^f7{$\div$} \gdef^^f8{\v r} \gdef^^f9{\ringaccent u} \gdef^^fa{\'u} \gdef^^fb{\H u} \gdef^^fc{\"u} \gdef^^fd{\'y} \gdef^^fe{\cedilla t} \gdef^^ff{\dotaccent{}} } % UTF-8 character definitions. % % This code to support UTF-8 is based on LaTeX's utf8.def, with some % changes for Texinfo conventions. It is included here under the GPL by % permission from Frank Mittelbach and the LaTeX team. % \newcount\countUTFx \newcount\countUTFy \newcount\countUTFz \gdef\UTFviiiTwoOctets#1#2{\expandafter \UTFviiiDefined\csname u8:#1\string #2\endcsname} % \gdef\UTFviiiThreeOctets#1#2#3{\expandafter \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname} % \gdef\UTFviiiFourOctets#1#2#3#4{\expandafter \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname} \gdef\UTFviiiDefined#1{% \ifx #1\relax \message{\linenumber Unicode char \string #1 not defined for Texinfo}% \else \expandafter #1% \fi } \begingroup \catcode`\~13 \catcode`\"12 \def\UTFviiiLoop{% \global\catcode\countUTFx\active \uccode`\~\countUTFx \uppercase\expandafter{\UTFviiiTmp}% \advance\countUTFx by 1 \ifnum\countUTFx < \countUTFy \expandafter\UTFviiiLoop \fi} \countUTFx = "C2 \countUTFy = "E0 \def\UTFviiiTmp{% \xdef~{\noexpand\UTFviiiTwoOctets\string~}} \UTFviiiLoop \countUTFx = "E0 \countUTFy = "F0 \def\UTFviiiTmp{% \xdef~{\noexpand\UTFviiiThreeOctets\string~}} \UTFviiiLoop \countUTFx = "F0 \countUTFy = "F4 \def\UTFviiiTmp{% \xdef~{\noexpand\UTFviiiFourOctets\string~}} \UTFviiiLoop \endgroup \begingroup \catcode`\"=12 \catcode`\<=12 \catcode`\.=12 \catcode`\,=12 \catcode`\;=12 \catcode`\!=12 \catcode`\~=13 \gdef\DeclareUnicodeCharacter#1#2{% \countUTFz = "#1\relax %\wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}% \begingroup \parseXMLCharref \def\UTFviiiTwoOctets##1##2{% \csname u8:##1\string ##2\endcsname}% \def\UTFviiiThreeOctets##1##2##3{% \csname u8:##1\string ##2\string ##3\endcsname}% \def\UTFviiiFourOctets##1##2##3##4{% \csname u8:##1\string ##2\string ##3\string ##4\endcsname}% \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter \gdef\UTFviiiTmp{#2}% \endgroup} \gdef\parseXMLCharref{% \ifnum\countUTFz < "A0\relax \errhelp = \EMsimple \errmessage{Cannot define Unicode char value < 00A0}% \else\ifnum\countUTFz < "800\relax \parseUTFviiiA,% \parseUTFviiiB C\UTFviiiTwoOctets.,% \else\ifnum\countUTFz < "10000\relax \parseUTFviiiA;% \parseUTFviiiA,% \parseUTFviiiB E\UTFviiiThreeOctets.{,;}% \else \parseUTFviiiA;% \parseUTFviiiA,% \parseUTFviiiA!% \parseUTFviiiB F\UTFviiiFourOctets.{!,;}% \fi\fi\fi } \gdef\parseUTFviiiA#1{% \countUTFx = \countUTFz \divide\countUTFz by 64 \countUTFy = \countUTFz \multiply\countUTFz by 64 \advance\countUTFx by -\countUTFz \advance\countUTFx by 128 \uccode `#1\countUTFx \countUTFz = \countUTFy} \gdef\parseUTFviiiB#1#2#3#4{% \advance\countUTFz by "#10\relax \uccode `#3\countUTFz \uppercase{\gdef\UTFviiiTmp{#2#3#4}}} \endgroup \def\utfeightchardefs{% \DeclareUnicodeCharacter{00A0}{\tie} \DeclareUnicodeCharacter{00A1}{\exclamdown} \DeclareUnicodeCharacter{00A3}{\pounds} \DeclareUnicodeCharacter{00A8}{\"{ }} \DeclareUnicodeCharacter{00A9}{\copyright} \DeclareUnicodeCharacter{00AA}{\ordf} \DeclareUnicodeCharacter{00AB}{\guillemetleft} \DeclareUnicodeCharacter{00AD}{\-} \DeclareUnicodeCharacter{00AE}{\registeredsymbol} \DeclareUnicodeCharacter{00AF}{\={ }} \DeclareUnicodeCharacter{00B0}{\ringaccent{ }} \DeclareUnicodeCharacter{00B4}{\'{ }} \DeclareUnicodeCharacter{00B8}{\cedilla{ }} \DeclareUnicodeCharacter{00BA}{\ordm} \DeclareUnicodeCharacter{00BB}{\guillemetright} \DeclareUnicodeCharacter{00BF}{\questiondown} \DeclareUnicodeCharacter{00C0}{\`A} \DeclareUnicodeCharacter{00C1}{\'A} \DeclareUnicodeCharacter{00C2}{\^A} \DeclareUnicodeCharacter{00C3}{\~A} \DeclareUnicodeCharacter{00C4}{\"A} \DeclareUnicodeCharacter{00C5}{\AA} \DeclareUnicodeCharacter{00C6}{\AE} \DeclareUnicodeCharacter{00C7}{\cedilla{C}} \DeclareUnicodeCharacter{00C8}{\`E} \DeclareUnicodeCharacter{00C9}{\'E} \DeclareUnicodeCharacter{00CA}{\^E} \DeclareUnicodeCharacter{00CB}{\"E} \DeclareUnicodeCharacter{00CC}{\`I} \DeclareUnicodeCharacter{00CD}{\'I} \DeclareUnicodeCharacter{00CE}{\^I} \DeclareUnicodeCharacter{00CF}{\"I} \DeclareUnicodeCharacter{00D0}{\DH} \DeclareUnicodeCharacter{00D1}{\~N} \DeclareUnicodeCharacter{00D2}{\`O} \DeclareUnicodeCharacter{00D3}{\'O} \DeclareUnicodeCharacter{00D4}{\^O} \DeclareUnicodeCharacter{00D5}{\~O} \DeclareUnicodeCharacter{00D6}{\"O} \DeclareUnicodeCharacter{00D8}{\O} \DeclareUnicodeCharacter{00D9}{\`U} \DeclareUnicodeCharacter{00DA}{\'U} \DeclareUnicodeCharacter{00DB}{\^U} \DeclareUnicodeCharacter{00DC}{\"U} \DeclareUnicodeCharacter{00DD}{\'Y} \DeclareUnicodeCharacter{00DE}{\TH} \DeclareUnicodeCharacter{00DF}{\ss} \DeclareUnicodeCharacter{00E0}{\`a} \DeclareUnicodeCharacter{00E1}{\'a} \DeclareUnicodeCharacter{00E2}{\^a} \DeclareUnicodeCharacter{00E3}{\~a} \DeclareUnicodeCharacter{00E4}{\"a} \DeclareUnicodeCharacter{00E5}{\aa} \DeclareUnicodeCharacter{00E6}{\ae} \DeclareUnicodeCharacter{00E7}{\cedilla{c}} \DeclareUnicodeCharacter{00E8}{\`e} \DeclareUnicodeCharacter{00E9}{\'e} \DeclareUnicodeCharacter{00EA}{\^e} \DeclareUnicodeCharacter{00EB}{\"e} \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}} \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}} \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}} \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}} \DeclareUnicodeCharacter{00F0}{\dh} \DeclareUnicodeCharacter{00F1}{\~n} \DeclareUnicodeCharacter{00F2}{\`o} \DeclareUnicodeCharacter{00F3}{\'o} \DeclareUnicodeCharacter{00F4}{\^o} \DeclareUnicodeCharacter{00F5}{\~o} \DeclareUnicodeCharacter{00F6}{\"o} \DeclareUnicodeCharacter{00F8}{\o} \DeclareUnicodeCharacter{00F9}{\`u} \DeclareUnicodeCharacter{00FA}{\'u} \DeclareUnicodeCharacter{00FB}{\^u} \DeclareUnicodeCharacter{00FC}{\"u} \DeclareUnicodeCharacter{00FD}{\'y} \DeclareUnicodeCharacter{00FE}{\th} \DeclareUnicodeCharacter{00FF}{\"y} \DeclareUnicodeCharacter{0100}{\=A} \DeclareUnicodeCharacter{0101}{\=a} \DeclareUnicodeCharacter{0102}{\u{A}} \DeclareUnicodeCharacter{0103}{\u{a}} \DeclareUnicodeCharacter{0104}{\ogonek{A}} \DeclareUnicodeCharacter{0105}{\ogonek{a}} \DeclareUnicodeCharacter{0106}{\'C} \DeclareUnicodeCharacter{0107}{\'c} \DeclareUnicodeCharacter{0108}{\^C} \DeclareUnicodeCharacter{0109}{\^c} \DeclareUnicodeCharacter{0118}{\ogonek{E}} \DeclareUnicodeCharacter{0119}{\ogonek{e}} \DeclareUnicodeCharacter{010A}{\dotaccent{C}} \DeclareUnicodeCharacter{010B}{\dotaccent{c}} \DeclareUnicodeCharacter{010C}{\v{C}} \DeclareUnicodeCharacter{010D}{\v{c}} \DeclareUnicodeCharacter{010E}{\v{D}} \DeclareUnicodeCharacter{0112}{\=E} \DeclareUnicodeCharacter{0113}{\=e} \DeclareUnicodeCharacter{0114}{\u{E}} \DeclareUnicodeCharacter{0115}{\u{e}} \DeclareUnicodeCharacter{0116}{\dotaccent{E}} \DeclareUnicodeCharacter{0117}{\dotaccent{e}} \DeclareUnicodeCharacter{011A}{\v{E}} \DeclareUnicodeCharacter{011B}{\v{e}} \DeclareUnicodeCharacter{011C}{\^G} \DeclareUnicodeCharacter{011D}{\^g} \DeclareUnicodeCharacter{011E}{\u{G}} \DeclareUnicodeCharacter{011F}{\u{g}} \DeclareUnicodeCharacter{0120}{\dotaccent{G}} \DeclareUnicodeCharacter{0121}{\dotaccent{g}} \DeclareUnicodeCharacter{0124}{\^H} \DeclareUnicodeCharacter{0125}{\^h} \DeclareUnicodeCharacter{0128}{\~I} \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}} \DeclareUnicodeCharacter{012A}{\=I} \DeclareUnicodeCharacter{012B}{\={\dotless{i}}} \DeclareUnicodeCharacter{012C}{\u{I}} \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}} \DeclareUnicodeCharacter{0130}{\dotaccent{I}} \DeclareUnicodeCharacter{0131}{\dotless{i}} \DeclareUnicodeCharacter{0132}{IJ} \DeclareUnicodeCharacter{0133}{ij} \DeclareUnicodeCharacter{0134}{\^J} \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}} \DeclareUnicodeCharacter{0139}{\'L} \DeclareUnicodeCharacter{013A}{\'l} \DeclareUnicodeCharacter{0141}{\L} \DeclareUnicodeCharacter{0142}{\l} \DeclareUnicodeCharacter{0143}{\'N} \DeclareUnicodeCharacter{0144}{\'n} \DeclareUnicodeCharacter{0147}{\v{N}} \DeclareUnicodeCharacter{0148}{\v{n}} \DeclareUnicodeCharacter{014C}{\=O} \DeclareUnicodeCharacter{014D}{\=o} \DeclareUnicodeCharacter{014E}{\u{O}} \DeclareUnicodeCharacter{014F}{\u{o}} \DeclareUnicodeCharacter{0150}{\H{O}} \DeclareUnicodeCharacter{0151}{\H{o}} \DeclareUnicodeCharacter{0152}{\OE} \DeclareUnicodeCharacter{0153}{\oe} \DeclareUnicodeCharacter{0154}{\'R} \DeclareUnicodeCharacter{0155}{\'r} \DeclareUnicodeCharacter{0158}{\v{R}} \DeclareUnicodeCharacter{0159}{\v{r}} \DeclareUnicodeCharacter{015A}{\'S} \DeclareUnicodeCharacter{015B}{\'s} \DeclareUnicodeCharacter{015C}{\^S} \DeclareUnicodeCharacter{015D}{\^s} \DeclareUnicodeCharacter{015E}{\cedilla{S}} \DeclareUnicodeCharacter{015F}{\cedilla{s}} \DeclareUnicodeCharacter{0160}{\v{S}} \DeclareUnicodeCharacter{0161}{\v{s}} \DeclareUnicodeCharacter{0162}{\cedilla{t}} \DeclareUnicodeCharacter{0163}{\cedilla{T}} \DeclareUnicodeCharacter{0164}{\v{T}} \DeclareUnicodeCharacter{0168}{\~U} \DeclareUnicodeCharacter{0169}{\~u} \DeclareUnicodeCharacter{016A}{\=U} \DeclareUnicodeCharacter{016B}{\=u} \DeclareUnicodeCharacter{016C}{\u{U}} \DeclareUnicodeCharacter{016D}{\u{u}} \DeclareUnicodeCharacter{016E}{\ringaccent{U}} \DeclareUnicodeCharacter{016F}{\ringaccent{u}} \DeclareUnicodeCharacter{0170}{\H{U}} \DeclareUnicodeCharacter{0171}{\H{u}} \DeclareUnicodeCharacter{0174}{\^W} \DeclareUnicodeCharacter{0175}{\^w} \DeclareUnicodeCharacter{0176}{\^Y} \DeclareUnicodeCharacter{0177}{\^y} \DeclareUnicodeCharacter{0178}{\"Y} \DeclareUnicodeCharacter{0179}{\'Z} \DeclareUnicodeCharacter{017A}{\'z} \DeclareUnicodeCharacter{017B}{\dotaccent{Z}} \DeclareUnicodeCharacter{017C}{\dotaccent{z}} \DeclareUnicodeCharacter{017D}{\v{Z}} \DeclareUnicodeCharacter{017E}{\v{z}} \DeclareUnicodeCharacter{01C4}{D\v{Z}} \DeclareUnicodeCharacter{01C5}{D\v{z}} \DeclareUnicodeCharacter{01C6}{d\v{z}} \DeclareUnicodeCharacter{01C7}{LJ} \DeclareUnicodeCharacter{01C8}{Lj} \DeclareUnicodeCharacter{01C9}{lj} \DeclareUnicodeCharacter{01CA}{NJ} \DeclareUnicodeCharacter{01CB}{Nj} \DeclareUnicodeCharacter{01CC}{nj} \DeclareUnicodeCharacter{01CD}{\v{A}} \DeclareUnicodeCharacter{01CE}{\v{a}} \DeclareUnicodeCharacter{01CF}{\v{I}} \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}} \DeclareUnicodeCharacter{01D1}{\v{O}} \DeclareUnicodeCharacter{01D2}{\v{o}} \DeclareUnicodeCharacter{01D3}{\v{U}} \DeclareUnicodeCharacter{01D4}{\v{u}} \DeclareUnicodeCharacter{01E2}{\={\AE}} \DeclareUnicodeCharacter{01E3}{\={\ae}} \DeclareUnicodeCharacter{01E6}{\v{G}} \DeclareUnicodeCharacter{01E7}{\v{g}} \DeclareUnicodeCharacter{01E8}{\v{K}} \DeclareUnicodeCharacter{01E9}{\v{k}} \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}} \DeclareUnicodeCharacter{01F1}{DZ} \DeclareUnicodeCharacter{01F2}{Dz} \DeclareUnicodeCharacter{01F3}{dz} \DeclareUnicodeCharacter{01F4}{\'G} \DeclareUnicodeCharacter{01F5}{\'g} \DeclareUnicodeCharacter{01F8}{\`N} \DeclareUnicodeCharacter{01F9}{\`n} \DeclareUnicodeCharacter{01FC}{\'{\AE}} \DeclareUnicodeCharacter{01FD}{\'{\ae}} \DeclareUnicodeCharacter{01FE}{\'{\O}} \DeclareUnicodeCharacter{01FF}{\'{\o}} \DeclareUnicodeCharacter{021E}{\v{H}} \DeclareUnicodeCharacter{021F}{\v{h}} \DeclareUnicodeCharacter{0226}{\dotaccent{A}} \DeclareUnicodeCharacter{0227}{\dotaccent{a}} \DeclareUnicodeCharacter{0228}{\cedilla{E}} \DeclareUnicodeCharacter{0229}{\cedilla{e}} \DeclareUnicodeCharacter{022E}{\dotaccent{O}} \DeclareUnicodeCharacter{022F}{\dotaccent{o}} \DeclareUnicodeCharacter{0232}{\=Y} \DeclareUnicodeCharacter{0233}{\=y} \DeclareUnicodeCharacter{0237}{\dotless{j}} \DeclareUnicodeCharacter{02DB}{\ogonek{ }} \DeclareUnicodeCharacter{1E02}{\dotaccent{B}} \DeclareUnicodeCharacter{1E03}{\dotaccent{b}} \DeclareUnicodeCharacter{1E04}{\udotaccent{B}} \DeclareUnicodeCharacter{1E05}{\udotaccent{b}} \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}} \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}} \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}} \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}} \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}} \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}} \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}} \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}} \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}} \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}} \DeclareUnicodeCharacter{1E20}{\=G} \DeclareUnicodeCharacter{1E21}{\=g} \DeclareUnicodeCharacter{1E22}{\dotaccent{H}} \DeclareUnicodeCharacter{1E23}{\dotaccent{h}} \DeclareUnicodeCharacter{1E24}{\udotaccent{H}} \DeclareUnicodeCharacter{1E25}{\udotaccent{h}} \DeclareUnicodeCharacter{1E26}{\"H} \DeclareUnicodeCharacter{1E27}{\"h} \DeclareUnicodeCharacter{1E30}{\'K} \DeclareUnicodeCharacter{1E31}{\'k} \DeclareUnicodeCharacter{1E32}{\udotaccent{K}} \DeclareUnicodeCharacter{1E33}{\udotaccent{k}} \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}} \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}} \DeclareUnicodeCharacter{1E36}{\udotaccent{L}} \DeclareUnicodeCharacter{1E37}{\udotaccent{l}} \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}} \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}} \DeclareUnicodeCharacter{1E3E}{\'M} \DeclareUnicodeCharacter{1E3F}{\'m} \DeclareUnicodeCharacter{1E40}{\dotaccent{M}} \DeclareUnicodeCharacter{1E41}{\dotaccent{m}} \DeclareUnicodeCharacter{1E42}{\udotaccent{M}} \DeclareUnicodeCharacter{1E43}{\udotaccent{m}} \DeclareUnicodeCharacter{1E44}{\dotaccent{N}} \DeclareUnicodeCharacter{1E45}{\dotaccent{n}} \DeclareUnicodeCharacter{1E46}{\udotaccent{N}} \DeclareUnicodeCharacter{1E47}{\udotaccent{n}} \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}} \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}} \DeclareUnicodeCharacter{1E54}{\'P} \DeclareUnicodeCharacter{1E55}{\'p} \DeclareUnicodeCharacter{1E56}{\dotaccent{P}} \DeclareUnicodeCharacter{1E57}{\dotaccent{p}} \DeclareUnicodeCharacter{1E58}{\dotaccent{R}} \DeclareUnicodeCharacter{1E59}{\dotaccent{r}} \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}} \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}} \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}} \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}} \DeclareUnicodeCharacter{1E60}{\dotaccent{S}} \DeclareUnicodeCharacter{1E61}{\dotaccent{s}} \DeclareUnicodeCharacter{1E62}{\udotaccent{S}} \DeclareUnicodeCharacter{1E63}{\udotaccent{s}} \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}} \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}} \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}} \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}} \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}} \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}} \DeclareUnicodeCharacter{1E7C}{\~V} \DeclareUnicodeCharacter{1E7D}{\~v} \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}} \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}} \DeclareUnicodeCharacter{1E80}{\`W} \DeclareUnicodeCharacter{1E81}{\`w} \DeclareUnicodeCharacter{1E82}{\'W} \DeclareUnicodeCharacter{1E83}{\'w} \DeclareUnicodeCharacter{1E84}{\"W} \DeclareUnicodeCharacter{1E85}{\"w} \DeclareUnicodeCharacter{1E86}{\dotaccent{W}} \DeclareUnicodeCharacter{1E87}{\dotaccent{w}} \DeclareUnicodeCharacter{1E88}{\udotaccent{W}} \DeclareUnicodeCharacter{1E89}{\udotaccent{w}} \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}} \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}} \DeclareUnicodeCharacter{1E8C}{\"X} \DeclareUnicodeCharacter{1E8D}{\"x} \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}} \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}} \DeclareUnicodeCharacter{1E90}{\^Z} \DeclareUnicodeCharacter{1E91}{\^z} \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}} \DeclareUnicodeCharacter{1E93}{\udotaccent{z}} \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}} \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}} \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}} \DeclareUnicodeCharacter{1E97}{\"t} \DeclareUnicodeCharacter{1E98}{\ringaccent{w}} \DeclareUnicodeCharacter{1E99}{\ringaccent{y}} \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}} \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}} \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}} \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}} \DeclareUnicodeCharacter{1EBC}{\~E} \DeclareUnicodeCharacter{1EBD}{\~e} \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}} \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}} \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}} \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}} \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}} \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}} \DeclareUnicodeCharacter{1EF2}{\`Y} \DeclareUnicodeCharacter{1EF3}{\`y} \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}} \DeclareUnicodeCharacter{1EF8}{\~Y} \DeclareUnicodeCharacter{1EF9}{\~y} \DeclareUnicodeCharacter{2013}{--} \DeclareUnicodeCharacter{2014}{---} \DeclareUnicodeCharacter{2018}{\quoteleft} \DeclareUnicodeCharacter{2019}{\quoteright} \DeclareUnicodeCharacter{201A}{\quotesinglbase} \DeclareUnicodeCharacter{201C}{\quotedblleft} \DeclareUnicodeCharacter{201D}{\quotedblright} \DeclareUnicodeCharacter{201E}{\quotedblbase} \DeclareUnicodeCharacter{2022}{\bullet} \DeclareUnicodeCharacter{2026}{\dots} \DeclareUnicodeCharacter{2039}{\guilsinglleft} \DeclareUnicodeCharacter{203A}{\guilsinglright} \DeclareUnicodeCharacter{20AC}{\euro} \DeclareUnicodeCharacter{2192}{\expansion} \DeclareUnicodeCharacter{21D2}{\result} \DeclareUnicodeCharacter{2212}{\minus} \DeclareUnicodeCharacter{2217}{\point} \DeclareUnicodeCharacter{2261}{\equiv} }% end of \utfeightchardefs % US-ASCII character definitions. \def\asciichardefs{% nothing need be done \relax } % Make non-ASCII characters printable again for compatibility with % existing Texinfo documents that may use them, even without declaring a % document encoding. % \setnonasciicharscatcode \other \message{formatting,} \newdimen\defaultparindent \defaultparindent = 15pt \chapheadingskip = 15pt plus 4pt minus 2pt \secheadingskip = 12pt plus 3pt minus 2pt \subsecheadingskip = 9pt plus 2pt minus 2pt % Prevent underfull vbox error messages. \vbadness = 10000 % Don't be very finicky about underfull hboxes, either. \hbadness = 6666 % Following George Bush, get rid of widows and orphans. \widowpenalty=10000 \clubpenalty=10000 % Use TeX 3.0's \emergencystretch to help line breaking, but if we're % using an old version of TeX, don't do anything. We want the amount of % stretch added to depend on the line length, hence the dependence on % \hsize. We call this whenever the paper size is set. % \def\setemergencystretch{% \ifx\emergencystretch\thisisundefined % Allow us to assign to \emergencystretch anyway. \def\emergencystretch{\dimen0}% \else \emergencystretch = .15\hsize \fi } % Parameters in order: 1) textheight; 2) textwidth; % 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; % 7) physical page height; 8) physical page width. % % We also call \setleading{\textleading}, so the caller should define % \textleading. The caller should also set \parskip. % \def\internalpagesizes#1#2#3#4#5#6#7#8{% \voffset = #3\relax \topskip = #6\relax \splittopskip = \topskip % \vsize = #1\relax \advance\vsize by \topskip \outervsize = \vsize \advance\outervsize by 2\topandbottommargin \pageheight = \vsize % \hsize = #2\relax \outerhsize = \hsize \advance\outerhsize by 0.5in \pagewidth = \hsize % \normaloffset = #4\relax \bindingoffset = #5\relax % \ifpdf \pdfpageheight #7\relax \pdfpagewidth #8\relax % if we don't reset these, they will remain at "1 true in" of % whatever layout pdftex was dumped with. \pdfhorigin = 1 true in \pdfvorigin = 1 true in \fi % \setleading{\textleading} % \parindent = \defaultparindent \setemergencystretch } % @letterpaper (the default). \def\letterpaper{{\globaldefs = 1 \parskip = 3pt plus 2pt minus 1pt \textleading = 13.2pt % % If page is nothing but text, make it come out even. \internalpagesizes{607.2pt}{6in}% that's 46 lines {\voffset}{.25in}% {\bindingoffset}{36pt}% {11in}{8.5in}% }} % Use @smallbook to reset parameters for 7x9.25 trim size. \def\smallbook{{\globaldefs = 1 \parskip = 2pt plus 1pt \textleading = 12pt % \internalpagesizes{7.5in}{5in}% {-.2in}{0in}% {\bindingoffset}{16pt}% {9.25in}{7in}% % \lispnarrowing = 0.3in \tolerance = 700 \hfuzz = 1pt \contentsrightmargin = 0pt \defbodyindent = .5cm }} % Use @smallerbook to reset parameters for 6x9 trim size. % (Just testing, parameters still in flux.) \def\smallerbook{{\globaldefs = 1 \parskip = 1.5pt plus 1pt \textleading = 12pt % \internalpagesizes{7.4in}{4.8in}% {-.2in}{-.4in}% {0pt}{14pt}% {9in}{6in}% % \lispnarrowing = 0.25in \tolerance = 700 \hfuzz = 1pt \contentsrightmargin = 0pt \defbodyindent = .4cm }} % Use @afourpaper to print on European A4 paper. \def\afourpaper{{\globaldefs = 1 \parskip = 3pt plus 2pt minus 1pt \textleading = 13.2pt % % Double-side printing via postscript on Laserjet 4050 % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. % To change the settings for a different printer or situation, adjust % \normaloffset until the front-side and back-side texts align. Then % do the same for \bindingoffset. You can set these for testing in % your texinfo source file like this: % @tex % \global\normaloffset = -6mm % \global\bindingoffset = 10mm % @end tex \internalpagesizes{673.2pt}{160mm}% that's 51 lines {\voffset}{\hoffset}% {\bindingoffset}{44pt}% {297mm}{210mm}% % \tolerance = 700 \hfuzz = 1pt \contentsrightmargin = 0pt \defbodyindent = 5mm }} % Use @afivepaper to print on European A5 paper. % From romildo@urano.iceb.ufop.br, 2 July 2000. % He also recommends making @example and @lisp be small. \def\afivepaper{{\globaldefs = 1 \parskip = 2pt plus 1pt minus 0.1pt \textleading = 12.5pt % \internalpagesizes{160mm}{120mm}% {\voffset}{\hoffset}% {\bindingoffset}{8pt}% {210mm}{148mm}% % \lispnarrowing = 0.2in \tolerance = 800 \hfuzz = 1.2pt \contentsrightmargin = 0pt \defbodyindent = 2mm \tableindent = 12mm }} % A specific text layout, 24x15cm overall, intended for A4 paper. \def\afourlatex{{\globaldefs = 1 \afourpaper \internalpagesizes{237mm}{150mm}% {\voffset}{4.6mm}% {\bindingoffset}{7mm}% {297mm}{210mm}% % % Must explicitly reset to 0 because we call \afourpaper. \globaldefs = 0 }} % Use @afourwide to print on A4 paper in landscape format. \def\afourwide{{\globaldefs = 1 \afourpaper \internalpagesizes{241mm}{165mm}% {\voffset}{-2.95mm}% {\bindingoffset}{7mm}% {297mm}{210mm}% \globaldefs = 0 }} % @pagesizes TEXTHEIGHT[,TEXTWIDTH] % Perhaps we should allow setting the margins, \topskip, \parskip, % and/or leading, also. Or perhaps we should compute them somehow. % \parseargdef\pagesizes{\pagesizesyyy #1,,\finish} \def\pagesizesyyy#1,#2,#3\finish{{% \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi \globaldefs = 1 % \parskip = 3pt plus 2pt minus 1pt \setleading{\textleading}% % \dimen0 = #1\relax \advance\dimen0 by \voffset % \dimen2 = \hsize \advance\dimen2 by \normaloffset % \internalpagesizes{#1}{\hsize}% {\voffset}{\normaloffset}% {\bindingoffset}{44pt}% {\dimen0}{\dimen2}% }} % Set default to letter. % \letterpaper \message{and turning on texinfo input format.} \def^^L{\par} % remove \outer, so ^L can appear in an @comment % DEL is a comment character, in case @c does not suffice. \catcode`\^^? = 14 % Define macros to output various characters with catcode for normal text. \catcode`\"=\other \def\normaldoublequote{"} \catcode`\$=\other \def\normaldollar{$}%$ font-lock fix \catcode`\+=\other \def\normalplus{+} \catcode`\<=\other \def\normalless{<} \catcode`\>=\other \def\normalgreater{>} \catcode`\^=\other \def\normalcaret{^} \catcode`\_=\other \def\normalunderscore{_} \catcode`\|=\other \def\normalverticalbar{|} \catcode`\~=\other \def\normaltilde{~} % This macro is used to make a character print one way in \tt % (where it can probably be output as-is), and another way in other fonts, % where something hairier probably needs to be done. % % #1 is what to print if we are indeed using \tt; #2 is what to print % otherwise. Since all the Computer Modern typewriter fonts have zero % interword stretch (and shrink), and it is reasonable to expect all % typewriter fonts to have this, we can check that font parameter. % \def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} % Same as above, but check for italic font. Actually this also catches % non-italic slanted fonts since it is impossible to distinguish them from % italic fonts. But since this is only used by $ and it uses \sl anyway % this is not a problem. \def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} % Turn off all special characters except @ % (and those which the user can use as if they were ordinary). % Most of these we simply print from the \tt font, but for some, we can % use math or other variants that look better in normal text. \catcode`\"=\active \def\activedoublequote{{\tt\char34}} \let"=\activedoublequote \catcode`\~=\active \def~{{\tt\char126}} \chardef\hat=`\^ \catcode`\^=\active \def^{{\tt \hat}} \catcode`\_=\active \def_{\ifusingtt\normalunderscore\_} \let\realunder=_ % Subroutine for the previous macro. \def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } \catcode`\|=\active \def|{{\tt\char124}} \chardef \less=`\< \catcode`\<=\active \def<{{\tt \less}} \chardef \gtr=`\> \catcode`\>=\active \def>{{\tt \gtr}} \catcode`\+=\active \def+{{\tt \char 43}} \catcode`\$=\active \def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix % If a .fmt file is being used, characters that might appear in a file % name cannot be active until we have parsed the command line. % So turn them off again, and have \everyjob (or @setfilename) turn them on. % \otherifyactive is called near the end of this file. \def\otherifyactive{\catcode`+=\other \catcode`\_=\other} % Used sometimes to turn off (effectively) the active characters even after % parsing them. \def\turnoffactive{% \normalturnoffactive \otherbackslash } \catcode`\@=0 % \backslashcurfont outputs one backslash character in current font, % as in \char`\\. \global\chardef\backslashcurfont=`\\ \global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work % \realbackslash is an actual character `\' with catcode other, and % \doublebackslash is two of them (for the pdf outlines). {\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}} % In texinfo, backslash is an active character; it prints the backslash % in fixed width font. \catcode`\\=\active % @ for escape char from now on. % The story here is that in math mode, the \char of \backslashcurfont % ends up printing the roman \ from the math symbol font (because \char % in math mode uses the \mathcode, and plain.tex sets % \mathcode`\\="026E). It seems better for @backslashchar{} to always % print a typewriter backslash, hence we use an explicit \mathchar, % which is the decimal equivalent of "715c (class 7, e.g., use \fam; % ignored family value; char position "5C). We can't use " for the % usual hex value because it has already been made active. @def@normalbackslash{{@tt @ifmmode @mathchar29020 @else @backslashcurfont @fi}} @let@backslashchar = @normalbackslash % @backslashchar{} is for user documents. % On startup, @fixbackslash assigns: % @let \ = @normalbackslash % \rawbackslash defines an active \ to do \backslashcurfont. % \otherbackslash defines an active \ to be a literal `\' character with % catcode other. We switch back and forth between these. @gdef@rawbackslash{@let\=@backslashcurfont} @gdef@otherbackslash{@let\=@realbackslash} % Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of % the literal character `\'. Also revert - to its normal character, in % case the active - from code has slipped in. % {@catcode`- = @active @gdef@normalturnoffactive{% @let-=@normaldash @let"=@normaldoublequote @let$=@normaldollar %$ font-lock fix @let+=@normalplus @let<=@normalless @let>=@normalgreater @let\=@normalbackslash @let^=@normalcaret @let_=@normalunderscore @let|=@normalverticalbar @let~=@normaltilde @markupsetuplqdefault @markupsetuprqdefault @unsepspaces } } % Make _ and + \other characters, temporarily. % This is canceled by @fixbackslash. @otherifyactive % If a .fmt file is being used, we don't want the `\input texinfo' to show up. % That is what \eatinput is for; after that, the `\' should revert to printing % a backslash. % @gdef@eatinput input texinfo{@fixbackslash} @global@let\ = @eatinput % On the other hand, perhaps the file did not have a `\input texinfo'. Then % the first `\' in the file would cause an error. This macro tries to fix % that, assuming it is called before the first `\' could plausibly occur. % Also turn back on active characters that might appear in the input % file name, in case not using a pre-dumped format. % @gdef@fixbackslash{% @ifx\@eatinput @let\ = @normalbackslash @fi @catcode`+=@active @catcode`@_=@active } % Say @foo, not \foo, in error messages. @escapechar = `@@ % These (along with & and #) are made active for url-breaking, so need % active definitions as the normal characters. @def@normaldot{.} @def@normalquest{?} @def@normalslash{/} % These look ok in all fonts, so just make them not special. % @hashchar{} gets its own user-level command, because of #line. @catcode`@& = @other @def@normalamp{&} @catcode`@# = @other @def@normalhash{#} @catcode`@% = @other @def@normalpercent{%} @let @hashchar = @normalhash @c Finally, make ` and ' active, so that txicodequoteundirected and @c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we @c don't make ` and ' active, @code will not get them as active chars. @c Do this last of all since we use ` in the previous @catcode assignments. @catcode`@'=@active @catcode`@`=@active @markupsetuplqdefault @markupsetuprqdefault @c Local variables: @c eval: (add-hook 'write-file-hooks 'time-stamp) @c page-delimiter: "^\\\\message" @c time-stamp-start: "def\\\\texinfoversion{" @c time-stamp-format: "%:y-%02m-%02d.%02H" @c time-stamp-end: "}" @c End: @c vim:sw=2: @ignore arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115 @end ignore mu-0.9.18/guile/Makefile.in0000644000175000017500000011432013021065705012325 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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 = : build_triplet = @build@ host_triplet = @host@ subdir = guile ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/perlmod.m4 $(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 = texi.texi CONFIG_CLEAN_VPATH_FILES = 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; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(infodir)" \ "$(DESTDIR)$(scmdir)" LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = libguile_mu_la_DEPENDENCIES = ${top_builddir}/lib/libmu.la \ $(am__DEPENDENCIES_1) am_libguile_mu_la_OBJECTS = mu-guile.lo mu-guile-message.lo libguile_mu_la_OBJECTS = $(am_libguile_mu_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libguile_mu_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libguile_mu_la_LDFLAGS) $(LDFLAGS) -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__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=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 = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=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 = $(libguile_mu_la_SOURCES) DIST_SOURCES = $(libguile_mu_la_SOURCES) AM_V_DVIPS = $(am__v_DVIPS_@AM_V@) am__v_DVIPS_ = $(am__v_DVIPS_@AM_DEFAULT_V@) am__v_DVIPS_0 = @echo " DVIPS " $@; am__v_DVIPS_1 = AM_V_MAKEINFO = $(am__v_MAKEINFO_@AM_V@) am__v_MAKEINFO_ = $(am__v_MAKEINFO_@AM_DEFAULT_V@) am__v_MAKEINFO_0 = @echo " MAKEINFO" $@; am__v_MAKEINFO_1 = AM_V_INFOHTML = $(am__v_INFOHTML_@AM_V@) am__v_INFOHTML_ = $(am__v_INFOHTML_@AM_DEFAULT_V@) am__v_INFOHTML_0 = @echo " INFOHTML" $@; am__v_INFOHTML_1 = AM_V_TEXI2DVI = $(am__v_TEXI2DVI_@AM_V@) am__v_TEXI2DVI_ = $(am__v_TEXI2DVI_@AM_DEFAULT_V@) am__v_TEXI2DVI_0 = @echo " TEXI2DVI" $@; am__v_TEXI2DVI_1 = AM_V_TEXI2PDF = $(am__v_TEXI2PDF_@AM_V@) am__v_TEXI2PDF_ = $(am__v_TEXI2PDF_@AM_DEFAULT_V@) am__v_TEXI2PDF_0 = @echo " TEXI2PDF" $@; am__v_TEXI2PDF_1 = AM_V_texinfo = $(am__v_texinfo_@AM_V@) am__v_texinfo_ = $(am__v_texinfo_@AM_DEFAULT_V@) am__v_texinfo_0 = -q am__v_texinfo_1 = AM_V_texidevnull = $(am__v_texidevnull_@AM_V@) am__v_texidevnull_ = $(am__v_texidevnull_@AM_DEFAULT_V@) am__v_texidevnull_0 = > /dev/null am__v_texidevnull_1 = INFO_DEPS = $(srcdir)/mu-guile.info am__TEXINFO_TEX_DIR = $(srcdir) DVIS = mu-guile.dvi PDFS = mu-guile.pdf PSS = mu-guile.ps HTMLS = mu-guile.html TEXINFOS = mu-guile.texi TEXI2DVI = texi2dvi TEXI2PDF = $(TEXI2DVI) --pdf --batch MAKEINFOHTML = $(MAKEINFO) --html AM_MAKEINFOHTMLFLAGS = $(AM_MAKEINFOFLAGS) DVIPS = dvips 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 DATA = $(scm_DATA) 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 \ distdir 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 DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(mu_guile_TEXINFOS) $(srcdir)/Makefile.in \ $(srcdir)/texi.texi.in $(top_srcdir)/depcomp \ $(top_srcdir)/gtest.mk texinfo.tex DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) 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" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMACS = @EMACS@ EMACSLOADPATH = @EMACSLOADPATH@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GMIME_CFLAGS = @GMIME_CFLAGS@ GMIME_LIBS = @GMIME_LIBS@ GREP = @GREP@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ GUILE_BINARY = @GUILE_BINARY@ GUILE_CFLAGS = @GUILE_CFLAGS@ GUILE_LIBS = @GUILE_LIBS@ GUILE_SITEDIR = @GUILE_SITEDIR@ GUILE_SNARF = @GUILE_SNARF@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MU_DOC_DIR = @MU_DOC_DIR@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PMCCABE = @PMCCABE@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SORT = @SORT@ STRIP = @STRIP@ VERSION = @VERSION@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ XAPIAN_CONFIG = @XAPIAN_CONFIG@ XAPIAN_CXXFLAGS = @XAPIAN_CXXFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ 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 = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_makeinfo = @have_makeinfo@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ lispdir = @lispdir@ 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@ TEST_PROGS = # note, we need top_builddir for snarfing with 'make distcheck' (ie., # with separate builddir) SUBDIRS = . mu scripts examples tests AM_CPPFLAGS = -I. -I${top_builddir} -I${top_srcdir}/lib ${GUILE_CFLAGS} ${GLIB_CFLAGS} # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS = -Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement AM_CXXFLAGS = -Wall -Wextra -Wno-unused-parameter lib_LTLIBRARIES = \ libguile-mu.la libguile_mu_la_SOURCES = \ mu-guile.c \ mu-guile.h \ mu-guile-message.c \ mu-guile-message.h libguile_mu_la_LIBADD = \ ${top_builddir}/lib/libmu.la \ ${GUILE_LIBS} libguile_mu_la_LDFLAGS = -export-dynamic XFILES = \ mu-guile.x \ mu-guile-message.x info_TEXINFOS = \ mu-guile.texi mu_guile_TEXINFOS = \ fdl.texi BUILT_SOURCES = $(XFILES) snarfcppopts = $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(CFLAGS) $(AM_CPPFLAGS) SUFFIXES = .x .doc # FIXME: GUILE_SITEDIR would be better, but that # breaks 'make distcheck' scmdir = ${prefix}/share/guile/site/2.0/ scm_DATA = mu.scm EXTRA_DIST = $(scm_DATA) MKDEP = $(CC) -M -MG $(snarfcppopts) DISTCLEANFILES = $(XFILES) all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: .SUFFIXES: .x .doc .c .dvi .html .info .lo .o .obj .pdf .ps .texi $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtest.mk $(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) --gnu guile/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu guile/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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtest.mk $(am__empty): $(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): texi.texi: $(top_builddir)/config.status $(srcdir)/texi.texi.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libguile-mu.la: $(libguile_mu_la_OBJECTS) $(libguile_mu_la_DEPENDENCIES) $(EXTRA_libguile_mu_la_DEPENDENCIES) $(AM_V_CCLD)$(libguile_mu_la_LINK) -rpath $(libdir) $(libguile_mu_la_OBJECTS) $(libguile_mu_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-guile-message.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-guile.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs .texi.info: $(AM_V_MAKEINFO)restore=: && backupdir="$(am__leading_dot)am$$$$" && \ am__cwd=`pwd` && $(am__cd) $(srcdir) && \ rm -rf $$backupdir && mkdir $$backupdir && \ if ($(MAKEINFO) --version) >/dev/null 2>&1; then \ for f in $@ $@-[0-9] $@-[0-9][0-9] $(@:.info=).i[0-9] $(@:.info=).i[0-9][0-9]; do \ if test -f $$f; then mv $$f $$backupdir; restore=mv; else :; fi; \ done; \ else :; fi && \ cd "$$am__cwd"; \ if $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ -o $@ $<; \ then \ rc=0; \ $(am__cd) $(srcdir); \ else \ rc=$$?; \ $(am__cd) $(srcdir) && \ $$restore $$backupdir/* `echo "./$@" | sed 's|[^/]*$$||'`; \ fi; \ rm -rf $$backupdir; exit $$rc .texi.dvi: $(AM_V_TEXI2DVI)TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ $(TEXI2DVI) $(AM_V_texinfo) --build-dir=$(@:.dvi=.t2d) -o $@ $(AM_V_texidevnull) \ $< .texi.pdf: $(AM_V_TEXI2PDF)TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ $(TEXI2PDF) $(AM_V_texinfo) --build-dir=$(@:.pdf=.t2p) -o $@ $(AM_V_texidevnull) \ $< .texi.html: $(AM_V_MAKEINFO)rm -rf $(@:.html=.htp) $(AM_V_at)if $(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ -o $(@:.html=.htp) $<; \ then \ rm -rf $@ && mv $(@:.html=.htp) $@; \ else \ rm -rf $(@:.html=.htp); exit 1; \ fi $(srcdir)/mu-guile.info: mu-guile.texi $(mu_guile_TEXINFOS) mu-guile.dvi: mu-guile.texi $(mu_guile_TEXINFOS) mu-guile.pdf: mu-guile.texi $(mu_guile_TEXINFOS) mu-guile.html: mu-guile.texi $(mu_guile_TEXINFOS) .dvi.ps: $(AM_V_DVIPS)TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ $(DVIPS) $(AM_V_texinfo) -o $@ $< uninstall-dvi-am: @$(NORMAL_UNINSTALL) @list='$(DVIS)'; test -n "$(dvidir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(dvidir)/$$f'"; \ rm -f "$(DESTDIR)$(dvidir)/$$f"; \ done uninstall-html-am: @$(NORMAL_UNINSTALL) @list='$(HTMLS)'; test -n "$(htmldir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " rm -rf '$(DESTDIR)$(htmldir)/$$f'"; \ rm -rf "$(DESTDIR)$(htmldir)/$$f"; \ done uninstall-info-am: @$(PRE_UNINSTALL) @if test -d '$(DESTDIR)$(infodir)' && $(am__can_run_installinfo); then \ list='$(INFO_DEPS)'; \ for file in $$list; do \ relfile=`echo "$$file" | sed 's|^.*/||'`; \ echo " install-info --info-dir='$(DESTDIR)$(infodir)' --remove '$(DESTDIR)$(infodir)/$$relfile'"; \ if install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$$relfile"; \ then :; else test ! -f "$(DESTDIR)$(infodir)/$$relfile" || exit 1; fi; \ done; \ else :; fi @$(NORMAL_UNINSTALL) @list='$(INFO_DEPS)'; \ for file in $$list; do \ relfile=`echo "$$file" | sed 's|^.*/||'`; \ relfile_i=`echo "$$relfile" | sed 's|\.info$$||;s|$$|.i|'`; \ (if test -d "$(DESTDIR)$(infodir)" && cd "$(DESTDIR)$(infodir)"; then \ echo " cd '$(DESTDIR)$(infodir)' && rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]"; \ rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]; \ else :; fi); \ done uninstall-pdf-am: @$(NORMAL_UNINSTALL) @list='$(PDFS)'; test -n "$(pdfdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(pdfdir)/$$f'"; \ rm -f "$(DESTDIR)$(pdfdir)/$$f"; \ done uninstall-ps-am: @$(NORMAL_UNINSTALL) @list='$(PSS)'; test -n "$(psdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(psdir)/$$f'"; \ rm -f "$(DESTDIR)$(psdir)/$$f"; \ done dist-info: $(INFO_DEPS) @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ list='$(INFO_DEPS)'; \ for base in $$list; do \ case $$base in \ $(srcdir)/*) base=`echo "$$base" | sed "s|^$$srcdirstrip/||"`;; \ esac; \ if test -f $$base; then d=.; else d=$(srcdir); fi; \ base_i=`echo "$$base" | sed 's|\.info$$||;s|$$|.i|'`; \ for file in $$d/$$base $$d/$$base-[0-9] $$d/$$base-[0-9][0-9] $$d/$$base_i[0-9] $$d/$$base_i[0-9][0-9]; do \ if test -f $$file; then \ relfile=`expr "$$file" : "$$d/\(.*\)"`; \ test -f "$(distdir)/$$relfile" || \ cp -p $$file "$(distdir)/$$relfile"; \ else :; fi; \ done; \ done mostlyclean-aminfo: -rm -rf mu-guile.t2d mu-guile.t2p clean-aminfo: -test -z "mu-guile.dvi mu-guile.pdf mu-guile.ps mu-guile.html" \ || rm -rf mu-guile.dvi mu-guile.pdf mu-guile.ps mu-guile.html maintainer-clean-aminfo: @list='$(INFO_DEPS)'; for i in $$list; do \ i_i=`echo "$$i" | sed 's|\.info$$||;s|$$|.i|'`; \ echo " rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]"; \ rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]; \ done install-scmDATA: $(scm_DATA) @$(NORMAL_INSTALL) @list='$(scm_DATA)'; test -n "$(scmdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(scmdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(scmdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(scmdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(scmdir)" || exit $$?; \ done uninstall-scmDATA: @$(NORMAL_UNINSTALL) @list='$(scm_DATA)'; test -n "$(scmdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(scmdir)'; $(am__uninstall_files_from_dir) # 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" 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 distdir: $(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 @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 $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-info check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-recursive all-am: Makefile $(INFO_DEPS) $(LTLIBRARIES) $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(infodir)" "$(DESTDIR)$(scmdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) 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) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) 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-recursive clean-am: clean-aminfo clean-generic clean-libLTLIBRARIES \ clean-libtool mostlyclean-am distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: $(DVIS) html: html-recursive html-am: $(HTMLS) info: info-recursive info-am: $(INFO_DEPS) install-data-am: install-info-am install-scmDATA install-dvi: install-dvi-recursive install-dvi-am: $(DVIS) @$(NORMAL_INSTALL) @list='$(DVIS)'; test -n "$(dvidir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(dvidir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dvidir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dvidir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(dvidir)" || exit $$?; \ done install-exec-am: install-libLTLIBRARIES install-html: install-html-recursive install-html-am: $(HTMLS) @$(NORMAL_INSTALL) @list='$(HTMLS)'; list2=; test -n "$(htmldir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)'"; \ $(MKDIR_P) "$(DESTDIR)$(htmldir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p" || test -d "$$p"; then d=; else d="$(srcdir)/"; fi; \ $(am__strip_dir) \ d2=$$d$$p; \ if test -d "$$d2"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)/$$f'"; \ $(MKDIR_P) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \ echo " $(INSTALL_DATA) '$$d2'/* '$(DESTDIR)$(htmldir)/$$f'"; \ $(INSTALL_DATA) "$$d2"/* "$(DESTDIR)$(htmldir)/$$f" || exit $$?; \ else \ list2="$$list2 $$d2"; \ fi; \ done; \ test -z "$$list2" || { echo "$$list2" | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \ done; } install-info: install-info-recursive install-info-am: $(INFO_DEPS) @$(NORMAL_INSTALL) @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ list='$(INFO_DEPS)'; test -n "$(infodir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(infodir)'"; \ $(MKDIR_P) "$(DESTDIR)$(infodir)" || exit 1; \ fi; \ for file in $$list; do \ case $$file in \ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ esac; \ if test -f $$file; then d=.; else d=$(srcdir); fi; \ file_i=`echo "$$file" | sed 's|\.info$$||;s|$$|.i|'`; \ for ifile in $$d/$$file $$d/$$file-[0-9] $$d/$$file-[0-9][0-9] \ $$d/$$file_i[0-9] $$d/$$file_i[0-9][0-9] ; do \ if test -f $$ifile; then \ echo "$$ifile"; \ else : ; fi; \ done; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(infodir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(infodir)" || exit $$?; done @$(POST_INSTALL) @if $(am__can_run_installinfo); then \ list='$(INFO_DEPS)'; test -n "$(infodir)" || list=; \ for file in $$list; do \ relfile=`echo "$$file" | sed 's|^.*/||'`; \ echo " install-info --info-dir='$(DESTDIR)$(infodir)' '$(DESTDIR)$(infodir)/$$relfile'";\ install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$$relfile" || :;\ done; \ else : ; fi install-man: install-pdf: install-pdf-recursive install-pdf-am: $(PDFS) @$(NORMAL_INSTALL) @list='$(PDFS)'; test -n "$(pdfdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pdfdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pdfdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pdfdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pdfdir)" || exit $$?; done install-ps: install-ps-recursive install-ps-am: $(PSS) @$(NORMAL_INSTALL) @list='$(PSS)'; test -n "$(psdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(psdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(psdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(psdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(psdir)" || exit $$?; done installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-aminfo \ maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-aminfo mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: $(PDFS) ps: ps-recursive ps-am: $(PSS) uninstall-am: uninstall-dvi-am uninstall-html-am uninstall-info-am \ uninstall-libLTLIBRARIES uninstall-pdf-am uninstall-ps-am \ uninstall-scmDATA .MAKE: $(am__recursive_targets) all check install install-am \ install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-aminfo clean-generic clean-libLTLIBRARIES \ clean-libtool cscopelist-am ctags ctags-am dist-info distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir 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-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-scmDATA install-strip \ installcheck installcheck-am installdirs installdirs-am \ maintainer-clean maintainer-clean-aminfo \ maintainer-clean-generic mostlyclean mostlyclean-aminfo \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-dvi-am uninstall-html-am uninstall-info-am \ uninstall-libLTLIBRARIES uninstall-pdf-am uninstall-ps-am \ uninstall-scmDATA .PRECIOUS: Makefile # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof .c.x: $(GUILE_SNARF) -o $@ $< $(snarfcppopts) # 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: mu-0.9.18/guile/tests/0000755000175000017500000000000013021065721011477 500000000000000mu-0.9.18/guile/tests/Makefile.in0000644000175000017500000005411213021065705013471 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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@ # Copyright (C) 2008-2013 Dirk-Jan C. Binnema 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 = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = guile/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/perlmod.m4 $(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__EXEEXT_1 = test-mu-guile$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_mu_guile_OBJECTS = test-mu-guile.$(OBJEXT) dummy.$(OBJEXT) test_mu_guile_OBJECTS = $(am_test_mu_guile_OBJECTS) test_mu_guile_DEPENDENCIES = \ ${top_builddir}/lib/tests/libtestmucommon.la AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = 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__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=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 = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=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 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(test_mu_guile_SOURCES) DIST_SOURCES = $(test_mu_guile_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac 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 \ $(top_srcdir)/gtest.mk DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMACS = @EMACS@ EMACSLOADPATH = @EMACSLOADPATH@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GMIME_CFLAGS = @GMIME_CFLAGS@ GMIME_LIBS = @GMIME_LIBS@ GREP = @GREP@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ GUILE_BINARY = @GUILE_BINARY@ GUILE_CFLAGS = @GUILE_CFLAGS@ GUILE_LIBS = @GUILE_LIBS@ GUILE_SITEDIR = @GUILE_SITEDIR@ GUILE_SNARF = @GUILE_SNARF@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MU_DOC_DIR = @MU_DOC_DIR@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PMCCABE = @PMCCABE@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SORT = @SORT@ STRIP = @STRIP@ VERSION = @VERSION@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ XAPIAN_CONFIG = @XAPIAN_CONFIG@ XAPIAN_CXXFLAGS = @XAPIAN_CXXFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ 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 = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_makeinfo = @have_makeinfo@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ lispdir = @lispdir@ 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@ TEST_PROGS = test-mu-guile AM_CPPFLAGS = $(XAPIAN_CXXFLAGS) \ $(GMIME_CFLAGS) \ $(GLIB_CFLAGS) \ -I ${top_srcdir} \ -I ${top_srcdir}/lib \ -I ${top_srcdir}/lib/tests \ -DMU_TESTMAILDIR=\"${top_srcdir}/lib/tests/testdir\" \ -DMU_TESTMAILDIR2=\"${top_srcdir}/lib/tests/testdir2\" \ -DMU_TESTMAILDIR3=\"${top_srcdir}/lib/tests/testdir3\" \ -DMU_PROGRAM=\"${abs_top_builddir}/mu/mu\" \ -DMU_GUILE_MODULE_PATH=\"${abs_top_srcdir}/guile/\" \ -DMU_GUILE_LIBRARY_PATH=\"${abs_top_builddir}/guile/.libs\" \ -DABS_CURDIR=\"${abs_builddir}\" \ -DABS_SRCDIR=\"${abs_srcdir}\" # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS = -Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement AM_CXXFLAGS = -Wall -Wextra -Wno-unused-parameter test_mu_guile_SOURCES = test-mu-guile.c dummy.cc test_mu_guile_LDADD = ${top_builddir}/lib/tests/libtestmucommon.la # we need to use dummy.cc to enforce c++ linking... BUILT_SOURCES = \ dummy.cc EXTRA_DIST = test-mu-guile.scm all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .cc .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtest.mk $(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) --gnu guile/tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu guile/tests/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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtest.mk $(am__empty): $(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): clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-mu-guile$(EXEEXT): $(test_mu_guile_OBJECTS) $(test_mu_guile_DEPENDENCIES) $(EXTRA_test_mu_guile_DEPENDENCIES) @rm -f test-mu-guile$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_mu_guile_OBJECTS) $(test_mu_guile_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-guile.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< .cc.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cc.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cc.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs 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: $(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) installdirs: install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: 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: 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." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-am clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -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-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: all check install install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir 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 maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof dummy.cc: touch dummy.cc # 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: mu-0.9.18/guile/tests/test-mu-guile.c0000644000175000017500000000553313020504331014264 00000000000000/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include "../mu-query.h" #include #include #include #include "test-mu-common.h" #include "mu-store.h" /* tests for the command line interface, uses testdir2 */ static gchar* fill_database (void) { gchar *cmdline, *tmpdir; GError *err; tmpdir = test_mu_common_get_random_tmpdir(); cmdline = g_strdup_printf ("%s index --muhome=%s --maildir=%s" " --quiet", MU_PROGRAM, tmpdir, MU_TESTMAILDIR2); if (g_test_verbose()) g_print ("%s\n", cmdline); err = NULL; if (!g_spawn_command_line_sync (cmdline, NULL, NULL, NULL, &err)) { g_printerr ("Error: %s\n", err ? err->message : "?"); g_assert (0); } g_free (cmdline); return tmpdir; } static void test_something (const char *what) { char *dir, *cmdline; gint result; dir = fill_database (); cmdline = g_strdup_printf ( "LD_LIBRARY_PATH=%s %s -q -L %s -e main %s/test-mu-guile.scm " "--muhome=%s --test=%s", MU_GUILE_LIBRARY_PATH, GUILE_BINARY, MU_GUILE_MODULE_PATH, ABS_SRCDIR, dir, what); if (g_test_verbose ()) g_print ("cmdline: %s\n", cmdline); result = system (cmdline); g_assert (result == 0); g_free (dir); g_free (cmdline); } static void test_mu_guile_queries (void) { test_something ("queries"); } static void test_mu_guile_messages (void) { test_something ("message"); } static void test_mu_guile_stats (void) { test_something ("stats"); } int main (int argc, char *argv[]) { int rv; g_test_init (&argc, &argv, NULL); if (!set_en_us_utf8_locale()) return 0; /* don't error out... */ g_test_add_func ("/guile/queries", test_mu_guile_queries); g_test_add_func ("/guile/message", test_mu_guile_messages); g_test_add_func ("/guile/stats", test_mu_guile_stats); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_LEVEL_WARNING| G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); rv = g_test_run (); return rv; } mu-0.9.18/guile/tests/Makefile.am0000644000175000017500000000370112605152236013461 00000000000000# Copyright (C) 2008-2013 Dirk-Jan C. Binnema ## ## 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, 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. include $(top_srcdir)/gtest.mk AM_CPPFLAGS=$(XAPIAN_CXXFLAGS) \ $(GMIME_CFLAGS) \ $(GLIB_CFLAGS) \ -I ${top_srcdir} \ -I ${top_srcdir}/lib \ -I ${top_srcdir}/lib/tests \ -DMU_TESTMAILDIR=\"${top_srcdir}/lib/tests/testdir\" \ -DMU_TESTMAILDIR2=\"${top_srcdir}/lib/tests/testdir2\" \ -DMU_TESTMAILDIR3=\"${top_srcdir}/lib/tests/testdir3\" \ -DMU_PROGRAM=\"${abs_top_builddir}/mu/mu\" \ -DMU_GUILE_MODULE_PATH=\"${abs_top_srcdir}/guile/\" \ -DMU_GUILE_LIBRARY_PATH=\"${abs_top_builddir}/guile/.libs\" \ -DABS_CURDIR=\"${abs_builddir}\" \ -DABS_SRCDIR=\"${abs_srcdir}\" # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter noinst_PROGRAMS= $(TEST_PROGS) TEST_PROGS += test-mu-guile test_mu_guile_SOURCES= test-mu-guile.c dummy.cc test_mu_guile_LDADD=${top_builddir}/lib/tests/libtestmucommon.la # we need to use dummy.cc to enforce c++ linking... BUILT_SOURCES= \ dummy.cc dummy.cc: touch dummy.cc EXTRA_DIST=test-mu-guile.scm mu-0.9.18/guile/tests/test-mu-guile.scm0000755000175000017500000001041613020504331014623 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema ;; ;; 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, 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. (setlocale LC_ALL "") (use-modules (srfi srfi-1)) (use-modules (ice-9 getopt-long) (ice-9 optargs) (ice-9 popen) (ice-9 format)) (use-modules (mu) (mu stats)) (define (n-results-or-exit query n) "Run QUERY, and exit 1 if the number of results != N." (let ((lst (mu:message-list query))) (if (not (= (length lst) n)) (begin (simple-format (current-error-port) "Query: \"~A\"; expected ~A, got ~A\n" query n (length lst)) (exit 1))))) (define (test-queries) "Test a bunch of queries (or die trying)." (n-results-or-exit "hello" 1) (n-results-or-exit "f:john fruit" 1) (n-results-or-exit "f:soc@example.com" 1) (n-results-or-exit "t:alki@example.com" 1) (n-results-or-exit "t:alcibiades" 1) (n-results-or-exit "f:soc@example.com OR f:john" 2) (n-results-or-exit "f:soc@example.com OR f:john OR t:edmond" 3) (n-results-or-exit "t:julius" 1) (n-results-or-exit "s:dude" 1) (n-results-or-exit "t:dantès" 1) (n-results-or-exit "file:sittingbull.jpg" 1) (n-results-or-exit "file:custer.jpg" 1) (n-results-or-exit "file:custer.*" 1) (n-results-or-exit "j:sit*" 1) (n-results-or-exit "mime:image/jpeg" 1) (n-results-or-exit "mime:text/plain" 13) (n-results-or-exit "y:text*" 13) (n-results-or-exit "y:image*" 1) (n-results-or-exit "mime:message/rfc822" 2)) (define (error-exit msg . args) "Print error and exit." (let ((msg (apply format #f msg args))) (simple-format (current-error-port) "*ERROR*: ~A\n" msg) (exit 1))) (define (str-equal-or-exit got exp) "S1 == S2 or exit 1." ;; (format #t "'~A' <=> '~A'\n" s1 s2) (if (not (string= exp got)) (error-exit "Expected \"~A\", got \"~A\"\n" exp got))) (define (test-message) "Test functions for a particular message." (let ((msg (car (mu:message-list "hello")))) (str-equal-or-exit (mu:subject msg) "Fwd: rfc822") (str-equal-or-exit (mu:to msg) "martin") (str-equal-or-exit (mu:from msg) "foobar ") (str-equal-or-exit (mu:header msg "X-Mailer") "Ximian Evolution 1.4.5") (if (not (equal? (mu:priority msg) mu:prio:normal)) (error-exit "Expected ~A, got ~A" (mu:priority msg) mu:prio:normal))) (let ((msg (car (mu:message-list "atoms")))) (str-equal-or-exit (mu:subject msg) "atoms") (str-equal-or-exit (mu:to msg) "Democritus ") (str-equal-or-exit (mu:from msg) "\"Richard P. Feynman\" ") (str-equal-or-exit (mu:header msg "Content-transfer-encoding") "7BIT") (if (not (equal? (mu:priority msg) mu:prio:high)) (error-exit "Expected ~a, got ~a" (mu:priority msg) mu:prio:high)))) (define (num-equal-or-exit got exp) "S1 == S2 or exit 1." ;; (format #t "'~A' <=> '~A'\n" s1 s2) (if (not (= exp got)) (error-exit "Expected \"~S\", got \"~S\"\n" exp got))) (define (test-stats) "Test statistical functions." ;; average (num-equal-or-exit (mu:average mu:size) 82152/13) (num-equal-or-exit (floor (mu:stddev mu:size)) 13020.0) (num-equal-or-exit (mu:max mu:size) 46308) (num-equal-or-exit (mu:min mu:size) 111)) (define (main args) (let* ((optionspec '((muhome (value #t)) (test (value #t)))) (options (getopt-long args optionspec)) (muhome (option-ref options 'muhome #f)) (test (option-ref options 'test #f))) (mu:initialize muhome) (if test (cond ((string= test "queries") (test-queries)) ((string= test "message") (test-message)) ((string= test "stats") (test-stats)) (#t (exit 1)))))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/tests/dummy.cc0000644000175000017500000000000012605152317013054 00000000000000mu-0.9.18/guile/scripts/0000755000175000017500000000000013021065721012024 500000000000000mu-0.9.18/guile/scripts/Makefile.in0000644000175000017500000004064513021065705014024 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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 = : build_triplet = @build@ host_triplet = @host@ subdir = guile/scripts ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/perlmod.m4 $(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__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; }; \ } am__installdirs = "$(DESTDIR)$(muguiledistscriptdir)" SCRIPTS = $(muguiledistscript_SCRIPTS) 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 = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/gtest.mk DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMACS = @EMACS@ EMACSLOADPATH = @EMACSLOADPATH@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GMIME_CFLAGS = @GMIME_CFLAGS@ GMIME_LIBS = @GMIME_LIBS@ GREP = @GREP@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ GUILE_BINARY = @GUILE_BINARY@ GUILE_CFLAGS = @GUILE_CFLAGS@ GUILE_LIBS = @GUILE_LIBS@ GUILE_SITEDIR = @GUILE_SITEDIR@ GUILE_SNARF = @GUILE_SNARF@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MU_DOC_DIR = @MU_DOC_DIR@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PMCCABE = @PMCCABE@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SORT = @SORT@ STRIP = @STRIP@ VERSION = @VERSION@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ XAPIAN_CONFIG = @XAPIAN_CONFIG@ XAPIAN_CXXFLAGS = @XAPIAN_CXXFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ 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 = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_makeinfo = @have_makeinfo@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ lispdir = @lispdir@ 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@ TEST_PROGS = EXTRA_DIST = \ msgs-count.scm \ msgs-per-year.scm \ msgs-per-hour.scm \ msgs-per-month.scm \ msgs-per-day.scm \ msgs-per-year-month.scm \ find-dups.scm muguiledistscriptdir = $(pkgdatadir)/scripts/ muguiledistscript_SCRIPTS = $(EXTRA_DIST) all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtest.mk $(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) --gnu guile/scripts/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu guile/scripts/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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtest.mk $(am__empty): $(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-muguiledistscriptSCRIPTS: $(muguiledistscript_SCRIPTS) @$(NORMAL_INSTALL) @list='$(muguiledistscript_SCRIPTS)'; test -n "$(muguiledistscriptdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(muguiledistscriptdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(muguiledistscriptdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | 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; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$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_SCRIPT) $$files '$(DESTDIR)$(muguiledistscriptdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(muguiledistscriptdir)$$dir" || exit $$?; \ } \ ; done uninstall-muguiledistscriptSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(muguiledistscript_SCRIPTS)'; test -n "$(muguiledistscriptdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(muguiledistscriptdir)'; $(am__uninstall_files_from_dir) mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(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: check-am all-am: Makefile $(SCRIPTS) installdirs: for dir in "$(DESTDIR)$(muguiledistscriptdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: 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: 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-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-muguiledistscriptSCRIPTS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: 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 Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-muguiledistscriptSCRIPTS .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir 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-muguiledistscriptSCRIPTS install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-muguiledistscriptSCRIPTS .PRECIOUS: Makefile # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof # 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: mu-0.9.18/guile/scripts/msgs-per-year.scm0000755000175000017500000000332013020504331015136 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema ;; ;; 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, 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. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=: limit to messages matching query ;; INFO: --muhome=: path to mu home dir ;; INFO: --output: the output format, such as "png", "wxt" ;; INFO: (depending on the environment) (use-modules (mu) (mu script) (mu stats) (mu plot)) (define (per-year expr output) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. OUTPUT corresponds to the output format, as per gnuplot's 'set terminal'." (mu:plot-histogram (sort (mu:tabulate (lambda (msg) (+ 1900 (tm:year (localtime (mu:date msg))))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per year matching ~a" expr) "Year" "Messages" output)) (define (main args) (mu:run-stats args per-year)) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/scripts/Makefile.am0000644000175000017500000000206713020504331013777 00000000000000## Copyright (C) 2012-2013 Dirk-Jan C. Binnema ## ## 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, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk EXTRA_DIST= \ msgs-count.scm \ msgs-per-year.scm \ msgs-per-hour.scm \ msgs-per-month.scm \ msgs-per-day.scm \ msgs-per-year-month.scm \ find-dups.scm muguiledistscriptdir = $(pkgdatadir)/scripts/ muguiledistscript_SCRIPTS = $(EXTRA_DIST) mu-0.9.18/guile/scripts/msgs-per-day.scm0000755000175000017500000000334013020504331014755 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema ;; ;; 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, 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. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=: limit to messages matching query ;; INFO: --muhome=: path to mu home dir ;; INFO: --output: the output format, such as "png", "wxt" ;; INFO: (depending on the environment) (use-modules (mu) (mu script) (mu stats) (mu plot)) (define (per-day expr output) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. OUTPUT corresponds to the output format, as per gnuplot's 'set terminal'." (mu:plot-histogram (mu:weekday-numbers->names (sort (mu:tabulate (lambda (msg) (tm:wday (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y))))) (format #f "Messages per weekday matching ~a" expr) "Day" "Messages" output)) (define (main args) (mu:run-stats args per-day)) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/scripts/msgs-count.scm0000755000175000017500000000242513020504331014547 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2013 Dirk-Jan C. Binnema ;; ;; 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, 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. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=: limit to messages matching query ;; INFO: --muhome=: path to mu home dir (use-modules (mu) (mu script) (mu stats)) (define (count expr output) "Print the total number of messages matching the query EXPR. OUTPUT is ignored." (display (mu:count expr)) (newline)) (define (main args) (mu:run-stats args count)) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/scripts/msgs-per-month.scm0000755000175000017500000000334113020504331015326 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema ;; ;; 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, 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. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=: limit to messages matching query ;; INFO: --muhome=: path to mu home dir ;; INFO: --output: the output format, such as "png", "wxt" ;; INFO: (depending on the environment) (use-modules (mu) (mu script) (mu stats) (mu plot)) (define (per-month expr output) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. OUTPUT corresponds to the output format, as per gnuplot's 'set terminal'." (mu:plot-histogram (mu:month-numbers->names (sort (mu:tabulate (lambda (msg) (tm:mon (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y))))) (format #f "Messages per month matching ~a" expr) "Month" "Messages" output)) (define (main args) (mu:run-stats args per-month)) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/scripts/msgs-per-year-month.scm0000755000175000017500000000350013020504331016261 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema ;; ;; 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, 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. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=: limit to messages matching query ;; INFO: --muhome=: path to mu home dir ;; INFO: --output: the output format, such as "png", "wxt" ;; INFO: (depending on the environment) (use-modules (mu) (mu script) (mu stats) (mu plot)) (define (per-year-month expr output) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. OUTPUT corresponds to the output format, as per gnuplot's 'set terminal'." (mu:plot-histogram (sort (mu:tabulate (lambda (msg) (string->number (format #f "~d~2'0d" (+ 1900 (tm:year (localtime (mu:date msg)))) (tm:mon (localtime (mu:date msg)))))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per year/month matching ~a" expr) "Year/Month" "Messages" output)) (define (main args) (mu:run-stats args per-year-month)) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/scripts/msgs-per-hour.scm0000755000175000017500000000330413020504331015155 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema ;; ;; 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, 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. ;; INFO: graph the number of messages per day (using gnuplot) ;; INFO: options: ;; INFO: --query=: limit to messages matching query ;; INFO: --muhome=: path to mu home dir ;; INFO: --output: the output format, such as "png", "wxt" ;; INFO: (depending on the environment) (use-modules (mu) (mu script) (mu stats) (mu plot)) (define (per-hour expr output) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. OUTPUT corresponds to the output format, as per gnuplot's 'set terminal'." (mu:plot-histogram (sort (mu:tabulate (lambda (msg) (tm:hour (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per hour matching ~a" expr) "Hour" "Messages" output)) (define (main args) (mu:run-stats args per-hour)) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/scripts/find-dups.scm0000755000175000017500000000725113020504331014343 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2013-2015 Dirk-Jan C. Binnema ;; ;; 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, 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. ;; INFO: find duplicate messages ;; INFO: options: ;; INFO: --muhome=: path to mu home dir ;; INFO: --delete: delete all but the first one (experimental, be careful!) (use-modules (mu) (mu script) (mu stats)) (use-modules (ice-9 getopt-long) (ice-9 optargs) (ice-9 popen) (ice-9 format) (ice-9 rdelim)) (define (md5sum path) (let* ((port (open-pipe* OPEN_READ "md5sum" path)) (md5 (read-delimited " " port))) (close-pipe port) md5)) (define (find-dups delete expr) (let ((id-table (make-hash-table 20000))) ;; fill the hash with => (mu:for-each-message (lambda (msg) (let* ((id (format #f "~a-~d" (mu:message-id msg) (mu:size msg))) (lst (hash-ref id-table id))) (if lst (set! lst (cons (mu:path msg) lst)) (set! lst (list (mu:path msg)))) (hash-set! id-table id lst))) expr) ;; list all the paths with multiple elements; check the md5sum to ;; make 100%-minus-ε sure they are really the same file. (hash-for-each (lambda (id paths) (if (> (length paths) 1) (let ((hash (make-hash-table 10))) (for-each (lambda (path) (when (file-exists? path) (let* ((md5 (md5sum path)) (lst (hash-ref hash md5))) (if lst (set! lst (cons path lst)) (set! lst (list path))) (hash-set! hash md5 lst)))) paths) ;; hash now maps the md5sum to the messages... (hash-for-each (lambda (md5 mpaths) (if (> (length mpaths) 1) (begin ;;(format #t "md5sum: ~a:\n" md5) (let ((num 1)) (for-each (lambda (path) (if (equal? num 1) (format #t "~a\n" path) (begin (format #t "~a: ~a\n" (if delete "deleting" "dup") path) (if delete (delete-file path)))) (set! num (+ 1 num))) mpaths))))) hash)))) id-table))) (define (main args) "Find duplicate messages and, potentially, delete the dups. Be careful with that! Interpret argument-list ARGS (like command-line arguments). Possible arguments are: --muhome (path to alternative mu home directory). --delete (delete all but the first one). Run mu index afterwards. --expr (expression to constrain search)." (setlocale LC_ALL "") (let* ((optionspec '( (muhome (value #t)) (delete (value #f)) (expr (value #t)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (help (option-ref options 'help #f)) (delete (option-ref options 'delete #f)) (expr (option-ref options 'expr #t)) (muhome (option-ref options 'muhome #f))) (mu:initialize muhome) (find-dups delete expr))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/texi.texi.in0000644000175000017500000000011712605152236012532 00000000000000@c the version for mu for including in texinfo docs @set mu-version @VERSION@ mu-0.9.18/guile/Makefile.am0000644000175000017500000000414112605152236012316 00000000000000## Copyright (C) 2011-2013 Dirk-Jan C. Binnema ## ## 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, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk # note, we need top_builddir for snarfing with 'make distcheck' (ie., # with separate builddir) SUBDIRS= . mu scripts examples tests AM_CPPFLAGS=-I. -I${top_builddir} -I${top_srcdir}/lib ${GUILE_CFLAGS} ${GLIB_CFLAGS} # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter lib_LTLIBRARIES= \ libguile-mu.la libguile_mu_la_SOURCES= \ mu-guile.c \ mu-guile.h \ mu-guile-message.c \ mu-guile-message.h libguile_mu_la_LIBADD= \ ${top_builddir}/lib/libmu.la \ ${GUILE_LIBS} libguile_mu_la_LDFLAGS= -export-dynamic XFILES= \ mu-guile.x \ mu-guile-message.x info_TEXINFOS= \ mu-guile.texi mu_guile_TEXINFOS= \ fdl.texi BUILT_SOURCES=$(XFILES) snarfcppopts= $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(CFLAGS) $(AM_CPPFLAGS) SUFFIXES = .x .doc .c.x: $(GUILE_SNARF) -o $@ $< $(snarfcppopts) # FIXME: GUILE_SITEDIR would be better, but that # breaks 'make distcheck' scmdir=${prefix}/share/guile/site/2.0/ scm_DATA=mu.scm EXTRA_DIST=$(scm_DATA) ## Add -MG to make the .x magic work with auto-dep code. MKDEP = $(CC) -M -MG $(snarfcppopts) DISTCLEANFILES=$(XFILES) mu-0.9.18/guile/mu-guile-message.h0000644000175000017500000000203212605152236013576 00000000000000/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_GUILE_MESSAGE_H__ #define __MU_GUILE_MESSAGE_H__ #include G_BEGIN_DECLS /** * Initialize this mu guile module. * * @param data * * @return */ void* mu_guile_message_init (void *data); G_END_DECLS #endif /*__MU_GUILE_MESSAGE_H__*/ mu-0.9.18/guile/fdl.texi0000644000175000017500000005103012605152236011721 00000000000000@c The GNU Free Documentation License. @center Version 1.2, November 2002 @c This file is intended to be included within another document, @c hence no sectioning command or @node. @display Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, 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. @end display @enumerate 0 @item PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document @dfn{free} in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of ``copyleft'', which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. @item APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The ``Document'', below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as ``you''. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A ``Modified Version'' of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A ``Secondary Section'' is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The ``Invariant Sections'' are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The ``Cover Texts'' are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A ``Transparent'' copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not ``Transparent'' is called ``Opaque''. Examples of suitable formats for Transparent copies include plain @sc{ascii} without markup, Texinfo input format, La@TeX{} input format, @acronym{SGML} or @acronym{XML} using a publicly available @acronym{DTD}, and standard-conforming simple @acronym{HTML}, PostScript or @acronym{PDF} designed for human modification. Examples of transparent image formats include @acronym{PNG}, @acronym{XCF} and @acronym{JPG}. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, @acronym{SGML} or @acronym{XML} for which the @acronym{DTD} and/or processing tools are not generally available, and the machine-generated @acronym{HTML}, PostScript or @acronym{PDF} produced by some word processors for output purposes only. The ``Title Page'' means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, ``Title Page'' means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text. A section ``Entitled XYZ'' means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as ``Acknowledgements'', ``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' of such a section when you modify the Document means that it remains a section ``Entitled XYZ'' according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. @item VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. @item COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. @item MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: @enumerate A @item Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. @item List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. @item State on the Title page the name of the publisher of the Modified Version, as the publisher. @item Preserve all the copyright notices of the Document. @item Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. @item Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. @item Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. @item Include an unaltered copy of this License. @item Preserve the section Entitled ``History'', Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled ``History'' in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. @item Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the ``History'' section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. @item For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. @item Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. @item Delete any section Entitled ``Endorsements''. Such a section may not be included in the Modified Version. @item Do not retitle any existing section to be Entitled ``Endorsements'' or to conflict in title with any Invariant Section. @item Preserve any Warranty Disclaimers. @end enumerate If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section Entitled ``Endorsements'', provided it contains nothing but endorsements of your Modified Version by various parties---for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. @item COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled ``History'' in the various original documents, forming one section Entitled ``History''; likewise combine any sections Entitled ``Acknowledgements'', and any sections Entitled ``Dedications''. You must delete all sections Entitled ``Endorsements.'' @item COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. @item AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an ``aggregate'' if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. @item TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled ``Acknowledgements'', ``Dedications'', or ``History'', the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. @item TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document 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. @item FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See @uref{http://www.gnu.org/copyleft/}. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License ``or any later version'' applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. @end enumerate @page @heading ADDENDUM: How to use this License for your documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: @smallexample @group Copyright (C) @var{year} @var{your name}. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. @end group @end smallexample If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the ``with@dots{}Texts.'' line with this: @smallexample @group with the Invariant Sections being @var{list their titles}, with the Front-Cover Texts being @var{list}, and with the Back-Cover Texts being @var{list}. @end group @end smallexample If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. @c Local Variables: @c ispell-local-pdict: "ispell-dict" @c End: mu-0.9.18/guile/mu/0000755000175000017500000000000013021065721010756 500000000000000mu-0.9.18/guile/mu/Makefile.in0000644000175000017500000003663013021065705012755 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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 = : build_triplet = @build@ host_triplet = @host@ subdir = guile/mu ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/perlmod.m4 $(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_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 = 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; }; \ } am__installdirs = "$(DESTDIR)$(scmdir)" DATA = $(scm_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/gtest.mk README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMACS = @EMACS@ EMACSLOADPATH = @EMACSLOADPATH@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GMIME_CFLAGS = @GMIME_CFLAGS@ GMIME_LIBS = @GMIME_LIBS@ GREP = @GREP@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ GUILE_BINARY = @GUILE_BINARY@ GUILE_CFLAGS = @GUILE_CFLAGS@ GUILE_LIBS = @GUILE_LIBS@ GUILE_SITEDIR = @GUILE_SITEDIR@ GUILE_SNARF = @GUILE_SNARF@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MU_DOC_DIR = @MU_DOC_DIR@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PMCCABE = @PMCCABE@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SORT = @SORT@ STRIP = @STRIP@ VERSION = @VERSION@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ XAPIAN_CONFIG = @XAPIAN_CONFIG@ XAPIAN_CXXFLAGS = @XAPIAN_CXXFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ 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 = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_makeinfo = @have_makeinfo@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ lispdir = @lispdir@ 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@ TEST_PROGS = # FIXME: GUILE_SITEDIR would be better, but that # breaks 'make distcheck' scmdir = ${prefix}/share/guile/site/2.0/mu/ scm_DATA = \ stats.scm \ plot.scm \ script.scm EXTRA_DIST = $(scm_DATA) all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtest.mk $(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) --gnu guile/mu/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu guile/mu/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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtest.mk $(am__empty): $(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): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-scmDATA: $(scm_DATA) @$(NORMAL_INSTALL) @list='$(scm_DATA)'; test -n "$(scmdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(scmdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(scmdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(scmdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(scmdir)" || exit $$?; \ done uninstall-scmDATA: @$(NORMAL_UNINSTALL) @list='$(scm_DATA)'; test -n "$(scmdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(scmdir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(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: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(scmdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: 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: 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-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-scmDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: 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 Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-scmDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir 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-scmDATA install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags-am uninstall uninstall-am uninstall-scmDATA .PRECIOUS: Makefile # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof # 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: mu-0.9.18/guile/mu/Makefile.am0000644000175000017500000000177012605152236012744 00000000000000## Copyright (C) 2008-2013 Dirk-Jan C. Binnema ## ## 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, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk # FIXME: GUILE_SITEDIR would be better, but that # breaks 'make distcheck' scmdir=${prefix}/share/guile/site/2.0/mu/ scm_DATA= \ stats.scm \ plot.scm \ script.scm EXTRA_DIST=$(scm_DATA) mu-0.9.18/guile/mu/script.scm0000644000175000017500000000405213020504331012701 00000000000000;; Copyright (C) 2012-2013 Dirk-Jan C. Binnema ;; ;; 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, 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. (define-module (mu script) :export (mu:run-stats)) (use-modules (ice-9 getopt-long) (ice-9 optargs) (ice-9 popen) (ice-9 format)) (use-modules (mu) (mu stats) (mu plot)) (define (help-and-exit) "Show some help." (display (string-append "usage: script [--help] [--textonly] " "[--muhome=] [--query=") (newline)) (exit 0)) (define (mu:run-stats args func) "Run some statistics function. Interpret argument-list ARGS (like command-line arguments). Possible arguments are: --help (show some help and exit) --muhome (path to alternative mu home directory) --output (a string describing the output, e.g. \"dumb\", \"png\" \"wxt\") searchexpr (a search query) then call FUNC with args SEARCHEXPR and OUTPUT." (setlocale LC_ALL "") (let* ((optionspec '((muhome (value #t)) (query (value #t)) (output (value #f)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (query (option-ref options 'query #f)) (help (option-ref options 'help #f)) (output (option-ref options 'output #f)) (muhome (option-ref options 'muhome #f)) (restargs (option-ref options '() #f))) (if help (help-and-exit)) (mu:initialize muhome) (func (or query "") output))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/mu/README0000644000175000017500000001502312605152236011564 00000000000000* OUTDATED * * README ** What is muile? `muile' is a little experiment/toy using the equally experimental mu guile bindings, to be found in libmuguile/ in the top-level source directory. `guile'[1] is an interpreter/library for the Scheme programming language[2], specifically meant for extending other programs. It is, in fact, the official GNU language for doing so. 'muile' requires guile 2.x to get the full support. Older versions may not support e.g. the 'mu-stats.scm' things discussed below. The combination of mu + guile is called `muile', and allows you to write little Scheme-programs to query the mu-database, or inspect individual messages. It is still in an experimental stage, but useful already. ** How do I get it? The git-version and the future 0.9.7 version of mu will automatically build muile if you have guile. I've been using guile 2.x from git, but installing the 'guile-1.8-dev' package (Ubuntu/Debian) should do the trick. (I only did very minimal testing with guile 1.8 though). Then, configure mu. The configure output should tell you about whether guile was found (and where). If it's found, build mu, and toys/muile should be created, as well. ** What can I do with it? Go to toys/muile and start muile. You'll end up with a guile-shell where you can type scheme [1], it looks something like this (for guile 2.x): ,---- | scheme@(guile-user)> `---- Now, let's load a message (of course, replace with a message on your system): ,---- | scheme@(guile-user)> (define msg (mu:msg:make-from-file "/home/djcb/Maildir/cur/12131e7b20a2:2,S")) `---- This defines a variable 'msg', which holds some message on your file system. It's now easy to inspect this message: ,---- | scheme@(guile-user)> (define msg (mu:msg:make-from-file "/home/djcb/Maildir/cur/12131e7b20a2:2,S")) `---- Now, we can inspect this message a bit: ,---- | scheme@(guile-user)> (mu:msg:subject msg) | $1 = "See me in bikini :-)" | scheme@(guile-user)> (mu:msg:flags msg) | $2 = (mu:attach mu:unread) `---- and so on. Note, it's probably easiest to explore the various mu: methods using autocompletion; to enable that make sure you have ,---- | (use-modules (ice-9 readline)) | (activate-readline) `---- in your ~/.guile configuration. ** does this tool have some parameters? Yes, there is --muhome to set a non-default place for the message database (see the documentation on --muhome in the mu-find manpage). And there is --msg= where you specify some particular message file; it will be available as 'mu:current-msg' in the guile (muile) environment. For example: ,---- | ./muile --msg=~/Maildir/inbox/cur/1311310172_1234:2,S | [...] | scheme@(guile-user)> mu:current-msg | $1 = # | scheme@(guile-user)> (mu:msg:size mu:current-msg) | $2 = 7206 `---- ** What about searching messages in the database? That's easy, too - it does require a little more scheme knowledge. For searching messages there is the mu:store:for-each function, which takes two arguments; the first argument is a function that will be called for each message found. The optional second argument is the search expression (following 'mu find' syntax); if don't provide the argument, all messages match. So how does this work in practice? Let's see I want to see the subject and sender for messages about milk: ,---- | (mu:store:for-each (lambda(msg) (format #t "~s ~s\n" (mu:msg:from msg) (mu:msg:subject msg))) "milk") `---- or slightly more readable: ,---- | (mu:store:for-each | (lambda(msg) | (format #t "~s ~s\n" (mu:msg:from msg) (mu:msg:subject msg))) | "milk") `---- As you can see, I provide an anonymous ('lambda') function which will be called for each message matching 'milk'. Admittedly, this requires a bit of Scheme-knowledge... but this time is good as any to learn this nice language. ** Can I do some statistics on my messages? Yes you can. In fact, it's pretty easy. If you load (in the muile/ directory) the file 'mu-stats.scm': ,---- | (load "mu-stats.scm") `---- you'll get a bunch of functions (with names starting with 'mu:stats') to make this very easy. Let's see, suppose I want to see how many messages I get per weekday: ,---- | scheme@(guile-user)> (mu:stats:per-weekday) | $1 = ((0 . 2255) (1 . 2788) (2 . 2868) (3 . 2599) (4 . 2629) (5 . 2287) (6 . 1851)) `---- Note, Sunday=0, Monday=1 and so on. Apparently, I get/send most of e-mail on Tuesdays, and least on Saturday. And note that mu:stats:per-weekdays takes an optional search expression argument, to limit the results to messages matching that, e.g., to only consider messages related to emacs during this year: ,---- | scheme@(guile-user)> (mu:stats:per-weekday "emacs date:2011..now") | $8 = ((0 . 54) (1 . 22) (2 . 46) (3 . 47) (4 . 39) (5 . 54) (6 . 50)) `---- There's also 'mu:stats:per-month', 'mu:stats:per-year', 'mu:stats:per-hour'. I learnt that during 3-4am I sent/receive only about a third of what I sent during 11-12pm. ** What about getting the top-10 people in the To:-field? Easy. ,---- | scheme@(guile-user)> (mu:stats:top-n-to) | $1 = ((("Abc" "myself@example.com") . 4465) (("Def" "somebodyelse@example.com") . 2114) | (and so on) `---- I've changed the names a bit to protect the innocent, but what the function does is return a list of pairs of ( ) . descending in order of frequency. Note, 'mu:stats:top-n-to' takes two optional arguments - first the 'n' in top-n (default is 10), and seconds as search expression to limit the messages considered. There are also the functions 'mu:stats:top-n-subject' and 'mu:stats:top-n-from' which do the same, mutatis mutandis, and it's quite easy to add your own (see the mu-stats.scm for examples) ** What about showing the results in a table? Even easier. Try: ,---- | (mu:stats:table (mu:stats:top-n-to)) `---- or ,---- | (mu:stats:table (mu:stats:per-weekday)) `---- You can also export the table: ,---- | (mu:stats:export (mu:stats:per-weekday)) `---- which will create a temporary file with the results, for further processing in e.g. 'R' or 'gnuplot'. [1] http://www.gnu.org/s/guile/ [2] http://en.wikipedia.org/wiki/Scheme_(programming_language) # Local Variables: # mode: org; org-startup-folded: nil # End: mu-0.9.18/guile/mu/stats.scm0000644000175000017500000001340412605152236012547 00000000000000;; ;; Copyright (C) 2011-2013 Dirk-Jan C. Binnema ;; ;; 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, 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. (define-module (mu stats) :use-module (oop goops) :use-module (mu) :use-module (srfi srfi-1) :use-module (ice-9 i18n) :use-module (ice-9 r5rs) :export ( mu:tabulate mu:top-n-most-frequent mu:count mu:average mu:stddev mu:correl mu:max mu:min mu:weekday-numbers->names mu:month-numbers->names)) (define* (mu:tabulate func #:optional (expr #t)) "Execute FUNC for each message matching EXPR, and return an alist with maps each result of FUNC to its frequency. If the result of FUNC is a list, add each of its values separately. FUNC is a function takes a instance as its argument. For example, to tabulate messages by weekday, one could use: (mu:tabulate (lambda(msg) (tm:wday (localtime (date msg))))), and get back a list like ((1 . 2) (2 . 5)(3 . 4)(4 . 4)(5 . 12)(6 . 7)(7. 2))." (let* ((table '()) ;; func to add a value to our table (update-table (lambda (val) (let ((old-freq (or (assoc-ref table val) 0))) (set! table (assoc-set! table val (1+ old-freq))))))) (mu:for-each-message (lambda(msg) (let ((val (func msg))) (if (list? val) (for-each update-table val) (update-table val)))) expr) table)) (define* (top-n func less n #:optional (expr #t)) "Take the results of (mu:tabulate FUNC EXPR), sort using LESS (a function taking two arguments A and B (cons cells, (VAL . FREQ)), and returns #t if A < B, #f otherwise), and then take the first N." (take (sort (mu:tabulate func expr) less) n)) (define* (mu:top-n-most-frequent func n #:optional (expr #t)) "Take the results of (mu:tabulate FUNC EXPR), and return the N items with the higest frequency." (top-n func (lambda (a b) (> (cdr a) (cdr b))) n expr)) (define* (mu:count #:optional (expr #t)) "Count the number of messages matching EXPR. If EXPR is not provided, match /all/ messages." (let ((num 0)) (mu:for-each-message (lambda (msg) (set! num (1+ num))) expr) num)) (define (average lst) "Calculate the average of a list LST of numbers, or #f if undefined." (if (null? lst) #f (/ (apply + lst) (length lst)))) (define (stddev lst) "Calculate the standard deviation of a list LST of numbers or #f if undefined." (let* ((avg (average lst)) (sosq (if avg (apply + (map (lambda (x)(* (- x avg) (- x avg))) lst))))) (if sosq (sqrt (/ sosq (length lst)))))) (define* (mu:average func #:optional (expr #t)) "Get the average value of FUNC applied to all messages matching EXPR (or #t for all). Returns #f if undefined." (average (map func (mu:message-list expr)))) (define* (mu:stddev func #:optional (expr #t)) "Get the standard deviation the the values of FUNC applied to all messages matching EXPR (or #t for all). This is the 'population' stddev, not the 'sample' stddev. Returns #f if undefined." (stddev (map func (mu:message-list expr)))) (define* (mu:max func #:optional (expr #t)) "Get the maximum value of FUNC applied to all messages matching EXPR (or #t for all). Returns #f if undefined." (apply max (map func (mu:message-list expr)))) (define* (mu:min func #:optional (expr #t)) "Get the minimum value of FUNC applied to all messages matching EXPR (or #t for all). Returns #f if undefined." (apply min (map func (mu:message-list expr)))) (define (correl lst) "Calculate Pearson's correlation coefficient for a list LST of cons pair, where the car and cdr of the pairs are values from data set 1 and 2, respectively." (let ((n (length lst)) (sx (apply + (map car lst))) (sy (apply + (map cdr lst))) (sxy (apply + (map (lambda (cell) (* (car cell) (cdr cell))) lst))) (sxx (apply + (map (lambda (cell) (* (car cell) (car cell))) lst))) (syy (apply + (map (lambda (cell) (* (cdr cell) (cdr cell))) lst)))) (/ (- (* n sxy) (* sx sy)) (sqrt (* (- (* n sxx) (* sx sx)) (- (* n syy) (* sy sy))))))) (define* (mu:correl func1 func2 #:optional (expr #t)) "Determine Pearson's correlation coefficient between the value for functions FUNC1 and FUNC2 to all messages matching EXPR (or #t for all). Returns #f if undefined." (let ((data (map (lambda (msg) (cons (func1 msg) (func2 msg))) (mu:message-list expr)))) (if data (correl data) #f))) ;; a list of abbreviated, localized day names (define day-names (map locale-day-short (iota 7 1))) (define (mu:weekday-numbers->names table) "Convert a list of pairs with the car denoting a day number (0-6) into a list of pairs with the car replaced by the corresponding day name (abbreviated) for the current locale." (map (lambda (pair) (cons (list-ref day-names (car pair)) (cdr pair))) table)) ;; a list of abbreviated, localized month names (define month-names (map locale-month-short (iota 12 1))) (define (mu:month-numbers->names table) "Convert a list of pairs with the car denoting a month number (0-11) into a list of pairs with the car replaced by the corresponding day name (abbreviated)." (map (lambda (pair) (cons (list-ref month-names (car pair)) (cdr pair))) table)) mu-0.9.18/guile/mu/plot.scm0000644000175000017500000000544613020504331012363 00000000000000;; ;; Copyright (C) 2011-2013 Dirk-Jan C. Binnema ;; ;; 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, 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. (define-module (mu plot) :use-module (mu) :use-module (ice-9 popen) :export ( mu:plot ;; alias for mu:plot-histogram mu:plot-histogram )) (define (export-pairs pairs) "Write a temporary file with the list of PAIRS in table format, and return the file name." (let* ((datafile (tmpnam)) (output (open datafile (logior O_CREAT O_WRONLY) #O0600))) (for-each (lambda(pair) (display (format #f "~a ~a\n" (car pair) (cdr pair)) output)) pairs) (close output) datafile)) (define (find-program-in-path prog) "Find exutable program PROG in PATH; return the full path, or #f if not found." (let* ((path (parse-path (getenv "PATH"))) (progpath (search-path path prog))) (if (not progpath) #f (if (access? progpath X_OK) ;; is progpath #f)))) (define* (mu:plot-histogram data title x-label y-label #:optional (output "dumb") (extra-gnuplot-opts '())) "Plot DATA with TITLE, X-LABEL and X-LABEL using the gnuplot program. DATA is a list of cons-pairs (X . Y). OUTPUT is a string that determines the type of output that gnuplot produces, depending on the system. Which options are available depends on the particulars for the gnuplot installation, but typical examples would be \"dumb\" for text-only display, \"wxterm\" to write to a graphical window, or \"png\" to write a PNG-image to stdout. EXTRA-GNUPLOT-OPTS is a list of any additional options for gnuplot." (if (not (find-program-in-path "gnuplot")) (error "cannot find 'gnuplot' in path")) (let ((datafile (export-pairs data)) (gnuplot (open-pipe "gnuplot -p" OPEN_WRITE))) (display (string-append "reset\n" "set term " (or output "dumb") "\n" "set title \"" title "\"\n" "set xlabel \"" x-label "\"\n" "set ylabel \"" y-label "\"\n" "set boxwidth 0.9\n" (string-join extra-gnuplot-opts "\n") "plot \"" datafile "\" using 2:xticlabels(1) with boxes fs solid\n") gnuplot) (close-pipe gnuplot))) ;; backward compatibility (define mu-plot mu:plot-histogram) mu-0.9.18/guile/examples/0000755000175000017500000000000013021065721012153 500000000000000mu-0.9.18/guile/examples/Makefile.in0000644000175000017500000003226513021065705014152 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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 = : build_triplet = @build@ host_triplet = @host@ subdir = guile/examples ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/perlmod.m4 $(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_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 = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/gtest.mk DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMACS = @EMACS@ EMACSLOADPATH = @EMACSLOADPATH@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GMIME_CFLAGS = @GMIME_CFLAGS@ GMIME_LIBS = @GMIME_LIBS@ GREP = @GREP@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ GUILE_BINARY = @GUILE_BINARY@ GUILE_CFLAGS = @GUILE_CFLAGS@ GUILE_LIBS = @GUILE_LIBS@ GUILE_SITEDIR = @GUILE_SITEDIR@ GUILE_SNARF = @GUILE_SNARF@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MU_DOC_DIR = @MU_DOC_DIR@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PMCCABE = @PMCCABE@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SORT = @SORT@ STRIP = @STRIP@ VERSION = @VERSION@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ XAPIAN_CONFIG = @XAPIAN_CONFIG@ XAPIAN_CXXFLAGS = @XAPIAN_CXXFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ 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 = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_makeinfo = @have_makeinfo@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ lispdir = @lispdir@ 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@ TEST_PROGS = EXTRA_DIST = \ msg-graphs \ contacts-export \ org2mu4e \ mu-biff all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtest.mk $(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) --gnu guile/examples/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu guile/examples/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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtest.mk $(am__empty): $(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): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(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: check-am all-am: Makefile installdirs: install: install-am install-exec: 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: 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-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: 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 Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir 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 \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am .PRECIOUS: Makefile # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof # 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: mu-0.9.18/guile/examples/Makefile.am0000644000175000017500000000160112605152236014132 00000000000000## Copyright (C) 2012-2013 Dirk-Jan C. Binnema ## ## 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, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk EXTRA_DIST= \ msg-graphs \ contacts-export \ org2mu4e \ mu-biff mu-0.9.18/guile/examples/org2mu4e0000755000175000017500000000500412605152236013471 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema ;; ;; 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, 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. (use-modules (ice-9 getopt-long) (ice-9 format)) (use-modules (mu)) (define (display-org-header query) "Print the header for the org-file for QUERY." (format #t "* Messages matching '~a'\n\n" query)) (define (org-mu4e-link msg) "Create a link for this message understandable by org-mu4e." (let* ((subject ;; cleanup subject (string-map (lambda (kar) (if (member kar '(#\] #\[)) #\space kar)) (or (mu:subject msg) "No subject")))) (format #f "[[mu4e:msgid:~a][~s]]" (mu:message-id msg) subject))) (define (display-org-entry msg tag) "Write an org entry for MSG." (format #t "** ~a ~a\n\t~s\n\t~s\n" (org-mu4e-link msg) (if tag (string-concatenate `(":" ,tag "::")) "") (or (mu:from msg) "?") (let ((body (mu:body-txt msg))) (if (not body) ;; get a 'summary' of the body text "" (string-map (lambda (c) (if (or (char=? c #\newline) (char=? c #\return)) #\space c)) (substring body 0 (min (string-length body) 100))))))) (define (main args) (let* ((optionspec '( (muhome (value #t)) (tag (value #t)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (msg (string-append "usage: mu4e-org [--help] [--muhome=] [--tag=] ")) (help (option-ref options 'help #f)) (tag (option-ref options 'tag #f)) (muhome (option-ref options 'muhome #f)) (query (string-concatenate (option-ref options '() '())))) (if help (begin (display msg) (exit 0)) (begin (mu:initialize muhome) (display-org-header query) (mu:for-each-message (lambda (msg) (display-org-entry msg tag)) query))))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/examples/contacts-export0000755000175000017500000000522312605152236015165 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012 Dirk-Jan C. Binnema ;; ;; 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, 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. (use-modules (ice-9 getopt-long) (ice-9 format)) (use-modules (srfi srfi-1)) (use-modules (mu)) (define (sort-by-freq c1 c2) (< (mu:frequency c1) (mu:frequency c2))) (define (sort-by-newness c1 c2) (< (mu:last-seen c1) (mu:last-seen c2))) (define (main args) (let* ((optionspec '( (muhome (value #t)) (sort-by (value #t)) (revert (value #f)) (format (value #t)) (limit (value #t)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (msg (string-append "usage: contacts-export [--help] [--muhome=] " "--format= " "--sort-by= [--revert] [--limit=]\n")) (help (option-ref options 'help #f)) (muhome (option-ref options 'muhome #f)) (sort-by (or (option-ref options 'sort-by #f) "frequency")) (revert (option-ref options 'revert #f)) (form (or (option-ref options 'format #f) "plain")) (limit (string->number (option-ref options 'limit "1000000")))) (if help (begin (display msg) (exit 0)) (begin (setlocale LC_ALL "") (mu:initialize muhome) (let* ((sort-func (cond ((string= sort-by "frequency") sort-by-freq) ((string= sort-by "newness") sort-by-newness) (else (begin (display msg) (exit 1))))) (contacts '())) ;; make a list of all contacts (mu:for-each-contact (lambda (c) (set! contacts (cons c contacts)))) ;; should we sort it? (if sort-by (set! contacts (sort! contacts (if revert (negate sort-func) sort-func)))) ;; should we limit the number? (if (and limit (< limit (length contacts))) (set! contacts (take! contacts limit))) ;; export! (for-each (lambda (c) (format #t "~a\n" (mu:contact->string c form))) contacts)))))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/examples/msg-graphs0000755000175000017500000001074612605152236014106 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema ;; ;; 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, 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. (setlocale LC_ALL "") (use-modules (ice-9 getopt-long) (ice-9 optargs) (ice-9 popen) (ice-9 format)) (use-modules (mu) (mu stats) (mu plot)) ;;(use-modules (mu) (mu message) (mu stats) (mu plot)) (define (per-hour expr plain-text) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. If PLAIN-TEXT is true, use a plain-text display, otherwise, use a graphical window." (mu:plot (sort (mu:tabulate (lambda (msg) (tm:hour (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per hour matching ~a" expr) "Hour" "Messages" plain-text)) (define (per-day expr plain-text) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. If PLAIN-TEXT is true, use a plain-text display, otherwise, use a graphical window." (mu:plot (mu:weekday-numbers->names (sort (mu:tabulate (lambda (msg) (tm:wday (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y))))) (format #f "Messages per weekday matching ~a" expr) "Day" "Messages" plain-text)) (define (per-month expr plain-text) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. If PLAIN-TEXT is true, use a plain-text display, otherwise, use a graphical window." (mu:plot (mu:month-numbers->names (sort (mu:tabulate (lambda (msg) (tm:mon (localtime (mu:date msg)))) expr) (lambda (x y) (< (car x) (car y))))) (format #f "Messages per month matching ~a" expr) "Month" "Messages" plain-text)) (define (per-year-month expr plain-text) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. If PLAIN-TEXT is true, use a plain-text display, otherwise, use a graphical window." (mu:plot (sort (mu:tabulate (lambda (msg) (string->number (format #f "~d~2'0d" (+ 1900 (tm:year (localtime (mu:date msg)))) (tm:mon (localtime (mu:date msg)))))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per year/month matching ~a" expr) "Year/Month" "Messages" plain-text)) (define (per-year expr plain-text) "Count the total number of messages for each weekday (0-6 for Sun..Sat) that match EXPR. If PLAIN-TEXT is true, use a plain-text display, otherwise, use a graphical window." (mu:plot (sort (mu:tabulate (lambda (msg) (+ 1900 (tm:year (localtime (mu:date msg))))) expr) (lambda (x y) (< (car x) (car y)))) (format #f "Messages per year matching ~a" expr) "Year" "Messages" plain-text)) (define (main args) (let* ((optionspec '( (muhome (value #t)) (what (value #t)) (text (value #f)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (msg (string-append "usage: mu-msg-stats [--help] [--text] " "[--muhome=] " "--what= [searchexpr]\n")) (help (option-ref options 'help #f)) (what (option-ref options 'what #f)) (text (option-ref options 'text #f)) (muhome (option-ref options 'muhome #f)) (restargs (option-ref options '() #f)) (expr (if restargs (string-join restargs) ""))) (if (or help (not what)) (begin (display msg) (exit (if help 0 1)))) (mu:initialize muhome) (cond ((string= what "per-hour") (per-hour expr text)) ((string= what "per-day") (per-day expr text)) ((string= what "per-month") (per-month expr text)) ((string= what "per-year-month") (per-year-month expr text)) ((string= what "per-year") (per-year expr text)) (else (begin (display msg) (exit 1)))))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/examples/mu-biff0000755000175000017500000000355312605152236013361 00000000000000#!/bin/sh exec guile -e main -s $0 $@ !# ;; ;; Copyright (C) 2012 Dirk-Jan C. Binnema ;; ;; 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, 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. ;; script to list the message matching which are newer than ;; minutes ;; use it, eg. like: ;; $ mu-biff --newer-than=`date +%s --date='5 minutes ago'` "maildir:/inbox" (use-modules (ice-9 getopt-long) (ice-9 format)) (use-modules (mu)) (define (main args) (let* ((optionspec '((muhome (value #t)) (newer-than (value #t)) (help (single-char #\h) (value #f)))) (options (getopt-long args optionspec)) (msg (string-append "usage: mu-biff [--help] [--muhome=]" " [--newer-than=] ")) (help (option-ref options 'help #f)) (newer-than (string->number (option-ref options 'newer-than "0"))) (muhome (option-ref options 'muhome #f)) (query (string-concatenate (option-ref options '() '())))) (if help (begin (display msg) (newline) (exit 0)) (begin (mu:initialize muhome) (mu:for-each-message (lambda (msg) (if (> (mu:timestamp msg) newer-than) (format #t "~a ~a\n" (mu:from msg) (mu:subject msg)))) query))))) ;; Local Variables: ;; mode: scheme ;; End: mu-0.9.18/guile/mu-guile-message.c0000644000175000017500000003725012605152236013603 00000000000000/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include "mu-guile.h" #include #include #include #include #include /* pseudo field, not in Xapian */ #define MU_GUILE_MSG_FIELD_ID_TIMESTAMP (MU_MSG_FIELD_ID_NUM + 1) /* some symbols */ static SCM SYMB_PRIO_LOW, SYMB_PRIO_NORMAL, SYMB_PRIO_HIGH; static SCM SYMB_FLAG_NEW, SYMB_FLAG_PASSED, SYMB_FLAG_REPLIED, SYMB_FLAG_SEEN, SYMB_FLAG_TRASHED, SYMB_FLAG_DRAFT, SYMB_FLAG_FLAGGED, SYMB_FLAG_SIGNED, SYMB_FLAG_ENCRYPTED, SYMB_FLAG_HAS_ATTACH, SYMB_FLAG_UNREAD; static SCM SYMB_CONTACT_TO, SYMB_CONTACT_CC, SYMB_CONTACT_BCC, SYMB_CONTACT_FROM; struct _MuMsgWrapper { MuMsg *_msg; gboolean _unrefme; }; typedef struct _MuMsgWrapper MuMsgWrapper; static long MSG_TAG; static gboolean mu_guile_scm_is_msg (SCM scm) { return SCM_NIMP(scm) && (long)SCM_CAR(scm) == MSG_TAG; } SCM mu_guile_msg_to_scm (MuMsg *msg) { MuMsgWrapper *msgwrap; g_return_val_if_fail (msg, SCM_UNDEFINED); msgwrap = scm_gc_malloc (sizeof (MuMsgWrapper), "msg"); msgwrap->_msg = msg; msgwrap->_unrefme = FALSE; SCM_RETURN_NEWSMOB (MSG_TAG, msgwrap); } struct _FlagData { MuFlags flags; SCM lst; }; typedef struct _FlagData FlagData; #define MU_GUILE_INITIALIZED_OR_ERROR \ do { if (!(mu_guile_initialized())) \ return mu_guile_error (FUNC_NAME, 0, \ "mu not initialized; call mu:initialize", \ SCM_UNDEFINED); \ } while (0) static void check_flag (MuFlags flag, FlagData *fdata) { SCM flag_scm; if (!(fdata->flags & flag)) return; switch (flag) { case MU_FLAG_NEW: flag_scm = SYMB_FLAG_NEW; break; case MU_FLAG_PASSED: flag_scm = SYMB_FLAG_PASSED; break; case MU_FLAG_REPLIED: flag_scm = SYMB_FLAG_REPLIED; break; case MU_FLAG_SEEN: flag_scm = SYMB_FLAG_SEEN; break; case MU_FLAG_TRASHED: flag_scm = SYMB_FLAG_TRASHED; break; case MU_FLAG_SIGNED: flag_scm = SYMB_FLAG_SIGNED; break; case MU_FLAG_DRAFT: flag_scm = SYMB_FLAG_DRAFT; break; case MU_FLAG_FLAGGED: flag_scm = SYMB_FLAG_FLAGGED; break; case MU_FLAG_ENCRYPTED: flag_scm = SYMB_FLAG_ENCRYPTED; break; case MU_FLAG_HAS_ATTACH: flag_scm = SYMB_FLAG_HAS_ATTACH; break; case MU_FLAG_UNREAD: flag_scm = SYMB_FLAG_UNREAD; break; default: flag_scm = SCM_UNDEFINED; } fdata->lst = scm_append_x (scm_list_2(fdata->lst, scm_list_1 (flag_scm))); } static SCM get_flags_scm (MuMsg *msg) { FlagData fdata; fdata.flags = mu_msg_get_flags (msg); fdata.lst = SCM_EOL; mu_flags_foreach ((MuFlagsForeachFunc)check_flag, &fdata); return fdata.lst; } static SCM get_prio_scm (MuMsg *msg) { switch (mu_msg_get_prio (msg)) { case MU_MSG_PRIO_LOW: return SYMB_PRIO_LOW; case MU_MSG_PRIO_NORMAL: return SYMB_PRIO_NORMAL; case MU_MSG_PRIO_HIGH: return SYMB_PRIO_HIGH; default: g_return_val_if_reached (SCM_UNDEFINED); } } static SCM msg_string_list_field (MuMsg *msg, MuMsgFieldId mfid) { SCM scmlst; const GSList *lst; lst = mu_msg_get_field_string_list (msg, mfid); for (scmlst = SCM_EOL; lst; lst = g_slist_next(lst)) { SCM item; item = scm_list_1 (mu_guile_scm_from_str((const char*)lst->data)); scmlst = scm_append_x (scm_list_2(scmlst, item)); } return scmlst; } static SCM get_body (MuMsg *msg, gboolean html) { SCM data; const char* body; MuMsgOptions opts; opts = MU_MSG_OPTION_NONE; if (html) body = mu_msg_get_body_html (msg, opts); else body = mu_msg_get_body_text (msg, opts); if (body) data = mu_guile_scm_from_str (body); else data = SCM_BOOL_F; /* explicitly close the file backend, so we won't run of fds */ mu_msg_unload_msg_file (msg); return data; } SCM_DEFINE (get_field, "mu:c:get-field", 2, 0, 0, (SCM MSG, SCM FIELD), "Get the field FIELD from message MSG.\n") #define FUNC_NAME s_get_field { MuMsgWrapper *msgwrap; MuMsgFieldId mfid; msgwrap = (MuMsgWrapper*) SCM_CDR(MSG); MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_integer_p(FIELD), FIELD, SCM_ARG2, FUNC_NAME); mfid = scm_to_int (FIELD); SCM_ASSERT (mfid < MU_MSG_FIELD_ID_NUM || mfid == MU_GUILE_MSG_FIELD_ID_TIMESTAMP, FIELD, SCM_ARG2, FUNC_NAME); switch (mfid) { case MU_MSG_FIELD_ID_PRIO: return get_prio_scm (msgwrap->_msg); case MU_MSG_FIELD_ID_FLAGS: return get_flags_scm (msgwrap->_msg); case MU_MSG_FIELD_ID_BODY_HTML: return get_body (msgwrap->_msg, TRUE); case MU_MSG_FIELD_ID_BODY_TEXT: return get_body (msgwrap->_msg, FALSE); /* our pseudo-field; we get it from the message file */ case MU_GUILE_MSG_FIELD_ID_TIMESTAMP: return scm_from_uint ( (unsigned)mu_msg_get_timestamp(msgwrap->_msg)); default: break; } switch (mu_msg_field_type (mfid)) { case MU_MSG_FIELD_TYPE_STRING: return mu_guile_scm_from_str (mu_msg_get_field_string(msgwrap->_msg, mfid)); case MU_MSG_FIELD_TYPE_BYTESIZE: case MU_MSG_FIELD_TYPE_TIME_T: return scm_from_uint ( mu_msg_get_field_numeric (msgwrap->_msg, mfid)); case MU_MSG_FIELD_TYPE_INT: return scm_from_int ( mu_msg_get_field_numeric (msgwrap->_msg, mfid)); case MU_MSG_FIELD_TYPE_STRING_LIST: return msg_string_list_field (msgwrap->_msg, mfid); default: SCM_ASSERT (0, FIELD, SCM_ARG2, FUNC_NAME); } } #undef FUNC_NAME struct _EachContactData { SCM lst; MuMsgContactType ctype; }; typedef struct _EachContactData EachContactData; static void contacts_to_list (MuMsgContact *contact, EachContactData *ecdata) { SCM item; if (ecdata->ctype != MU_MSG_CONTACT_TYPE_ALL && mu_msg_contact_type (contact) != ecdata->ctype) return; item = scm_list_1 (scm_cons (mu_guile_scm_from_str(mu_msg_contact_name (contact)), mu_guile_scm_from_str(mu_msg_contact_address (contact)))); ecdata->lst = scm_append_x (scm_list_2(ecdata->lst, item)); } SCM_DEFINE (get_contacts, "mu:c:get-contacts", 2, 0, 0, (SCM MSG, SCM CONTACT_TYPE), "Get a list of contact information pairs.\n") #define FUNC_NAME s_get_contacts { MuMsgWrapper *msgwrap; EachContactData ecdata; MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_symbol_p (CONTACT_TYPE) || scm_is_bool(CONTACT_TYPE), CONTACT_TYPE, SCM_ARG2, FUNC_NAME); if (CONTACT_TYPE == SCM_BOOL_F) return SCM_UNSPECIFIED; /* nothing to do */ else if (CONTACT_TYPE == SCM_BOOL_T) ecdata.ctype = MU_MSG_CONTACT_TYPE_ALL; else { if (scm_is_eq (CONTACT_TYPE, SYMB_CONTACT_TO)) ecdata.ctype = MU_MSG_CONTACT_TYPE_TO; else if (scm_is_eq (CONTACT_TYPE, SYMB_CONTACT_CC)) ecdata.ctype = MU_MSG_CONTACT_TYPE_CC; else if (scm_is_eq (CONTACT_TYPE, SYMB_CONTACT_BCC)) ecdata.ctype = MU_MSG_CONTACT_TYPE_BCC; else if (scm_is_eq (CONTACT_TYPE, SYMB_CONTACT_FROM)) ecdata.ctype = MU_MSG_CONTACT_TYPE_FROM; else return mu_guile_error (FUNC_NAME, 0, "invalid contact type", SCM_UNDEFINED); } ecdata.lst = SCM_EOL; msgwrap = (MuMsgWrapper*) SCM_CDR(MSG); mu_msg_contact_foreach (msgwrap->_msg, (MuMsgContactForeachFunc)contacts_to_list, &ecdata); /* explicitly close the file backend, so we won't run out of fds */ mu_msg_unload_msg_file (msgwrap->_msg); return ecdata.lst; } #undef FUNC_NAME struct _AttInfo { SCM attlist; gboolean attachments_only; }; typedef struct _AttInfo AttInfo; static void each_part (MuMsg *msg, MuMsgPart *part, AttInfo *attinfo) { char *mime_type, *filename; SCM elm; if (!part->type) return; if (attinfo->attachments_only && !mu_msg_part_maybe_attachment (part)) return; mime_type = g_strdup_printf ("%s/%s", part->type, part->subtype); filename = mu_msg_part_get_filename (part, FALSE); elm = scm_list_5 ( /* msg */ mu_guile_scm_from_str (mu_msg_get_path(msg)), /* index */ scm_from_uint(part->index), /* filename or #f */ filename ? mu_guile_scm_from_str (filename) : SCM_BOOL_F, /* mime-type */ mime_type ? mu_guile_scm_from_str (mime_type): SCM_BOOL_F, /* size */ part->size > 0 ? scm_from_uint (part->size) : SCM_BOOL_F); g_free (mime_type); g_free (filename); attinfo->attlist = scm_cons (elm, attinfo->attlist); } SCM_DEFINE (get_parts, "mu:c:get-parts", 1, 1, 0, (SCM MSG, SCM ATTS_ONLY), "Get the list of mime-parts for MSG. If ATTS_ONLY is #t, only" "get parts that are (look like) attachments. The resulting list has " "elements which are list of the form (index name mime-type size).\n") #define FUNC_NAME s_get_parts { MuMsgWrapper *msgwrap; AttInfo attinfo; MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_is_bool(ATTS_ONLY), ATTS_ONLY, SCM_ARG2, FUNC_NAME); attinfo.attlist = SCM_EOL; /* empty list */ attinfo.attachments_only = ATTS_ONLY == SCM_BOOL_T ? TRUE : FALSE; msgwrap = (MuMsgWrapper*) SCM_CDR(MSG); mu_msg_part_foreach (msgwrap->_msg, MU_MSG_OPTION_NONE, (MuMsgPartForeachFunc)each_part, &attinfo); /* explicitly close the file backend, so we won't run of fds */ mu_msg_unload_msg_file (msgwrap->_msg); return attinfo.attlist; } #undef FUNC_NAME SCM_DEFINE (get_header, "mu:c:get-header", 2, 0, 0, (SCM MSG, SCM HEADER), "Get an arbitary HEADER from MSG.\n") #define FUNC_NAME s_get_header { MuMsgWrapper *msgwrap; char *header; SCM val; MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_is_string (HEADER)||HEADER==SCM_UNDEFINED, HEADER, SCM_ARG2, FUNC_NAME); msgwrap = (MuMsgWrapper*) SCM_CDR(MSG); header = scm_to_utf8_string (HEADER); val = mu_guile_scm_from_str (mu_msg_get_header(msgwrap->_msg, header)); free (header); /* explicitly close the file backend, so we won't run of fds */ mu_msg_unload_msg_file (msgwrap->_msg); return val; } #undef FUNC_NAME static void call_func (SCM FUNC, MuMsgIter *iter, const char* func_name) { SCM msgsmob; MuMsg *msg; msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */ msgsmob = mu_guile_msg_to_scm (mu_msg_ref(msg)); scm_call_1 (FUNC, msgsmob); } static MuMsgIter* get_query_iter (MuQuery *query, const char* expr, int maxnum) { MuMsgIter *iter; GError *err; err = NULL; iter = mu_query_run (query, expr, MU_MSG_FIELD_ID_NONE, maxnum, MU_QUERY_FLAG_NONE, &err); if (!iter) { mu_guile_g_error ("", err); g_clear_error (&err); } return iter; } SCM_DEFINE (for_each_message, "mu:c:for-each-message", 3, 0, 0, (SCM FUNC, SCM EXPR, SCM MAXNUM), "Call FUNC for each msg in the message store matching EXPR. EXPR is" "either a string containing a mu search expression or a boolean; in the former " "case, limit the messages to only those matching the expression, in the " "latter case, match /all/ messages if the EXPR equals #t, and match " "none if EXPR equals #f.") #define FUNC_NAME s_for_each_message { MuMsgIter *iter; char* expr; MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT (scm_procedure_p (FUNC), FUNC, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_is_bool(EXPR) || scm_is_string (EXPR), EXPR, SCM_ARG2, FUNC_NAME); SCM_ASSERT (scm_is_integer (MAXNUM), MAXNUM, SCM_ARG3, FUNC_NAME); if (EXPR == SCM_BOOL_F) return SCM_UNSPECIFIED; /* nothing to do */ if (EXPR == SCM_BOOL_T) expr = strdup (""); /* note, "" matches *all* messages */ else expr = scm_to_utf8_string(EXPR); iter = get_query_iter (mu_guile_instance()->query, expr, scm_to_int(MAXNUM)); free (expr); if (!iter) return SCM_UNSPECIFIED; while (!mu_msg_iter_is_done(iter)) { call_func (FUNC, iter, FUNC_NAME); mu_msg_iter_next (iter); } mu_msg_iter_destroy (iter); return SCM_UNSPECIFIED; } #undef FUNC_NAME static SCM register_symbol (const char *name) { SCM scm; scm = scm_from_utf8_symbol (name); scm_c_define (name, scm); scm_c_export (name, NULL); return scm; } static void define_symbols (void) { SYMB_CONTACT_TO = register_symbol ("mu:contact:to"); SYMB_CONTACT_CC = register_symbol ("mu:contact:cc"); SYMB_CONTACT_FROM = register_symbol ("mu:contact:from"); SYMB_CONTACT_BCC = register_symbol ("mu:contact:bcc"); SYMB_PRIO_LOW = register_symbol ("mu:prio:low"); SYMB_PRIO_NORMAL = register_symbol ("mu:prio:normal"); SYMB_PRIO_HIGH = register_symbol ("mu:prio:high"); SYMB_FLAG_NEW = register_symbol ("mu:flag:new"); SYMB_FLAG_PASSED = register_symbol ("mu:flag:passed"); SYMB_FLAG_REPLIED = register_symbol ("mu:flag:replied"); SYMB_FLAG_SEEN = register_symbol ("mu:flag:seen"); SYMB_FLAG_TRASHED = register_symbol ("mu:flag:trashed"); SYMB_FLAG_DRAFT = register_symbol ("mu:flag:draft"); SYMB_FLAG_FLAGGED = register_symbol ("mu:flag:flagged"); SYMB_FLAG_SIGNED = register_symbol ("mu:flag:signed"); SYMB_FLAG_ENCRYPTED = register_symbol ("mu:flag:encrypted"); SYMB_FLAG_HAS_ATTACH = register_symbol ("mu:flag:has-attach"); SYMB_FLAG_UNREAD = register_symbol ("mu:flag:unread"); } static struct { const char* name; unsigned val; } VAR_PAIRS[] = { { "mu:field:bcc", MU_MSG_FIELD_ID_BCC }, { "mu:field:body-html", MU_MSG_FIELD_ID_BODY_HTML }, { "mu:field:body-txt", MU_MSG_FIELD_ID_BODY_TEXT }, { "mu:field:cc", MU_MSG_FIELD_ID_CC }, { "mu:field:date", MU_MSG_FIELD_ID_DATE }, { "mu:field:flags", MU_MSG_FIELD_ID_FLAGS }, { "mu:field:from", MU_MSG_FIELD_ID_FROM }, { "mu:field:maildir", MU_MSG_FIELD_ID_MAILDIR }, { "mu:field:message-id",MU_MSG_FIELD_ID_MSGID }, { "mu:field:path", MU_MSG_FIELD_ID_PATH }, { "mu:field:prio", MU_MSG_FIELD_ID_PRIO }, { "mu:field:refs", MU_MSG_FIELD_ID_REFS }, { "mu:field:size", MU_MSG_FIELD_ID_SIZE }, { "mu:field:subject", MU_MSG_FIELD_ID_SUBJECT }, { "mu:field:tags", MU_MSG_FIELD_ID_TAGS }, { "mu:field:to", MU_MSG_FIELD_ID_TO }, /* non-Xapian field: timestamp */ { "mu:field:timestamp", MU_GUILE_MSG_FIELD_ID_TIMESTAMP } }; static void define_vars (void) { unsigned u; for (u = 0; u != G_N_ELEMENTS(VAR_PAIRS); ++u) { scm_c_define (VAR_PAIRS[u].name, scm_from_uint (VAR_PAIRS[u].val)); scm_c_export (VAR_PAIRS[u].name, NULL); } } static SCM msg_mark (SCM msg_smob) { MuMsgWrapper *msgwrap; msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob); msgwrap->_unrefme = TRUE; return SCM_UNSPECIFIED; } static size_t msg_free (SCM msg_smob) { MuMsgWrapper *msgwrap; msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob); if (msgwrap->_unrefme) mu_msg_unref (msgwrap->_msg); return sizeof (MuMsgWrapper); } static int msg_print (SCM msg_smob, SCM port, scm_print_state * pstate) { MuMsgWrapper *msgwrap; msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob); scm_puts ("#_msg), port); scm_puts (">", port); return 1; } void* mu_guile_message_init (void *data) { MSG_TAG = scm_make_smob_type ("msg", sizeof(MuMsgWrapper)); scm_set_smob_mark (MSG_TAG, msg_mark); scm_set_smob_free (MSG_TAG, msg_free); scm_set_smob_print (MSG_TAG, msg_print); define_vars (); define_symbols (); #ifndef SCM_MAGIC_SNARFER #include "mu-guile-message.x" #endif /*SCM_MAGIC_SNARFER*/ return NULL; } mu-0.9.18/guile/mu-guile.h0000644000175000017500000000420212605152236012155 00000000000000/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_GUILE_H__ #define __MU_GUILE_H__ #include #include G_BEGIN_DECLS struct _MuGuile { MuQuery *query; }; typedef struct _MuGuile MuGuile; /** * get the single MuGuile instance * * @return the instance or NULL in case of error */ MuGuile *mu_guile_instance (void); /** * whether mu-guile is initialized * * @return TRUE if MuGuile is Initialized, FALSE otherwise */ gboolean mu_guile_initialized (void); /** * raise a guile error (based on a GError) * * @param func_name function name * @param err the error * * @return SCM_UNSPECIFIED */ SCM mu_guile_g_error (const char *func_name, GError *err); /** * raise a guile error * * @param func_name function * @param status err code * @param fmt format string for error msg * @param args params for format string * * @return SCM_UNSPECIFIED */ SCM mu_guile_error (const char *func_name, int status, const char *fmt, SCM args); /** * convert a const char* into an SCM -- either a string or, if str == * NULL, #f. It assumes str is in UTF8 encoding, and replace * characters with '?' if needed. * * @param str a string or NULL * * @return a guile string or #f */ SCM mu_guile_scm_from_str (const char *str); /** * Initialize this mu guile module. * * @param data * * @return */ void* mu_guile_init (void *data); G_END_DECLS #endif /*__MU_GUILE_H__*/ mu-0.9.18/guile/mu.scm0000644000175000017500000002452713020504331011406 00000000000000;; Copyright (C) 2011-2013 Dirk-Jan C. Binnema ;; ;; 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, 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. (define-module (mu) :use-module (oop goops) :use-module (ice-9 optargs) :use-module (texinfo string-utils) :export ( ;; classes ;; general ;; mu:initialize ;; mu:initialized? mu:log-warning mu:log-message mu:log-critical ;; search funcs mu:for-each-message mu:for-each-msg mu:message-list ;; message funcs mu:header ;; message accessors mu:field:bcc mu:field:body-html mu:field:body-txt mu:field:cc mu:field:date mu:field:flags mu:field:from mu:field:maildir mu:field:message-id mu:field:path mu:field:prio mu:field:refs mu:field:size mu:field:subject mu:field:tags mu:field:timestamp mu:field:to ;; contact funcs mu:name mu:email mu:contact->string ;; mu:for-each-contact ;; mu:contacts ;; ;; mu:frequency mu:last-seen ;; parts ;; message function mu:attachments mu:parts ;; methods mu:name mu:mime-type ;; size ;; mu:save ;; mu:save-as )) ;; this is needed for guile < 2.0.4 (setlocale LC_ALL "") ;; load the binary (load-extension "libguile-mu" "mu_guile_init") (load-extension "libguile-mu" "mu_guile_message_init") ;; define some dummies so we don't get errors during byte compilation (eval-when (compile) (define mu:c:get-field) (define mu:c:get-contacts) (define mu:c:for-each-message) (define mu:c:get-header) (define mu:critical) (define mu:c:log) (define mu:message) (define mu:c:log) (define mu:warning) (define mu:c:log) (define mu:c:get-parts)) (define (mu:log-warning frm . args) "Log FRM with ARGS at warning." (mu:c:log mu:warning frm args)) (define (mu:log-message frm . args) "Log FRM with ARGS at warning." (mu:c:log mu:message frm args)) (define (mu:log-critical frm . args) "Log FRM with ARGS at warning." (mu:c:log mu:critical frm args)) (define-class () (msg #:init-keyword #:msg)) ;; the MuMsg-smob we're wrapping (define-syntax define-getter (syntax-rules () ((define-getter method-name field) (begin (define-method (method-name (msg )) (mu:c:get-field (slot-ref msg 'msg) field)) (export method-name))))) (define-getter mu:bcc mu:field:bcc) (define-getter mu:body-html mu:field:body-html) (define-getter mu:body-txt mu:field:body-txt) (define-getter mu:cc mu:field:cc) (define-getter mu:date mu:field:date) (define-getter mu:flags mu:field:flags) (define-getter mu:from mu:field:from) (define-getter mu:maildir mu:field:maildir) (define-getter mu:message-id mu:field:message-id) (define-getter mu:path mu:field:path) (define-getter mu:priority mu:field:prio) (define-getter mu:references mu:field:refs) (define-getter mu:size mu:field:size) (define-getter mu:subject mu:field:subject) (define-getter mu:tags mu:field:tags) (define-getter mu:timestamp mu:field:timestamp) (define-getter mu:to mu:field:to) (define-method (mu:header (msg ) (hdr )) "Get an arbitrary header HDR from message MSG; return #f if it does not exist." (mu:c:get-header (slot-ref msg 'msg) hdr)) (define* (mu:for-each-message func #:optional (expr #t) (maxresults -1)) "Execute function FUNC for each message that matches mu search expression EXPR. If EXPR is not provided, match /all/ messages in the store. MAXRESULTS specifies the maximum of messages to return, or -1 (the default) for no limit." (mu:c:for-each-message (lambda (msg) (func (make #:msg msg))) expr maxresults)) ;; backward-compatibility alias (define mu:for-each-msg mu:for-each-message) (define* (mu:message-list #:optional (expr #t) (maxresults -1)) "Return a list of all messages matching mu search expression EXPR. If EXPR is not provided, return a list of /all/ messages in the store. MAXRESULTS specifies the maximum of messages to return, or -1 (the default) for no limit." (let ((lst '())) (mu:for-each-message (lambda (m) (set! lst (append! lst (list m)))) expr maxresults) lst)) ;; contacts (define-class () (name #:init-value #f #:accessor mu:name #:init-keyword #:name) (email #:init-value #f #:accessor mu:email #:init-keyword #:email)) (define-method (mu:contacts (msg ) contact-type) "Get all contacts for MSG of the given CONTACT-TYPE. MSG is of type , while contact type is either `mu:contact:to', `mu:contact:cc', `mu:contact:from' or `mu:contact:bcc' to get the corresponding type of contacts, or #t to get all. Returns a list of objects." (map (lambda (pair) ;; a pair (na . addr) (make #:name (car pair) #:email (cdr pair))) (mu:c:get-contacts (slot-ref msg 'msg) contact-type))) (define-method (mu:contacts (msg )) "Get contacts of all types for message MSG as a list of objects." (mu:contacts msg #t)) (define-class () (tstamp #:init-value 0 #:accessor mu:timestamp #:init-keyword #:timestamp) (last-seen #:init-value 0 #:accessor mu:last-seen) (freq #:init-value 1 #:accessor mu:frequency)) (define* (mu:for-each-contact proc #:optional (expr #t)) "Execute PROC for each contact. PROC receives a instance as parameter. If EXPR is specified, only consider contacts in messages matching EXPR." (let ((c-hash (make-hash-table 4096))) (mu:for-each-message (lambda (msg) (for-each (lambda (ct) (let ((ct-ws (make #:name (mu:name ct) #:email (mu:email ct) #:timestamp (mu:date msg)))) (update-contacts-hash c-hash ct-ws))) (mu:contacts msg #t))) expr) (hash-for-each ;; c-hash now contains a map of email->contact (lambda (email ct-ws) (proc ct-ws)) c-hash))) (define-method (update-contacts-hash c-hash (nc )) "Update the contacts hash with a new and/or existing contact." ;; xc: existing-contact, nc: new contact (let ((xc (hash-ref c-hash (mu:email nc)))) (if (not xc) ;; no existing contact with this email address? (hash-set! c-hash (mu:email nc) nc) ;; store the new contact. ;; otherwise: (begin ;; 1) update the frequency for the existing contact (set! (mu:frequency xc) (1+ (mu:frequency xc))) ;; 2) update the name if the new one is not empty and its timestamp is newer ;; in that case, also update the timestamp (if (and (mu:name nc) (> (string-length (mu:name nc))) (> (mu:timestamp nc) (mu:timestamp xc))) (set! (mu:name xc) (mu:name nc)) (set! (mu:timestamp xc) (mu:timestamp nc))) ;; 3) update last-seen with timestamp, if x's timestamp is newer (if (> (mu:timestamp nc) (mu:last-seen xc)) (set! (mu:last-seen xc) (mu:timestamp nc))) ;; okay --> now xc has been updated; but it back in the hash (hash-set! c-hash (mu:email xc) xc))))) (define-method (mu:contact->string (contact ) (form )) "Convert a contact to a string in format FORM, which is a string, either \"org-contact\", \"mutt-alias\", \"mutt-ab\", \"wanderlust\", \"quoted\" \"plain\"." (let* ((name (mu:name contact)) (email (mu:email contact)) (nick ;; simplistic nick guessing... (string-map (lambda(kar) (if (char-alphabetic? kar) kar #\_)) (string-downcase (or name email))))) (cond ((string= form "plain") (format #f "~a~a~a" (or name "") (if name " " "") email)) ((string= form "org-contact") (format #f "* ~s\n:PROPERTIES:\n:EMAIL:~a\n:NICK:~a\n:END:" (or name email) email nick)) ((string= form "wanderlust") (format #f "~a ~s ~s" nick (or name email) email)) ((string= form "mutt-alias") (format #f "alias ~a ~a <~a>" nick (or name email) email)) ((string= form "mutt-ab") (format #f "~a\t~a\t" email (or name ""))) ((string= form "quoted") (string-append "\"" (escape-special-chars (string-append (if name (format #f "\"~a\" " name) "") (format #f "<~a>" email)) "\"" #\\) "\"")) (else (error "Unsupported format"))))) ;; message parts (define-class () (msgpath #:init-value #f #:init-keyword #:msgpath) (index #:init-value #f #:init-keyword #:index) (name #:init-value #f #:getter mu:name #:init-keyword #:name) (mime-type #:init-value #f #:getter mu:mime-type #:init-keyword #:mime-type) (size #:init-value 0 #:getter mu:size #:init-keyword #:size)) (define-method (get-parts (msg ) (files-only )) "Get the part for MSG as a list of objects; if FILES-ONLY is #t, only get the part with file names." (map (lambda (part) (make #:msgpath (list-ref part 0) #:index (list-ref part 1) #:name (list-ref part 2) #:mime-type (list-ref part 3) #:size (list-ref part 4))) (mu:c:get-parts (slot-ref msg 'msg) files-only))) (define-method (mu:attachments (msg )) "Get the attachments for MSG as a list of objects." (get-parts msg #t)) (define-method (mu:parts (msg )) "Get the MIME-parts for MSG as a list of objects." (get-parts msg #f)) ;; (define-method (mu:save (part )) ;; "Save PART to a temporary file, and return the file name. If the ;; part had a filename, the temporary file's file name will be just that; ;; otherwise a name is made up." ;; (mu:save-part (slot-ref part 'msgpath) (slot-ref part 'index))) ;; (define-method (mu:save-as (part ) (filepath )) ;; "Save message-part PART to file system path PATH." ;; (copy-file (save part) filepath)) mu-0.9.18/guile/mu-guile.texi0000644000175000017500000010040213020504331012663 00000000000000\input texinfo.tex @c -*-texinfo-*- @c %**start of header @setfilename mu-guile.info @settitle mu-guile user manual @c Use proper quote and backtick for code sections in PDF output @c Cf. Texinfo manual 14.2 @set txicodequoteundirected @set txicodequotebacktick @documentencoding UTF-8 @c %**end of header @include texi.texi @copying Copyright @copyright{} 2012 Dirk-Jan C. Binnema @quotation Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License.'' @end quotation @end copying @titlepage @title @t{mu-guile} - extending @t{mu} with Guile Scheme @subtitle version @value{mu-version} @author Dirk-Jan C. Binnema @c The following two commands start the copyright page. @page @vskip 0pt plus 1filll @insertcopying @end titlepage @dircategory The Algorithmic Language Scheme @direntry * Mu-guile: (mu-guile). Guile-bindings for the mu e-mail indexer/searcher @end direntry @contents @ifnottex @node Top @top mu-guile manual @end ifnottex @iftex @node Welcome to mu-guile @unnumbered Welcome to mu-guile @end iftex Welcome to @t{mu-guile}! @t{mu} is a program for indexing and searching your e-mails. It can search your messages in many different ways, but sometimes that may not be enough. If you have very specific queries, or want do generate some statistics, you need some more power. @t{mu-guile} is made for those cases. @t{mu-guile} exposes the internals of @t{mu} and its database to the @t{guile} programming language. Guile is the @emph{GNU Ubiquitous Intelligent Language for Extensions} - a version of the @emph{Scheme} programming language and the official GNU extension language. Guile/Scheme is a member of the @emph{Lisp} family of programming languages -- like emacs-lisp, @emph{Racket}, Common Lisp. If you're not familiar with Scheme, @t{mu-guile} is an excellent opportunity to learn a bit about! Trust me, it's not very hard -- and it it's @emph{fun}! @menu * Getting started:: * Initializing mu-guile:: * Messages:: * Contacts:: * Attachments and other parts:: * Statistics:: * Plotting data:: * Writing scripts:: Appendices * Recipies:: Snippets do specific things * GNU Free Documentation License:: The license of this manual. @end menu @node Getting started @chapter Getting started @menu * Installation:: * Making sure it works:: @end menu This chapter walks you through the installation and the basic setup. @node Installation @section Installation @t{mu-guile} is part of @t{mu} - by installing the latter, the former is necessarily installed as well. At the time of writing, there are no distribution-provided packaged versions of @t{mu-guile}; so for now, you need to follow the steps below. @subsection Guile 2.x @t{mu-guile} is built automatically when @t{mu} is built, if you have @t{guile} version 2 or higher. (@t{mu} checks for this during @t{configure}). Thus, the first step is to ensure you have @t{guile} installed. On Debian/Ubuntu you can install @t{guile} 2.x using the @t{guile-2.0-dev} package (and its dependencies): @example $ sudo apt-get install guile-2.0-dev @end example At the time of writing, there are no official packages for Fedora@footnote{@url{https://bugzilla.redhat.com/show_bug.cgi?id=678238}}. If you are using Fedora or any other system that does not have packages, you need to compile @t{guile} from source@footnote{@url{http://www.gnu.org/software/guile/manual/html_node/Obtaining-and-Installing-Guile.html#Obtaining-and-Installing-Guile}}. @subsection gnuplot For creating graphs with @t{mu-guile}, you need the @t{gnuplot} program -- most likely, there is a package available for your system; for example: @example $ sudo apt-get install gnuplot @end example and in Fedora: @example $ sudo yum install gnuplot @end example @subsection mu Assuming @t{guile} 2.x is installed correctly, @t{mu} finds it during its @t{configure}-stage, and creates @t{mu-guile}. Building @t{mu} follows the normal steps -- please see the @t{mu} documentation for the details. The output of @t{./configure} should end with a little text describing the detected versions of various libraries @t{mu} depends on. In particular, it should mention the @t{guile} version, e.g. @example Guile version : 2.0.3.82-a2c66 @end example If you don't see any line referring to @t{guile}, please install it, and run @t{configure} again. After a succesfull @t{./configure}, we can make and install the package: @example $ make && sudo make install @end example @subsection mu-guile After this, @t{mu} and @t{mu-guile} are installed -- usually somewhere under @t{/usr/local}.You may need to update @t{guile}'s @code{%load-path} to find it there. You can check the current @code{%load-path} with the following: @example guile -c '(display %load-path)(newline)' @end example If necessary, you can add the @t{%load-path} by adding to your @file{~/.guile}: @lisp (set! %load-path (cons "/usr/local/share/guile/site/2.0" %load-path)) @end lisp Or, alternatively, you can set @t{GUILE_LOAD_PATH}: @example export GUILE_LOAD_PATH="/usr/local/share/guile/site/2.0" @end example In both cases the directory should be the directory that contains the installed @t{mu.scm}; if you installed @t{mu} under a different prefix, you must change the @code{%load-path} accordingly. After this, you should be ready to go! @node Making sure it works @section Making sure it works Assuming @t{mu-guile} has been installed correctly (@ref{Installation}), and also assuming that you have already indexed your e-mail messages (if necessary, see the @t{mu-index} man-page), we are ready to start @t{mu-guile}; a session may look something like this: @cartouche @verbatim GNU Guile 2.0.5.123-4bd53 Copyright (C) 1995-2012 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> @end verbatim @end cartouche @noindent Now, copy-paste the following after the prompt: @cartouche @lisp (use-modules (mu)) (mu:initialize) (for-each (lambda(msg) (format #t "Subject: ~a\n" (mu:subject msg))) (mu:message-list "hello")) @end lisp @end cartouche @noindent After pressing @key{Enter}, you should get a list of all subjects of messages that match @t{hello}: @verbatim ... Subject: RE: The Bird Serpent War Cataclysm Subject: Hello! Subject: Re: post-run tomorrow Subject: When all is lost ... @end verbatim @noindent If all this works, congratulations! @t{mu-guile} is installed now, ready to serve your every searching need! @node Initializing mu-guile @chapter Initializing mu-guile We now have installed @t{mu-guile}, and in @ref{Making sure it works} confirmed that things work by trying some simple script. In this and the following chapters, we take a closer look at programming with @t{mu-guile}. It is possible to write separate programs with @t{mu-guile}, but for now we'll do things @emph{interactively}, that is, from the Guile-prompt (``@abbr{REPL}''). As we have seen, we start our @t{mu-guile} session by starting @t{guile}: @verbatim $ guile @end verbatim @cartouche @verbatim GNU Guile 2.0.5.123-4bd53 Copyright (C) 1995-2012 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> @end verbatim @end cartouche The first thing we need to do is loading the modules. All the basics are in the @t{(mu)} module, with some statistical extras in @t{(mu stats)}, and some graph plotting functionality in @t{(mu plot)}@footnote{@code{(mu plot)} requires the @t{gnuplot} program}. Let's load all of them: @verbatim scheme@(guile-user)> (use-modules (mu) (mu stats) (mu plot)) @end verbatim The first time you do this, @t{guile} will probably respond by showing some messages about compiling the modules, and then return to you with another prompt. Before we can do anything with @t{mu guile}, we need to initialize the system: @verbatim scheme@(guile-user)> (mu:initialize) @end verbatim This opens the database for reading, using the default location of @file{~/.mu}@footnote{If you keep your @t{mu} database in a non-standard place, use @code{(mu:initialize "/path/to/my/mu/")}} Now, @t{mu-guile} is ready to go. In the next chapter, we go through the modules and show what you can do with them. @node Messages @chapter Messages In this chapter, we discuss searching messages and doing things with them. @menu * Finding messages:: query for messages in the databse * Message methods:: what methods are available for messages? * Example - the longest subject:: find the messages with the longest subject @end menu @node Finding messages @section Finding messages Now we are ready to retrieve some messages from the system. There are two main procedures to do this: @itemize @item @code{(mu:message-list [])} @item @code{(mu:for-each-message [])} @end itemize @noindent The first procedure, @code{mu:message-list} returns a list of all messages matching @t{}; if you leave @t{} out, it returns @emph{all} messages. For example, to get all messages with @t{coffee} in the subject line: @verbatim scheme@(guile-user)> (mu:message-list "subject:coffee") $1 = (#< 9040640> #< 9040630> #< 9040570>) @end verbatim @noindent Apparently, we have three messages matching @t{subject:coffee}, so we get a list of three @code{} objects. Let's just use the @code{mu:subject} procedure ('method') provided by @code{} objects to retrieve the subject-field (more about methods in the next section). For your convenience, @t{guile} has saved the result of our last query in a variable called @t{$1}, so to get the subject of the first message in the list, we can do: @verbatim scheme@(guile-user)> (mu:subject (car $1)) $2 = "Re: best coffee ever!" @end verbatim @noindent The second procedure we mentioned, @code{mu:for-each-message}, executes some procedure for each message matched by the search expression (or @emph{all} messages if the search expression is omitted): @verbatim scheme@(guile-user)> (mu:for-each-message (lambda(msg) (display (mu:subject msg)) (newline)) "subject:coffee") Re: best coffee ever! best coffee ever! Coffee beans scheme@(guile-user)> @end verbatim @noindent Using @code{mu:message-list} and/or @code{mu:for-each-message}@footnote{Implementation node: @code{mu:message-list} is implemented in terms of @code{mu:for-each-message}, not the other way around. Due to the way @t{mu} works, @code{mu:for-each-message} is rather more efficient than a combination of @code{for-each} and @code{mu:message-list}} and a couple of @t{} methods, together with what Guile/Scheme provides, should allow for many interesting programs. @node Message methods @section Message methods Now that we've seen how to retrieve lists of message objects (@code{}), let's see what we can do with such an object. @code{} defines the following methods that all take a single @code{} object as a parameter. We won't go into the exact meanings for all of these procedures here - for the details about various flags / properties, please refer to the @t{mu-find} man-page. @itemize @item @code{(mu:bcc msg)}: the @t{Bcc} field of the message, or @t{#f} if there is none @item @code{(mu:body-html msg)}: : the html body of the message, or @t{#f} if there is none @item @code{(mu:body-txt msg)}: the plain-text body of the message, or @t{#f} if there is none @item @code{(mu:cc msg)}: the @t{Bcc} field of the message, or @t{#f} if there is none @item @code{(mu:date msg)}: the @t{Date} field of the message, or 0 if there is none @item @code{(mu:flags msg)}: list of message-flags for this message @item @code{(mu:from msg)}: the @t{From} field of the message, or @t{#f} if there is none @item @code{(mu:maildir msg)}: the maildir this message lives in, or @t{#f} if there is none @item @code{(mu:message-id msg)}: the @t{Message-Id} field of the message, or @t{#f} if there is none @item @code{(mu:path msg)}: the file system path for this message @item @code{(mu:priority msg)}: the priority of this message (either @t{mu:prio:low}, @t{mu:prio:normal} or @t{mu:prio:high} @item @code{(mu:references msg)}: the list of messages (message-ids) this message refers to in(mu: the @t{References:} header @item @code{(mu:size msg)}: size of the message in bytes @item @code{(mu:subject msg)}: the @t{Subject} field of the message, or @t{#f} if there is none. @item @code{(mu:tags msg)}: list of tags for this message @item @code{(mu:timestamp msg)}: the timestamp (mtime) of the message file, or #f if there is none. message file @item @code{(mu:to msg)}: the sender of the message, or @t{#f} if there is none @end itemize With these methods, we can query messages for their properties; for example: @verbatim scheme@(guile-user)> (define msg (car (mu:message-list "snow"))) scheme@(guile-user)> (mu:subject msg) $1 = "Re: Running in the snow is beautiful" scheme@(guile-user)> (mu:flags msg) $2 = (mu:flag:replied mu:flag:seen) scheme@(guile-user)> (strftime "%F" (localtime (mu:date msg))) $3 = "2011-01-15" @end verbatim There are a couple more methods: @itemize @item @code{(mu:header msg "")} returns an arbitrary message header (or @t{#f} if not found) -- e.g. @code{(header msg "User-Agent")} @item If you include the @t{mu contact} module, the @code{(mu:contacts msg [contact-type])} method (to get a list of contacts) is added. @xref{Contacts}. @item If you include the @t{mu part} module, the @code{((mu:parts msg)} and @code{(mu:attachments msg)} methods are added. @xref{Attachments and other parts}. @end itemize @node Example - the longest subject @section Example - the longest subject Now, let's write a little example -- let's find out what is the @emph{longest subject} of any e-mail messages we received in the year 2011. You can try this if you put the following in a separate file, make it executable, and run it like any program. @lisp #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu)) (use-modules (srfi srfi-1)) (mu:initialize) ;; note: (subject msg) => #f if there is no subject (define list-of-subjects (map (lambda (msg) (or (mu:subject msg) "")) (mu:message-list "date:2011..2011"))) ;; see the mu-find manpage for the date syntax (define longest-subject (fold (lambda (subj1 subj2) (if (> (string-length subj1) (string-length subj2)) subj1 subj2)) "" list-of-subjects)) (format #t "Longest subject: ~s\n" longest-subject) @end lisp There are many other ways to solve the same problem, for example by using an iterative approach with @code{mu:for-each-message}, but it should show how one can easily write little programs to answer specific questions about your e-mail corpus. @node Contacts @chapter Contacts We can retrieve the sender and recipients of an e-mail message using methods like @code{mu:from}, @code{mu:to} etc.; @xref{Message methods}. These procedures return the list of recipients as a single string; however, often it is more useful to deal with recipients as separate objects. @menu * Contact procedures and objects:: * All contacts:: * Utility procedures:: * Example - mutt export:: @end menu @node Contact procedures and objects @section Contact procedures and objects Message objects (@pxref{Messages}) have a method @t{mu:contacts}: @code{(mu:contacts [])} The @t{} is a symbol, one of @code{mu:to}, @code{mu:from}, @code{mu:cc} or @code{mu:bcc}. This will then get the contact objects for the contacts of the corresponding type. If you leave out the contact-type (or specify @t{#t} for it, you will get a list of @emph{all} contact objects for the message. A contact object (@code{}) has two methods: @itemize @item @code{mu:name} returns the name of the contact, or #f if there is none @item @code{mu:email} returns the e-mail address of the contact, or #f if there is none @end itemize Let's get a list of all names and e-mail addresses in the 'To:' field, of messages matching 'book': @lisp (use-modules (mu)) (mu:initialize) (mu:for-each-message (lambda (msg) (for-each (lambda (contact) (format #t "~a => ~a\n" (or (mu:email contact) "") (or (mu:name contact) "no-name"))) (mu:contacts msg mu:field:to))) "book") @end lisp This shows what the methods do, but for many uses, it would be more useful to have each of the contacts only show up @emph{once} - for that, please refer to @xref{All contacts}. @node All contacts @section All contacts Sometimes you may want to inspect @emph{all} the different contacts in the @t{mu} database. This is useful, for instance, when exporting contacts to some external format that can then be important in an e-mail program. To enable this, there is the procedure @code{mu:for-each-contact}, defined as @code{(mu:for-each-contact procedure [search-expression])}. This will aggregate the unique contacts from @emph{all} messages matching @t{} (when it is left empty, it will match all messages in the database), and execute @t{procedure} for each of them. The @t{procedure} receives an object of the type @t{}, which is a @emph{subclass} of the @t{} class discussed in @xref{Contact procedures and objects}. @t{} objects expose the following additional methods: @itemize @item @code{(mu:frequency )}: returns the @emph{number of times} this contact occured in one of the address fields @item @code{(mu:last-seen )}: returns the @emph{most recent time} the contact was seen in one of the address fields, as a @t{time_t} value @end itemize The method assumes an e-mail address is unique for a certain contact; if a certain e-mail address occurs with different names, it uses the most recent non-empty name. @node Utility procedures @section Utility procedures To make dealing with contacts even easier, there are a number of utility procedures that can save you a bit of typing. For converting contacts to some textual form, there is @code{(mu:contact->string format)}, which takes a contact and returns a text string with the given format. Currently supported formats are @t{"org-contact}, @t{"mutt-alias"}, @t{"mutt-ab"}, @t{"wanderlust"} and @t{"plain"}. @node Example - mutt export @section Example - mutt export Let's see how we could export the addresses in the @t{mu} database to the addressbook format of the venerable @t{mutt}@footnote{@url{http://www.mutt.org/}} e-mail client. The addressbook format that @t{mutt} uses is a sequence of lines that look something like: @verbatim alias [] "<" ">" @end verbatim @t{mu guile} provides the procedure @code{(mu:contact->string format)} that we can use to do the conversion. We may want to focus on people with whom we have frequent correspondence; so we may want to limit ourselves to people we have seen at least 10 times in the last year. It is a bit hard to @emph{guess} the nick name for e-mail contacts, but @code{mu:contact->string} tries something based on the name. You can always adjust them later by hand, obviously. @lisp #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu)) (mu:initialize) ;; Get a list of contacts that were seen at least 20 times since 2010 (define (selected-contacts) (let ((addrs '()) (start (car (mktime (car (strptime "%F" "2010-01-01"))))) (minfreq 20)) (mu:for-each-contact (lambda (contact) (if (and (mu:email contact) (>= (mu:frequency contact) minfreq) (>= (mu:last-seen contact) start)) (set! addrs (cons contact addrs))))) addrs)) (for-each (lambda (contact) (format #t "~a\n" (mu:contact->string contact "mutt-alias"))) (selected-contacts)) @end lisp This simple program could be improved in many ways; this is left as an excercise to the reader. @node Attachments and other parts @chapter Attachments and other parts To deal with @emph{attachments}, or, more in general @emph{MIME-parts}, there is the @t{mu part} module. @menu * Parts and their methods:: * Attachment example:: @end menu @node Parts and their methods @section Parts and their methods The module defines the @code{} class, and adds two methods to @code{} objects: @itemize @item @code{(mu:parts msg)} - returns a list @code{} objects, one for each MIME-parts in the message. @item @code{(mu:attachments msg)} - like @code{parts}, but only list those MIME-parts that look like proper attachments. @end itemize A @code{} object exposes a few methods to get information about the part: @itemize @item @code{(mu:name )} - returns the file name of the mime-part, or @code{#f} if there is none. @item @code{(mu:mime-type )} - returns the mime-type of the mime-part, or @code{#f} if there is none. @item @code{(mu:size )} - returns the size in bytes of the mime-part @end itemize Then, we may want to save the part to a file; this can be done using either: @itemize @item @code{(mu:save part )} - save a part to a temporary file, return the file name@footnote{the temporary filename is a predictable procedure of (user-id, msg-path, part-index)} @item @code{(mu:save-as )} - save part to file at path @end itemize @node Attachment example @section Attachment example Let's look at some small example. Let's get a list of the biggest attachments in messages about Luxemburg: @lisp #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu)) (mu:initialize) (define (all-attachments expr) "Return a list of (name . size) for all attachments in messages matching EXPR." (let ((pairs '())) (mu:for-each-message (lambda (msg) (for-each (lambda (att) ;; add (filename . size) to the list (set! pairs (cons (cons (mu:name att) (or (mu:size att) 0)) pairs))) (mu:attachments msg))) expr) pairs)) (for-each (lambda (att) (format #t "~a: ~,1fKb\n" (car att) (exact->inexact (/ (cdr att) 1024)))) (sort (all-attachments "Luxemburg") (lambda (att1 att2) (< (cdr att1) (cdr att2))))) @end lisp As an exercise for the reader, you might want to re-rewrite the @code{all-attachments} in terms of @code{mu:message-list}, which would probably be a bit more elegant. @node Statistics @chapter Statistics @t{mu-guile} offers some convenience procedures to determine various statistics about the messages in the database. @menu * Basics:: @code{mu:count}, @code{mu:average}, ... * Tabulating values:: @code{mu:tabulate} * Most frequent values:: @code{mu:top-n-most-frequent} @end menu @node Basics @section Basics Let's look at some of the basic statistical operations available, in an interactive session: @example GNU Guile 2.0.5.123-4bd53 Copyright (C) 1995-2012 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@@(guile-user)> ;; load modules, initialize mu scheme@@(guile-user)> (use-modules (mu) (mu stats)) scheme@@(guile-user)> (mu:initialize) scheme@@(guile-user)> scheme@@(guile-user)> ;; count the number of messages with 'hello' in their subject scheme@@(guile-user)> (mu:count "subject:hello") $1 = 162 scheme@@(guile-user)> ;; average the size of messages with hello in their subject scheme@@(guile-user)> (mu:average mu:size "subject:hello") $2 = 34597733/81 scheme@@(guile-user)> (exact->inexact $2) $3 = 427132.506172839 scheme@@(guile-user)> ;; calculate the correlation between message size and scheme@@(guile-user)> ;; subject length scheme@@(guile-user)> (mu:correl mu:size (lambda (msg) (string-length (mu:subject msg))) "subject:hello") $5 = -0.10804368622292 scheme@@(guile-user)> @end example @node Tabulating values @section Tabulating values @code{(mu:tabulate [])} applies @t{} to each message matching @t{} (leave empty to match @emph{all} messages), and returns a associative list (a list of pairs) with each of the different results of @t{} and their frequencies. For fields that contain lists of values (such as address-fields), each of the values in the list is added separately. @subsection Example: messages per weekday We demonstrate @code{mu:tabulate} with an example. Suppose we want to know how many messages we receive per weekday: @lisp #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu) (mu stats) (mu plot)) (mu:initialize) ;; create a list like (("Sun" . 13) ("Mon" . 23) ...) (define weekday-table (mu:weekday-numbers->names (sort (mu:tabulate (lambda (msg) (tm:wday (localtime (mu:date msg))))) (lambda (a b) (< (car a) (car b)))))) (for-each (lambda (elm) (format #t "~a: ~a\n" (car elm) (cdr elm))) weekday-table) @end lisp The procedure @code{weekday-table} uses @code{mu:tabulate-message} to get the frequencies per hour -- this returns a list of pairs: @verbatim ((5 . 2339) (0 . 2278) (4 . 2800) (2 . 3184) (6 . 1856) (3 . 2833) (1 . 2993)) @end verbatim We sort these pairs by the day number, and then apply @code{mu:weekday-numbers->names}, which takes the list, and returns a list where the day numbers are replace by there abbreviated name (in the current locale). Note, there is also @code{mu:month-numbers->names}. The script then outputs these numbers in the following form: @verbatim Sun: 2278 Mon: 2993 Tue: 3184 Wed: 2833 Thu: 2800 Fri: 2339 Sat: 1856 @end verbatim Clearly, Saturday is a slow day for e-mail... @node Most frequent values @section Most frequent values In the above example, the number of values is small (the seven weekdays); however, in many cases there can be many different values (for example, all different message subjects), many of which may not be very interesting -- all we need to know is the top-10 of most frequently seen values. This is fairly easy to achieve using @code{mu:tabulate} -- to get the top-10 subjects@footnote{this requires the @code{(srfi srfi-1)}-module}, we can use something like this: @lisp (take (sort (mu:tabulate mu:subject) (lambda (a b) (> (cdr a) (cdr b)))) 10) @end lisp If this is not short enough, @t{mu-guile} offers a convenience procedure to do this: @code{mu:top-n-most-frequent}. For example, to get the top-10 people we sent mail to most often: @lisp (mu:top-n-most-frequent mu:to 10 "maildir:/sent") @end lisp Can't make it much easier than that! @node Plotting data @chapter Plotting data You can plot the results in the format produced by @code{mu:tabulate} with the @t{(mu plot)} module, an experimental module that requires the @t{gnuplot}@footnote{@url{http://www.gnuplot.info/}} program to be installed on your system. The @code{mu:plot-histogram} procedure takes the following arguments: @code{(mu:plot-histogram <x-label> <y-label> [<want-ascii>])} Here, @code{<data>} is a table of data in the format that @code{mu:tabulate} produces. @code{<title>}, @code{<x-label>} and @code{<y-lablel>} are, respectively, the title of the graph, and the labels for X- and Y-axis. Finally, if you pass @t{#t} for the final @code{<want-ascii>} parameter, a plain-text rendering of the graph will be produced; otherwise, a graphical window will be shown. An example should clarify how this works in practice; let's plot the number of message per hour: @lisp #!/bin/sh exec guile -s $0 $@ !# (use-modules (mu) (mu stats) (mu plot)) (mu:initialize) (define (mail-per-hour-table) (sort (mu:tabulate (lambda (msg) (tm:hour (localtime (mu:date msg))))) (lambda (x y) (< (car x) (car y))))) (mu:plot-histogram (mail-per-hour-table) "Mail per hour" "Hour" "Frequency") @end lisp @cartouche @verbatim Mail per hour Frequency 1200 ++--+--+--+--+-+--+--+--+--+-+--+--+--+-+--+--+--+--+-+--+--+--+--++ |+ + + + + + + "/tmp/fileHz7D2u" using 2:xticlabels(1) ******** 1100 ++ *** +* **** * * * 1000 *+ * **** * +* * * ****** **** * ** * * 900 *+ * * ** **** * **** ** * +* * * * ** * * ********* * ** ** * * 800 *+ * **** ** * * * * ** * * ** ** * +* 700 *+ *** **** * ** * * * * ** **** * ** ** * +* * * * **** * * ** * * * * ** * **** ** ** * * 600 *+ * **** * * * * ** * * * * ** * * * ** ** * +* * * ** * * * * * ** * * * * ** * * * ** ** * * 500 *+ * ** * * * * * ** * * * * ** * * * ** ** * +* * * ** **** *** * * * ** * * * * ** * * * ** ** * * 400 *+ * ** ** **** * * * * * ** * * * * ** * * * ** ** * +* *+ *+**+**+* +*******+* +* +*+ *+**+* +*+ *+ *+**+* +*+ *+**+**+* +* 300 ******************************************************************** 0 1 2 3 4 5 6 7 8 910 11 12 1314 15 16 17 1819 20 21 22 23 Hour @end verbatim @end cartouche @node Writing scripts @chapter Writing scripts The @t{mu} program has built-in support for running guile-scripts, and comes with a number of examples. You can get a list of all scripts with the @t{mu script} command: @verbatim $ mu script Available scripts (use --verbose for details): * find-dups: find duplicate messages * msgs-count: count the number of messages matching some query * msgs-per-day: graph the number of messages per day * msgs-per-hour: graph the number of messages per hour * msgs-per-month: graph the number of messages per month * msgs-per-year: graph the number of messages per year * msgs-per-year-month: graph the number of messages per year-month @end verbatim You can then execute such a script by its name: @verbatim $ mu msgs-per-month --textonly --query=hello Messages per month matching hello 240 ++-+-----+----+-----+-----+-----+----+-----+-----+-----+----+-----+-++ | + + + + "/tmp/filewi9H0N" using 2:xticlabels(1) ****** | 220 ++ * * ****** | * * * * 200 ++ * * * +* | * * * * 180 ++ ****** * * * +* | * * * * * * 160 ****** * * * * * +* * * * * * * * * * ******* * * * * ****** * * 140 *+ ** * * * * * * ******** +* * ** ******* * * * * * ** ** * 120 *+ ** ** ******* * * * * ** ** +* * ** ** ** * * * ******* ** ** * 100 *+ ** ** ** * * * * ** ** ** +* * + ** + ** + ** + * + * + + * + * + ** + ** + ** + * 80 ********************************************************************** Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Month @end verbatim Please refer to the @t{mu-script} man-page for some details on writing your own scripts. @node Recipies @appendix Recipies @itemize @item Calculating the average length of subject-lines @lisp ;; the average length of all our (let ((len 0) (n 0)) (mu:for-each-message (lambda (msg) (set! len (+ len (string-length (or (mu:subject msg) "")))) (set! n (+ n 1)))) (if (= n 0) 0 (/ len n))) ;; this gives a rational, exact result; ;; use exact->inexact to get decimals ;; we we can make this short with the mu:average (with (mu stats)) (mu:average (lambda (msg) (string-length (or (mu:subject msg) "")))) @end lisp @end itemize @node GNU Free Documentation License @appendix GNU Free Documentation License @include fdl.texi @bye ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/guile/mu-guile.info�����������������������������������������������������������������������0000644�0001750�0001750�00000161156�13021037647�012676� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������This is mu-guile.info, produced by makeinfo version 6.1 from mu-guile.texi. Copyright © 2012 Dirk-Jan C. Binnema Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled “GNU Free Documentation License.” INFO-DIR-SECTION The Algorithmic Language Scheme START-INFO-DIR-ENTRY * Mu-guile: (mu-guile). Guile-bindings for the mu e-mail indexer/searcher END-INFO-DIR-ENTRY  File: mu-guile.info, Node: Top, Next: Getting started, Up: (dir) mu-guile manual *************** Welcome to mu-guile! mu is a program for indexing and searching your e-mails. It can search your messages in many different ways, but sometimes that may not be enough. If you have very specific queries, or want do generate some statistics, you need some more power. mu-guile is made for those cases. mu-guile exposes the internals of mu and its database to the guile programming language. Guile is the _GNU Ubiquitous Intelligent Language for Extensions_ - a version of the _Scheme_ programming language and the official GNU extension language. Guile/Scheme is a member of the _Lisp_ family of programming languages – like emacs-lisp, _Racket_, Common Lisp. If you’re not familiar with Scheme, mu-guile is an excellent opportunity to learn a bit about! Trust me, it’s not very hard – and it it’s _fun_! * Menu: * Getting started:: * Initializing mu-guile:: * Messages:: * Contacts:: * Attachments and other parts:: * Statistics:: * Plotting data:: * Writing scripts:: Appendices * Recipies:: Snippets do specific things * GNU Free Documentation License:: The license of this manual.  File: mu-guile.info, Node: Getting started, Next: Initializing mu-guile, Prev: Top, Up: Top 1 Getting started ***************** * Menu: * Installation:: * Making sure it works:: This chapter walks you through the installation and the basic setup.  File: mu-guile.info, Node: Installation, Next: Making sure it works, Up: Getting started 1.1 Installation ================ mu-guile is part of mu - by installing the latter, the former is necessarily installed as well. At the time of writing, there are no distribution-provided packaged versions of mu-guile; so for now, you need to follow the steps below. 1.1.1 Guile 2.x --------------- mu-guile is built automatically when mu is built, if you have guile version 2 or higher. (mu checks for this during configure). Thus, the first step is to ensure you have guile installed. On Debian/Ubuntu you can install guile 2.x using the guile-2.0-dev package (and its dependencies): $ sudo apt-get install guile-2.0-dev At the time of writing, there are no official packages for Fedora(1). If you are using Fedora or any other system that does not have packages, you need to compile guile from source(2). 1.1.2 gnuplot ------------- For creating graphs with mu-guile, you need the gnuplot program – most likely, there is a package available for your system; for example: $ sudo apt-get install gnuplot and in Fedora: $ sudo yum install gnuplot 1.1.3 mu -------- Assuming guile 2.x is installed correctly, mu finds it during its configure-stage, and creates mu-guile. Building mu follows the normal steps – please see the mu documentation for the details. The output of ./configure should end with a little text describing the detected versions of various libraries mu depends on. In particular, it should mention the guile version, e.g. Guile version : 2.0.3.82-a2c66 If you don’t see any line referring to guile, please install it, and run configure again. After a succesfull ./configure, we can make and install the package: $ make && sudo make install 1.1.4 mu-guile -------------- After this, mu and mu-guile are installed – usually somewhere under /usr/local.You may need to update guile’s ‘%load-path’ to find it there. You can check the current ‘%load-path’ with the following: guile -c '(display %load-path)(newline)' If necessary, you can add the %load-path by adding to your ‘~/.guile’: (set! %load-path (cons "/usr/local/share/guile/site/2.0" %load-path)) Or, alternatively, you can set GUILE_LOAD_PATH: export GUILE_LOAD_PATH="/usr/local/share/guile/site/2.0" In both cases the directory should be the directory that contains the installed mu.scm; if you installed mu under a different prefix, you must change the ‘%load-path’ accordingly. After this, you should be ready to go! ---------- Footnotes ---------- (1) <https://bugzilla.redhat.com/show_bug.cgi?id=678238> (2) <http://www.gnu.org/software/guile/manual/html_node/Obtaining-and-Installing-Guile.html#Obtaining-and-Installing-Guile>  File: mu-guile.info, Node: Making sure it works, Prev: Installation, Up: Getting started 1.2 Making sure it works ======================== Assuming mu-guile has been installed correctly (*note Installation::), and also assuming that you have already indexed your e-mail messages (if necessary, see the mu-index man-page), we are ready to start mu-guile; a session may look something like this: GNU Guile 2.0.5.123-4bd53 Copyright (C) 1995-2012 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> Now, copy-paste the following after the prompt: (use-modules (mu)) (mu:initialize) (for-each (lambda(msg) (format #t "Subject: ~a\n" (mu:subject msg))) (mu:message-list "hello")) After pressing <Enter>, you should get a list of all subjects of messages that match hello: ... Subject: RE: The Bird Serpent War Cataclysm Subject: Hello! Subject: Re: post-run tomorrow Subject: When all is lost ... If all this works, congratulations! mu-guile is installed now, ready to serve your every searching need!  File: mu-guile.info, Node: Initializing mu-guile, Next: Messages, Prev: Getting started, Up: Top 2 Initializing mu-guile *********************** We now have installed mu-guile, and in *note Making sure it works:: confirmed that things work by trying some simple script. In this and the following chapters, we take a closer look at programming with mu-guile. It is possible to write separate programs with mu-guile, but for now we’ll do things _interactively_, that is, from the Guile-prompt (“REPL”). As we have seen, we start our mu-guile session by starting guile: $ guile GNU Guile 2.0.5.123-4bd53 Copyright (C) 1995-2012 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> The first thing we need to do is loading the modules. All the basics are in the (mu) module, with some statistical extras in (mu stats), and some graph plotting functionality in (mu plot)(1). Let’s load all of them: scheme@(guile-user)> (use-modules (mu) (mu stats) (mu plot)) The first time you do this, guile will probably respond by showing some messages about compiling the modules, and then return to you with another prompt. Before we can do anything with mu guile, we need to initialize the system: scheme@(guile-user)> (mu:initialize) This opens the database for reading, using the default location of ‘~/.mu’(2) Now, mu-guile is ready to go. In the next chapter, we go through the modules and show what you can do with them. ---------- Footnotes ---------- (1) ‘(mu plot)’ requires the gnuplot program (2) If you keep your mu database in a non-standard place, use ‘(mu:initialize "/path/to/my/mu/")’  File: mu-guile.info, Node: Messages, Next: Contacts, Prev: Initializing mu-guile, Up: Top 3 Messages ********** In this chapter, we discuss searching messages and doing things with them. * Menu: * Finding messages:: query for messages in the databse * Message methods:: what methods are available for messages? * Example - the longest subject:: find the messages with the longest subject  File: mu-guile.info, Node: Finding messages, Next: Message methods, Up: Messages 3.1 Finding messages ==================== Now we are ready to retrieve some messages from the system. There are two main procedures to do this: • ‘(mu:message-list [<search-expression>])’ • ‘(mu:for-each-message <procedure> [<search-expression>])’ The first procedure, ‘mu:message-list’ returns a list of all messages matching <search-expression>; if you leave <search-expression> out, it returns _all_ messages. For example, to get all messages with coffee in the subject line: scheme@(guile-user)> (mu:message-list "subject:coffee") $1 = (#<<mu:message> 9040640> #<<mu:message> 9040630> #<<mu:message> 9040570>) Apparently, we have three messages matching subject:coffee, so we get a list of three ‘<mu:message>’ objects. Let’s just use the ‘mu:subject’ procedure (’method’) provided by ‘<mu:message>’ objects to retrieve the subject-field (more about methods in the next section). For your convenience, guile has saved the result of our last query in a variable called $1, so to get the subject of the first message in the list, we can do: scheme@(guile-user)> (mu:subject (car $1)) $2 = "Re: best coffee ever!" The second procedure we mentioned, ‘mu:for-each-message’, executes some procedure for each message matched by the search expression (or _all_ messages if the search expression is omitted): scheme@(guile-user)> (mu:for-each-message (lambda(msg) (display (mu:subject msg)) (newline)) "subject:coffee") Re: best coffee ever! best coffee ever! Coffee beans scheme@(guile-user)> Using ‘mu:message-list’ and/or ‘mu:for-each-message’(1) and a couple of <mu:message> methods, together with what Guile/Scheme provides, should allow for many interesting programs. ---------- Footnotes ---------- (1) Implementation node: ‘mu:message-list’ is implemented in terms of ‘mu:for-each-message’, not the other way around. Due to the way mu works, ‘mu:for-each-message’ is rather more efficient than a combination of ‘for-each’ and ‘mu:message-list’  File: mu-guile.info, Node: Message methods, Next: Example - the longest subject, Prev: Finding messages, Up: Messages 3.2 Message methods =================== Now that we’ve seen how to retrieve lists of message objects (‘<mu:message>’), let’s see what we can do with such an object. ‘<mu:message>’ defines the following methods that all take a single ‘<mu:message>’ object as a parameter. We won’t go into the exact meanings for all of these procedures here - for the details about various flags / properties, please refer to the mu-find man-page. • ‘(mu:bcc msg)’: the Bcc field of the message, or #f if there is none • ‘(mu:body-html msg)’: : the html body of the message, or #f if there is none • ‘(mu:body-txt msg)’: the plain-text body of the message, or #f if there is none • ‘(mu:cc msg)’: the Bcc field of the message, or #f if there is none • ‘(mu:date msg)’: the Date field of the message, or 0 if there is none • ‘(mu:flags msg)’: list of message-flags for this message • ‘(mu:from msg)’: the From field of the message, or #f if there is none • ‘(mu:maildir msg)’: the maildir this message lives in, or #f if there is none • ‘(mu:message-id msg)’: the Message-Id field of the message, or #f if there is none • ‘(mu:path msg)’: the file system path for this message • ‘(mu:priority msg)’: the priority of this message (either mu:prio:low, mu:prio:normal or mu:prio:high • ‘(mu:references msg)’: the list of messages (message-ids) this message refers to in(mu: the References: header • ‘(mu:size msg)’: size of the message in bytes • ‘(mu:subject msg)’: the Subject field of the message, or #f if there is none. • ‘(mu:tags msg)’: list of tags for this message • ‘(mu:timestamp msg)’: the timestamp (mtime) of the message file, or #f if there is none. message file • ‘(mu:to msg)’: the sender of the message, or #f if there is none With these methods, we can query messages for their properties; for example: scheme@(guile-user)> (define msg (car (mu:message-list "snow"))) scheme@(guile-user)> (mu:subject msg) $1 = "Re: Running in the snow is beautiful" scheme@(guile-user)> (mu:flags msg) $2 = (mu:flag:replied mu:flag:seen) scheme@(guile-user)> (strftime "%F" (localtime (mu:date msg))) $3 = "2011-01-15" There are a couple more methods: • ‘(mu:header msg "<header-name>")’ returns an arbitrary message header (or #f if not found) – e.g. ‘(header msg "User-Agent")’ • If you include the mu contact module, the ‘(mu:contacts msg [contact-type])’ method (to get a list of contacts) is added. *Note Contacts::. • If you include the mu part module, the ‘((mu:parts msg)’ and ‘(mu:attachments msg)’ methods are added. *Note Attachments and other parts::.  File: mu-guile.info, Node: Example - the longest subject, Prev: Message methods, Up: Messages 3.3 Example - the longest subject ================================= Now, let’s write a little example – let’s find out what is the _longest subject_ of any e-mail messages we received in the year 2011. You can try this if you put the following in a separate file, make it executable, and run it like any program. #!/bin/sh exec guile -s $0 $ !# (use-modules (mu)) (use-modules (srfi srfi-1)) (mu:initialize) ;; note: (subject msg) => #f if there is no subject (define list-of-subjects (map (lambda (msg) (or (mu:subject msg) "")) (mu:message-list "date:2011..2011"))) ;; see the mu-find manpage for the date syntax (define longest-subject (fold (lambda (subj1 subj2) (if (> (string-length subj1) (string-length subj2)) subj1 subj2)) "" list-of-subjects)) (format #t "Longest subject: ~s\n" longest-subject) There are many other ways to solve the same problem, for example by using an iterative approach with ‘mu:for-each-message’, but it should show how one can easily write little programs to answer specific questions about your e-mail corpus.  File: mu-guile.info, Node: Contacts, Next: Attachments and other parts, Prev: Messages, Up: Top 4 Contacts ********** We can retrieve the sender and recipients of an e-mail message using methods like ‘mu:from’, ‘mu:to’ etc.; *Note Message methods::. These procedures return the list of recipients as a single string; however, often it is more useful to deal with recipients as separate objects. * Menu: * Contact procedures and objects:: * All contacts:: * Utility procedures:: * Example - mutt export::  File: mu-guile.info, Node: Contact procedures and objects, Next: All contacts, Up: Contacts 4.1 Contact procedures and objects ================================== Message objects (*note Messages::) have a method mu:contacts: ‘(mu:contacts <message-object> [<contact-type>])’ The <contact-type> is a symbol, one of ‘mu:to’, ‘mu:from’, ‘mu:cc’ or ‘mu:bcc’. This will then get the contact objects for the contacts of the corresponding type. If you leave out the contact-type (or specify #t for it, you will get a list of _all_ contact objects for the message. A contact object (‘<mu:contact>’) has two methods: • ‘mu:name’ returns the name of the contact, or #f if there is none • ‘mu:email’ returns the e-mail address of the contact, or #f if there is none Let’s get a list of all names and e-mail addresses in the ’To:’ field, of messages matching ’book’: (use-modules (mu)) (mu:initialize) (mu:for-each-message (lambda (msg) (for-each (lambda (contact) (format #t "~a => ~a\n" (or (mu:email contact) "") (or (mu:name contact) "no-name"))) (mu:contacts msg mu:field:to))) "book") This shows what the methods do, but for many uses, it would be more useful to have each of the contacts only show up _once_ - for that, please refer to *Note All contacts::.  File: mu-guile.info, Node: All contacts, Next: Utility procedures, Prev: Contact procedures and objects, Up: Contacts 4.2 All contacts ================ Sometimes you may want to inspect _all_ the different contacts in the mu database. This is useful, for instance, when exporting contacts to some external format that can then be important in an e-mail program. To enable this, there is the procedure ‘mu:for-each-contact’, defined as ‘(mu:for-each-contact procedure [search-expression])’. This will aggregate the unique contacts from _all_ messages matching <search-expression> (when it is left empty, it will match all messages in the database), and execute procedure for each of them. The procedure receives an object of the type <mu:contact-with-stats>, which is a _subclass_ of the <mu:contact> class discussed in *Note Contact procedures and objects::. <mu:contact-with-stats> objects expose the following additional methods: • ‘(mu:frequency <contact>)’: returns the _number of times_ this contact occured in one of the address fields • ‘(mu:last-seen <contact>)’: returns the _most recent time_ the contact was seen in one of the address fields, as a time_t value The method assumes an e-mail address is unique for a certain contact; if a certain e-mail address occurs with different names, it uses the most recent non-empty name.  File: mu-guile.info, Node: Utility procedures, Next: Example - mutt export, Prev: All contacts, Up: Contacts 4.3 Utility procedures ====================== To make dealing with contacts even easier, there are a number of utility procedures that can save you a bit of typing. For converting contacts to some textual form, there is ‘(mu:contact->string <mu:contact> format)’, which takes a contact and returns a text string with the given format. Currently supported formats are "org-contact, "mutt-alias", "mutt-ab", "wanderlust" and "plain".  File: mu-guile.info, Node: Example - mutt export, Prev: Utility procedures, Up: Contacts 4.4 Example - mutt export ========================= Let’s see how we could export the addresses in the mu database to the addressbook format of the venerable mutt(1) e-mail client. The addressbook format that mutt uses is a sequence of lines that look something like: alias <nick> [<name>] "<" <email> ">" mu guile provides the procedure ‘(mu:contact->string <mu:contact> format)’ that we can use to do the conversion. We may want to focus on people with whom we have frequent correspondence; so we may want to limit ourselves to people we have seen at least 10 times in the last year. It is a bit hard to _guess_ the nick name for e-mail contacts, but ‘mu:contact->string’ tries something based on the name. You can always adjust them later by hand, obviously. #!/bin/sh exec guile -s $0 $ !# (use-modules (mu)) (mu:initialize) ;; Get a list of contacts that were seen at least 20 times since 2010 (define (selected-contacts) (let ((addrs '()) (start (car (mktime (car (strptime "%F" "2010-01-01"))))) (minfreq 20)) (mu:for-each-contact (lambda (contact) (if (and (mu:email contact) (>= (mu:frequency contact) minfreq) (>= (mu:last-seen contact) start)) (set! addrs (cons contact addrs))))) addrs)) (for-each (lambda (contact) (format #t "~a\n" (mu:contact->string contact "mutt-alias"))) (selected-contacts)) This simple program could be improved in many ways; this is left as an excercise to the reader. ---------- Footnotes ---------- (1) <http://www.mutt.org/>  File: mu-guile.info, Node: Attachments and other parts, Next: Statistics, Prev: Contacts, Up: Top 5 Attachments and other parts ***************************** To deal with _attachments_, or, more in general _MIME-parts_, there is the mu part module. * Menu: * Parts and their methods:: * Attachment example::  File: mu-guile.info, Node: Parts and their methods, Next: Attachment example, Up: Attachments and other parts 5.1 Parts and their methods =========================== The module defines the ‘<mu-part>’ class, and adds two methods to ‘<mu:message>’ objects: • ‘(mu:parts msg)’ - returns a list ‘<mu-part>’ objects, one for each MIME-parts in the message. • ‘(mu:attachments msg)’ - like ‘parts’, but only list those MIME-parts that look like proper attachments. A ‘<mu:part>’ object exposes a few methods to get information about the part: • ‘(mu:name <part>)’ - returns the file name of the mime-part, or ‘#f’ if there is none. • ‘(mu:mime-type <part>)’ - returns the mime-type of the mime-part, or ‘#f’ if there is none. • ‘(mu:size <part>)’ - returns the size in bytes of the mime-part Then, we may want to save the part to a file; this can be done using either: • ‘(mu:save part <part>)’ - save a part to a temporary file, return the file name(1) • ‘(mu:save-as <part> <path>)’ - save part to file at path ---------- Footnotes ---------- (1) the temporary filename is a predictable procedure of (user-id, msg-path, part-index)  File: mu-guile.info, Node: Attachment example, Prev: Parts and their methods, Up: Attachments and other parts 5.2 Attachment example ====================== Let’s look at some small example. Let’s get a list of the biggest attachments in messages about Luxemburg: #!/bin/sh exec guile -s $0 $ !# (use-modules (mu)) (mu:initialize) (define (all-attachments expr) "Return a list of (name . size) for all attachments in messages matching EXPR." (let ((pairs '())) (mu:for-each-message (lambda (msg) (for-each (lambda (att) ;; add (filename . size) to the list (set! pairs (cons (cons (mu:name att) (or (mu:size att) 0)) pairs))) (mu:attachments msg))) expr) pairs)) (for-each (lambda (att) (format #t "~a: ~,1fKb\n" (car att) (exact->inexact (/ (cdr att) 1024)))) (sort (all-attachments "Luxemburg") (lambda (att1 att2) (< (cdr att1) (cdr att2))))) As an exercise for the reader, you might want to re-rewrite the ‘all-attachments’ in terms of ‘mu:message-list’, which would probably be a bit more elegant.  File: mu-guile.info, Node: Statistics, Next: Plotting data, Prev: Attachments and other parts, Up: Top 6 Statistics ************ mu-guile offers some convenience procedures to determine various statistics about the messages in the database. * Menu: * Basics:: ‘mu:count’, ‘mu:average’, ... * Tabulating values:: ‘mu:tabulate’ * Most frequent values:: ‘mu:top-n-most-frequent’  File: mu-guile.info, Node: Basics, Next: Tabulating values, Up: Statistics 6.1 Basics ========== Let’s look at some of the basic statistical operations available, in an interactive session: GNU Guile 2.0.5.123-4bd53 Copyright (C) 1995-2012 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> ;; load modules, initialize mu scheme@(guile-user)> (use-modules (mu) (mu stats)) scheme@(guile-user)> (mu:initialize) scheme@(guile-user)> scheme@(guile-user)> ;; count the number of messages with 'hello' in their subject scheme@(guile-user)> (mu:count "subject:hello") $1 = 162 scheme@(guile-user)> ;; average the size of messages with hello in their subject scheme@(guile-user)> (mu:average mu:size "subject:hello") $2 = 34597733/81 scheme@(guile-user)> (exact->inexact $2) $3 = 427132.506172839 scheme@(guile-user)> ;; calculate the correlation between message size and scheme@(guile-user)> ;; subject length scheme@(guile-user)> (mu:correl mu:size (lambda (msg) (string-length (mu:subject msg))) "subject:hello") $5 = -0.10804368622292 scheme@(guile-user)>  File: mu-guile.info, Node: Tabulating values, Next: Most frequent values, Prev: Basics, Up: Statistics 6.2 Tabulating values ===================== ‘(mu:tabulate <procedure> [<search-expr>])’ applies <procedure> to each message matching <search-expr> (leave empty to match _all_ messages), and returns a associative list (a list of pairs) with each of the different results of <procedure> and their frequencies. For fields that contain lists of values (such as address-fields), each of the values in the list is added separately. 6.2.1 Example: messages per weekday ----------------------------------- We demonstrate ‘mu:tabulate’ with an example. Suppose we want to know how many messages we receive per weekday: #!/bin/sh exec guile -s $0 $ !# (use-modules (mu) (mu stats) (mu plot)) (mu:initialize) ;; create a list like (("Sun" . 13) ("Mon" . 23) ...) (define weekday-table (mu:weekday-numbers->names (sort (mu:tabulate (lambda (msg) (tm:wday (localtime (mu:date msg))))) (lambda (a b) (< (car a) (car b)))))) (for-each (lambda (elm) (format #t "~a: ~a\n" (car elm) (cdr elm))) weekday-table) The procedure ‘weekday-table’ uses ‘mu:tabulate-message’ to get the frequencies per hour – this returns a list of pairs: ((5 . 2339) (0 . 2278) (4 . 2800) (2 . 3184) (6 . 1856) (3 . 2833) (1 . 2993)) We sort these pairs by the day number, and then apply ‘mu:weekday-numbers->names’, which takes the list, and returns a list where the day numbers are replace by there abbreviated name (in the current locale). Note, there is also ‘mu:month-numbers->names’. The script then outputs these numbers in the following form: Sun: 2278 Mon: 2993 Tue: 3184 Wed: 2833 Thu: 2800 Fri: 2339 Sat: 1856 Clearly, Saturday is a slow day for e-mail...  File: mu-guile.info, Node: Most frequent values, Prev: Tabulating values, Up: Statistics 6.3 Most frequent values ======================== In the above example, the number of values is small (the seven weekdays); however, in many cases there can be many different values (for example, all different message subjects), many of which may not be very interesting – all we need to know is the top-10 of most frequently seen values. This is fairly easy to achieve using ‘mu:tabulate’ – to get the top-10 subjects(1), we can use something like this: (take (sort (mu:tabulate mu:subject) (lambda (a b) (> (cdr a) (cdr b)))) 10) If this is not short enough, mu-guile offers a convenience procedure to do this: ‘mu:top-n-most-frequent’. For example, to get the top-10 people we sent mail to most often: (mu:top-n-most-frequent mu:to 10 "maildir:/sent") Can’t make it much easier than that! ---------- Footnotes ---------- (1) this requires the ‘(srfi srfi-1)’-module  File: mu-guile.info, Node: Plotting data, Next: Writing scripts, Prev: Statistics, Up: Top 7 Plotting data *************** You can plot the results in the format produced by ‘mu:tabulate’ with the (mu plot) module, an experimental module that requires the gnuplot(1) program to be installed on your system. The ‘mu:plot-histogram’ procedure takes the following arguments: ‘(mu:plot-histogram <data> <title> <x-label> <y-label> [<want-ascii>])’ Here, ‘<data>’ is a table of data in the format that ‘mu:tabulate’ produces. ‘<title>’, ‘<x-label>’ and ‘<y-lablel>’ are, respectively, the title of the graph, and the labels for X- and Y-axis. Finally, if you pass #t for the final ‘<want-ascii>’ parameter, a plain-text rendering of the graph will be produced; otherwise, a graphical window will be shown. An example should clarify how this works in practice; let’s plot the number of message per hour: #!/bin/sh exec guile -s $0 $ !# (use-modules (mu) (mu stats) (mu plot)) (mu:initialize) (define (mail-per-hour-table) (sort (mu:tabulate (lambda (msg) (tm:hour (localtime (mu:date msg))))) (lambda (x y) (< (car x) (car y))))) (mu:plot-histogram (mail-per-hour-table) "Mail per hour" "Hour" "Frequency") Mail per hour Frequency 1200 ++--+--+--+--+-+--+--+--+--+-+--+--+--+-+--+--+--+--+-+--+--+--+--++ |+ + + + + + + "/tmp/fileHz7D2u" using 2:xticlabels(1) ******** 1100 ++ *** +* **** * * * 1000 *+ * **** * +* * * ****** **** * ** * * 900 *+ * * ** **** * **** ** * +* * * * ** * * ********* * ** ** * * 800 *+ * **** ** * * * * ** * * ** ** * +* 700 *+ *** **** * ** * * * * ** **** * ** ** * +* * * * **** * * ** * * * * ** * **** ** ** * * 600 *+ * **** * * * * ** * * * * ** * * * ** ** * +* * * ** * * * * * ** * * * * ** * * * ** ** * * 500 *+ * ** * * * * * ** * * * * ** * * * ** ** * +* * * ** **** *** * * * ** * * * * ** * * * ** ** * * 400 *+ * ** ** **** * * * * * ** * * * * ** * * * ** ** * +* *+ *+**+**+* +*******+* +* +*+ *+**+* +*+ *+ *+**+* +*+ *+**+**+* +* 300 ******************************************************************** 0 1 2 3 4 5 6 7 8 910 11 12 1314 15 16 17 1819 20 21 22 23 Hour ---------- Footnotes ---------- (1) <http://www.gnuplot.info/>  File: mu-guile.info, Node: Writing scripts, Next: Recipies, Prev: Plotting data, Up: Top 8 Writing scripts ***************** The mu program has built-in support for running guile-scripts, and comes with a number of examples. You can get a list of all scripts with the mu script command: $ mu script Available scripts (use --verbose for details): * find-dups: find duplicate messages * msgs-count: count the number of messages matching some query * msgs-per-day: graph the number of messages per day * msgs-per-hour: graph the number of messages per hour * msgs-per-month: graph the number of messages per month * msgs-per-year: graph the number of messages per year * msgs-per-year-month: graph the number of messages per year-month You can then execute such a script by its name: $ mu msgs-per-month --textonly --query=hello Messages per month matching hello 240 ++-+-----+----+-----+-----+-----+----+-----+-----+-----+----+-----+-++ | + + + + "/tmp/filewi9H0N" using 2:xticlabels(1) ****** | 220 ++ * * ****** | * * * * 200 ++ * * * +* | * * * * 180 ++ ****** * * * +* | * * * * * * 160 ****** * * * * * +* * * * * * * * * * ******* * * * * ****** * * 140 *+ ** * * * * * * ******** +* * ** ******* * * * * * ** ** * 120 *+ ** ** ******* * * * * ** ** +* * ** ** ** * * * ******* ** ** * 100 *+ ** ** ** * * * * ** ** ** +* * + ** + ** + ** + * + * + + * + * + ** + ** + ** + * 80 ********************************************************************** Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Month Please refer to the mu-script man-page for some details on writing your own scripts.  File: mu-guile.info, Node: Recipies, Next: GNU Free Documentation License, Prev: Writing scripts, Up: Top Appendix A Recipies ******************* • Calculating the average length of subject-lines ;; the average length of all our (let ((len 0) (n 0)) (mu:for-each-message (lambda (msg) (set! len (+ len (string-length (or (mu:subject msg) "")))) (set! n (+ n 1)))) (if (= n 0) 0 (/ len n))) ;; this gives a rational, exact result; ;; use exact->inexact to get decimals ;; we we can make this short with the mu:average (with (mu stats)) (mu:average (lambda (msg) (string-length (or (mu:subject msg) ""))))  File: mu-guile.info, Node: GNU Free Documentation License, Prev: Recipies, Up: Top Appendix B GNU Free Documentation License ***************************************** Version 1.2, November 2002 Copyright © 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, 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. 0. PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document “free” in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of “copyleft”, which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. 1. APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The “Document”, below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as “you”. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A “Modified Version” of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A “Secondary Section” is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document’s overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The “Invariant Sections” are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The “Cover Texts” are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A “Transparent” copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not “Transparent” is called “Opaque”. Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only. The “Title Page” means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, “Title Page” means the text near the most prominent appearance of the work’s title, preceding the beginning of the body of the text. A section “Entitled XYZ” means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as “Acknowledgements”, “Dedications”, “Endorsements”, or “History”.) To “Preserve the Title” of such a section when you modify the Document means that it remains a section “Entitled XYZ” according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. 2. VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. 3. COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document’s license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. 4. MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. C. State on the Title page the name of the publisher of the Modified Version, as the publisher. D. Preserve all the copyright notices of the Document. E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document’s license notice. H. Include an unaltered copy of this License. I. Preserve the section Entitled “History”, Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled “History” in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the “History” section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. K. For any section Entitled “Acknowledgements” or “Dedications”, Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. M. Delete any section Entitled “Endorsements”. Such a section may not be included in the Modified Version. N. Do not retitle any existing section to be Entitled “Endorsements” or to conflict in title with any Invariant Section. O. Preserve any Warranty Disclaimers. If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version’s license notice. These titles must be distinct from any other section titles. You may add a section Entitled “Endorsements”, provided it contains nothing but endorsements of your Modified Version by various parties—for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. 5. COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled “History” in the various original documents, forming one section Entitled “History”; likewise combine any sections Entitled “Acknowledgements”, and any sections Entitled “Dedications”. You must delete all sections Entitled “Endorsements.” 6. COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. 7. AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an “aggregate” if the copyright resulting from the compilation is not used to limit the legal rights of the compilation’s users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document’s Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. 8. TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled “Acknowledgements”, “Dedications”, or “History”, the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. 9. TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document 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. 10. FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See <http://www.gnu.org/copyleft/>. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License “or any later version” applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. ADDENDUM: How to use this License for your documents ==================================================== To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: Copyright (C) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the “with...Texts.” line with this: with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.  Tag Table: Node: Top690 Node: Getting started1914 Node: Installation2172 Ref: Installation-Footnote-14842 Ref: Installation-Footnote-24903 Node: Making sure it works5032 Node: Initializing mu-guile6312 Ref: Initializing mu-guile-Footnote-18031 Ref: Initializing mu-guile-Footnote-28084 Node: Messages8190 Node: Finding messages8590 Ref: Finding messages-Footnote-110552 Node: Message methods10823 Node: Example - the longest subject13801 Node: Contacts15071 Node: Contact procedures and objects15595 Node: All contacts17038 Node: Utility procedures18445 Node: Example - mutt export19004 Ref: Example - mutt export-Footnote-120730 Node: Attachments and other parts20761 Node: Parts and their methods21080 Ref: Parts and their methods-Footnote-122256 Node: Attachment example22349 Node: Statistics23555 Node: Basics23958 Node: Tabulating values25385 Node: Most frequent values27285 Ref: Most frequent values-Footnote-128280 Node: Plotting data28333 Ref: Plotting data-Footnote-130936 Node: Writing scripts30971 Node: Recipies33483 Node: GNU Free Documentation License34238  End Tag Table  Local Variables: coding: utf-8 End: ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/guile/mu-guile.c��������������������������������������������������������������������������0000644�0001750�0001750�00000012770�13020504331�012146� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include <config.h> #endif /*HAVE_CONFIG_H*/ #include <locale.h> #include <glib-object.h> #include <libguile.h> #include <mu-runtime.h> #include <mu-query.h> #include <mu-runtime.h> #include <mu-store.h> #include <mu-query.h> #include <mu-msg.h> #include "mu-guile.h" SCM mu_guile_scm_from_str (const char *str) { if (!str) return SCM_BOOL_F; else return scm_from_stringn (str, strlen(str), "UTF-8", SCM_FAILED_CONVERSION_QUESTION_MARK); } SCM mu_guile_error (const char *func_name, int status, const char *fmt, SCM args) { scm_error_scm (scm_from_locale_symbol ("MuError"), scm_from_utf8_string (func_name ? func_name : "<nameless>"), scm_from_utf8_string (fmt), args, scm_list_1 (scm_from_int (status))); return SCM_UNSPECIFIED; } SCM mu_guile_g_error (const char *func_name, GError *err) { scm_error_scm (scm_from_locale_symbol ("MuError"), scm_from_utf8_string (func_name), scm_from_utf8_string (err ? err->message : "error"), SCM_UNDEFINED, SCM_UNDEFINED); return SCM_UNSPECIFIED; } /* there can be only one */ static MuGuile *_singleton = NULL; static gboolean mu_guile_init_instance (const char *muhome) { MuStore *store; MuQuery *query; GError *err; setlocale (LC_ALL, ""); if (!mu_runtime_init (muhome, "guile")) return FALSE; err = NULL; store = mu_store_new_read_only (mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), &err); if (!store) goto errexit; query = mu_query_new (store, &err); mu_store_unref (store); if (!query) goto errexit; _singleton = g_new0 (MuGuile, 1); _singleton->query = query; return TRUE; errexit: mu_guile_g_error (__func__, err); g_clear_error (&err); return FALSE; } static void mu_guile_uninit_instance (void) { g_return_if_fail (_singleton); mu_query_destroy (_singleton->query); g_free (_singleton); _singleton = NULL; mu_runtime_uninit (); } MuGuile* mu_guile_instance (void) { g_return_val_if_fail (_singleton, NULL); return _singleton; } gboolean mu_guile_initialized (void) { return _singleton != NULL; } SCM_DEFINE_PUBLIC (mu_initialize, "mu:initialize", 0, 1, 0, (SCM MUHOME), "Initialize mu - needed before you call any of the other " "functions. Optionally, you can provide MUHOME which should be an " "absolute path to your mu home directory " "-- typically, the default, ~/.mu, should be just fine.") #define FUNC_NAME s_mu_initialize { char *muhome; gboolean rv; SCM_ASSERT (scm_is_string (MUHOME) || MUHOME == SCM_BOOL_F || SCM_UNBNDP(MUHOME), MUHOME, SCM_ARG1, FUNC_NAME); if (mu_guile_initialized()) return mu_guile_error (FUNC_NAME, 0, "Already initialized", SCM_UNSPECIFIED); if (SCM_UNBNDP(MUHOME) || MUHOME == SCM_BOOL_F) muhome = NULL; else muhome = scm_to_utf8_string (MUHOME); rv = mu_guile_init_instance (muhome); free (muhome); if (!rv) return mu_guile_error (FUNC_NAME, 0, "Failed to initialize mu", SCM_UNSPECIFIED); /* cleanup when we're exiting */ atexit (mu_guile_uninit_instance); return SCM_UNSPECIFIED; } #undef FUNC_NAME SCM_DEFINE_PUBLIC (mu_initialized_p, "mu:initialized?", 0, 0, 0, (void), "Whether mu is initialized or not.\n") #define FUNC_NAME s_mu_initialized_p { return mu_guile_initialized() ? SCM_BOOL_T : SCM_BOOL_F; } #undef FUNC_NAME SCM_DEFINE (log_func, "mu:c:log", 1, 0, 1, (SCM LEVEL, SCM FRM, SCM ARGS), "log some message at LEVEL using a list of ARGS applied to FRM" "(in 'simple-format' notation).\n") #define FUNC_NAME s_log_func { gchar *output; SCM str; int level; SCM_ASSERT (scm_integer_p(LEVEL), LEVEL, SCM_ARG1, FUNC_NAME); SCM_ASSERT (scm_is_string(FRM), FRM, SCM_ARG2, "<write_log>"); SCM_VALIDATE_REST_ARGUMENT(ARGS); level = scm_to_int (LEVEL); if (level != G_LOG_LEVEL_MESSAGE && level != G_LOG_LEVEL_WARNING && level != G_LOG_LEVEL_CRITICAL) return mu_guile_error (FUNC_NAME, 0, "invalid log level", SCM_UNSPECIFIED); str = scm_simple_format (SCM_BOOL_F, FRM, ARGS); if (!scm_is_string (str)) return SCM_UNSPECIFIED; output = scm_to_utf8_string (str); g_log (G_LOG_DOMAIN, level, "%s", output); free (output); return SCM_UNSPECIFIED; } #undef FUNC_NAME static struct { const char* name; unsigned val; } VAR_PAIRS[] = { { "mu:message", G_LOG_LEVEL_MESSAGE }, { "mu:warning", G_LOG_LEVEL_WARNING }, { "mu:critical", G_LOG_LEVEL_CRITICAL } }; static void define_vars (void) { unsigned u; for (u = 0; u != G_N_ELEMENTS(VAR_PAIRS); ++u) { scm_c_define (VAR_PAIRS[u].name, scm_from_uint (VAR_PAIRS[u].val)); scm_c_export (VAR_PAIRS[u].name, NULL); } } void* mu_guile_init (void *data) { define_vars (); #ifndef SCM_MAGIC_SNARFER #include "mu-guile.x" #endif /*SCM_MAGIC_SNARFER*/ return NULL; } ��������mu-0.9.18/mu4e/�������������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�010102� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/texinfo.tex��������������������������������������������������������������������������0000644�0001750�0001750�00001167036�12605152262�012242� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% texinfo.tex -- TeX macros to handle Texinfo files. % % Load plain if necessary, i.e., if running under initex. \expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi % \def\texinfoversion{2013-02-01.11} % % Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, % 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, % 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc. % % This texinfo.tex file is free software: you can redistribute it and/or % modify it under the terms of the GNU General Public License as % published by the Free Software Foundation, either version 3 of the % License, or (at your option) any later version. % % This texinfo.tex file is distributed in the hope that it will be % useful, but WITHOUT ANY WARRANTY; without even the implied warranty % of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU % General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program. If not, see <http://www.gnu.org/licenses/>. % % As a special exception, when this file is read by TeX when processing % a Texinfo source document, you may use the result without % restriction. This Exception is an additional permission under section 7 % of the GNU General Public License, version 3 ("GPLv3"). % % Please try the latest version of texinfo.tex before submitting bug % reports; you can get the latest version from: % http://ftp.gnu.org/gnu/texinfo/ (the Texinfo release area), or % http://ftpmirror.gnu.org/texinfo/ (same, via a mirror), or % http://www.gnu.org/software/texinfo/ (the Texinfo home page) % The texinfo.tex in any given distribution could well be out % of date, so if that's what you're using, please check. % % Send bug reports to bug-texinfo@gnu.org. Please include including a % complete document in each bug report with which we can reproduce the % problem. Patches are, of course, greatly appreciated. % % To process a Texinfo manual with TeX, it's most reliable to use the % texi2dvi shell script that comes with the distribution. For a simple % manual foo.texi, however, you can get away with this: % tex foo.texi % texindex foo.?? % tex foo.texi % tex foo.texi % dvips foo.dvi -o # or whatever; this makes foo.ps. % The extra TeX runs get the cross-reference information correct. % Sometimes one run after texindex suffices, and sometimes you need more % than two; texi2dvi does it as many times as necessary. % % It is possible to adapt texinfo.tex for other languages, to some % extent. You can get the existing language-specific files from the % full Texinfo distribution. % % The GNU Texinfo home page is http://www.gnu.org/software/texinfo. \message{Loading texinfo [version \texinfoversion]:} % If in a .fmt file, print the version number % and turn on active characters that we couldn't do earlier because % they might have appeared in the input file name. \everyjob{\message{[Texinfo version \texinfoversion]}% \catcode`+=\active \catcode`\_=\active} \chardef\other=12 % We never want plain's \outer definition of \+ in Texinfo. % For @tex, we can use \tabalign. \let\+ = \relax % Save some plain tex macros whose names we will redefine. \let\ptexb=\b \let\ptexbullet=\bullet \let\ptexc=\c \let\ptexcomma=\, \let\ptexdot=\. \let\ptexdots=\dots \let\ptexend=\end \let\ptexequiv=\equiv \let\ptexexclam=\! \let\ptexfootnote=\footnote \let\ptexgtr=> \let\ptexhat=^ \let\ptexi=\i \let\ptexindent=\indent \let\ptexinsert=\insert \let\ptexlbrace=\{ \let\ptexless=< \let\ptexnewwrite\newwrite \let\ptexnoindent=\noindent \let\ptexplus=+ \let\ptexraggedright=\raggedright \let\ptexrbrace=\} \let\ptexslash=\/ \let\ptexstar=\* \let\ptext=\t \let\ptextop=\top {\catcode`\'=\active \global\let\ptexquoteright'}% active in plain's math mode % If this character appears in an error message or help string, it % starts a new line in the output. \newlinechar = `^^J % Use TeX 3.0's \inputlineno to get the line number, for better error % messages, but if we're using an old version of TeX, don't do anything. % \ifx\inputlineno\thisisundefined \let\linenumber = \empty % Pre-3.0. \else \def\linenumber{l.\the\inputlineno:\space} \fi % Set up fixed words for English if not already set. \ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi \ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi \ifx\putworderror\undefined \gdef\putworderror{error}\fi \ifx\putwordfile\undefined \gdef\putwordfile{file}\fi \ifx\putwordin\undefined \gdef\putwordin{in}\fi \ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi \ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi \ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi \ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi \ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi \ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi \ifx\putwordof\undefined \gdef\putwordof{of}\fi \ifx\putwordon\undefined \gdef\putwordon{on}\fi \ifx\putwordpage\undefined \gdef\putwordpage{page}\fi \ifx\putwordsection\undefined \gdef\putwordsection{section}\fi \ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi \ifx\putwordsee\undefined \gdef\putwordsee{see}\fi \ifx\putwordSee\undefined \gdef\putwordSee{See}\fi \ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi \ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi % \ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi \ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi \ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi \ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi \ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi \ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi \ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi \ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi \ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi \ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi \ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi \ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi % \ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi \ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi \ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi \ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi \ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi % Since the category of space is not known, we have to be careful. \chardef\spacecat = 10 \def\spaceisspace{\catcode`\ =\spacecat} % sometimes characters are active, so we need control sequences. \chardef\ampChar = `\& \chardef\colonChar = `\: \chardef\commaChar = `\, \chardef\dashChar = `\- \chardef\dotChar = `\. \chardef\exclamChar= `\! \chardef\hashChar = `\# \chardef\lquoteChar= `\` \chardef\questChar = `\? \chardef\rquoteChar= `\' \chardef\semiChar = `\; \chardef\slashChar = `\/ \chardef\underChar = `\_ % Ignore a token. % \def\gobble#1{} % The following is used inside several \edef's. \def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} % Hyphenation fixes. \hyphenation{ Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script ap-pen-dix bit-map bit-maps data-base data-bases eshell fall-ing half-way long-est man-u-script man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces spell-ing spell-ings stand-alone strong-est time-stamp time-stamps which-ever white-space wide-spread wrap-around } % Margin to add to right of even pages, to left of odd pages. \newdimen\bindingoffset \newdimen\normaloffset \newdimen\pagewidth \newdimen\pageheight % For a final copy, take out the rectangles % that mark overfull boxes (in case you have decided % that the text looks ok even though it passes the margin). % \def\finalout{\overfullrule=0pt } % Sometimes it is convenient to have everything in the transcript file % and nothing on the terminal. We don't just call \tracingall here, % since that produces some useless output on the terminal. We also make % some effort to order the tracing commands to reduce output in the log % file; cf. trace.sty in LaTeX. % \def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% \def\loggingall{% \tracingstats2 \tracingpages1 \tracinglostchars2 % 2 gives us more in etex \tracingparagraphs1 \tracingoutput1 \tracingmacros2 \tracingrestores1 \showboxbreadth\maxdimen \showboxdepth\maxdimen \ifx\eTeXversion\thisisundefined\else % etex gives us more logging \tracingscantokens1 \tracingifs1 \tracinggroups1 \tracingnesting2 \tracingassigns1 \fi \tracingcommands3 % 3 gives us more in etex \errorcontextlines16 }% % @errormsg{MSG}. Do the index-like expansions on MSG, but if things % aren't perfect, it's not the end of the world, being an error message, % after all. % \def\errormsg{\begingroup \indexnofonts \doerrormsg} \def\doerrormsg#1{\errmessage{#1}} % add check for \lastpenalty to plain's definitions. If the last thing % we did was a \nobreak, we don't want to insert more space. % \def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount \removelastskip\penalty-50\smallskip\fi\fi} \def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount \removelastskip\penalty-100\medskip\fi\fi} \def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount \removelastskip\penalty-200\bigskip\fi\fi} % Do @cropmarks to get crop marks. % \newif\ifcropmarks \let\cropmarks = \cropmarkstrue % % Dimensions to add cropmarks at corners. % Added by P. A. MacKay, 12 Nov. 1986 % \newdimen\outerhsize \newdimen\outervsize % set by the paper size routines \newdimen\cornerlong \cornerlong=1pc \newdimen\cornerthick \cornerthick=.3pt \newdimen\topandbottommargin \topandbottommargin=.75in % Output a mark which sets \thischapter, \thissection and \thiscolor. % We dump everything together because we only have one kind of mark. % This works because we only use \botmark / \topmark, not \firstmark. % % A mark contains a subexpression of the \ifcase ... \fi construct. % \get*marks macros below extract the needed part using \ifcase. % % Another complication is to let the user choose whether \thischapter % (\thissection) refers to the chapter (section) in effect at the top % of a page, or that at the bottom of a page. The solution is % described on page 260 of The TeXbook. It involves outputting two % marks for the sectioning macros, one before the section break, and % one after. I won't pretend I can describe this better than DEK... \def\domark{% \toks0=\expandafter{\lastchapterdefs}% \toks2=\expandafter{\lastsectiondefs}% \toks4=\expandafter{\prevchapterdefs}% \toks6=\expandafter{\prevsectiondefs}% \toks8=\expandafter{\lastcolordefs}% \mark{% \the\toks0 \the\toks2 \noexpand\or \the\toks4 \the\toks6 \noexpand\else \the\toks8 }% } % \topmark doesn't work for the very first chapter (after the title % page or the contents), so we use \firstmark there -- this gets us % the mark with the chapter defs, unless the user sneaks in, e.g., % @setcolor (or @url, or @link, etc.) between @contents and the very % first @chapter. \def\gettopheadingmarks{% \ifcase0\topmark\fi \ifx\thischapter\empty \ifcase0\firstmark\fi \fi } \def\getbottomheadingmarks{\ifcase1\botmark\fi} \def\getcolormarks{\ifcase2\topmark\fi} % Avoid "undefined control sequence" errors. \def\lastchapterdefs{} \def\lastsectiondefs{} \def\prevchapterdefs{} \def\prevsectiondefs{} \def\lastcolordefs{} % Main output routine. \chardef\PAGE = 255 \output = {\onepageout{\pagecontents\PAGE}} \newbox\headlinebox \newbox\footlinebox % \onepageout takes a vbox as an argument. Note that \pagecontents % does insertions, but you have to call it yourself. \def\onepageout#1{% \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi % \ifodd\pageno \advance\hoffset by \bindingoffset \else \advance\hoffset by -\bindingoffset\fi % % Do this outside of the \shipout so @code etc. will be expanded in % the headline as they should be, not taken literally (outputting ''code). \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}% \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}% % {% % Have to do this stuff outside the \shipout because we want it to % take effect in \write's, yet the group defined by the \vbox ends % before the \shipout runs. % \indexdummies % don't expand commands in the output. \normalturnoffactive % \ in index entries must not stay \, e.g., if % the page break happens to be in the middle of an example. % We don't want .vr (or whatever) entries like this: % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}} % "\acronym" won't work when it's read back in; % it needs to be % {\code {{\tt \backslashcurfont }acronym} \shipout\vbox{% % Do this early so pdf references go to the beginning of the page. \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi % \ifcropmarks \vbox to \outervsize\bgroup \hsize = \outerhsize \vskip-\topandbottommargin \vtop to0pt{% \line{\ewtop\hfil\ewtop}% \nointerlineskip \line{% \vbox{\moveleft\cornerthick\nstop}% \hfill \vbox{\moveright\cornerthick\nstop}% }% \vss}% \vskip\topandbottommargin \line\bgroup \hfil % center the page within the outer (page) hsize. \ifodd\pageno\hskip\bindingoffset\fi \vbox\bgroup \fi % \unvbox\headlinebox \pagebody{#1}% \ifdim\ht\footlinebox > 0pt % Only leave this space if the footline is nonempty. % (We lessened \vsize for it in \oddfootingyyy.) % The \baselineskip=24pt in plain's \makefootline has no effect. \vskip 24pt \unvbox\footlinebox \fi % \ifcropmarks \egroup % end of \vbox\bgroup \hfil\egroup % end of (centering) \line\bgroup \vskip\topandbottommargin plus1fill minus1fill \boxmaxdepth = \cornerthick \vbox to0pt{\vss \line{% \vbox{\moveleft\cornerthick\nsbot}% \hfill \vbox{\moveright\cornerthick\nsbot}% }% \nointerlineskip \line{\ewbot\hfil\ewbot}% }% \egroup % \vbox from first cropmarks clause \fi }% end of \shipout\vbox }% end of group with \indexdummies \advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi } \newinsert\margin \dimen\margin=\maxdimen \def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} {\catcode`\@ =11 \gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi % marginal hacks, juha@viisa.uucp (Juha Takala) \ifvoid\margin\else % marginal info is present \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi \dimen@=\dp#1\relax \unvbox#1\relax \ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi \ifr@ggedbottom \kern-\dimen@ \vfil \fi} } % Here are the rules for the cropmarks. Note that they are % offset so that the space between them is truly \outerhsize or \outervsize % (P. A. MacKay, 12 November, 1986) % \def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} \def\nstop{\vbox {\hrule height\cornerthick depth\cornerlong width\cornerthick}} \def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} \def\nsbot{\vbox {\hrule height\cornerlong depth\cornerthick width\cornerthick}} % Parse an argument, then pass it to #1. The argument is the rest of % the input line (except we remove a trailing comment). #1 should be a % macro which expects an ordinary undelimited TeX argument. % \def\parsearg{\parseargusing{}} \def\parseargusing#1#2{% \def\argtorun{#2}% \begingroup \obeylines \spaceisspace #1% \parseargline\empty% Insert the \empty token, see \finishparsearg below. } {\obeylines % \gdef\parseargline#1^^M{% \endgroup % End of the group started in \parsearg. \argremovecomment #1\comment\ArgTerm% }% } % First remove any @comment, then any @c comment. \def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} \def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} % Each occurrence of `\^^M' or `<space>\^^M' is replaced by a single space. % % \argremovec might leave us with trailing space, e.g., % @end itemize @c foo % This space token undergoes the same procedure and is eventually removed % by \finishparsearg. % \def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} \def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} \def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% \def\temp{#3}% \ifx\temp\empty % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: \let\temp\finishparsearg \else \let\temp\argcheckspaces \fi % Put the space token in: \temp#1 #3\ArgTerm } % If a _delimited_ argument is enclosed in braces, they get stripped; so % to get _exactly_ the rest of the line, we had to prevent such situation. % We prepended an \empty token at the very beginning and we expand it now, % just before passing the control to \argtorun. % (Similarly, we have to think about #3 of \argcheckspacesY above: it is % either the null string, or it ends with \^^M---thus there is no danger % that a pair of braces would be stripped. % % But first, we have to remove the trailing space token. % \def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} % \parseargdef\foo{...} % is roughly equivalent to % \def\foo{\parsearg\Xfoo} % \def\Xfoo#1{...} % % Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my % favourite TeX trick. --kasal, 16nov03 \def\parseargdef#1{% \expandafter \doparseargdef \csname\string#1\endcsname #1% } \def\doparseargdef#1#2{% \def#2{\parsearg#1}% \def#1##1% } % Several utility definitions with active space: { \obeyspaces \gdef\obeyedspace{ } % Make each space character in the input produce a normal interword % space in the output. Don't allow a line break at this space, as this % is used only in environments like @example, where each line of input % should produce a line of output anyway. % \gdef\sepspaces{\obeyspaces\let =\tie} % If an index command is used in an @example environment, any spaces % therein should become regular spaces in the raw index file, not the % expansion of \tie (\leavevmode \penalty \@M \ ). \gdef\unsepspaces{\let =\space} } \def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} % Define the framework for environments in texinfo.tex. It's used like this: % % \envdef\foo{...} % \def\Efoo{...} % % It's the responsibility of \envdef to insert \begingroup before the % actual body; @end closes the group after calling \Efoo. \envdef also % defines \thisenv, so the current environment is known; @end checks % whether the environment name matches. The \checkenv macro can also be % used to check whether the current environment is the one expected. % % Non-false conditionals (@iftex, @ifset) don't fit into this, so they % are not treated as environments; they don't open a group. (The % implementation of @end takes care not to call \endgroup in this % special case.) % At run-time, environments start with this: \def\startenvironment#1{\begingroup\def\thisenv{#1}} % initialize \let\thisenv\empty % ... but they get defined via ``\envdef\foo{...}'': \long\def\envdef#1#2{\def#1{\startenvironment#1#2}} \def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} % Check whether we're in the right environment: \def\checkenv#1{% \def\temp{#1}% \ifx\thisenv\temp \else \badenverr \fi } % Environment mismatch, #1 expected: \def\badenverr{% \errhelp = \EMsimple \errmessage{This command can appear only \inenvironment\temp, not \inenvironment\thisenv}% } \def\inenvironment#1{% \ifx#1\empty outside of any environment% \else in environment \expandafter\string#1% \fi } % @end foo executes the definition of \Efoo. % But first, it executes a specialized version of \checkenv % \parseargdef\end{% \if 1\csname iscond.#1\endcsname \else % The general wording of \badenverr may not be ideal. \expandafter\checkenv\csname#1\endcsname \csname E#1\endcsname \endgroup \fi } \newhelp\EMsimple{Press RETURN to continue.} % Be sure we're in horizontal mode when doing a tie, since we make space % equivalent to this in @example-like environments. Otherwise, a space % at the beginning of a line will start with \penalty -- and % since \penalty is valid in vertical mode, we'd end up putting the % penalty on the vertical list instead of in the new paragraph. {\catcode`@ = 11 % Avoid using \@M directly, because that causes trouble % if the definition is written into an index file. \global\let\tiepenalty = \@M \gdef\tie{\leavevmode\penalty\tiepenalty\ } } % @: forces normal size whitespace following. \def\:{\spacefactor=1000 } % @* forces a line break. \def\*{\unskip\hfil\break\hbox{}\ignorespaces} % @/ allows a line break. \let\/=\allowbreak % @. is an end-of-sentence period. \def\.{.\spacefactor=\endofsentencespacefactor\space} % @! is an end-of-sentence bang. \def\!{!\spacefactor=\endofsentencespacefactor\space} % @? is an end-of-sentence query. \def\?{?\spacefactor=\endofsentencespacefactor\space} % @frenchspacing on|off says whether to put extra space after punctuation. % \def\onword{on} \def\offword{off} % \parseargdef\frenchspacing{% \def\temp{#1}% \ifx\temp\onword \plainfrenchspacing \else\ifx\temp\offword \plainnonfrenchspacing \else \errhelp = \EMsimple \errmessage{Unknown @frenchspacing option `\temp', must be on|off}% \fi\fi } % @w prevents a word break. Without the \leavevmode, @w at the % beginning of a paragraph, when TeX is still in vertical mode, would % produce a whole line of output instead of starting the paragraph. \def\w#1{\leavevmode\hbox{#1}} % @group ... @end group forces ... to be all on one page, by enclosing % it in a TeX vbox. We use \vtop instead of \vbox to construct the box % to keep its height that of a normal line. According to the rules for % \topskip (p.114 of the TeXbook), the glue inserted is % max (\topskip - \ht (first item), 0). If that height is large, % therefore, no glue is inserted, and the space between the headline and % the text is small, which looks bad. % % Another complication is that the group might be very large. This can % cause the glue on the previous page to be unduly stretched, because it % does not have much material. In this case, it's better to add an % explicit \vfill so that the extra space is at the bottom. The % threshold for doing this is if the group is more than \vfilllimit % percent of a page (\vfilllimit can be changed inside of @tex). % \newbox\groupbox \def\vfilllimit{0.7} % \envdef\group{% \ifnum\catcode`\^^M=\active \else \errhelp = \groupinvalidhelp \errmessage{@group invalid in context where filling is enabled}% \fi \startsavinginserts % \setbox\groupbox = \vtop\bgroup % Do @comment since we are called inside an environment such as % @example, where each end-of-line in the input causes an % end-of-line in the output. We don't want the end-of-line after % the `@group' to put extra space in the output. Since @group % should appear on a line by itself (according to the Texinfo % manual), we don't worry about eating any user text. \comment } % % The \vtop produces a box with normal height and large depth; thus, TeX puts % \baselineskip glue before it, and (when the next line of text is done) % \lineskip glue after it. Thus, space below is not quite equal to space % above. But it's pretty close. \def\Egroup{% % To get correct interline space between the last line of the group % and the first line afterwards, we have to propagate \prevdepth. \endgraf % Not \par, as it may have been set to \lisppar. \global\dimen1 = \prevdepth \egroup % End the \vtop. % \dimen0 is the vertical size of the group's box. \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox % \dimen2 is how much space is left on the page (more or less). \dimen2 = \pageheight \advance\dimen2 by -\pagetotal % if the group doesn't fit on the current page, and it's a big big % group, force a page break. \ifdim \dimen0 > \dimen2 \ifdim \pagetotal < \vfilllimit\pageheight \page \fi \fi \box\groupbox \prevdepth = \dimen1 \checkinserts } % % TeX puts in an \escapechar (i.e., `@') at the beginning of the help % message, so this ends up printing `@group can only ...'. % \newhelp\groupinvalidhelp{% group can only be used in environments such as @example,^^J% where each line of input produces a line of output.} % @need space-in-mils % forces a page break if there is not space-in-mils remaining. \newdimen\mil \mil=0.001in \parseargdef\need{% % Ensure vertical mode, so we don't make a big box in the middle of a % paragraph. \par % % If the @need value is less than one line space, it's useless. \dimen0 = #1\mil \dimen2 = \ht\strutbox \advance\dimen2 by \dp\strutbox \ifdim\dimen0 > \dimen2 % % Do a \strut just to make the height of this box be normal, so the % normal leading is inserted relative to the preceding line. % And a page break here is fine. \vtop to #1\mil{\strut\vfil}% % % TeX does not even consider page breaks if a penalty added to the % main vertical list is 10000 or more. But in order to see if the % empty box we just added fits on the page, we must make it consider % page breaks. On the other hand, we don't want to actually break the % page after the empty box. So we use a penalty of 9999. % % There is an extremely small chance that TeX will actually break the % page at this \penalty, if there are no other feasible breakpoints in % sight. (If the user is using lots of big @group commands, which % almost-but-not-quite fill up a page, TeX will have a hard time doing % good page breaking, for example.) However, I could not construct an % example where a page broke at this \penalty; if it happens in a real % document, then we can reconsider our strategy. \penalty9999 % % Back up by the size of the box, whether we did a page break or not. \kern -#1\mil % % Do not allow a page break right after this kern. \nobreak \fi } % @br forces paragraph break (and is undocumented). \let\br = \par % @page forces the start of a new page. % \def\page{\par\vfill\supereject} % @exdent text.... % outputs text on separate line in roman font, starting at standard page margin % This records the amount of indent in the innermost environment. % That's how much \exdent should take out. \newskip\exdentamount % This defn is used inside fill environments such as @defun. \parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} % This defn is used inside nofill environments such as @example. \parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount \leftline{\hskip\leftskip{\rm#1}}}} % @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current % paragraph. For more general purposes, use the \margin insertion % class. WHICH is `l' or `r'. Not documented, written for gawk manual. % \newskip\inmarginspacing \inmarginspacing=1cm \def\strutdepth{\dp\strutbox} % \def\doinmargin#1#2{\strut\vadjust{% \nobreak \kern-\strutdepth \vtop to \strutdepth{% \baselineskip=\strutdepth \vss % if you have multiple lines of stuff to put here, you'll need to % make the vbox yourself of the appropriate size. \ifx#1l% \llap{\ignorespaces #2\hskip\inmarginspacing}% \else \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% \fi \null }% }} \def\inleftmargin{\doinmargin l} \def\inrightmargin{\doinmargin r} % % @inmargin{TEXT [, RIGHT-TEXT]} % (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; % else use TEXT for both). % \def\inmargin#1{\parseinmargin #1,,\finish} \def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0 > 0pt \def\lefttext{#1}% have both texts \def\righttext{#2}% \else \def\lefttext{#1}% have only one text \def\righttext{#1}% \fi % \ifodd\pageno \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin \else \def\temp{\inleftmargin\lefttext}% \fi \temp } % @| inserts a changebar to the left of the current line. It should % surround any changed text. This approach does *not* work if the % change spans more than two lines of output. To handle that, we would % have adopt a much more difficult approach (putting marks into the main % vertical list for the beginning and end of each change). This command % is not documented, not supported, and doesn't work. % \def\|{% % \vadjust can only be used in horizontal mode. \leavevmode % % Append this vertical mode material after the current line in the output. \vadjust{% % We want to insert a rule with the height and depth of the current % leading; that is exactly what \strutbox is supposed to record. \vskip-\baselineskip % % \vadjust-items are inserted at the left edge of the type. So % the \llap here moves out into the left-hand margin. \llap{% % % For a thicker or thinner bar, change the `1pt'. \vrule height\baselineskip width1pt % % This is the space between the bar and the text. \hskip 12pt }% }% } % @include FILE -- \input text of FILE. % \def\include{\parseargusing\filenamecatcodes\includezzz} \def\includezzz#1{% \pushthisfilestack \def\thisfile{#1}% {% \makevalueexpandable % we want to expand any @value in FILE. \turnoffactive % and allow special characters in the expansion \indexnofonts % Allow `@@' and other weird things in file names. \wlog{texinfo.tex: doing @include of #1^^J}% \edef\temp{\noexpand\input #1 }% % % This trickery is to read FILE outside of a group, in case it makes % definitions, etc. \expandafter }\temp \popthisfilestack } \def\filenamecatcodes{% \catcode`\\=\other \catcode`~=\other \catcode`^=\other \catcode`_=\other \catcode`|=\other \catcode`<=\other \catcode`>=\other \catcode`+=\other \catcode`-=\other \catcode`\`=\other \catcode`\'=\other } \def\pushthisfilestack{% \expandafter\pushthisfilestackX\popthisfilestack\StackTerm } \def\pushthisfilestackX{% \expandafter\pushthisfilestackY\thisfile\StackTerm } \def\pushthisfilestackY #1\StackTerm #2\StackTerm {% \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% } \def\popthisfilestack{\errthisfilestackempty} \def\errthisfilestackempty{\errmessage{Internal error: the stack of filenames is empty.}} % \def\thisfile{} % @center line % outputs that line, centered. % \parseargdef\center{% \ifhmode \let\centersub\centerH \else \let\centersub\centerV \fi \centersub{\hfil \ignorespaces#1\unskip \hfil}% \let\centersub\relax % don't let the definition persist, just in case } \def\centerH#1{{% \hfil\break \advance\hsize by -\leftskip \advance\hsize by -\rightskip \line{#1}% \break }} % \newcount\centerpenalty \def\centerV#1{% % The idea here is the same as in \startdefun, \cartouche, etc.: if % @center is the first thing after a section heading, we need to wipe % out the negative parskip inserted by \sectionheading, but still % prevent a page break here. \centerpenalty = \lastpenalty \ifnum\centerpenalty>10000 \vskip\parskip \fi \ifnum\centerpenalty>9999 \penalty\centerpenalty \fi \line{\kern\leftskip #1\kern\rightskip}% } % @sp n outputs n lines of vertical space % \parseargdef\sp{\vskip #1\baselineskip} % @comment ...line which is ignored... % @c is the same as @comment % @ignore ... @end ignore is another way to write a comment % \def\comment{\begingroup \catcode`\^^M=\other% \catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% \commentxxx} {\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}} % \let\c=\comment % @paragraphindent NCHARS % We'll use ems for NCHARS, close enough. % NCHARS can also be the word `asis' or `none'. % We cannot feasibly implement @paragraphindent asis, though. % \def\asisword{asis} % no translation, these are keywords \def\noneword{none} % \parseargdef\paragraphindent{% \def\temp{#1}% \ifx\temp\asisword \else \ifx\temp\noneword \defaultparindent = 0pt \else \defaultparindent = #1em \fi \fi \parindent = \defaultparindent } % @exampleindent NCHARS % We'll use ems for NCHARS like @paragraphindent. % It seems @exampleindent asis isn't necessary, but % I preserve it to make it similar to @paragraphindent. \parseargdef\exampleindent{% \def\temp{#1}% \ifx\temp\asisword \else \ifx\temp\noneword \lispnarrowing = 0pt \else \lispnarrowing = #1em \fi \fi } % @firstparagraphindent WORD % If WORD is `none', then suppress indentation of the first paragraph % after a section heading. If WORD is `insert', then do indent at such % paragraphs. % % The paragraph indentation is suppressed or not by calling % \suppressfirstparagraphindent, which the sectioning commands do. % We switch the definition of this back and forth according to WORD. % By default, we suppress indentation. % \def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} \def\insertword{insert} % \parseargdef\firstparagraphindent{% \def\temp{#1}% \ifx\temp\noneword \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent \else\ifx\temp\insertword \let\suppressfirstparagraphindent = \relax \else \errhelp = \EMsimple \errmessage{Unknown @firstparagraphindent option `\temp'}% \fi\fi } % Here is how we actually suppress indentation. Redefine \everypar to % \kern backwards by \parindent, and then reset itself to empty. % % We also make \indent itself not actually do anything until the next % paragraph. % \gdef\dosuppressfirstparagraphindent{% \gdef\indent{% \restorefirstparagraphindent \indent }% \gdef\noindent{% \restorefirstparagraphindent \noindent }% \global\everypar = {% \kern -\parindent \restorefirstparagraphindent }% } \gdef\restorefirstparagraphindent{% \global \let \indent = \ptexindent \global \let \noindent = \ptexnoindent \global \everypar = {}% } % @refill is a no-op. \let\refill=\relax % If working on a large document in chapters, it is convenient to % be able to disable indexing, cross-referencing, and contents, for test runs. % This is done with @novalidate (before @setfilename). % \newif\iflinks \linkstrue % by default we want the aux files. \let\novalidate = \linksfalse % @setfilename is done at the beginning of every texinfo file. % So open here the files we need to have open while reading the input. % This makes it possible to make a .fmt file for texinfo. \def\setfilename{% \fixbackslash % Turn off hack to swallow `\input texinfo'. \iflinks \tryauxfile % Open the new aux file. TeX will close it automatically at exit. \immediate\openout\auxfile=\jobname.aux \fi % \openindices needs to do some work in any case. \openindices \let\setfilename=\comment % Ignore extra @setfilename cmds. % % If texinfo.cnf is present on the system, read it. % Useful for site-wide @afourpaper, etc. \openin 1 texinfo.cnf \ifeof 1 \else \input texinfo.cnf \fi \closein 1 % \comment % Ignore the actual filename. } % Called from \setfilename. % \def\openindices{% \newindex{cp}% \newcodeindex{fn}% \newcodeindex{vr}% \newcodeindex{tp}% \newcodeindex{ky}% \newcodeindex{pg}% } % @bye. \outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} \message{pdf,} % adobe `portable' document format \newcount\tempnum \newcount\lnkcount \newtoks\filename \newcount\filenamelength \newcount\pgn \newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD \newbox\boxA \newcount\countA \newif\ifpdf \newif\ifpdfmakepagedest % when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 % can be set). So we test for \relax and 0 as well as being undefined. \ifx\pdfoutput\thisisundefined \else \ifx\pdfoutput\relax \else \ifcase\pdfoutput \else \pdftrue \fi \fi \fi % PDF uses PostScript string constants for the names of xref targets, % for display in the outlines, and in other places. Thus, we have to % double any backslashes. Otherwise, a name like "\node" will be % interpreted as a newline (\n), followed by o, d, e. Not good. % % See http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html and % related messages. The final outcome is that it is up to the TeX user % to double the backslashes and otherwise make the string valid, so % that's what we do. pdftex 1.30.0 (ca.2005) introduced a primitive to % do this reliably, so we use it. % #1 is a control sequence in which to do the replacements, % which we \xdef. \def\txiescapepdf#1{% \ifx\pdfescapestring\thisisundefined % No primitive available; should we give a warning or log? % Many times it won't matter. \else % The expandable \pdfescapestring primitive escapes parentheses, % backslashes, and other special chars. \xdef#1{\pdfescapestring{#1}}% \fi } \newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images with PDF output, and none of those formats could be found. (.eps cannot be supported due to the design of the PDF format; use regular TeX (DVI output) for that.)} \ifpdf % % Color manipulation macros based on pdfcolor.tex, % except using rgb instead of cmyk; the latter is said to render as a % very dark gray on-screen and a very dark halftone in print, instead % of actual black. \def\rgbDarkRed{0.50 0.09 0.12} \def\rgbBlack{0 0 0} % % k sets the color for filling (usual text, etc.); % K sets the color for stroking (thin rules, e.g., normal _'s). \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}} % % Set color, and create a mark which defines \thiscolor accordingly, % so that \makeheadline knows which color to restore. \def\setcolor#1{% \xdef\lastcolordefs{\gdef\noexpand\thiscolor{#1}}% \domark \pdfsetcolor{#1}% } % \def\maincolor{\rgbBlack} \pdfsetcolor{\maincolor} \edef\thiscolor{\maincolor} \def\lastcolordefs{} % \def\makefootline{% \baselineskip24pt \line{\pdfsetcolor{\maincolor}\the\footline}% } % \def\makeheadline{% \vbox to 0pt{% \vskip-22.5pt \line{% \vbox to8.5pt{}% % Extract \thiscolor definition from the marks. \getcolormarks % Typeset the headline with \maincolor, then restore the color. \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% }% \vss }% \nointerlineskip } % % \pdfcatalog{/PageMode /UseOutlines} % % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). \def\dopdfimage#1#2#3{% \def\pdfimagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% \def\pdfimageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% % % pdftex (and the PDF format) support .pdf, .png, .jpg (among % others). Let's try in that order, PDF first since if % someone has a scalable image, presumably better to use that than a % bitmap. \let\pdfimgext=\empty \begingroup \openin 1 #1.pdf \ifeof 1 \openin 1 #1.PDF \ifeof 1 \openin 1 #1.png \ifeof 1 \openin 1 #1.jpg \ifeof 1 \openin 1 #1.jpeg \ifeof 1 \openin 1 #1.JPG \ifeof 1 \errhelp = \nopdfimagehelp \errmessage{Could not find image file #1 for pdf}% \else \gdef\pdfimgext{JPG}% \fi \else \gdef\pdfimgext{jpeg}% \fi \else \gdef\pdfimgext{jpg}% \fi \else \gdef\pdfimgext{png}% \fi \else \gdef\pdfimgext{PDF}% \fi \else \gdef\pdfimgext{pdf}% \fi \closein 1 \endgroup % % without \immediate, ancient pdftex seg faults when the same image is % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) \ifnum\pdftexversion < 14 \immediate\pdfimage \else \immediate\pdfximage \fi \ifdim \wd0 >0pt width \pdfimagewidth \fi \ifdim \wd2 >0pt height \pdfimageheight \fi \ifnum\pdftexversion<13 #1.\pdfimgext \else {#1.\pdfimgext}% \fi \ifnum\pdftexversion < 14 \else \pdfrefximage \pdflastximage \fi} % \def\pdfmkdest#1{{% % We have to set dummies so commands such as @code, and characters % such as \, aren't expanded when present in a section title. \indexnofonts \turnoffactive \makevalueexpandable \def\pdfdestname{#1}% \txiescapepdf\pdfdestname \safewhatsit{\pdfdest name{\pdfdestname} xyz}% }} % % used to mark target names; must be expandable. \def\pdfmkpgn#1{#1} % % by default, use a color that is dark enough to print on paper as % nearly black, but still distinguishable for online viewing. \def\urlcolor{\rgbDarkRed} \def\linkcolor{\rgbDarkRed} \def\endlink{\setcolor{\maincolor}\pdfendlink} % % Adding outlines to PDF; macros for calculating structure of outlines % come from Petr Olsak \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% \else \csname#1\endcsname \fi} \def\advancenumber#1{\tempnum=\expnumber{#1}\relax \advance\tempnum by 1 \expandafter\xdef\csname#1\endcsname{\the\tempnum}} % % #1 is the section text, which is what will be displayed in the % outline by the pdf viewer. #2 is the pdf expression for the number % of subentries (or empty, for subsubsections). #3 is the node text, % which might be empty if this toc entry had no corresponding node. % #4 is the page number % \def\dopdfoutline#1#2#3#4{% % Generate a link to the node text if that exists; else, use the % page number. We could generate a destination for the section % text in the case where a section has no node, but it doesn't % seem worth the trouble, since most documents are normally structured. \edef\pdfoutlinedest{#3}% \ifx\pdfoutlinedest\empty \def\pdfoutlinedest{#4}% \else \txiescapepdf\pdfoutlinedest \fi % % Also escape PDF chars in the display string. \edef\pdfoutlinetext{#1}% \txiescapepdf\pdfoutlinetext % \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}% } % \def\pdfmakeoutlines{% \begingroup % Read toc silently, to get counts of subentries for \pdfoutline. \def\partentry##1##2##3##4{}% ignore parts in the outlines \def\numchapentry##1##2##3##4{% \def\thischapnum{##2}% \def\thissecnum{0}% \def\thissubsecnum{0}% }% \def\numsecentry##1##2##3##4{% \advancenumber{chap\thischapnum}% \def\thissecnum{##2}% \def\thissubsecnum{0}% }% \def\numsubsecentry##1##2##3##4{% \advancenumber{sec\thissecnum}% \def\thissubsecnum{##2}% }% \def\numsubsubsecentry##1##2##3##4{% \advancenumber{subsec\thissubsecnum}% }% \def\thischapnum{0}% \def\thissecnum{0}% \def\thissubsecnum{0}% % % use \def rather than \let here because we redefine \chapentry et % al. a second time, below. \def\appentry{\numchapentry}% \def\appsecentry{\numsecentry}% \def\appsubsecentry{\numsubsecentry}% \def\appsubsubsecentry{\numsubsubsecentry}% \def\unnchapentry{\numchapentry}% \def\unnsecentry{\numsecentry}% \def\unnsubsecentry{\numsubsecentry}% \def\unnsubsubsecentry{\numsubsubsecentry}% \readdatafile{toc}% % % Read toc second time, this time actually producing the outlines. % The `-' means take the \expnumber as the absolute number of % subentries, which we calculated on our first read of the .toc above. % % We use the node names as the destinations. \def\numchapentry##1##2##3##4{% \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% \def\numsecentry##1##2##3##4{% \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% \def\numsubsecentry##1##2##3##4{% \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% \def\numsubsubsecentry##1##2##3##4{% count is always zero \dopdfoutline{##1}{}{##3}{##4}}% % % PDF outlines are displayed using system fonts, instead of % document fonts. Therefore we cannot use special characters, % since the encoding is unknown. For example, the eogonek from % Latin 2 (0xea) gets translated to a | character. Info from % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. % % TODO this right, we have to translate 8-bit characters to % their "best" equivalent, based on the @documentencoding. Too % much work for too little return. Just use the ASCII equivalents % we use for the index sort strings. % \indexnofonts \setupdatafile % We can have normal brace characters in the PDF outlines, unlike % Texinfo index files. So set that up. \def\{{\lbracecharliteral}% \def\}{\rbracecharliteral}% \catcode`\\=\active \otherbackslash \input \tocreadfilename \endgroup } {\catcode`[=1 \catcode`]=2 \catcode`{=\other \catcode`}=\other \gdef\lbracecharliteral[{]% \gdef\rbracecharliteral[}]% ] % \def\skipspaces#1{\def\PP{#1}\def\D{|}% \ifx\PP\D\let\nextsp\relax \else\let\nextsp\skipspaces \addtokens{\filename}{\PP}% \advance\filenamelength by 1 \fi \nextsp} \def\getfilename#1{% \filenamelength=0 % If we don't expand the argument now, \skipspaces will get % snagged on things like "@value{foo}". \edef\temp{#1}% \expandafter\skipspaces\temp|\relax } \ifnum\pdftexversion < 14 \let \startlink \pdfannotlink \else \let \startlink \pdfstartlink \fi % make a live url in pdf output. \def\pdfurl#1{% \begingroup % it seems we really need yet another set of dummies; have not % tried to figure out what each command should do in the context % of @url. for now, just make @/ a no-op, that's the only one % people have actually reported a problem with. % \normalturnoffactive \def\@{@}% \let\/=\empty \makevalueexpandable % do we want to go so far as to use \indexnofonts instead of just % special-casing \var here? \def\var##1{##1}% % \leavevmode\setcolor{\urlcolor}% \startlink attr{/Border [0 0 0]}% user{/Subtype /Link /A << /S /URI /URI (#1) >>}% \endgroup} \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} \def\maketoks{% \expandafter\poptoks\the\toksA|ENDTOKS|\relax \ifx\first0\adn0 \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 \else \ifnum0=\countA\else\makelink\fi \ifx\first.\let\next=\done\else \let\next=\maketoks \addtokens{\toksB}{\the\toksD} \ifx\first,\addtokens{\toksB}{\space}\fi \fi \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \next} \def\makelink{\addtokens{\toksB}% {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} \def\pdflink#1{% \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} \setcolor{\linkcolor}#1\endlink} \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} \else % non-pdf mode \let\pdfmkdest = \gobble \let\pdfurl = \gobble \let\endlink = \relax \let\setcolor = \gobble \let\pdfsetcolor = \gobble \let\pdfmakeoutlines = \relax \fi % \ifx\pdfoutput \message{fonts,} % Change the current font style to #1, remembering it in \curfontstyle. % For now, we do not accumulate font styles: @b{@i{foo}} prints foo in % italics, not bold italics. % \def\setfontstyle#1{% \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. \csname ten#1\endcsname % change the current font } % Select #1 fonts with the current style. % \def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname} \def\rm{\fam=0 \setfontstyle{rm}} \def\it{\fam=\itfam \setfontstyle{it}} \def\sl{\fam=\slfam \setfontstyle{sl}} \def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} \def\tt{\fam=\ttfam \setfontstyle{tt}} % Unfortunately, we have to override this for titles and the like, since % in those cases "rm" is bold. Sigh. \def\rmisbold{\rm\def\curfontstyle{bf}} % Texinfo sort of supports the sans serif font style, which plain TeX does not. % So we set up a \sf. \newfam\sffam \def\sf{\fam=\sffam \setfontstyle{sf}} \let\li = \sf % Sometimes we call it \li, not \sf. % We don't need math for this font style. \def\ttsl{\setfontstyle{ttsl}} % Set the baselineskip to #1, and the lineskip and strut size % correspondingly. There is no deep meaning behind these magic numbers % used as factors; they just match (closely enough) what Knuth defined. % \def\lineskipfactor{.08333} \def\strutheightpercent{.70833} \def\strutdepthpercent {.29167} % % can get a sort of poor man's double spacing by redefining this. \def\baselinefactor{1} % \newdimen\textleading \def\setleading#1{% \dimen0 = #1\relax \normalbaselineskip = \baselinefactor\dimen0 \normallineskip = \lineskipfactor\normalbaselineskip \normalbaselines \setbox\strutbox =\hbox{% \vrule width0pt height\strutheightpercent\baselineskip depth \strutdepthpercent \baselineskip }% } % PDF CMaps. See also LaTeX's t1.cmap. % % do nothing with this by default. \expandafter\let\csname cmapOT1\endcsname\gobble \expandafter\let\csname cmapOT1IT\endcsname\gobble \expandafter\let\csname cmapOT1TT\endcsname\gobble % if we are producing pdf, and we have \pdffontattr, then define cmaps. % (\pdffontattr was introduced many years ago, but people still run % older pdftex's; it's easy to conditionalize, so we do.) \ifpdf \ifx\pdffontattr\thisisundefined \else \begingroup \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap %%DocumentNeededResources: ProcSet (CIDInit) %%IncludeResource: ProcSet (CIDInit) %%BeginResource: CMap (TeX-OT1-0) %%Title: (TeX-OT1-0 TeX OT1 0) %%Version: 1.000 %%EndComments /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (TeX) /Ordering (OT1) /Supplement 0 >> def /CMapName /TeX-OT1-0 def /CMapType 2 def 1 begincodespacerange <00> <7F> endcodespacerange 8 beginbfrange <00> <01> <0393> <09> <0A> <03A8> <23> <26> <0023> <28> <3B> <0028> <3F> <5B> <003F> <5D> <5E> <005D> <61> <7A> <0061> <7B> <7C> <2013> endbfrange 40 beginbfchar <02> <0398> <03> <039B> <04> <039E> <05> <03A0> <06> <03A3> <07> <03D2> <08> <03A6> <0B> <00660066> <0C> <00660069> <0D> <0066006C> <0E> <006600660069> <0F> <00660066006C> <10> <0131> <11> <0237> <12> <0060> <13> <00B4> <14> <02C7> <15> <02D8> <16> <00AF> <17> <02DA> <18> <00B8> <19> <00DF> <1A> <00E6> <1B> <0153> <1C> <00F8> <1D> <00C6> <1E> <0152> <1F> <00D8> <21> <0021> <22> <201D> <27> <2019> <3C> <00A1> <3D> <003D> <3E> <00BF> <5C> <201C> <5F> <02D9> <60> <2018> <7D> <02DD> <7E> <007E> <7F> <00A8> endbfchar endcmap CMapName currentdict /CMap defineresource pop end end %%EndResource %%EOF }\endgroup \expandafter\edef\csname cmapOT1\endcsname#1{% \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% }% % % \cmapOT1IT \begingroup \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap %%DocumentNeededResources: ProcSet (CIDInit) %%IncludeResource: ProcSet (CIDInit) %%BeginResource: CMap (TeX-OT1IT-0) %%Title: (TeX-OT1IT-0 TeX OT1IT 0) %%Version: 1.000 %%EndComments /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (TeX) /Ordering (OT1IT) /Supplement 0 >> def /CMapName /TeX-OT1IT-0 def /CMapType 2 def 1 begincodespacerange <00> <7F> endcodespacerange 8 beginbfrange <00> <01> <0393> <09> <0A> <03A8> <25> <26> <0025> <28> <3B> <0028> <3F> <5B> <003F> <5D> <5E> <005D> <61> <7A> <0061> <7B> <7C> <2013> endbfrange 42 beginbfchar <02> <0398> <03> <039B> <04> <039E> <05> <03A0> <06> <03A3> <07> <03D2> <08> <03A6> <0B> <00660066> <0C> <00660069> <0D> <0066006C> <0E> <006600660069> <0F> <00660066006C> <10> <0131> <11> <0237> <12> <0060> <13> <00B4> <14> <02C7> <15> <02D8> <16> <00AF> <17> <02DA> <18> <00B8> <19> <00DF> <1A> <00E6> <1B> <0153> <1C> <00F8> <1D> <00C6> <1E> <0152> <1F> <00D8> <21> <0021> <22> <201D> <23> <0023> <24> <00A3> <27> <2019> <3C> <00A1> <3D> <003D> <3E> <00BF> <5C> <201C> <5F> <02D9> <60> <2018> <7D> <02DD> <7E> <007E> <7F> <00A8> endbfchar endcmap CMapName currentdict /CMap defineresource pop end end %%EndResource %%EOF }\endgroup \expandafter\edef\csname cmapOT1IT\endcsname#1{% \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% }% % % \cmapOT1TT \begingroup \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap %%DocumentNeededResources: ProcSet (CIDInit) %%IncludeResource: ProcSet (CIDInit) %%BeginResource: CMap (TeX-OT1TT-0) %%Title: (TeX-OT1TT-0 TeX OT1TT 0) %%Version: 1.000 %%EndComments /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (TeX) /Ordering (OT1TT) /Supplement 0 >> def /CMapName /TeX-OT1TT-0 def /CMapType 2 def 1 begincodespacerange <00> <7F> endcodespacerange 5 beginbfrange <00> <01> <0393> <09> <0A> <03A8> <21> <26> <0021> <28> <5F> <0028> <61> <7E> <0061> endbfrange 32 beginbfchar <02> <0398> <03> <039B> <04> <039E> <05> <03A0> <06> <03A3> <07> <03D2> <08> <03A6> <0B> <2191> <0C> <2193> <0D> <0027> <0E> <00A1> <0F> <00BF> <10> <0131> <11> <0237> <12> <0060> <13> <00B4> <14> <02C7> <15> <02D8> <16> <00AF> <17> <02DA> <18> <00B8> <19> <00DF> <1A> <00E6> <1B> <0153> <1C> <00F8> <1D> <00C6> <1E> <0152> <1F> <00D8> <20> <2423> <27> <2019> <60> <2018> <7F> <00A8> endbfchar endcmap CMapName currentdict /CMap defineresource pop end end %%EndResource %%EOF }\endgroup \expandafter\edef\csname cmapOT1TT\endcsname#1{% \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% }% \fi\fi % Set the font macro #1 to the font named \fontprefix#2. % #3 is the font's design size, #4 is a scale factor, #5 is the CMap % encoding (only OT1, OT1IT and OT1TT are allowed, or empty to omit). % Example: % #1 = \textrm % #2 = \rmshape % #3 = 10 % #4 = \mainmagstep % #5 = OT1 % \def\setfont#1#2#3#4#5{% \font#1=\fontprefix#2#3 scaled #4 \csname cmap#5\endcsname#1% } % This is what gets called when #5 of \setfont is empty. \let\cmap\gobble % % (end of cmaps) % Use cm as the default font prefix. % To specify the font prefix, you must define \fontprefix % before you read in texinfo.tex. \ifx\fontprefix\thisisundefined \def\fontprefix{cm} \fi % Support font families that don't use the same naming scheme as CM. \def\rmshape{r} \def\rmbshape{bx} % where the normal face is bold \def\bfshape{b} \def\bxshape{bx} \def\ttshape{tt} \def\ttbshape{tt} \def\ttslshape{sltt} \def\itshape{ti} \def\itbshape{bxti} \def\slshape{sl} \def\slbshape{bxsl} \def\sfshape{ss} \def\sfbshape{ss} \def\scshape{csc} \def\scbshape{csc} % Definitions for a main text size of 11pt. (The default in Texinfo.) % \def\definetextfontsizexi{% % Text fonts (11.2pt, magstep1). \def\textnominalsize{11pt} \edef\mainmagstep{\magstephalf} \setfont\textrm\rmshape{10}{\mainmagstep}{OT1} \setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} \setfont\textbf\bfshape{10}{\mainmagstep}{OT1} \setfont\textit\itshape{10}{\mainmagstep}{OT1IT} \setfont\textsl\slshape{10}{\mainmagstep}{OT1} \setfont\textsf\sfshape{10}{\mainmagstep}{OT1} \setfont\textsc\scshape{10}{\mainmagstep}{OT1} \setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} \font\texti=cmmi10 scaled \mainmagstep \font\textsy=cmsy10 scaled \mainmagstep \def\textecsize{1095} % A few fonts for @defun names and args. \setfont\defbf\bfshape{10}{\magstep1}{OT1} \setfont\deftt\ttshape{10}{\magstep1}{OT1TT} \setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT} \def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} % Fonts for indices, footnotes, small examples (9pt). \def\smallnominalsize{9pt} \setfont\smallrm\rmshape{9}{1000}{OT1} \setfont\smalltt\ttshape{9}{1000}{OT1TT} \setfont\smallbf\bfshape{10}{900}{OT1} \setfont\smallit\itshape{9}{1000}{OT1IT} \setfont\smallsl\slshape{9}{1000}{OT1} \setfont\smallsf\sfshape{9}{1000}{OT1} \setfont\smallsc\scshape{10}{900}{OT1} \setfont\smallttsl\ttslshape{10}{900}{OT1TT} \font\smalli=cmmi9 \font\smallsy=cmsy9 \def\smallecsize{0900} % Fonts for small examples (8pt). \def\smallernominalsize{8pt} \setfont\smallerrm\rmshape{8}{1000}{OT1} \setfont\smallertt\ttshape{8}{1000}{OT1TT} \setfont\smallerbf\bfshape{10}{800}{OT1} \setfont\smallerit\itshape{8}{1000}{OT1IT} \setfont\smallersl\slshape{8}{1000}{OT1} \setfont\smallersf\sfshape{8}{1000}{OT1} \setfont\smallersc\scshape{10}{800}{OT1} \setfont\smallerttsl\ttslshape{10}{800}{OT1TT} \font\smalleri=cmmi8 \font\smallersy=cmsy8 \def\smallerecsize{0800} % Fonts for title page (20.4pt): \def\titlenominalsize{20pt} \setfont\titlerm\rmbshape{12}{\magstep3}{OT1} \setfont\titleit\itbshape{10}{\magstep4}{OT1IT} \setfont\titlesl\slbshape{10}{\magstep4}{OT1} \setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} \setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} \setfont\titlesf\sfbshape{17}{\magstep1}{OT1} \let\titlebf=\titlerm \setfont\titlesc\scbshape{10}{\magstep4}{OT1} \font\titlei=cmmi12 scaled \magstep3 \font\titlesy=cmsy10 scaled \magstep4 \def\titleecsize{2074} % Chapter (and unnumbered) fonts (17.28pt). \def\chapnominalsize{17pt} \setfont\chaprm\rmbshape{12}{\magstep2}{OT1} \setfont\chapit\itbshape{10}{\magstep3}{OT1IT} \setfont\chapsl\slbshape{10}{\magstep3}{OT1} \setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT} \setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT} \setfont\chapsf\sfbshape{17}{1000}{OT1} \let\chapbf=\chaprm \setfont\chapsc\scbshape{10}{\magstep3}{OT1} \font\chapi=cmmi12 scaled \magstep2 \font\chapsy=cmsy10 scaled \magstep3 \def\chapecsize{1728} % Section fonts (14.4pt). \def\secnominalsize{14pt} \setfont\secrm\rmbshape{12}{\magstep1}{OT1} \setfont\secit\itbshape{10}{\magstep2}{OT1IT} \setfont\secsl\slbshape{10}{\magstep2}{OT1} \setfont\sectt\ttbshape{12}{\magstep1}{OT1TT} \setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT} \setfont\secsf\sfbshape{12}{\magstep1}{OT1} \let\secbf\secrm \setfont\secsc\scbshape{10}{\magstep2}{OT1} \font\seci=cmmi12 scaled \magstep1 \font\secsy=cmsy10 scaled \magstep2 \def\sececsize{1440} % Subsection fonts (13.15pt). \def\ssecnominalsize{13pt} \setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1} \setfont\ssecit\itbshape{10}{1315}{OT1IT} \setfont\ssecsl\slbshape{10}{1315}{OT1} \setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT} \setfont\ssecttsl\ttslshape{10}{1315}{OT1TT} \setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1} \let\ssecbf\ssecrm \setfont\ssecsc\scbshape{10}{1315}{OT1} \font\sseci=cmmi12 scaled \magstephalf \font\ssecsy=cmsy10 scaled 1315 \def\ssececsize{1200} % Reduced fonts for @acro in text (10pt). \def\reducednominalsize{10pt} \setfont\reducedrm\rmshape{10}{1000}{OT1} \setfont\reducedtt\ttshape{10}{1000}{OT1TT} \setfont\reducedbf\bfshape{10}{1000}{OT1} \setfont\reducedit\itshape{10}{1000}{OT1IT} \setfont\reducedsl\slshape{10}{1000}{OT1} \setfont\reducedsf\sfshape{10}{1000}{OT1} \setfont\reducedsc\scshape{10}{1000}{OT1} \setfont\reducedttsl\ttslshape{10}{1000}{OT1TT} \font\reducedi=cmmi10 \font\reducedsy=cmsy10 \def\reducedecsize{1000} \textleading = 13.2pt % line spacing for 11pt CM \textfonts % reset the current fonts \rm } % end of 11pt text font size definitions, \definetextfontsizexi % Definitions to make the main text be 10pt Computer Modern, with % section, chapter, etc., sizes following suit. This is for the GNU % Press printing of the Emacs 22 manual. Maybe other manuals in the % future. Used with @smallbook, which sets the leading to 12pt. % \def\definetextfontsizex{% % Text fonts (10pt). \def\textnominalsize{10pt} \edef\mainmagstep{1000} \setfont\textrm\rmshape{10}{\mainmagstep}{OT1} \setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} \setfont\textbf\bfshape{10}{\mainmagstep}{OT1} \setfont\textit\itshape{10}{\mainmagstep}{OT1IT} \setfont\textsl\slshape{10}{\mainmagstep}{OT1} \setfont\textsf\sfshape{10}{\mainmagstep}{OT1} \setfont\textsc\scshape{10}{\mainmagstep}{OT1} \setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} \font\texti=cmmi10 scaled \mainmagstep \font\textsy=cmsy10 scaled \mainmagstep \def\textecsize{1000} % A few fonts for @defun names and args. \setfont\defbf\bfshape{10}{\magstephalf}{OT1} \setfont\deftt\ttshape{10}{\magstephalf}{OT1TT} \setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT} \def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} % Fonts for indices, footnotes, small examples (9pt). \def\smallnominalsize{9pt} \setfont\smallrm\rmshape{9}{1000}{OT1} \setfont\smalltt\ttshape{9}{1000}{OT1TT} \setfont\smallbf\bfshape{10}{900}{OT1} \setfont\smallit\itshape{9}{1000}{OT1IT} \setfont\smallsl\slshape{9}{1000}{OT1} \setfont\smallsf\sfshape{9}{1000}{OT1} \setfont\smallsc\scshape{10}{900}{OT1} \setfont\smallttsl\ttslshape{10}{900}{OT1TT} \font\smalli=cmmi9 \font\smallsy=cmsy9 \def\smallecsize{0900} % Fonts for small examples (8pt). \def\smallernominalsize{8pt} \setfont\smallerrm\rmshape{8}{1000}{OT1} \setfont\smallertt\ttshape{8}{1000}{OT1TT} \setfont\smallerbf\bfshape{10}{800}{OT1} \setfont\smallerit\itshape{8}{1000}{OT1IT} \setfont\smallersl\slshape{8}{1000}{OT1} \setfont\smallersf\sfshape{8}{1000}{OT1} \setfont\smallersc\scshape{10}{800}{OT1} \setfont\smallerttsl\ttslshape{10}{800}{OT1TT} \font\smalleri=cmmi8 \font\smallersy=cmsy8 \def\smallerecsize{0800} % Fonts for title page (20.4pt): \def\titlenominalsize{20pt} \setfont\titlerm\rmbshape{12}{\magstep3}{OT1} \setfont\titleit\itbshape{10}{\magstep4}{OT1IT} \setfont\titlesl\slbshape{10}{\magstep4}{OT1} \setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} \setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} \setfont\titlesf\sfbshape{17}{\magstep1}{OT1} \let\titlebf=\titlerm \setfont\titlesc\scbshape{10}{\magstep4}{OT1} \font\titlei=cmmi12 scaled \magstep3 \font\titlesy=cmsy10 scaled \magstep4 \def\titleecsize{2074} % Chapter fonts (14.4pt). \def\chapnominalsize{14pt} \setfont\chaprm\rmbshape{12}{\magstep1}{OT1} \setfont\chapit\itbshape{10}{\magstep2}{OT1IT} \setfont\chapsl\slbshape{10}{\magstep2}{OT1} \setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT} \setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT} \setfont\chapsf\sfbshape{12}{\magstep1}{OT1} \let\chapbf\chaprm \setfont\chapsc\scbshape{10}{\magstep2}{OT1} \font\chapi=cmmi12 scaled \magstep1 \font\chapsy=cmsy10 scaled \magstep2 \def\chapecsize{1440} % Section fonts (12pt). \def\secnominalsize{12pt} \setfont\secrm\rmbshape{12}{1000}{OT1} \setfont\secit\itbshape{10}{\magstep1}{OT1IT} \setfont\secsl\slbshape{10}{\magstep1}{OT1} \setfont\sectt\ttbshape{12}{1000}{OT1TT} \setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT} \setfont\secsf\sfbshape{12}{1000}{OT1} \let\secbf\secrm \setfont\secsc\scbshape{10}{\magstep1}{OT1} \font\seci=cmmi12 \font\secsy=cmsy10 scaled \magstep1 \def\sececsize{1200} % Subsection fonts (10pt). \def\ssecnominalsize{10pt} \setfont\ssecrm\rmbshape{10}{1000}{OT1} \setfont\ssecit\itbshape{10}{1000}{OT1IT} \setfont\ssecsl\slbshape{10}{1000}{OT1} \setfont\ssectt\ttbshape{10}{1000}{OT1TT} \setfont\ssecttsl\ttslshape{10}{1000}{OT1TT} \setfont\ssecsf\sfbshape{10}{1000}{OT1} \let\ssecbf\ssecrm \setfont\ssecsc\scbshape{10}{1000}{OT1} \font\sseci=cmmi10 \font\ssecsy=cmsy10 \def\ssececsize{1000} % Reduced fonts for @acro in text (9pt). \def\reducednominalsize{9pt} \setfont\reducedrm\rmshape{9}{1000}{OT1} \setfont\reducedtt\ttshape{9}{1000}{OT1TT} \setfont\reducedbf\bfshape{10}{900}{OT1} \setfont\reducedit\itshape{9}{1000}{OT1IT} \setfont\reducedsl\slshape{9}{1000}{OT1} \setfont\reducedsf\sfshape{9}{1000}{OT1} \setfont\reducedsc\scshape{10}{900}{OT1} \setfont\reducedttsl\ttslshape{10}{900}{OT1TT} \font\reducedi=cmmi9 \font\reducedsy=cmsy9 \def\reducedecsize{0900} \divide\parskip by 2 % reduce space between paragraphs \textleading = 12pt % line spacing for 10pt CM \textfonts % reset the current fonts \rm } % end of 10pt text font size definitions, \definetextfontsizex % We provide the user-level command % @fonttextsize 10 % (or 11) to redefine the text font size. pt is assumed. % \def\xiword{11} \def\xword{10} \def\xwordpt{10pt} % \parseargdef\fonttextsize{% \def\textsizearg{#1}% %\wlog{doing @fonttextsize \textsizearg}% % % Set \globaldefs so that documents can use this inside @tex, since % makeinfo 4.8 does not support it, but we need it nonetheless. % \begingroup \globaldefs=1 \ifx\textsizearg\xword \definetextfontsizex \else \ifx\textsizearg\xiword \definetextfontsizexi \else \errhelp=\EMsimple \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} \fi\fi \endgroup } % In order for the font changes to affect most math symbols and letters, % we have to define the \textfont of the standard families. Since % texinfo doesn't allow for producing subscripts and superscripts except % in the main text, we don't bother to reset \scriptfont and % \scriptscriptfont (which would also require loading a lot more fonts). % \def\resetmathfonts{% \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf \textfont\ttfam=\tentt \textfont\sffam=\tensf } % The font-changing commands redefine the meanings of \tenSTYLE, instead % of just \STYLE. We do this because \STYLE needs to also set the % current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire % \tenSTYLE to set the current font. % % Each font-changing command also sets the names \lsize (one size lower) % and \lllsize (three sizes lower). These relative commands are used in % the LaTeX logo and acronyms. % % This all needs generalizing, badly. % \def\textfonts{% \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy \let\tenttsl=\textttsl \def\curfontsize{text}% \def\lsize{reduced}\def\lllsize{smaller}% \resetmathfonts \setleading{\textleading}} \def\titlefonts{% \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy \let\tenttsl=\titlettsl \def\curfontsize{title}% \def\lsize{chap}\def\lllsize{subsec}% \resetmathfonts \setleading{27pt}} \def\titlefont#1{{\titlefonts\rmisbold #1}} \def\chapfonts{% \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy \let\tenttsl=\chapttsl \def\curfontsize{chap}% \def\lsize{sec}\def\lllsize{text}% \resetmathfonts \setleading{19pt}} \def\secfonts{% \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy \let\tenttsl=\secttsl \def\curfontsize{sec}% \def\lsize{subsec}\def\lllsize{reduced}% \resetmathfonts \setleading{16pt}} \def\subsecfonts{% \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy \let\tenttsl=\ssecttsl \def\curfontsize{ssec}% \def\lsize{text}\def\lllsize{small}% \resetmathfonts \setleading{15pt}} \let\subsubsecfonts = \subsecfonts \def\reducedfonts{% \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy \let\tenttsl=\reducedttsl \def\curfontsize{reduced}% \def\lsize{small}\def\lllsize{smaller}% \resetmathfonts \setleading{10.5pt}} \def\smallfonts{% \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy \let\tenttsl=\smallttsl \def\curfontsize{small}% \def\lsize{smaller}\def\lllsize{smaller}% \resetmathfonts \setleading{10.5pt}} \def\smallerfonts{% \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy \let\tenttsl=\smallerttsl \def\curfontsize{smaller}% \def\lsize{smaller}\def\lllsize{smaller}% \resetmathfonts \setleading{9.5pt}} % Fonts for short table of contents. \setfont\shortcontrm\rmshape{12}{1000}{OT1} \setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12 \setfont\shortcontsl\slshape{12}{1000}{OT1} \setfont\shortconttt\ttshape{12}{1000}{OT1TT} % Define these just so they can be easily changed for other fonts. \def\angleleft{$\langle$} \def\angleright{$\rangle$} % Set the fonts to use with the @small... environments. \let\smallexamplefonts = \smallfonts % About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample % can fit this many characters: % 8.5x11=86 smallbook=72 a4=90 a5=69 % If we use \scriptfonts (8pt), then we can fit this many characters: % 8.5x11=90+ smallbook=80 a4=90+ a5=77 % For me, subjectively, the few extra characters that fit aren't worth % the additional smallness of 8pt. So I'm making the default 9pt. % % By the way, for comparison, here's what fits with @example (10pt): % 8.5x11=71 smallbook=60 a4=75 a5=58 % --karl, 24jan03. % Set up the default fonts, so we can use them for creating boxes. % \definetextfontsizexi \message{markup,} % Check if we are currently using a typewriter font. Since all the % Computer Modern typewriter fonts have zero interword stretch (and % shrink), and it is reasonable to expect all typewriter fonts to have % this property, we can check that font parameter. % \def\ifmonospace{\ifdim\fontdimen3\font=0pt } % Markup style infrastructure. \defmarkupstylesetup\INITMACRO will % define and register \INITMACRO to be called on markup style changes. % \INITMACRO can check \currentmarkupstyle for the innermost % style and the set of \ifmarkupSTYLE switches for all styles % currently in effect. \newif\ifmarkupvar \newif\ifmarkupsamp \newif\ifmarkupkey %\newif\ifmarkupfile % @file == @samp. %\newif\ifmarkupoption % @option == @samp. \newif\ifmarkupcode \newif\ifmarkupkbd %\newif\ifmarkupenv % @env == @code. %\newif\ifmarkupcommand % @command == @code. \newif\ifmarkuptex % @tex (and part of @math, for now). \newif\ifmarkupexample \newif\ifmarkupverb \newif\ifmarkupverbatim \let\currentmarkupstyle\empty \def\setupmarkupstyle#1{% \csname markup#1true\endcsname \def\currentmarkupstyle{#1}% \markupstylesetup } \let\markupstylesetup\empty \def\defmarkupstylesetup#1{% \expandafter\def\expandafter\markupstylesetup \expandafter{\markupstylesetup #1}% \def#1% } % Markup style setup for left and right quotes. \defmarkupstylesetup\markupsetuplq{% \expandafter\let\expandafter \temp \csname markupsetuplq\currentmarkupstyle\endcsname \ifx\temp\relax \markupsetuplqdefault \else \temp \fi } \defmarkupstylesetup\markupsetuprq{% \expandafter\let\expandafter \temp \csname markupsetuprq\currentmarkupstyle\endcsname \ifx\temp\relax \markupsetuprqdefault \else \temp \fi } { \catcode`\'=\active \catcode`\`=\active \gdef\markupsetuplqdefault{\let`\lq} \gdef\markupsetuprqdefault{\let'\rq} \gdef\markupsetcodequoteleft{\let`\codequoteleft} \gdef\markupsetcodequoteright{\let'\codequoteright} } \let\markupsetuplqcode \markupsetcodequoteleft \let\markupsetuprqcode \markupsetcodequoteright % \let\markupsetuplqexample \markupsetcodequoteleft \let\markupsetuprqexample \markupsetcodequoteright % \let\markupsetuplqkbd \markupsetcodequoteleft \let\markupsetuprqkbd \markupsetcodequoteright % \let\markupsetuplqsamp \markupsetcodequoteleft \let\markupsetuprqsamp \markupsetcodequoteright % \let\markupsetuplqverb \markupsetcodequoteleft \let\markupsetuprqverb \markupsetcodequoteright % \let\markupsetuplqverbatim \markupsetcodequoteleft \let\markupsetuprqverbatim \markupsetcodequoteright % Allow an option to not use regular directed right quote/apostrophe % (char 0x27), but instead the undirected quote from cmtt (char 0x0d). % The undirected quote is ugly, so don't make it the default, but it % works for pasting with more pdf viewers (at least evince), the % lilypond developers report. xpdf does work with the regular 0x27. % \def\codequoteright{% \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax '% \else \char'15 \fi \else \char'15 \fi } % % and a similar option for the left quote char vs. a grave accent. % Modern fonts display ASCII 0x60 as a grave accent, so some people like % the code environments to do likewise. % \def\codequoteleft{% \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax % [Knuth] pp. 380,381,391 % \relax disables Spanish ligatures ?` and !` of \tt font. \relax`% \else \char'22 \fi \else \char'22 \fi } % Commands to set the quote options. % \parseargdef\codequoteundirected{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETtxicodequoteundirected\endcsname = t% \else\ifx\temp\offword \expandafter\let\csname SETtxicodequoteundirected\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @codequoteundirected value `\temp', must be on|off}% \fi\fi } % \parseargdef\codequotebacktick{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETtxicodequotebacktick\endcsname = t% \else\ifx\temp\offword \expandafter\let\csname SETtxicodequotebacktick\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @codequotebacktick value `\temp', must be on|off}% \fi\fi } % [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font. \def\noligaturesquoteleft{\relax\lq} % Count depth in font-changes, for error checks \newcount\fontdepth \fontdepth=0 % Font commands. % #1 is the font command (\sl or \it), #2 is the text to slant. % If we are in a monospaced environment, however, 1) always use \ttsl, % and 2) do not add an italic correction. \def\dosmartslant#1#2{% \ifusingtt {{\ttsl #2}\let\next=\relax}% {\def\next{{#1#2}\futurelet\next\smartitaliccorrection}}% \next } \def\smartslanted{\dosmartslant\sl} \def\smartitalic{\dosmartslant\it} % Output an italic correction unless \next (presumed to be the following % character) is such as not to need one. \def\smartitaliccorrection{% \ifx\next,% \else\ifx\next-% \else\ifx\next.% \else\ptexslash \fi\fi\fi \aftersmartic } % Unconditional use \ttsl, and no ic. @var is set to this for defuns. \def\ttslanted#1{{\ttsl #1}} % @cite is like \smartslanted except unconditionally use \sl. We never want % ttsl for book titles, do we? \def\cite#1{{\sl #1}\futurelet\next\smartitaliccorrection} \def\aftersmartic{} \def\var#1{% \let\saveaftersmartic = \aftersmartic \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}% \smartslanted{#1}% } \let\i=\smartitalic \let\slanted=\smartslanted \let\dfn=\smartslanted \let\emph=\smartitalic % Explicit font changes: @r, @sc, undocumented @ii. \def\r#1{{\rm #1}} % roman font \def\sc#1{{\smallcaps#1}} % smallcaps font \def\ii#1{{\it #1}} % italic font % @b, explicit bold. Also @strong. \def\b#1{{\bf #1}} \let\strong=\b % @sansserif, explicit sans. \def\sansserif#1{{\sf #1}} % We can't just use \exhyphenpenalty, because that only has effect at % the end of a paragraph. Restore normal hyphenation at the end of the % group within which \nohyphenation is presumably called. % \def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} \def\restorehyphenation{\hyphenchar\font = `- } % Set sfcode to normal for the chars that usually have another value. % Can't use plain's \frenchspacing because it uses the `\x notation, and % sometimes \x has an active definition that messes things up. % \catcode`@=11 \def\plainfrenchspacing{% \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m \def\endofsentencespacefactor{1000}% for @. and friends } \def\plainnonfrenchspacing{% \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 \def\endofsentencespacefactor{3000}% for @. and friends } \catcode`@=\other \def\endofsentencespacefactor{3000}% default % @t, explicit typewriter. \def\t#1{% {\tt \rawbackslash \plainfrenchspacing #1}% \null } % @samp. \def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}} % @indicateurl is \samp, that is, with quotes. \let\indicateurl=\samp % @code (and similar) prints in typewriter, but with spaces the same % size as normal in the surrounding text, without hyphenation, etc. % This is a subroutine for that. \def\tclose#1{% {% % Change normal interword space to be same as for the current font. \spaceskip = \fontdimen2\font % % Switch to typewriter. \tt % % But `\ ' produces the large typewriter interword space. \def\ {{\spaceskip = 0pt{} }}% % % Turn off hyphenation. \nohyphenation % \rawbackslash \plainfrenchspacing #1% }% \null % reset spacefactor to 1000 } % We *must* turn on hyphenation at `-' and `_' in @code. % Otherwise, it is too hard to avoid overfull hboxes % in the Emacs manual, the Library manual, etc. % % Unfortunately, TeX uses one parameter (\hyphenchar) to control % both hyphenation at - and hyphenation within words. % We must therefore turn them both off (\tclose does that) % and arrange explicitly to hyphenate at a dash. % -- rms. { \catcode`\-=\active \catcode`\_=\active \catcode`\'=\active \catcode`\`=\active \global\let'=\rq \global\let`=\lq % default definitions % \global\def\code{\begingroup \setupmarkupstyle{code}% % The following should really be moved into \setupmarkupstyle handlers. \catcode\dashChar=\active \catcode\underChar=\active \ifallowcodebreaks \let-\codedash \let_\codeunder \else \let-\normaldash \let_\realunder \fi \codex } } \def\codex #1{\tclose{#1}\endgroup} \def\normaldash{-} \def\codedash{-\discretionary{}{}{}} \def\codeunder{% % this is all so @math{@code{var_name}+1} can work. In math mode, _ % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) % will therefore expand the active definition of _, which is us % (inside @code that is), therefore an endless loop. \ifusingtt{\ifmmode \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. \else\normalunderscore \fi \discretionary{}{}{}}% {\_}% } % An additional complication: the above will allow breaks after, e.g., % each of the four underscores in __typeof__. This is bad. % @allowcodebreaks provides a document-level way to turn breaking at - % and _ on and off. % \newif\ifallowcodebreaks \allowcodebreakstrue \def\keywordtrue{true} \def\keywordfalse{false} \parseargdef\allowcodebreaks{% \def\txiarg{#1}% \ifx\txiarg\keywordtrue \allowcodebreakstrue \else\ifx\txiarg\keywordfalse \allowcodebreaksfalse \else \errhelp = \EMsimple \errmessage{Unknown @allowcodebreaks option `\txiarg', must be true|false}% \fi\fi } % For @command, @env, @file, @option quotes seem unnecessary, % so use \code rather than \samp. \let\command=\code \let\env=\code \let\file=\code \let\option=\code % @uref (abbreviation for `urlref') takes an optional (comma-separated) % second argument specifying the text to display and an optional third % arg as text to display instead of (rather than in addition to) the url % itself. First (mandatory) arg is the url. % (This \urefnobreak definition isn't used now, leaving it for a while % for comparison.) \def\urefnobreak#1{\dourefnobreak #1,,,\finish} \def\dourefnobreak#1,#2,#3,#4\finish{\begingroup \unsepspaces \pdfurl{#1}% \setbox0 = \hbox{\ignorespaces #3}% \ifdim\wd0 > 0pt \unhbox0 % third arg given, show only that \else \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0 > 0pt \ifpdf \unhbox0 % PDF: 2nd arg given, show only it \else \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url \fi \else \code{#1}% only url given, so show it \fi \fi \endlink \endgroup} % This \urefbreak definition is the active one. \def\urefbreak{\begingroup \urefcatcodes \dourefbreak} \let\uref=\urefbreak \def\dourefbreak#1{\urefbreakfinish #1,,,\finish} \def\urefbreakfinish#1,#2,#3,#4\finish{% doesn't work in @example \unsepspaces \pdfurl{#1}% \setbox0 = \hbox{\ignorespaces #3}% \ifdim\wd0 > 0pt \unhbox0 % third arg given, show only that \else \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0 > 0pt \ifpdf \unhbox0 % PDF: 2nd arg given, show only it \else \unhbox0\ (\urefcode{#1})% DVI: 2nd arg given, show both it and url \fi \else \urefcode{#1}% only url given, so show it \fi \fi \endlink \endgroup} % Allow line breaks around only a few characters (only). \def\urefcatcodes{% \catcode\ampChar=\active \catcode\dotChar=\active \catcode\hashChar=\active \catcode\questChar=\active \catcode\slashChar=\active } { \urefcatcodes % \global\def\urefcode{\begingroup \setupmarkupstyle{code}% \urefcatcodes \let&\urefcodeamp \let.\urefcodedot \let#\urefcodehash \let?\urefcodequest \let/\urefcodeslash \codex } % % By default, they are just regular characters. \global\def&{\normalamp} \global\def.{\normaldot} \global\def#{\normalhash} \global\def?{\normalquest} \global\def/{\normalslash} } % we put a little stretch before and after the breakable chars, to help % line breaking of long url's. The unequal skips make look better in % cmtt at least, especially for dots. \def\urefprestretch{\urefprebreak \hskip0pt plus.13em } \def\urefpoststretch{\urefpostbreak \hskip0pt plus.1em } % \def\urefcodeamp{\urefprestretch \&\urefpoststretch} \def\urefcodedot{\urefprestretch .\urefpoststretch} \def\urefcodehash{\urefprestretch \#\urefpoststretch} \def\urefcodequest{\urefprestretch ?\urefpoststretch} \def\urefcodeslash{\futurelet\next\urefcodeslashfinish} { \catcode`\/=\active \global\def\urefcodeslashfinish{% \urefprestretch \slashChar % Allow line break only after the final / in a sequence of % slashes, to avoid line break between the slashes in http://. \ifx\next/\else \urefpoststretch \fi } } % One more complication: by default we'll break after the special % characters, but some people like to break before the special chars, so % allow that. Also allow no breaking at all, for manual control. % \parseargdef\urefbreakstyle{% \def\txiarg{#1}% \ifx\txiarg\wordnone \def\urefprebreak{\nobreak}\def\urefpostbreak{\nobreak} \else\ifx\txiarg\wordbefore \def\urefprebreak{\allowbreak}\def\urefpostbreak{\nobreak} \else\ifx\txiarg\wordafter \def\urefprebreak{\nobreak}\def\urefpostbreak{\allowbreak} \else \errhelp = \EMsimple \errmessage{Unknown @urefbreakstyle setting `\txiarg'}% \fi\fi\fi } \def\wordafter{after} \def\wordbefore{before} \def\wordnone{none} \urefbreakstyle after % @url synonym for @uref, since that's how everyone uses it. % \let\url=\uref % rms does not like angle brackets --karl, 17may97. % So now @email is just like @uref, unless we are pdf. % %\def\email#1{\angleleft{\tt #1}\angleright} \ifpdf \def\email#1{\doemail#1,,\finish} \def\doemail#1,#2,#3\finish{\begingroup \unsepspaces \pdfurl{mailto:#1}% \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi \endlink \endgroup} \else \let\email=\uref \fi % @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), % `example' (@kbd uses ttsl only inside of @example and friends), % or `code' (@kbd uses normal tty font always). \parseargdef\kbdinputstyle{% \def\txiarg{#1}% \ifx\txiarg\worddistinct \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% \else\ifx\txiarg\wordexample \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% \else\ifx\txiarg\wordcode \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% \else \errhelp = \EMsimple \errmessage{Unknown @kbdinputstyle setting `\txiarg'}% \fi\fi\fi } \def\worddistinct{distinct} \def\wordexample{example} \def\wordcode{code} % Default is `distinct'. \kbdinputstyle distinct % @kbd is like @code, except that if the argument is just one @key command, % then @kbd has no effect. \def\kbd#1{{\def\look{#1}\expandafter\kbdsub\look??\par}} \def\xkey{\key} \def\kbdsub#1#2#3\par{% \def\one{#1}\def\three{#3}\def\threex{??}% \ifx\one\xkey\ifx\threex\three \key{#2}% \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi } % definition of @key that produces a lozenge. Doesn't adjust to text size. %\setfont\keyrm\rmshape{8}{1000}{OT1} %\font\keysy=cmsy9 %\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% % \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% % \vbox{\hrule\kern-0.4pt % \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% % \kern-0.4pt\hrule}% % \kern-.06em\raise0.4pt\hbox{\angleright}}}} % definition of @key with no lozenge. If the current font is already % monospace, don't change it; that way, we respect @kbdinputstyle. But % if it isn't monospace, then use \tt. % \def\key#1{{\setupmarkupstyle{key}% \nohyphenation \ifmonospace\else\tt\fi #1}\null} % @clicksequence{File @click{} Open ...} \def\clicksequence#1{\begingroup #1\endgroup} % @clickstyle @arrow (by default) \parseargdef\clickstyle{\def\click{#1}} \def\click{\arrow} % Typeset a dimension, e.g., `in' or `pt'. The only reason for the % argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. % \def\dmn#1{\thinspace #1} % @l was never documented to mean ``switch to the Lisp font'', % and it is not used as such in any manual I can find. We need it for % Polish suppressed-l. --karl, 22sep96. %\def\l#1{{\li #1}\null} % @acronym for "FBI", "NATO", and the like. % We print this one point size smaller, since it's intended for % all-uppercase. % \def\acronym#1{\doacronym #1,,\finish} \def\doacronym#1,#2,#3\finish{% {\selectfonts\lsize #1}% \def\temp{#2}% \ifx\temp\empty \else \space ({\unsepspaces \ignorespaces \temp \unskip})% \fi \null % reset \spacefactor=1000 } % @abbr for "Comput. J." and the like. % No font change, but don't do end-of-sentence spacing. % \def\abbr#1{\doabbr #1,,\finish} \def\doabbr#1,#2,#3\finish{% {\plainfrenchspacing #1}% \def\temp{#2}% \ifx\temp\empty \else \space ({\unsepspaces \ignorespaces \temp \unskip})% \fi \null % reset \spacefactor=1000 } % @asis just yields its argument. Used with @table, for example. % \def\asis#1{#1} % @math outputs its argument in math mode. % % One complication: _ usually means subscripts, but it could also mean % an actual _ character, as in @math{@var{some_variable} + 1}. So make % _ active, and distinguish by seeing if the current family is \slfam, % which is what @var uses. { \catcode`\_ = \active \gdef\mathunderscore{% \catcode`\_=\active \def_{\ifnum\fam=\slfam \_\else\sb\fi}% } } % Another complication: we want \\ (and @\) to output a math (or tt) \. % FYI, plain.tex uses \\ as a temporary control sequence (for no % particular reason), but this is not advertised and we don't care. % % The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. \def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} % \def\math{% \tex \mathunderscore \let\\ = \mathbackslash \mathactive % make the texinfo accent commands work in math mode \let\"=\ddot \let\'=\acute \let\==\bar \let\^=\hat \let\`=\grave \let\u=\breve \let\v=\check \let\~=\tilde \let\dotaccent=\dot $\finishmath } \def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. % Some active characters (such as <) are spaced differently in math. % We have to reset their definitions in case the @math was an argument % to a command which sets the catcodes (such as @item or @section). % { \catcode`^ = \active \catcode`< = \active \catcode`> = \active \catcode`+ = \active \catcode`' = \active \gdef\mathactive{% \let^ = \ptexhat \let< = \ptexless \let> = \ptexgtr \let+ = \ptexplus \let' = \ptexquoteright } } % ctrl is no longer a Texinfo command, but leave this definition for fun. \def\ctrl #1{{\tt \rawbackslash \hat}#1} % @inlinefmt{FMTNAME,PROCESSED-TEXT} and @inlineraw{FMTNAME,RAW-TEXT}. % Ignore unless FMTNAME == tex; then it is like @iftex and @tex, % except specified as a normal braced arg, so no newlines to worry about. % \def\outfmtnametex{tex} % \long\def\inlinefmt#1{\doinlinefmt #1,\finish} \long\def\doinlinefmt#1,#2,\finish{% \def\inlinefmtname{#1}% \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\fi } % For raw, must switch into @tex before parsing the argument, to avoid % setting catcodes prematurely. Doing it this way means that, for % example, @inlineraw{html, foo{bar} gets a parse error instead of being % ignored. But this isn't important because if people want a literal % *right* brace they would have to use a command anyway, so they may as % well use a command to get a left brace too. We could re-use the % delimiter character idea from \verb, but it seems like overkill. % \long\def\inlineraw{\tex \doinlineraw} \long\def\doinlineraw#1{\doinlinerawtwo #1,\finish} \def\doinlinerawtwo#1,#2,\finish{% \def\inlinerawname{#1}% \ifx\inlinerawname\outfmtnametex \ignorespaces #2\fi \endgroup % close group opened by \tex. } \message{glyphs,} % and logos. % @@ prints an @, as does @atchar{}. \def\@{\char64 } \let\atchar=\@ % @{ @} @lbracechar{} @rbracechar{} all generate brace characters. % Unless we're in typewriter, use \ecfont because the CM text fonts do % not have braces, and we don't want to switch into math. \def\mylbrace{{\ifmonospace\else\ecfont\fi \char123}} \def\myrbrace{{\ifmonospace\else\ecfont\fi \char125}} \let\{=\mylbrace \let\lbracechar=\{ \let\}=\myrbrace \let\rbracechar=\} \begingroup % Definitions to produce \{ and \} commands for indices, % and @{ and @} for the aux/toc files. \catcode`\{ = \other \catcode`\} = \other \catcode`\[ = 1 \catcode`\] = 2 \catcode`\! = 0 \catcode`\\ = \other !gdef!lbracecmd[\{]% !gdef!rbracecmd[\}]% !gdef!lbraceatcmd[@{]% !gdef!rbraceatcmd[@}]% !endgroup % @comma{} to avoid , parsing problems. \let\comma = , % Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent % Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. \let\, = \ptexc \let\dotaccent = \ptexdot \def\ringaccent#1{{\accent23 #1}} \let\tieaccent = \ptext \let\ubaraccent = \ptexb \let\udotaccent = \d % Other special characters: @questiondown @exclamdown @ordf @ordm % Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. \def\questiondown{?`} \def\exclamdown{!`} \def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}} \def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}} % Dotless i and dotless j, used for accents. \def\imacro{i} \def\jmacro{j} \def\dotless#1{% \def\temp{#1}% \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi \else \errmessage{@dotless can be used only with i or j}% \fi\fi } % The \TeX{} logo, as in plain, but resetting the spacing so that a % period following counts as ending a sentence. (Idea found in latex.) % \edef\TeX{\TeX \spacefactor=1000 } % @LaTeX{} logo. Not quite the same results as the definition in % latex.ltx, since we use a different font for the raised A; it's most % convenient for us to use an explicitly smaller font, rather than using % the \scriptstyle font (since we don't reset \scriptstyle and % \scriptscriptstyle). % \def\LaTeX{% L\kern-.36em {\setbox0=\hbox{T}% \vbox to \ht0{\hbox{% \ifx\textnominalsize\xwordpt % for 10pt running text, \lllsize (8pt) is too small for the A in LaTeX. % Revert to plain's \scriptsize, which is 7pt. \count255=\the\fam $\fam\count255 \scriptstyle A$% \else % For 11pt, we can use our lllsize. \selectfonts\lllsize A% \fi }% \vss }}% \kern-.15em \TeX } % Some math mode symbols. \def\bullet{$\ptexbullet$} \def\geq{\ifmmode \ge\else $\ge$\fi} \def\leq{\ifmmode \le\else $\le$\fi} \def\minus{\ifmmode -\else $-$\fi} % @dots{} outputs an ellipsis using the current font. % We do .5em per period so that it has the same spacing in the cm % typewriter fonts as three actual period characters; on the other hand, % in other typewriter fonts three periods are wider than 1.5em. So do % whichever is larger. % \def\dots{% \leavevmode \setbox0=\hbox{...}% get width of three periods \ifdim\wd0 > 1.5em \dimen0 = \wd0 \else \dimen0 = 1.5em \fi \hbox to \dimen0{% \hskip 0pt plus.25fil .\hskip 0pt plus1fil .\hskip 0pt plus1fil .\hskip 0pt plus.5fil }% } % @enddots{} is an end-of-sentence ellipsis. % \def\enddots{% \dots \spacefactor=\endofsentencespacefactor } % @point{}, @result{}, @expansion{}, @print{}, @equiv{}. % % Since these characters are used in examples, they should be an even number of % \tt widths. Each \tt character is 1en, so two makes it 1em. % \def\point{$\star$} \def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}} \def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} \def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}} \def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} \def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}} % The @error{} command. % Adapted from the TeXbook's \boxit. % \newbox\errorbox % {\tentt \global\dimen0 = 3em}% Width of the box. \dimen2 = .55pt % Thickness of rules % The text. (`r' is open on the right, `e' somewhat less so on the left.) \setbox0 = \hbox{\kern-.75pt \reducedsf \putworderror\kern-1.5pt} % \setbox\errorbox=\hbox to \dimen0{\hfil \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. \advance\hsize by -2\dimen2 % Rules. \vbox{% \hrule height\dimen2 \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. \kern3pt\vrule width\dimen2}% Space to right. \hrule height\dimen2} \hfil} % \def\error{\leavevmode\lower.7ex\copy\errorbox} % @pounds{} is a sterling sign, which Knuth put in the CM italic font. % \def\pounds{{\it\$}} % @euro{} comes from a separate font, depending on the current style. % We use the free feym* fonts from the eurosym package by Henrik % Theiling, which support regular, slanted, bold and bold slanted (and % "outlined" (blackboard board, sort of) versions, which we don't need). % It is available from http://www.ctan.org/tex-archive/fonts/eurosym. % % Although only regular is the truly official Euro symbol, we ignore % that. The Euro is designed to be slightly taller than the regular % font height. % % feymr - regular % feymo - slanted % feybr - bold % feybo - bold slanted % % There is no good (free) typewriter version, to my knowledge. % A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. % Hmm. % % Also doesn't work in math. Do we need to do math with euro symbols? % Hope not. % % \def\euro{{\eurofont e}} \def\eurofont{% % We set the font at each command, rather than predefining it in % \textfonts and the other font-switching commands, so that % installations which never need the symbol don't have to have the % font installed. % % There is only one designed size (nominal 10pt), so we always scale % that to the current nominal size. % % By the way, simply using "at 1em" works for cmr10 and the like, but % does not work for cmbx10 and other extended/shrunken fonts. % \def\eurosize{\csname\curfontsize nominalsize\endcsname}% % \ifx\curfontstyle\bfstylename % bold: \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize \else % regular: \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize \fi \thiseurofont } % Glyphs from the EC fonts. We don't use \let for the aliases, because % sometimes we redefine the original macro, and the alias should reflect % the redefinition. % % Use LaTeX names for the Icelandic letters. \def\DH{{\ecfont \char"D0}} % Eth \def\dh{{\ecfont \char"F0}} % eth \def\TH{{\ecfont \char"DE}} % Thorn \def\th{{\ecfont \char"FE}} % thorn % \def\guillemetleft{{\ecfont \char"13}} \def\guillemotleft{\guillemetleft} \def\guillemetright{{\ecfont \char"14}} \def\guillemotright{\guillemetright} \def\guilsinglleft{{\ecfont \char"0E}} \def\guilsinglright{{\ecfont \char"0F}} \def\quotedblbase{{\ecfont \char"12}} \def\quotesinglbase{{\ecfont \char"0D}} % % This positioning is not perfect (see the ogonek LaTeX package), but % we have the precomposed glyphs for the most common cases. We put the % tests to use those glyphs in the single \ogonek macro so we have fewer % dummy definitions to worry about for index entries, etc. % % ogonek is also used with other letters in Lithuanian (IOU), but using % the precomposed glyphs for those is not so easy since they aren't in % the same EC font. \def\ogonek#1{{% \def\temp{#1}% \ifx\temp\macrocharA\Aogonek \else\ifx\temp\macrochara\aogonek \else\ifx\temp\macrocharE\Eogonek \else\ifx\temp\macrochare\eogonek \else \ecfont \setbox0=\hbox{#1}% \ifdim\ht0=1ex\accent"0C #1% \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}% \fi \fi\fi\fi\fi }% } \def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A} \def\aogonek{{\ecfont \char"A1}}\def\macrochara{a} \def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E} \def\eogonek{{\ecfont \char"A6}}\def\macrochare{e} % % Use the ec* fonts (cm-super in outline format) for non-CM glyphs. \def\ecfont{% % We can't distinguish serif/sans and italic/slanted, but this % is used for crude hacks anyway (like adding French and German % quotes to documents typeset with CM, where we lose kerning), so % hopefully nobody will notice/care. \edef\ecsize{\csname\curfontsize ecsize\endcsname}% \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}% \ifmonospace % typewriter: \font\thisecfont = ectt\ecsize \space at \nominalsize \else \ifx\curfontstyle\bfstylename % bold: \font\thisecfont = ecb\ifusingit{i}{x}\ecsize \space at \nominalsize \else % regular: \font\thisecfont = ec\ifusingit{ti}{rm}\ecsize \space at \nominalsize \fi \fi \thisecfont } % @registeredsymbol - R in a circle. The font for the R should really % be smaller yet, but lllsize is the best we can do for now. % Adapted from the plain.tex definition of \copyright. % \def\registeredsymbol{% $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}% \hfil\crcr\Orb}}% }$% } % @textdegree - the normal degrees sign. % \def\textdegree{$^\circ$} % Laurent Siebenmann reports \Orb undefined with: % Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 % so we'll define it if necessary. % \ifx\Orb\thisisundefined \def\Orb{\mathhexbox20D} \fi % Quotes. \chardef\quotedblleft="5C \chardef\quotedblright=`\" \chardef\quoteleft=`\` \chardef\quoteright=`\' \message{page headings,} \newskip\titlepagetopglue \titlepagetopglue = 1.5in \newskip\titlepagebottomglue \titlepagebottomglue = 2pc % First the title page. Must do @settitle before @titlepage. \newif\ifseenauthor \newif\iffinishedtitlepage % Do an implicit @contents or @shortcontents after @end titlepage if the % user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. % \newif\ifsetcontentsaftertitlepage \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue \newif\ifsetshortcontentsaftertitlepage \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue \parseargdef\shorttitlepage{% \begingroup \hbox{}\vskip 1.5in \chaprm \centerline{#1}% \endgroup\page\hbox{}\page} \envdef\titlepage{% % Open one extra group, as we want to close it in the middle of \Etitlepage. \begingroup \parindent=0pt \textfonts % Leave some space at the very top of the page. \vglue\titlepagetopglue % No rule at page bottom unless we print one at the top with @title. \finishedtitlepagetrue % % Most title ``pages'' are actually two pages long, with space % at the top of the second. We don't want the ragged left on the second. \let\oldpage = \page \def\page{% \iffinishedtitlepage\else \finishtitlepage \fi \let\page = \oldpage \page \null }% } \def\Etitlepage{% \iffinishedtitlepage\else \finishtitlepage \fi % It is important to do the page break before ending the group, % because the headline and footline are only empty inside the group. % If we use the new definition of \page, we always get a blank page % after the title page, which we certainly don't want. \oldpage \endgroup % % Need this before the \...aftertitlepage checks so that if they are % in effect the toc pages will come out with page numbers. \HEADINGSon % % If they want short, they certainly want long too. \ifsetshortcontentsaftertitlepage \shortcontents \contents \global\let\shortcontents = \relax \global\let\contents = \relax \fi % \ifsetcontentsaftertitlepage \contents \global\let\contents = \relax \global\let\shortcontents = \relax \fi } \def\finishtitlepage{% \vskip4pt \hrule height 2pt width \hsize \vskip\titlepagebottomglue \finishedtitlepagetrue } % Settings used for typesetting titles: no hyphenation, no indentation, % don't worry much about spacing, ragged right. This should be used % inside a \vbox, and fonts need to be set appropriately first. Because % it is always used for titles, nothing else, we call \rmisbold. \par % should be specified before the end of the \vbox, since a vbox is a group. % \def\raggedtitlesettings{% \rmisbold \hyphenpenalty=10000 \parindent=0pt \tolerance=5000 \ptexraggedright } % Macros to be used within @titlepage: \let\subtitlerm=\tenrm \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} \parseargdef\title{% \checkenv\titlepage \vbox{\titlefonts \raggedtitlesettings #1\par}% % print a rule at the page bottom also. \finishedtitlepagefalse \vskip4pt \hrule height 4pt width \hsize \vskip4pt } \parseargdef\subtitle{% \checkenv\titlepage {\subtitlefont \rightline{#1}}% } % @author should come last, but may come many times. % It can also be used inside @quotation. % \parseargdef\author{% \def\temp{\quotation}% \ifx\thisenv\temp \def\quotationauthor{#1}% printed in \Equotation. \else \checkenv\titlepage \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi {\secfonts\rmisbold \leftline{#1}}% \fi } % Set up page headings and footings. \let\thispage=\folio \newtoks\evenheadline % headline on even pages \newtoks\oddheadline % headline on odd pages \newtoks\evenfootline % footline on even pages \newtoks\oddfootline % footline on odd pages % Now make TeX use those variables \headline={{\textfonts\rm \ifodd\pageno \the\oddheadline \else \the\evenheadline \fi}} \footline={{\textfonts\rm \ifodd\pageno \the\oddfootline \else \the\evenfootline \fi}\HEADINGShook} \let\HEADINGShook=\relax % Commands to set those variables. % For example, this is what @headings on does % @evenheading @thistitle|@thispage|@thischapter % @oddheading @thischapter|@thispage|@thistitle % @evenfooting @thisfile|| % @oddfooting ||@thisfile \def\evenheading{\parsearg\evenheadingxxx} \def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} \def\evenheadingyyy #1\|#2\|#3\|#4\finish{% \global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \def\oddheading{\parsearg\oddheadingxxx} \def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} \def\oddheadingyyy #1\|#2\|#3\|#4\finish{% \global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% \def\evenfooting{\parsearg\evenfootingxxx} \def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} \def\evenfootingyyy #1\|#2\|#3\|#4\finish{% \global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \def\oddfooting{\parsearg\oddfootingxxx} \def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} \def\oddfootingyyy #1\|#2\|#3\|#4\finish{% \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% % % Leave some space for the footline. Hopefully ok to assume % @evenfooting will not be used by itself. \global\advance\pageheight by -12pt \global\advance\vsize by -12pt } \parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} % @evenheadingmarks top \thischapter <- chapter at the top of a page % @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page % % The same set of arguments for: % % @oddheadingmarks % @evenfootingmarks % @oddfootingmarks % @everyheadingmarks % @everyfootingmarks \def\evenheadingmarks{\headingmarks{even}{heading}} \def\oddheadingmarks{\headingmarks{odd}{heading}} \def\evenfootingmarks{\headingmarks{even}{footing}} \def\oddfootingmarks{\headingmarks{odd}{footing}} \def\everyheadingmarks#1 {\headingmarks{even}{heading}{#1} \headingmarks{odd}{heading}{#1} } \def\everyfootingmarks#1 {\headingmarks{even}{footing}{#1} \headingmarks{odd}{footing}{#1} } % #1 = even/odd, #2 = heading/footing, #3 = top/bottom. \def\headingmarks#1#2#3 {% \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname \global\expandafter\let\csname get#1#2marks\endcsname \temp } \everyheadingmarks bottom \everyfootingmarks bottom % @headings double turns headings on for double-sided printing. % @headings single turns headings on for single-sided printing. % @headings off turns them off. % @headings on same as @headings double, retained for compatibility. % @headings after turns on double-sided headings after this page. % @headings doubleafter turns on double-sided headings after this page. % @headings singleafter turns on single-sided headings after this page. % By default, they are off at the start of a document, % and turned `on' after @end titlepage. \def\headings #1 {\csname HEADINGS#1\endcsname} \def\headingsoff{% non-global headings elimination \evenheadline={\hfil}\evenfootline={\hfil}% \oddheadline={\hfil}\oddfootline={\hfil}% } \def\HEADINGSoff{{\globaldefs=1 \headingsoff}} % global setting \HEADINGSoff % it's the default % When we turn headings on, set the page number to 1. % For double-sided printing, put current file name in lower left corner, % chapter name on inside top of right hand pages, document % title on inside top of left hand pages, and page numbers on outside top % edge of all pages. \def\HEADINGSdouble{% \global\pageno=1 \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\folio\hfil\thistitle}} \global\oddheadline={\line{\thischapter\hfil\folio}} \global\let\contentsalignmacro = \chapoddpage } \let\contentsalignmacro = \chappager % For single-sided printing, chapter title goes across top left of page, % page number on top right. \def\HEADINGSsingle{% \global\pageno=1 \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\thischapter\hfil\folio}} \global\oddheadline={\line{\thischapter\hfil\folio}} \global\let\contentsalignmacro = \chappager } \def\HEADINGSon{\HEADINGSdouble} \def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} \let\HEADINGSdoubleafter=\HEADINGSafter \def\HEADINGSdoublex{% \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\folio\hfil\thistitle}} \global\oddheadline={\line{\thischapter\hfil\folio}} \global\let\contentsalignmacro = \chapoddpage } \def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} \def\HEADINGSsinglex{% \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\thischapter\hfil\folio}} \global\oddheadline={\line{\thischapter\hfil\folio}} \global\let\contentsalignmacro = \chappager } % Subroutines used in generating headings % This produces Day Month Year style of output. % Only define if not already defined, in case a txi-??.tex file has set % up a different format (e.g., txi-cs.tex does this). \ifx\today\thisisundefined \def\today{% \number\day\space \ifcase\month \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec \fi \space\number\year} \fi % @settitle line... specifies the title of the document, for headings. % It generates no output of its own. \def\thistitle{\putwordNoTitle} \def\settitle{\parsearg{\gdef\thistitle}} \message{tables,} % Tables -- @table, @ftable, @vtable, @item(x). % default indentation of table text \newdimen\tableindent \tableindent=.8in % default indentation of @itemize and @enumerate text \newdimen\itemindent \itemindent=.3in % margin between end of table item and start of table text. \newdimen\itemmargin \itemmargin=.1in % used internally for \itemindent minus \itemmargin \newdimen\itemmax % Note @table, @ftable, and @vtable define @item, @itemx, etc., with % these defs. % They also define \itemindex % to index the item name in whatever manner is desired (perhaps none). \newif\ifitemxneedsnegativevskip \def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} \def\internalBitem{\smallbreak \parsearg\itemzzz} \def\internalBitemx{\itemxpar \parsearg\itemzzz} \def\itemzzz #1{\begingroup % \advance\hsize by -\rightskip \advance\hsize by -\tableindent \setbox0=\hbox{\itemindicate{#1}}% \itemindex{#1}% \nobreak % This prevents a break before @itemx. % % If the item text does not fit in the space we have, put it on a line % by itself, and do not allow a page break either before or after that % line. We do not start a paragraph here because then if the next % command is, e.g., @kindex, the whatsit would get put into the % horizontal list on a line by itself, resulting in extra blank space. \ifdim \wd0>\itemmax % % Make this a paragraph so we get the \parskip glue and wrapping, % but leave it ragged-right. \begingroup \advance\leftskip by-\tableindent \advance\hsize by\tableindent \advance\rightskip by0pt plus1fil\relax \leavevmode\unhbox0\par \endgroup % % We're going to be starting a paragraph, but we don't want the % \parskip glue -- logically it's part of the @item we just started. \nobreak \vskip-\parskip % % Stop a page break at the \parskip glue coming up. However, if % what follows is an environment such as @example, there will be no % \parskip glue; then the negative vskip we just inserted would % cause the example and the item to crash together. So we use this % bizarre value of 10001 as a signal to \aboveenvbreak to insert % \parskip glue after all. Section titles are handled this way also. % \penalty 10001 \endgroup \itemxneedsnegativevskipfalse \else % The item text fits into the space. Start a paragraph, so that the % following text (if any) will end up on the same line. \noindent % Do this with kerns and \unhbox so that if there is a footnote in % the item text, it can migrate to the main vertical list and % eventually be printed. \nobreak\kern-\tableindent \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 \unhbox0 \nobreak\kern\dimen0 \endgroup \itemxneedsnegativevskiptrue \fi } \def\item{\errmessage{@item while not in a list environment}} \def\itemx{\errmessage{@itemx while not in a list environment}} % @table, @ftable, @vtable. \envdef\table{% \let\itemindex\gobble \tablecheck{table}% } \envdef\ftable{% \def\itemindex ##1{\doind {fn}{\code{##1}}}% \tablecheck{ftable}% } \envdef\vtable{% \def\itemindex ##1{\doind {vr}{\code{##1}}}% \tablecheck{vtable}% } \def\tablecheck#1{% \ifnum \the\catcode`\^^M=\active \endgroup \errmessage{This command won't work in this context; perhaps the problem is that we are \inenvironment\thisenv}% \def\next{\doignore{#1}}% \else \let\next\tablex \fi \next } \def\tablex#1{% \def\itemindicate{#1}% \parsearg\tabley } \def\tabley#1{% {% \makevalueexpandable \edef\temp{\noexpand\tablez #1\space\space\space}% \expandafter }\temp \endtablez } \def\tablez #1 #2 #3 #4\endtablez{% \aboveenvbreak \ifnum 0#1>0 \advance \leftskip by #1\mil \fi \ifnum 0#2>0 \tableindent=#2\mil \fi \ifnum 0#3>0 \advance \rightskip by #3\mil \fi \itemmax=\tableindent \advance \itemmax by -\itemmargin \advance \leftskip by \tableindent \exdentamount=\tableindent \parindent = 0pt \parskip = \smallskipamount \ifdim \parskip=0pt \parskip=2pt \fi \let\item = \internalBitem \let\itemx = \internalBitemx } \def\Etable{\endgraf\afterenvbreak} \let\Eftable\Etable \let\Evtable\Etable \let\Eitemize\Etable \let\Eenumerate\Etable % This is the counter used by @enumerate, which is really @itemize \newcount \itemno \envdef\itemize{\parsearg\doitemize} \def\doitemize#1{% \aboveenvbreak \itemmax=\itemindent \advance\itemmax by -\itemmargin \advance\leftskip by \itemindent \exdentamount=\itemindent \parindent=0pt \parskip=\smallskipamount \ifdim\parskip=0pt \parskip=2pt \fi % % Try typesetting the item mark that if the document erroneously says % something like @itemize @samp (intending @table), there's an error % right away at the @itemize. It's not the best error message in the % world, but it's better than leaving it to the @item. This means if % the user wants an empty mark, they have to say @w{} not just @w. \def\itemcontents{#1}% \setbox0 = \hbox{\itemcontents}% % % @itemize with no arg is equivalent to @itemize @bullet. \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi % \let\item=\itemizeitem } % Definition of @item while inside @itemize and @enumerate. % \def\itemizeitem{% \advance\itemno by 1 % for enumerations {\let\par=\endgraf \smallbreak}% reasonable place to break {% % If the document has an @itemize directly after a section title, a % \nobreak will be last on the list, and \sectionheading will have % done a \vskip-\parskip. In that case, we don't want to zero % parskip, or the item text will crash with the heading. On the % other hand, when there is normal text preceding the item (as there % usually is), we do want to zero parskip, or there would be too much % space. In that case, we won't have a \nobreak before. At least % that's the theory. \ifnum\lastpenalty<10000 \parskip=0in \fi \noindent \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% % \vadjust{\penalty 1200}}% not good to break after first line of item. \flushcr } % \splitoff TOKENS\endmark defines \first to be the first token in % TOKENS, and \rest to be the remainder. % \def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% % Allow an optional argument of an uppercase letter, lowercase letter, % or number, to specify the first label in the enumerated list. No % argument is the same as `1'. % \envparseargdef\enumerate{\enumeratey #1 \endenumeratey} \def\enumeratey #1 #2\endenumeratey{% % If we were given no argument, pretend we were given `1'. \def\thearg{#1}% \ifx\thearg\empty \def\thearg{1}\fi % % Detect if the argument is a single token. If so, it might be a % letter. Otherwise, the only valid thing it can be is a number. % (We will always have one token, because of the test we just made. % This is a good thing, since \splitoff doesn't work given nothing at % all -- the first parameter is undelimited.) \expandafter\splitoff\thearg\endmark \ifx\rest\empty % Only one token in the argument. It could still be anything. % A ``lowercase letter'' is one whose \lccode is nonzero. % An ``uppercase letter'' is one whose \lccode is both nonzero, and % not equal to itself. % Otherwise, we assume it's a number. % % We need the \relax at the end of the \ifnum lines to stop TeX from % continuing to look for a <number>. % \ifnum\lccode\expandafter`\thearg=0\relax \numericenumerate % a number (we hope) \else % It's a letter. \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax \lowercaseenumerate % lowercase letter \else \uppercaseenumerate % uppercase letter \fi \fi \else % Multiple tokens in the argument. We hope it's a number. \numericenumerate \fi } % An @enumerate whose labels are integers. The starting integer is % given in \thearg. % \def\numericenumerate{% \itemno = \thearg \startenumeration{\the\itemno}% } % The starting (lowercase) letter is in \thearg. \def\lowercaseenumerate{% \itemno = \expandafter`\thearg \startenumeration{% % Be sure we're not beyond the end of the alphabet. \ifnum\itemno=0 \errmessage{No more lowercase letters in @enumerate; get a bigger alphabet}% \fi \char\lccode\itemno }% } % The starting (uppercase) letter is in \thearg. \def\uppercaseenumerate{% \itemno = \expandafter`\thearg \startenumeration{% % Be sure we're not beyond the end of the alphabet. \ifnum\itemno=0 \errmessage{No more uppercase letters in @enumerate; get a bigger alphabet} \fi \char\uccode\itemno }% } % Call \doitemize, adding a period to the first argument and supplying the % common last two arguments. Also subtract one from the initial value in % \itemno, since @item increments \itemno. % \def\startenumeration#1{% \advance\itemno by -1 \doitemize{#1.}\flushcr } % @alphaenumerate and @capsenumerate are abbreviations for giving an arg % to @enumerate. % \def\alphaenumerate{\enumerate{a}} \def\capsenumerate{\enumerate{A}} \def\Ealphaenumerate{\Eenumerate} \def\Ecapsenumerate{\Eenumerate} % @multitable macros % Amy Hendrickson, 8/18/94, 3/6/96 % % @multitable ... @end multitable will make as many columns as desired. % Contents of each column will wrap at width given in preamble. Width % can be specified either with sample text given in a template line, % or in percent of \hsize, the current width of text on page. % Table can continue over pages but will only break between lines. % To make preamble: % % Either define widths of columns in terms of percent of \hsize: % @multitable @columnfractions .25 .3 .45 % @item ... % % Numbers following @columnfractions are the percent of the total % current hsize to be used for each column. You may use as many % columns as desired. % Or use a template: % @multitable {Column 1 template} {Column 2 template} {Column 3 template} % @item ... % using the widest term desired in each column. % Each new table line starts with @item, each subsequent new column % starts with @tab. Empty columns may be produced by supplying @tab's % with nothing between them for as many times as empty columns are needed, % ie, @tab@tab@tab will produce two empty columns. % @item, @tab do not need to be on their own lines, but it will not hurt % if they are. % Sample multitable: % @multitable {Column 1 template} {Column 2 template} {Column 3 template} % @item first col stuff @tab second col stuff @tab third col % @item % first col stuff % @tab % second col stuff % @tab % third col % @item first col stuff @tab second col stuff % @tab Many paragraphs of text may be used in any column. % % They will wrap at the width determined by the template. % @item@tab@tab This will be in third column. % @end multitable % Default dimensions may be reset by user. % @multitableparskip is vertical space between paragraphs in table. % @multitableparindent is paragraph indent in table. % @multitablecolmargin is horizontal space to be left between columns. % @multitablelinespace is space to leave between table items, baseline % to baseline. % 0pt means it depends on current normal line spacing. % \newskip\multitableparskip \newskip\multitableparindent \newdimen\multitablecolspace \newskip\multitablelinespace \multitableparskip=0pt \multitableparindent=6pt \multitablecolspace=12pt \multitablelinespace=0pt % Macros used to set up halign preamble: % \let\endsetuptable\relax \def\xendsetuptable{\endsetuptable} \let\columnfractions\relax \def\xcolumnfractions{\columnfractions} \newif\ifsetpercent % #1 is the @columnfraction, usually a decimal number like .5, but might % be just 1. We just use it, whatever it is. % \def\pickupwholefraction#1 {% \global\advance\colcount by 1 \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% \setuptable } \newcount\colcount \def\setuptable#1{% \def\firstarg{#1}% \ifx\firstarg\xendsetuptable \let\go = \relax \else \ifx\firstarg\xcolumnfractions \global\setpercenttrue \else \ifsetpercent \let\go\pickupwholefraction \else \global\advance\colcount by 1 \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a % separator; typically that is always in the input, anyway. \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% \fi \fi \ifx\go\pickupwholefraction % Put the argument back for the \pickupwholefraction call, so % we'll always have a period there to be parsed. \def\go{\pickupwholefraction#1}% \else \let\go = \setuptable \fi% \fi \go } % multitable-only commands. % % @headitem starts a heading row, which we typeset in bold. % Assignments have to be global since we are inside the implicit group % of an alignment entry. \everycr resets \everytab so we don't have to % undo it ourselves. \def\headitemfont{\b}% for people to use in the template row; not changeable \def\headitem{% \checkenv\multitable \crcr \global\everytab={\bf}% can't use \headitemfont since the parsing differs \the\everytab % for the first item }% % % A \tab used to include \hskip1sp. But then the space in a template % line is not enough. That is bad. So let's go back to just `&' until % we again encounter the problem the 1sp was intended to solve. % --karl, nathan@acm.org, 20apr99. \def\tab{\checkenv\multitable &\the\everytab}% % @multitable ... @end multitable definitions: % \newtoks\everytab % insert after every tab. % \envdef\multitable{% \vskip\parskip \startsavinginserts % % @item within a multitable starts a normal row. % We use \def instead of \let so that if one of the multitable entries % contains an @itemize, we don't choke on the \item (seen as \crcr aka % \endtemplate) expanding \doitemize. \def\item{\crcr}% % \tolerance=9500 \hbadness=9500 \setmultitablespacing \parskip=\multitableparskip \parindent=\multitableparindent \overfullrule=0pt \global\colcount=0 % \everycr = {% \noalign{% \global\everytab={}% \global\colcount=0 % Reset the column counter. % Check for saved footnotes, etc. \checkinserts % Keeps underfull box messages off when table breaks over pages. %\filbreak % Maybe so, but it also creates really weird page breaks when the % table breaks over pages. Wouldn't \vfil be better? Wait until the % problem manifests itself, so it can be fixed for real --karl. }% }% % \parsearg\domultitable } \def\domultitable#1{% % To parse everything between @multitable and @item: \setuptable#1 \endsetuptable % % This preamble sets up a generic column definition, which will % be used as many times as user calls for columns. % \vtop will set a single line and will also let text wrap and % continue for many paragraphs if desired. \halign\bgroup &% \global\advance\colcount by 1 \multistrut \vtop{% % Use the current \colcount to find the correct column width: \hsize=\expandafter\csname col\the\colcount\endcsname % % In order to keep entries from bumping into each other % we will add a \leftskip of \multitablecolspace to all columns after % the first one. % % If a template has been used, we will add \multitablecolspace % to the width of each template entry. % % If the user has set preamble in terms of percent of \hsize we will % use that dimension as the width of the column, and the \leftskip % will keep entries from bumping into each other. Table will start at % left margin and final column will justify at right margin. % % Make sure we don't inherit \rightskip from the outer environment. \rightskip=0pt \ifnum\colcount=1 % The first column will be indented with the surrounding text. \advance\hsize by\leftskip \else \ifsetpercent \else % If user has not set preamble in terms of percent of \hsize % we will advance \hsize by \multitablecolspace. \advance\hsize by \multitablecolspace \fi % In either case we will make \leftskip=\multitablecolspace: \leftskip=\multitablecolspace \fi % Ignoring space at the beginning and end avoids an occasional spurious % blank line, when TeX decides to break the line at the space before the % box from the multistrut, so the strut ends up on a line by itself. % For example: % @multitable @columnfractions .11 .89 % @item @code{#} % @tab Legal holiday which is valid in major parts of the whole country. % Is automatically provided with highlighting sequences respectively % marking characters. \noindent\ignorespaces##\unskip\multistrut }\cr } \def\Emultitable{% \crcr \egroup % end the \halign \global\setpercentfalse } \def\setmultitablespacing{% \def\multistrut{\strut}% just use the standard line spacing % % Compute \multitablelinespace (if not defined by user) for use in % \multitableparskip calculation. We used define \multistrut based on % this, but (ironically) that caused the spacing to be off. % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. \ifdim\multitablelinespace=0pt \setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip \global\advance\multitablelinespace by-\ht0 \fi % Test to see if parskip is larger than space between lines of % table. If not, do nothing. % If so, set to same dimension as multitablelinespace. \ifdim\multitableparskip>\multitablelinespace \global\multitableparskip=\multitablelinespace \global\advance\multitableparskip-7pt % to keep parskip somewhat smaller % than skip between lines in the table. \fi% \ifdim\multitableparskip=0pt \global\multitableparskip=\multitablelinespace \global\advance\multitableparskip-7pt % to keep parskip somewhat smaller % than skip between lines in the table. \fi} \message{conditionals,} % @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, % @ifnotxml always succeed. They currently do nothing; we don't % attempt to check whether the conditionals are properly nested. But we % have to remember that they are conditionals, so that @end doesn't % attempt to close an environment group. % \def\makecond#1{% \expandafter\let\csname #1\endcsname = \relax \expandafter\let\csname iscond.#1\endcsname = 1 } \makecond{iftex} \makecond{ifnotdocbook} \makecond{ifnothtml} \makecond{ifnotinfo} \makecond{ifnotplaintext} \makecond{ifnotxml} % Ignore @ignore, @ifhtml, @ifinfo, and the like. % \def\direntry{\doignore{direntry}} \def\documentdescription{\doignore{documentdescription}} \def\docbook{\doignore{docbook}} \def\html{\doignore{html}} \def\ifdocbook{\doignore{ifdocbook}} \def\ifhtml{\doignore{ifhtml}} \def\ifinfo{\doignore{ifinfo}} \def\ifnottex{\doignore{ifnottex}} \def\ifplaintext{\doignore{ifplaintext}} \def\ifxml{\doignore{ifxml}} \def\ignore{\doignore{ignore}} \def\menu{\doignore{menu}} \def\xml{\doignore{xml}} % Ignore text until a line `@end #1', keeping track of nested conditionals. % % A count to remember the depth of nesting. \newcount\doignorecount \def\doignore#1{\begingroup % Scan in ``verbatim'' mode: \obeylines \catcode`\@ = \other \catcode`\{ = \other \catcode`\} = \other % % Make sure that spaces turn into tokens that match what \doignoretext wants. \spaceisspace % % Count number of #1's that we've seen. \doignorecount = 0 % % Swallow text until we reach the matching `@end #1'. \dodoignore{#1}% } { \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. \obeylines % % \gdef\dodoignore#1{% % #1 contains the command name as a string, e.g., `ifinfo'. % % Define a command to find the next `@end #1'. \long\def\doignoretext##1^^M@end #1{% \doignoretextyyy##1^^M@#1\_STOP_}% % % And this command to find another #1 command, at the beginning of a % line. (Otherwise, we would consider a line `@c @ifset', for % example, to count as an @ifset for nesting.) \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% % % And now expand that command. \doignoretext ^^M% }% } \def\doignoreyyy#1{% \def\temp{#1}% \ifx\temp\empty % Nothing found. \let\next\doignoretextzzz \else % Found a nested condition, ... \advance\doignorecount by 1 \let\next\doignoretextyyy % ..., look for another. % If we're here, #1 ends with ^^M\ifinfo (for example). \fi \next #1% the token \_STOP_ is present just after this macro. } % We have to swallow the remaining "\_STOP_". % \def\doignoretextzzz#1{% \ifnum\doignorecount = 0 % We have just found the outermost @end. \let\next\enddoignore \else % Still inside a nested condition. \advance\doignorecount by -1 \let\next\doignoretext % Look for the next @end. \fi \next } % Finish off ignored text. { \obeylines% % Ignore anything after the last `@end #1'; this matters in verbatim % environments, where otherwise the newline after an ignored conditional % would result in a blank line in the output. \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% } % @set VAR sets the variable VAR to an empty value. % @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. % % Since we want to separate VAR from REST-OF-LINE (which might be % empty), we can't just use \parsearg; we have to insert a space of our % own to delimit the rest of the line, and then take it out again if we % didn't need it. % We rely on the fact that \parsearg sets \catcode`\ =10. % \parseargdef\set{\setyyy#1 \endsetyyy} \def\setyyy#1 #2\endsetyyy{% {% \makevalueexpandable \def\temp{#2}% \edef\next{\gdef\makecsname{SET#1}}% \ifx\temp\empty \next{}% \else \setzzz#2\endsetzzz \fi }% } % Remove the trailing space \setxxx inserted. \def\setzzz#1 \endsetzzz{\next{#1}} % @clear VAR clears (i.e., unsets) the variable VAR. % \parseargdef\clear{% {% \makevalueexpandable \global\expandafter\let\csname SET#1\endcsname=\relax }% } % @value{foo} gets the text saved in variable foo. \def\value{\begingroup\makevalueexpandable\valuexxx} \def\valuexxx#1{\expandablevalue{#1}\endgroup} { \catcode`\- = \active \catcode`\_ = \active % \gdef\makevalueexpandable{% \let\value = \expandablevalue % We don't want these characters active, ... \catcode`\-=\other \catcode`\_=\other % ..., but we might end up with active ones in the argument if % we're called from @code, as @code{@value{foo-bar_}}, though. % So \let them to their normal equivalents. \let-\normaldash \let_\normalunderscore } } % We have this subroutine so that we can handle at least some @value's % properly in indexes (we call \makevalueexpandable in \indexdummies). % The command has to be fully expandable (if the variable is set), since % the result winds up in the index file. This means that if the % variable's value contains other Texinfo commands, it's almost certain % it will fail (although perhaps we could fix that with sufficient work % to do a one-level expansion on the result, instead of complete). % \def\expandablevalue#1{% \expandafter\ifx\csname SET#1\endcsname\relax {[No value for ``#1'']}% \message{Variable `#1', used in @value, is not set.}% \else \csname SET#1\endcsname \fi } % @ifset VAR ... @end ifset reads the `...' iff VAR has been defined % with @set. % % To get special treatment of `@end ifset,' call \makeond and the redefine. % \makecond{ifset} \def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} \def\doifset#1#2{% {% \makevalueexpandable \let\next=\empty \expandafter\ifx\csname SET#2\endcsname\relax #1% If not set, redefine \next. \fi \expandafter }\next } \def\ifsetfail{\doignore{ifset}} % @ifclear VAR ... @end executes the `...' iff VAR has never been % defined with @set, or has been undefined with @clear. % % The `\else' inside the `\doifset' parameter is a trick to reuse the % above code: if the variable is not set, do nothing, if it is set, % then redefine \next to \ifclearfail. % \makecond{ifclear} \def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} \def\ifclearfail{\doignore{ifclear}} % @ifcommandisdefined CMD ... @end executes the `...' if CMD (written % without the @) is in fact defined. We can only feasibly check at the % TeX level, so something like `mathcode' is going to considered % defined even though it is not a Texinfo command. % \makecond{ifcommanddefined} \def\ifcommanddefined{\parsearg{\doifcmddefined{\let\next=\ifcmddefinedfail}}} % \def\doifcmddefined#1#2{{% \makevalueexpandable \let\next=\empty \expandafter\ifx\csname #2\endcsname\relax #1% If not defined, \let\next as above. \fi \expandafter }\next } \def\ifcmddefinedfail{\doignore{ifcommanddefined}} % @ifcommandnotdefined CMD ... handled similar to @ifclear above. \makecond{ifcommandnotdefined} \def\ifcommandnotdefined{% \parsearg{\doifcmddefined{\else \let\next=\ifcmdnotdefinedfail}}} \def\ifcmdnotdefinedfail{\doignore{ifcommandnotdefined}} % Set the `txicommandconditionals' variable, so documents have a way to % test if the @ifcommand...defined conditionals are available. \set txicommandconditionals % @dircategory CATEGORY -- specify a category of the dir file % which this file should belong to. Ignore this in TeX. \let\dircategory=\comment % @defininfoenclose. \let\definfoenclose=\comment \message{indexing,} % Index generation facilities % Define \newwrite to be identical to plain tex's \newwrite % except not \outer, so it can be used within macros and \if's. \edef\newwrite{\makecsname{ptexnewwrite}} % \newindex {foo} defines an index named foo. % It automatically defines \fooindex such that % \fooindex ...rest of line... puts an entry in the index foo. % It also defines \fooindfile to be the number of the output channel for % the file that accumulates this index. The file's extension is foo. % The name of an index should be no more than 2 characters long % for the sake of vms. % \def\newindex#1{% \iflinks \expandafter\newwrite \csname#1indfile\endcsname \openout \csname#1indfile\endcsname \jobname.#1 % Open the file \fi \expandafter\xdef\csname#1index\endcsname{% % Define @#1index \noexpand\doindex{#1}} } % @defindex foo == \newindex{foo} % \def\defindex{\parsearg\newindex} % Define @defcodeindex, like @defindex except put all entries in @code. % \def\defcodeindex{\parsearg\newcodeindex} % \def\newcodeindex#1{% \iflinks \expandafter\newwrite \csname#1indfile\endcsname \openout \csname#1indfile\endcsname \jobname.#1 \fi \expandafter\xdef\csname#1index\endcsname{% \noexpand\docodeindex{#1}}% } % @synindex foo bar makes index foo feed into index bar. % Do this instead of @defindex foo if you don't want it as a separate index. % % @syncodeindex foo bar similar, but put all entries made for index foo % inside @code. % \def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} \def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} % #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), % #3 the target index (bar). \def\dosynindex#1#2#3{% % Only do \closeout if we haven't already done it, else we'll end up % closing the target index. \expandafter \ifx\csname donesynindex#2\endcsname \relax % The \closeout helps reduce unnecessary open files; the limit on the % Acorn RISC OS is a mere 16 files. \expandafter\closeout\csname#2indfile\endcsname \expandafter\let\csname donesynindex#2\endcsname = 1 \fi % redefine \fooindfile: \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname \expandafter\let\csname#2indfile\endcsname=\temp % redefine \fooindex: \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% } % Define \doindex, the driver for all \fooindex macros. % Argument #1 is generated by the calling \fooindex macro, % and it is "foo", the name of the index. % \doindex just uses \parsearg; it calls \doind for the actual work. % This is because \doind is more useful to call from other macros. % There is also \dosubind {index}{topic}{subtopic} % which makes an entry in a two-level index such as the operation index. \def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} \def\singleindexer #1{\doind{\indexname}{#1}} % like the previous two, but they put @code around the argument. \def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} \def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} % Take care of Texinfo commands that can appear in an index entry. % Since there are some commands we want to expand, and others we don't, % we have to laboriously prevent expansion for those that we don't. % \def\indexdummies{% \escapechar = `\\ % use backslash in output files. \def\@{@}% change to @@ when we switch to @ as escape char in index files. \def\ {\realbackslash\space }% % % Need these unexpandable (because we define \tt as a dummy) % definitions when @{ or @} appear in index entry text. Also, more % complicated, when \tex is in effect and \{ is a \delimiter again. % We can't use \lbracecmd and \rbracecmd because texindex assumes % braces and backslashes are used only as delimiters. Perhaps we % should define @lbrace and @rbrace commands a la @comma. \def\{{{\tt\char123}}% \def\}{{\tt\char125}}% % % I don't entirely understand this, but when an index entry is % generated from a macro call, the \endinput which \scanmacro inserts % causes processing to be prematurely terminated. This is, % apparently, because \indexsorttmp is fully expanded, and \endinput % is an expandable command. The redefinition below makes \endinput % disappear altogether for that purpose -- although logging shows that % processing continues to some further point. On the other hand, it % seems \endinput does not hurt in the printed index arg, since that % is still getting written without apparent harm. % % Sample source (mac-idx3.tex, reported by Graham Percival to % help-texinfo, 22may06): % @macro funindex {WORD} % @findex xyz % @end macro % ... % @funindex commtest % % The above is not enough to reproduce the bug, but it gives the flavor. % % Sample whatsit resulting: % .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}} % % So: \let\endinput = \empty % % Do the redefinitions. \commondummies } % For the aux and toc files, @ is the escape character. So we want to % redefine everything using @ as the escape character (instead of % \realbackslash, still used for index files). When everything uses @, % this will be simpler. % \def\atdummies{% \def\@{@@}% \def\ {@ }% \let\{ = \lbraceatcmd \let\} = \rbraceatcmd % % Do the redefinitions. \commondummies \otherbackslash } % Called from \indexdummies and \atdummies. % \def\commondummies{% % % \definedummyword defines \#1 as \string\#1\space, thus effectively % preventing its expansion. This is used only for control words, % not control letters, because the \space would be incorrect for % control characters, but is needed to separate the control word % from whatever follows. % % For control letters, we have \definedummyletter, which omits the % space. % % These can be used both for control words that take an argument and % those that do not. If it is followed by {arg} in the input, then % that will dutifully get written to the index (or wherever). % \def\definedummyword ##1{\def##1{\string##1\space}}% \def\definedummyletter##1{\def##1{\string##1}}% \let\definedummyaccent\definedummyletter % \commondummiesnofonts % \definedummyletter\_% \definedummyletter\-% % % Non-English letters. \definedummyword\AA \definedummyword\AE \definedummyword\DH \definedummyword\L \definedummyword\O \definedummyword\OE \definedummyword\TH \definedummyword\aa \definedummyword\ae \definedummyword\dh \definedummyword\exclamdown \definedummyword\l \definedummyword\o \definedummyword\oe \definedummyword\ordf \definedummyword\ordm \definedummyword\questiondown \definedummyword\ss \definedummyword\th % % Although these internal commands shouldn't show up, sometimes they do. \definedummyword\bf \definedummyword\gtr \definedummyword\hat \definedummyword\less \definedummyword\sf \definedummyword\sl \definedummyword\tclose \definedummyword\tt % \definedummyword\LaTeX \definedummyword\TeX % % Assorted special characters. \definedummyword\arrow \definedummyword\bullet \definedummyword\comma \definedummyword\copyright \definedummyword\registeredsymbol \definedummyword\dots \definedummyword\enddots \definedummyword\entrybreak \definedummyword\equiv \definedummyword\error \definedummyword\euro \definedummyword\expansion \definedummyword\geq \definedummyword\guillemetleft \definedummyword\guillemetright \definedummyword\guilsinglleft \definedummyword\guilsinglright \definedummyword\lbracechar \definedummyword\leq \definedummyword\minus \definedummyword\ogonek \definedummyword\pounds \definedummyword\point \definedummyword\print \definedummyword\quotedblbase \definedummyword\quotedblleft \definedummyword\quotedblright \definedummyword\quoteleft \definedummyword\quoteright \definedummyword\quotesinglbase \definedummyword\rbracechar \definedummyword\result \definedummyword\textdegree % % We want to disable all macros so that they are not expanded by \write. \macrolist % \normalturnoffactive % % Handle some cases of @value -- where it does not contain any % (non-fully-expandable) commands. \makevalueexpandable } % \commondummiesnofonts: common to \commondummies and \indexnofonts. % \def\commondummiesnofonts{% % Control letters and accents. \definedummyletter\!% \definedummyaccent\"% \definedummyaccent\'% \definedummyletter\*% \definedummyaccent\,% \definedummyletter\.% \definedummyletter\/% \definedummyletter\:% \definedummyaccent\=% \definedummyletter\?% \definedummyaccent\^% \definedummyaccent\`% \definedummyaccent\~% \definedummyword\u \definedummyword\v \definedummyword\H \definedummyword\dotaccent \definedummyword\ogonek \definedummyword\ringaccent \definedummyword\tieaccent \definedummyword\ubaraccent \definedummyword\udotaccent \definedummyword\dotless % % Texinfo font commands. \definedummyword\b \definedummyword\i \definedummyword\r \definedummyword\sansserif \definedummyword\sc \definedummyword\slanted \definedummyword\t % % Commands that take arguments. \definedummyword\abbr \definedummyword\acronym \definedummyword\anchor \definedummyword\cite \definedummyword\code \definedummyword\command \definedummyword\dfn \definedummyword\dmn \definedummyword\email \definedummyword\emph \definedummyword\env \definedummyword\file \definedummyword\image \definedummyword\indicateurl \definedummyword\inforef \definedummyword\kbd \definedummyword\key \definedummyword\math \definedummyword\option \definedummyword\pxref \definedummyword\ref \definedummyword\samp \definedummyword\strong \definedummyword\tie \definedummyword\uref \definedummyword\url \definedummyword\var \definedummyword\verb \definedummyword\w \definedummyword\xref } % \indexnofonts is used when outputting the strings to sort the index % by, and when constructing control sequence names. It eliminates all % control sequences and just writes whatever the best ASCII sort string % would be for a given command (usually its argument). % \def\indexnofonts{% % Accent commands should become @asis. \def\definedummyaccent##1{\let##1\asis}% % We can just ignore other control letters. \def\definedummyletter##1{\let##1\empty}% % All control words become @asis by default; overrides below. \let\definedummyword\definedummyaccent % \commondummiesnofonts % % Don't no-op \tt, since it isn't a user-level command % and is used in the definitions of the active chars like <, >, |, etc. % Likewise with the other plain tex font commands. %\let\tt=\asis % \def\ { }% \def\@{@}% \def\_{\normalunderscore}% \def\-{}% @- shouldn't affect sorting % % Unfortunately, texindex is not prepared to handle braces in the % content at all. So for index sorting, we map @{ and @} to strings % starting with |, since that ASCII character is between ASCII { and }. \def\{{|a}% \def\lbracechar{|a}% % \def\}{|b}% \def\rbracechar{|b}% % % Non-English letters. \def\AA{AA}% \def\AE{AE}% \def\DH{DZZ}% \def\L{L}% \def\OE{OE}% \def\O{O}% \def\TH{ZZZ}% \def\aa{aa}% \def\ae{ae}% \def\dh{dzz}% \def\exclamdown{!}% \def\l{l}% \def\oe{oe}% \def\ordf{a}% \def\ordm{o}% \def\o{o}% \def\questiondown{?}% \def\ss{ss}% \def\th{zzz}% % \def\LaTeX{LaTeX}% \def\TeX{TeX}% % % Assorted special characters. % (The following {} will end up in the sort string, but that's ok.) \def\arrow{->}% \def\bullet{bullet}% \def\comma{,}% \def\copyright{copyright}% \def\dots{...}% \def\enddots{...}% \def\equiv{==}% \def\error{error}% \def\euro{euro}% \def\expansion{==>}% \def\geq{>=}% \def\guillemetleft{<<}% \def\guillemetright{>>}% \def\guilsinglleft{<}% \def\guilsinglright{>}% \def\leq{<=}% \def\minus{-}% \def\point{.}% \def\pounds{pounds}% \def\print{-|}% \def\quotedblbase{"}% \def\quotedblleft{"}% \def\quotedblright{"}% \def\quoteleft{`}% \def\quoteright{'}% \def\quotesinglbase{,}% \def\registeredsymbol{R}% \def\result{=>}% \def\textdegree{o}% % \expandafter\ifx\csname SETtxiindexlquoteignore\endcsname\relax \else \indexlquoteignore \fi % % We need to get rid of all macros, leaving only the arguments (if present). % Of course this is not nearly correct, but it is the best we can do for now. % makeinfo does not expand macros in the argument to @deffn, which ends up % writing an index entry, and texindex isn't prepared for an index sort entry % that starts with \. % % Since macro invocations are followed by braces, we can just redefine them % to take a single TeX argument. The case of a macro invocation that % goes to end-of-line is not handled. % \macrolist } % Undocumented (for FSFS 2nd ed.): @set txiindexlquoteignore makes us % ignore left quotes in the sort term. {\catcode`\`=\active \gdef\indexlquoteignore{\let`=\empty}} \let\indexbackslash=0 %overridden during \printindex. \let\SETmarginindex=\relax % put index entries in margin (undocumented)? % Most index entries go through here, but \dosubind is the general case. % #1 is the index name, #2 is the entry text. \def\doind#1#2{\dosubind{#1}{#2}{}} % Workhorse for all \fooindexes. % #1 is name of index, #2 is stuff to put there, #3 is subentry -- % empty if called from \doind, as we usually are (the main exception % is with most defuns, which call us directly). % \def\dosubind#1#2#3{% \iflinks {% % Store the main index entry text (including the third arg). \toks0 = {#2}% % If third arg is present, precede it with a space. \def\thirdarg{#3}% \ifx\thirdarg\empty \else \toks0 = \expandafter{\the\toks0 \space #3}% \fi % \edef\writeto{\csname#1indfile\endcsname}% % \safewhatsit\dosubindwrite }% \fi } % Write the entry in \toks0 to the index file: % \def\dosubindwrite{% % Put the index entry in the margin if desired. \ifx\SETmarginindex\relax\else \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}% \fi % % Remember, we are within a group. \indexdummies % Must do this here, since \bf, etc expand at this stage \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now % so it will be output as is; and it will print as backslash. % % Process the index entry with all font commands turned off, to % get the string to sort by. {\indexnofonts \edef\temp{\the\toks0}% need full expansion \xdef\indexsorttmp{\temp}% }% % % Set up the complete index entry, with both the sort key and % the original text, including any font commands. We write % three arguments to \entry to the .?? file (four in the % subentry case), texindex reduces to two when writing the .??s % sorted result. \edef\temp{% \write\writeto{% \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}% }% \temp } % Take care of unwanted page breaks/skips around a whatsit: % % If a skip is the last thing on the list now, preserve it % by backing up by \lastskip, doing the \write, then inserting % the skip again. Otherwise, the whatsit generated by the % \write or \pdfdest will make \lastskip zero. The result is that % sequences like this: % @end defun % @tindex whatever % @defun ... % will have extra space inserted, because the \medbreak in the % start of the @defun won't see the skip inserted by the @end of % the previous defun. % % But don't do any of this if we're not in vertical mode. We % don't want to do a \vskip and prematurely end a paragraph. % % Avoid page breaks due to these extra skips, too. % % But wait, there is a catch there: % We'll have to check whether \lastskip is zero skip. \ifdim is not % sufficient for this purpose, as it ignores stretch and shrink parts % of the skip. The only way seems to be to check the textual % representation of the skip. % % The following is almost like \def\zeroskipmacro{0.0pt} except that % the ``p'' and ``t'' characters have catcode \other, not 11 (letter). % \edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} % \newskip\whatsitskip \newcount\whatsitpenalty % % ..., ready, GO: % \def\safewhatsit#1{\ifhmode #1% \else % \lastskip and \lastpenalty cannot both be nonzero simultaneously. \whatsitskip = \lastskip \edef\lastskipmacro{\the\lastskip}% \whatsitpenalty = \lastpenalty % % If \lastskip is nonzero, that means the last item was a % skip. And since a skip is discardable, that means this % -\whatsitskip glue we're inserting is preceded by a % non-discardable item, therefore it is not a potential % breakpoint, therefore no \nobreak needed. \ifx\lastskipmacro\zeroskipmacro \else \vskip-\whatsitskip \fi % #1% % \ifx\lastskipmacro\zeroskipmacro % If \lastskip was zero, perhaps the last item was a penalty, and % perhaps it was >=10000, e.g., a \nobreak. In that case, we want % to re-insert the same penalty (values >10000 are used for various % signals); since we just inserted a non-discardable item, any % following glue (such as a \parskip) would be a breakpoint. For example: % @deffn deffn-whatever % @vindex index-whatever % Description. % would allow a break between the index-whatever whatsit % and the "Description." paragraph. \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi \else % On the other hand, if we had a nonzero \lastskip, % this make-up glue would be preceded by a non-discardable item % (the whatsit from the \write), so we must insert a \nobreak. \nobreak\vskip\whatsitskip \fi \fi} % The index entry written in the file actually looks like % \entry {sortstring}{page}{topic} % or % \entry {sortstring}{page}{topic}{subtopic} % The texindex program reads in these files and writes files % containing these kinds of lines: % \initial {c} % before the first topic whose initial is c % \entry {topic}{pagelist} % for a topic that is used without subtopics % \primary {topic} % for the beginning of a topic that is used with subtopics % \secondary {subtopic}{pagelist} % for each subtopic. % Define the user-accessible indexing commands % @findex, @vindex, @kindex, @cindex. \def\findex {\fnindex} \def\kindex {\kyindex} \def\cindex {\cpindex} \def\vindex {\vrindex} \def\tindex {\tpindex} \def\pindex {\pgindex} \def\cindexsub {\begingroup\obeylines\cindexsub} {\obeylines % \gdef\cindexsub "#1" #2^^M{\endgroup % \dosubind{cp}{#2}{#1}}} % Define the macros used in formatting output of the sorted index material. % @printindex causes a particular index (the ??s file) to get printed. % It does not print any chapter heading (usually an @unnumbered). % \parseargdef\printindex{\begingroup \dobreak \chapheadingskip{10000}% % \smallfonts \rm \tolerance = 9500 \plainfrenchspacing \everypar = {}% don't want the \kern\-parindent from indentation suppression. % % See if the index file exists and is nonempty. % Change catcode of @ here so that if the index file contains % \initial {@} % as its first line, TeX doesn't complain about mismatched braces % (because it thinks @} is a control sequence). \catcode`\@ = 11 \openin 1 \jobname.#1s \ifeof 1 % \enddoublecolumns gets confused if there is no text in the index, % and it loses the chapter title and the aux file entries for the % index. The easiest way to prevent this problem is to make sure % there is some text. \putwordIndexNonexistent \else % % If the index file exists but is empty, then \openin leaves \ifeof % false. We have to make TeX try to read something from the file, so % it can discover if there is anything in it. \read 1 to \temp \ifeof 1 \putwordIndexIsEmpty \else % Index files are almost Texinfo source, but we use \ as the escape % character. It would be better to use @, but that's too big a change % to make right now. \def\indexbackslash{\backslashcurfont}% \catcode`\\ = 0 \escapechar = `\\ \begindoublecolumns \input \jobname.#1s \enddoublecolumns \fi \fi \closein 1 \endgroup} % These macros are used by the sorted index file itself. % Change them to control the appearance of the index. \def\initial#1{{% % Some minor font changes for the special characters. \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt % % Remove any glue we may have, we'll be inserting our own. \removelastskip % % We like breaks before the index initials, so insert a bonus. \nobreak \vskip 0pt plus 3\baselineskip \penalty 0 \vskip 0pt plus -3\baselineskip % % Typeset the initial. Making this add up to a whole number of % baselineskips increases the chance of the dots lining up from column % to column. It still won't often be perfect, because of the stretch % we need before each entry, but it's better. % % No shrink because it confuses \balancecolumns. \vskip 1.67\baselineskip plus .5\baselineskip \leftline{\secbf #1}% % Do our best not to break after the initial. \nobreak \vskip .33\baselineskip plus .1\baselineskip }} % \entry typesets a paragraph consisting of the text (#1), dot leaders, and % then page number (#2) flushed to the right margin. It is used for index % and table of contents entries. The paragraph is indented by \leftskip. % % A straightforward implementation would start like this: % \def\entry#1#2{... % But this freezes the catcodes in the argument, and can cause problems to % @code, which sets - active. This problem was fixed by a kludge--- % ``-'' was active throughout whole index, but this isn't really right. % The right solution is to prevent \entry from swallowing the whole text. % --kasal, 21nov03 \def\entry{% \begingroup % % Start a new paragraph if necessary, so our assignments below can't % affect previous text. \par % % Do not fill out the last line with white space. \parfillskip = 0in % % No extra space above this paragraph. \parskip = 0in % % Do not prefer a separate line ending with a hyphen to fewer lines. \finalhyphendemerits = 0 % % \hangindent is only relevant when the entry text and page number % don't both fit on one line. In that case, bob suggests starting the % dots pretty far over on the line. Unfortunately, a large % indentation looks wrong when the entry text itself is broken across % lines. So we use a small indentation and put up with long leaders. % % \hangafter is reset to 1 (which is the value we want) at the start % of each paragraph, so we need not do anything with that. \hangindent = 2em % % When the entry text needs to be broken, just fill out the first line % with blank space. \rightskip = 0pt plus1fil % % A bit of stretch before each entry for the benefit of balancing % columns. \vskip 0pt plus1pt % % When reading the text of entry, convert explicit line breaks % from @* into spaces. The user might give these in long section % titles, for instance. \def\*{\unskip\space\ignorespaces}% \def\entrybreak{\hfil\break}% % % Swallow the left brace of the text (first parameter): \afterassignment\doentry \let\temp = } \def\entrybreak{\unskip\space\ignorespaces}% \def\doentry{% \bgroup % Instead of the swallowed brace. \noindent \aftergroup\finishentry % And now comes the text of the entry. } \def\finishentry#1{% % #1 is the page number. % % The following is kludged to not output a line of dots in the index if % there are no page numbers. The next person who breaks this will be % cursed by a Unix daemon. \setbox\boxA = \hbox{#1}% \ifdim\wd\boxA = 0pt \ % \else % % If we must, put the page number on a line of its own, and fill out % this line with blank space. (The \hfil is overwhelmed with the % fill leaders glue in \indexdotfill if the page number does fit.) \hfil\penalty50 \null\nobreak\indexdotfill % Have leaders before the page number. % % The `\ ' here is removed by the implicit \unskip that TeX does as % part of (the primitive) \par. Without it, a spurious underfull % \hbox ensues. \ifpdf \pdfgettoks#1.% \ \the\toksA \else \ #1% \fi \fi \par \endgroup } % Like plain.tex's \dotfill, except uses up at least 1 em. \def\indexdotfill{\cleaders \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill} \def\primary #1{\line{#1\hfil}} \newskip\secondaryindent \secondaryindent=0.5cm \def\secondary#1#2{{% \parfillskip=0in \parskip=0in \hangindent=1in \hangafter=1 \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill \ifpdf \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. \else #2 \fi \par }} % Define two-column mode, which we use to typeset indexes. % Adapted from the TeXbook, page 416, which is to say, % the manmac.tex format used to print the TeXbook itself. \catcode`\@=11 \newbox\partialpage \newdimen\doublecolumnhsize \def\begindoublecolumns{\begingroup % ended by \enddoublecolumns % Grab any single-column material above us. \output = {% % % Here is a possibility not foreseen in manmac: if we accumulate a % whole lot of material, we might end up calling this \output % routine twice in a row (see the doublecol-lose test, which is % essentially a couple of indexes with @setchapternewpage off). In % that case we just ship out what is in \partialpage with the normal % output routine. Generally, \partialpage will be empty when this % runs and this will be a no-op. See the indexspread.tex test case. \ifvoid\partialpage \else \onepageout{\pagecontents\partialpage}% \fi % \global\setbox\partialpage = \vbox{% % Unvbox the main output page. \unvbox\PAGE \kern-\topskip \kern\baselineskip }% }% \eject % run that output routine to set \partialpage % % Use the double-column output routine for subsequent pages. \output = {\doublecolumnout}% % % Change the page size parameters. We could do this once outside this % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 % format, but then we repeat the same computation. Repeating a couple % of assignments once per index is clearly meaningless for the % execution time, so we may as well do it in one place. % % First we halve the line length, less a little for the gutter between % the columns. We compute the gutter based on the line length, so it % changes automatically with the paper format. The magic constant % below is chosen so that the gutter has the same value (well, +-<1pt) % as it did when we hard-coded it. % % We put the result in a separate register, \doublecolumhsize, so we % can restore it in \pagesofar, after \hsize itself has (potentially) % been clobbered. % \doublecolumnhsize = \hsize \advance\doublecolumnhsize by -.04154\hsize \divide\doublecolumnhsize by 2 \hsize = \doublecolumnhsize % % Double the \vsize as well. (We don't need a separate register here, % since nobody clobbers \vsize.) \vsize = 2\vsize } % The double-column output routine for all double-column pages except % the last. % \def\doublecolumnout{% \splittopskip=\topskip \splitmaxdepth=\maxdepth % Get the available space for the double columns -- the normal % (undoubled) page height minus any material left over from the % previous page. \dimen@ = \vsize \divide\dimen@ by 2 \advance\dimen@ by -\ht\partialpage % % box0 will be the left-hand column, box2 the right. \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ \onepageout\pagesofar \unvbox255 \penalty\outputpenalty } % % Re-output the contents of the output page -- any previous material, % followed by the two boxes we just split, in box0 and box2. \def\pagesofar{% \unvbox\partialpage % \hsize = \doublecolumnhsize \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}% } % % All done with double columns. \def\enddoublecolumns{% % The following penalty ensures that the page builder is exercised % _before_ we change the output routine. This is necessary in the % following situation: % % The last section of the index consists only of a single entry. % Before this section, \pagetotal is less than \pagegoal, so no % break occurs before the last section starts. However, the last % section, consisting of \initial and the single \entry, does not % fit on the page and has to be broken off. Without the following % penalty the page builder will not be exercised until \eject % below, and by that time we'll already have changed the output % routine to the \balancecolumns version, so the next-to-last % double-column page will be processed with \balancecolumns, which % is wrong: The two columns will go to the main vertical list, with % the broken-off section in the recent contributions. As soon as % the output routine finishes, TeX starts reconsidering the page % break. The two columns and the broken-off section both fit on the % page, because the two columns now take up only half of the page % goal. When TeX sees \eject from below which follows the final % section, it invokes the new output routine that we've set after % \balancecolumns below; \onepageout will try to fit the two columns % and the final section into the vbox of \pageheight (see % \pagebody), causing an overfull box. % % Note that glue won't work here, because glue does not exercise the % page builder, unlike penalties (see The TeXbook, pp. 280-281). \penalty0 % \output = {% % Split the last of the double-column material. Leave it on the % current page, no automatic page break. \balancecolumns % % If we end up splitting too much material for the current page, % though, there will be another page break right after this \output % invocation ends. Having called \balancecolumns once, we do not % want to call it again. Therefore, reset \output to its normal % definition right away. (We hope \balancecolumns will never be % called on to balance too much material, but if it is, this makes % the output somewhat more palatable.) \global\output = {\onepageout{\pagecontents\PAGE}}% }% \eject \endgroup % started in \begindoublecolumns % % \pagegoal was set to the doubled \vsize above, since we restarted % the current page. We're now back to normal single-column % typesetting, so reset \pagegoal to the normal \vsize (after the % \endgroup where \vsize got restored). \pagegoal = \vsize } % % Called at the end of the double column material. \def\balancecolumns{% \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. \dimen@ = \ht0 \advance\dimen@ by \topskip \advance\dimen@ by-\baselineskip \divide\dimen@ by 2 % target to split to %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}% \splittopskip = \topskip % Loop until we get a decent breakpoint. {% \vbadness = 10000 \loop \global\setbox3 = \copy0 \global\setbox1 = \vsplit3 to \dimen@ \ifdim\ht3>\dimen@ \global\advance\dimen@ by 1pt \repeat }% %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}% \setbox0=\vbox to\dimen@{\unvbox1}% \setbox2=\vbox to\dimen@{\unvbox3}% % \pagesofar } \catcode`\@ = \other \message{sectioning,} % Chapters, sections, etc. % Let's start with @part. \outer\parseargdef\part{\partzzz{#1}} \def\partzzz#1{% \chapoddpage \null \vskip.3\vsize % move it down on the page a bit \begingroup \noindent \titlefonts\rmisbold #1\par % the text \let\lastnode=\empty % no node to associate with \writetocentry{part}{#1}{}% but put it in the toc \headingsoff % no headline or footline on the part page \chapoddpage \endgroup } % \unnumberedno is an oxymoron. But we count the unnumbered % sections so that we can refer to them unambiguously in the pdf % outlines by their "section number". We avoid collisions with chapter % numbers by starting them at 10000. (If a document ever has 10000 % chapters, we're in trouble anyway, I'm sure.) \newcount\unnumberedno \unnumberedno = 10000 \newcount\chapno \newcount\secno \secno=0 \newcount\subsecno \subsecno=0 \newcount\subsubsecno \subsubsecno=0 % This counter is funny since it counts through charcodes of letters A, B, ... \newcount\appendixno \appendixno = `\@ % % \def\appendixletter{\char\the\appendixno} % We do the following ugly conditional instead of the above simple % construct for the sake of pdftex, which needs the actual % letter in the expansion, not just typeset. % \def\appendixletter{% \ifnum\appendixno=`A A% \else\ifnum\appendixno=`B B% \else\ifnum\appendixno=`C C% \else\ifnum\appendixno=`D D% \else\ifnum\appendixno=`E E% \else\ifnum\appendixno=`F F% \else\ifnum\appendixno=`G G% \else\ifnum\appendixno=`H H% \else\ifnum\appendixno=`I I% \else\ifnum\appendixno=`J J% \else\ifnum\appendixno=`K K% \else\ifnum\appendixno=`L L% \else\ifnum\appendixno=`M M% \else\ifnum\appendixno=`N N% \else\ifnum\appendixno=`O O% \else\ifnum\appendixno=`P P% \else\ifnum\appendixno=`Q Q% \else\ifnum\appendixno=`R R% \else\ifnum\appendixno=`S S% \else\ifnum\appendixno=`T T% \else\ifnum\appendixno=`U U% \else\ifnum\appendixno=`V V% \else\ifnum\appendixno=`W W% \else\ifnum\appendixno=`X X% \else\ifnum\appendixno=`Y Y% \else\ifnum\appendixno=`Z Z% % The \the is necessary, despite appearances, because \appendixletter is % expanded while writing the .toc file. \char\appendixno is not % expandable, thus it is written literally, thus all appendixes come out % with the same letter (or @) in the toc without it. \else\char\the\appendixno \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} % Each @chapter defines these (using marks) as the number+name, number % and name of the chapter. Page headings and footings can use % these. @section does likewise. \def\thischapter{} \def\thischapternum{} \def\thischaptername{} \def\thissection{} \def\thissectionnum{} \def\thissectionname{} \newcount\absseclevel % used to calculate proper heading level \newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count % @raisesections: treat @section as chapter, @subsection as section, etc. \def\raisesections{\global\advance\secbase by -1} \let\up=\raisesections % original BFox name % @lowersections: treat @chapter as section, @section as subsection, etc. \def\lowersections{\global\advance\secbase by 1} \let\down=\lowersections % original BFox name % we only have subsub. \chardef\maxseclevel = 3 % % A numbered section within an unnumbered changes to unnumbered too. % To achieve this, remember the "biggest" unnum. sec. we are currently in: \chardef\unnlevel = \maxseclevel % % Trace whether the current chapter is an appendix or not: % \chapheadtype is "N" or "A", unnumbered chapters are ignored. \def\chapheadtype{N} % Choose a heading macro % #1 is heading type % #2 is heading level % #3 is text for heading \def\genhead#1#2#3{% % Compute the abs. sec. level: \absseclevel=#2 \advance\absseclevel by \secbase % Make sure \absseclevel doesn't fall outside the range: \ifnum \absseclevel < 0 \absseclevel = 0 \else \ifnum \absseclevel > 3 \absseclevel = 3 \fi \fi % The heading type: \def\headtype{#1}% \if \headtype U% \ifnum \absseclevel < \unnlevel \chardef\unnlevel = \absseclevel \fi \else % Check for appendix sections: \ifnum \absseclevel = 0 \edef\chapheadtype{\headtype}% \else \if \headtype A\if \chapheadtype N% \errmessage{@appendix... within a non-appendix chapter}% \fi\fi \fi % Check for numbered within unnumbered: \ifnum \absseclevel > \unnlevel \def\headtype{U}% \else \chardef\unnlevel = 3 \fi \fi % Now print the heading: \if \headtype U% \ifcase\absseclevel \unnumberedzzz{#3}% \or \unnumberedseczzz{#3}% \or \unnumberedsubseczzz{#3}% \or \unnumberedsubsubseczzz{#3}% \fi \else \if \headtype A% \ifcase\absseclevel \appendixzzz{#3}% \or \appendixsectionzzz{#3}% \or \appendixsubseczzz{#3}% \or \appendixsubsubseczzz{#3}% \fi \else \ifcase\absseclevel \chapterzzz{#3}% \or \seczzz{#3}% \or \numberedsubseczzz{#3}% \or \numberedsubsubseczzz{#3}% \fi \fi \fi \suppressfirstparagraphindent } % an interface: \def\numhead{\genhead N} \def\apphead{\genhead A} \def\unnmhead{\genhead U} % @chapter, @appendix, @unnumbered. Increment top-level counter, reset % all lower-level sectioning counters to zero. % % Also set \chaplevelprefix, which we prepend to @float sequence numbers % (e.g., figures), q.v. By default (before any chapter), that is empty. \let\chaplevelprefix = \empty % \outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz \def\chapterzzz#1{% % section resetting is \global in case the chapter is in a group, such % as an @include file. \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 \global\advance\chapno by 1 % % Used for \float. \gdef\chaplevelprefix{\the\chapno.}% \resetallfloatnos % % \putwordChapter can contain complex things in translations. \toks0=\expandafter{\putwordChapter}% \message{\the\toks0 \space \the\chapno}% % % Write the actual heading. \chapmacro{#1}{Ynumbered}{\the\chapno}% % % So @section and the like are numbered underneath this chapter. \global\let\section = \numberedsec \global\let\subsection = \numberedsubsec \global\let\subsubsection = \numberedsubsubsec } \outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz % \def\appendixzzz#1{% \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 \global\advance\appendixno by 1 \gdef\chaplevelprefix{\appendixletter.}% \resetallfloatnos % % \putwordAppendix can contain complex things in translations. \toks0=\expandafter{\putwordAppendix}% \message{\the\toks0 \space \appendixletter}% % \chapmacro{#1}{Yappendix}{\appendixletter}% % \global\let\section = \appendixsec \global\let\subsection = \appendixsubsec \global\let\subsubsection = \appendixsubsubsec } % normally unnmhead0 calls unnumberedzzz: \outer\parseargdef\unnumbered{\unnmhead0{#1}} \def\unnumberedzzz#1{% \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 \global\advance\unnumberedno by 1 % % Since an unnumbered has no number, no prefix for figures. \global\let\chaplevelprefix = \empty \resetallfloatnos % % This used to be simply \message{#1}, but TeX fully expands the % argument to \message. Therefore, if #1 contained @-commands, TeX % expanded them. For example, in `@unnumbered The @cite{Book}', TeX % expanded @cite (which turns out to cause errors because \cite is meant % to be executed, not expanded). % % Anyway, we don't want the fully-expanded definition of @cite to appear % as a result of the \message, we just want `@cite' itself. We use % \the<toks register> to achieve this: TeX expands \the<toks> only once, % simply yielding the contents of <toks register>. (We also do this for % the toc entries.) \toks0 = {#1}% \message{(\the\toks0)}% % \chapmacro{#1}{Ynothing}{\the\unnumberedno}% % \global\let\section = \unnumberedsec \global\let\subsection = \unnumberedsubsec \global\let\subsubsection = \unnumberedsubsubsec } % @centerchap is like @unnumbered, but the heading is centered. \outer\parseargdef\centerchap{% % Well, we could do the following in a group, but that would break % an assumption that \chapmacro is called at the outermost level. % Thus we are safer this way: --kasal, 24feb04 \let\centerparametersmaybe = \centerparameters \unnmhead0{#1}% \let\centerparametersmaybe = \relax } % @top is like @unnumbered. \let\top\unnumbered % Sections. % \outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz \def\seczzz#1{% \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% } % normally calls appendixsectionzzz: \outer\parseargdef\appendixsection{\apphead1{#1}} \def\appendixsectionzzz#1{% \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% } \let\appendixsec\appendixsection % normally calls unnumberedseczzz: \outer\parseargdef\unnumberedsec{\unnmhead1{#1}} \def\unnumberedseczzz#1{% \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% } % Subsections. % % normally calls numberedsubseczzz: \outer\parseargdef\numberedsubsec{\numhead2{#1}} \def\numberedsubseczzz#1{% \global\subsubsecno=0 \global\advance\subsecno by 1 \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% } % normally calls appendixsubseczzz: \outer\parseargdef\appendixsubsec{\apphead2{#1}} \def\appendixsubseczzz#1{% \global\subsubsecno=0 \global\advance\subsecno by 1 \sectionheading{#1}{subsec}{Yappendix}% {\appendixletter.\the\secno.\the\subsecno}% } % normally calls unnumberedsubseczzz: \outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} \def\unnumberedsubseczzz#1{% \global\subsubsecno=0 \global\advance\subsecno by 1 \sectionheading{#1}{subsec}{Ynothing}% {\the\unnumberedno.\the\secno.\the\subsecno}% } % Subsubsections. % % normally numberedsubsubseczzz: \outer\parseargdef\numberedsubsubsec{\numhead3{#1}} \def\numberedsubsubseczzz#1{% \global\advance\subsubsecno by 1 \sectionheading{#1}{subsubsec}{Ynumbered}% {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% } % normally appendixsubsubseczzz: \outer\parseargdef\appendixsubsubsec{\apphead3{#1}} \def\appendixsubsubseczzz#1{% \global\advance\subsubsecno by 1 \sectionheading{#1}{subsubsec}{Yappendix}% {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% } % normally unnumberedsubsubseczzz: \outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} \def\unnumberedsubsubseczzz#1{% \global\advance\subsubsecno by 1 \sectionheading{#1}{subsubsec}{Ynothing}% {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% } % These macros control what the section commands do, according % to what kind of chapter we are in (ordinary, appendix, or unnumbered). % Define them by default for a numbered chapter. \let\section = \numberedsec \let\subsection = \numberedsubsec \let\subsubsection = \numberedsubsubsec % Define @majorheading, @heading and @subheading \def\majorheading{% {\advance\chapheadingskip by 10pt \chapbreak }% \parsearg\chapheadingzzz } \def\chapheading{\chapbreak \parsearg\chapheadingzzz} \def\chapheadingzzz#1{% \vbox{\chapfonts \raggedtitlesettings #1\par}% \nobreak\bigskip \nobreak \suppressfirstparagraphindent } % @heading, @subheading, @subsubheading. \parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} \suppressfirstparagraphindent} \parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} \suppressfirstparagraphindent} \parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} \suppressfirstparagraphindent} % These macros generate a chapter, section, etc. heading only % (including whitespace, linebreaking, etc. around it), % given all the information in convenient, parsed form. % Args are the skip and penalty (usually negative) \def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} % Parameter controlling skip before chapter headings (if needed) \newskip\chapheadingskip % Define plain chapter starts, and page on/off switching for it. \def\chapbreak{\dobreak \chapheadingskip {-4000}} \def\chappager{\par\vfill\supereject} % Because \domark is called before \chapoddpage, the filler page will % get the headings for the next chapter, which is wrong. But we don't % care -- we just disable all headings on the filler page. \def\chapoddpage{% \chappager \ifodd\pageno \else \begingroup \headingsoff \null \chappager \endgroup \fi } \def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} \def\CHAPPAGoff{% \global\let\contentsalignmacro = \chappager \global\let\pchapsepmacro=\chapbreak \global\let\pagealignmacro=\chappager} \def\CHAPPAGon{% \global\let\contentsalignmacro = \chappager \global\let\pchapsepmacro=\chappager \global\let\pagealignmacro=\chappager \global\def\HEADINGSon{\HEADINGSsingle}} \def\CHAPPAGodd{% \global\let\contentsalignmacro = \chapoddpage \global\let\pchapsepmacro=\chapoddpage \global\let\pagealignmacro=\chapoddpage \global\def\HEADINGSon{\HEADINGSdouble}} \CHAPPAGon % Chapter opening. % % #1 is the text, #2 is the section type (Ynumbered, Ynothing, % Yappendix, Yomitfromtoc), #3 the chapter number. % % To test against our argument. \def\Ynothingkeyword{Ynothing} \def\Yomitfromtockeyword{Yomitfromtoc} \def\Yappendixkeyword{Yappendix} % \def\chapmacro#1#2#3{% % Insert the first mark before the heading break (see notes for \domark). \let\prevchapterdefs=\lastchapterdefs \let\prevsectiondefs=\lastsectiondefs \gdef\lastsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}% \gdef\thissection{}}% % \def\temptype{#2}% \ifx\temptype\Ynothingkeyword \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% \gdef\thischapter{\thischaptername}}% \else\ifx\temptype\Yomitfromtockeyword \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% \gdef\thischapter{}}% \else\ifx\temptype\Yappendixkeyword \toks0={#1}% \xdef\lastchapterdefs{% \gdef\noexpand\thischaptername{\the\toks0}% \gdef\noexpand\thischapternum{\appendixletter}% % \noexpand\putwordAppendix avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thischapter{\noexpand\putwordAppendix{} \noexpand\thischapternum: \noexpand\thischaptername}% }% \else \toks0={#1}% \xdef\lastchapterdefs{% \gdef\noexpand\thischaptername{\the\toks0}% \gdef\noexpand\thischapternum{\the\chapno}% % \noexpand\putwordChapter avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thischapter{\noexpand\putwordChapter{} \noexpand\thischapternum: \noexpand\thischaptername}% }% \fi\fi\fi % % Output the mark. Pass it through \safewhatsit, to take care of % the preceding space. \safewhatsit\domark % % Insert the chapter heading break. \pchapsepmacro % % Now the second mark, after the heading break. No break points % between here and the heading. \let\prevchapterdefs=\lastchapterdefs \let\prevsectiondefs=\lastsectiondefs \domark % {% \chapfonts \rmisbold % % Have to define \lastsection before calling \donoderef, because the % xref code eventually uses it. On the other hand, it has to be called % after \pchapsepmacro, or the headline will change too soon. \gdef\lastsection{#1}% % % Only insert the separating space if we have a chapter/appendix % number, and don't print the unnumbered ``number''. \ifx\temptype\Ynothingkeyword \setbox0 = \hbox{}% \def\toctype{unnchap}% \else\ifx\temptype\Yomitfromtockeyword \setbox0 = \hbox{}% contents like unnumbered, but no toc entry \def\toctype{omit}% \else\ifx\temptype\Yappendixkeyword \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% \def\toctype{app}% \else \setbox0 = \hbox{#3\enspace}% \def\toctype{numchap}% \fi\fi\fi % % Write the toc entry for this chapter. Must come before the % \donoderef, because we include the current node name in the toc % entry, and \donoderef resets it to empty. \writetocentry{\toctype}{#1}{#3}% % % For pdftex, we have to write out the node definition (aka, make % the pdfdest) after any page break, but before the actual text has % been typeset. If the destination for the pdf outline is after the % text, then jumping from the outline may wind up with the text not % being visible, for instance under high magnification. \donoderef{#2}% % % Typeset the actual heading. \nobreak % Avoid page breaks at the interline glue. \vbox{\raggedtitlesettings \hangindent=\wd0 \centerparametersmaybe \unhbox0 #1\par}% }% \nobreak\bigskip % no page break after a chapter title \nobreak } % @centerchap -- centered and unnumbered. \let\centerparametersmaybe = \relax \def\centerparameters{% \advance\rightskip by 3\rightskip \leftskip = \rightskip \parfillskip = 0pt } % I don't think this chapter style is supported any more, so I'm not % updating it with the new noderef stuff. We'll see. --karl, 11aug03. % \def\setchapterstyle #1 {\csname CHAPF#1\endcsname} % \def\unnchfopen #1{% \chapoddpage \vbox{\chapfonts \raggedtitlesettings #1\par}% \nobreak\bigskip\nobreak } \def\chfopen #1#2{\chapoddpage {\chapfonts \vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% \par\penalty 5000 % } \def\centerchfopen #1{% \chapoddpage \vbox{\chapfonts \raggedtitlesettings \hfill #1\hfill}% \nobreak\bigskip \nobreak } \def\CHAPFopen{% \global\let\chapmacro=\chfopen \global\let\centerchapmacro=\centerchfopen} % Section titles. These macros combine the section number parts and % call the generic \sectionheading to do the printing. % \newskip\secheadingskip \def\secheadingbreak{\dobreak \secheadingskip{-1000}} % Subsection titles. \newskip\subsecheadingskip \def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} % Subsubsection titles. \def\subsubsecheadingskip{\subsecheadingskip} \def\subsubsecheadingbreak{\subsecheadingbreak} % Print any size, any type, section title. % % #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is % the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the % section number. % \def\seckeyword{sec} % \def\sectionheading#1#2#3#4{% {% \checkenv{}% should not be in an environment. % % Switch to the right set of fonts. \csname #2fonts\endcsname \rmisbold % \def\sectionlevel{#2}% \def\temptype{#3}% % % Insert first mark before the heading break (see notes for \domark). \let\prevsectiondefs=\lastsectiondefs \ifx\temptype\Ynothingkeyword \ifx\sectionlevel\seckeyword \gdef\lastsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}% \gdef\thissection{\thissectionname}}% \fi \else\ifx\temptype\Yomitfromtockeyword % Don't redefine \thissection. \else\ifx\temptype\Yappendixkeyword \ifx\sectionlevel\seckeyword \toks0={#1}% \xdef\lastsectiondefs{% \gdef\noexpand\thissectionname{\the\toks0}% \gdef\noexpand\thissectionnum{#4}% % \noexpand\putwordSection avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thissection{\noexpand\putwordSection{} \noexpand\thissectionnum: \noexpand\thissectionname}% }% \fi \else \ifx\sectionlevel\seckeyword \toks0={#1}% \xdef\lastsectiondefs{% \gdef\noexpand\thissectionname{\the\toks0}% \gdef\noexpand\thissectionnum{#4}% % \noexpand\putwordSection avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thissection{\noexpand\putwordSection{} \noexpand\thissectionnum: \noexpand\thissectionname}% }% \fi \fi\fi\fi % % Go into vertical mode. Usually we'll already be there, but we % don't want the following whatsit to end up in a preceding paragraph % if the document didn't happen to have a blank line. \par % % Output the mark. Pass it through \safewhatsit, to take care of % the preceding space. \safewhatsit\domark % % Insert space above the heading. \csname #2headingbreak\endcsname % % Now the second mark, after the heading break. No break points % between here and the heading. \let\prevsectiondefs=\lastsectiondefs \domark % % Only insert the space after the number if we have a section number. \ifx\temptype\Ynothingkeyword \setbox0 = \hbox{}% \def\toctype{unn}% \gdef\lastsection{#1}% \else\ifx\temptype\Yomitfromtockeyword % for @headings -- no section number, don't include in toc, % and don't redefine \lastsection. \setbox0 = \hbox{}% \def\toctype{omit}% \let\sectionlevel=\empty \else\ifx\temptype\Yappendixkeyword \setbox0 = \hbox{#4\enspace}% \def\toctype{app}% \gdef\lastsection{#1}% \else \setbox0 = \hbox{#4\enspace}% \def\toctype{num}% \gdef\lastsection{#1}% \fi\fi\fi % % Write the toc entry (before \donoderef). See comments in \chapmacro. \writetocentry{\toctype\sectionlevel}{#1}{#4}% % % Write the node reference (= pdf destination for pdftex). % Again, see comments in \chapmacro. \donoderef{#3}% % % Interline glue will be inserted when the vbox is completed. % That glue will be a valid breakpoint for the page, since it'll be % preceded by a whatsit (usually from the \donoderef, or from the % \writetocentry if there was no node). We don't want to allow that % break, since then the whatsits could end up on page n while the % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. \nobreak % % Output the actual section heading. \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright \hangindent=\wd0 % zero if no section number \unhbox0 #1}% }% % Add extra space after the heading -- half of whatever came above it. % Don't allow stretch, though. \kern .5 \csname #2headingskip\endcsname % % Do not let the kern be a potential breakpoint, as it would be if it % was followed by glue. \nobreak % % We'll almost certainly start a paragraph next, so don't let that % glue accumulate. (Not a breakpoint because it's preceded by a % discardable item.) However, when a paragraph is not started next % (\startdefun, \cartouche, \center, etc.), this needs to be wiped out % or the negative glue will cause weirdly wrong output, typically % obscuring the section heading with something else. \vskip-\parskip % % This is so the last item on the main vertical list is a known % \penalty > 10000, so \startdefun, etc., can recognize the situation % and do the needful. \penalty 10001 } \message{toc,} % Table of contents. \newwrite\tocfile % Write an entry to the toc file, opening it if necessary. % Called from @chapter, etc. % % Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} % We append the current node name (if any) and page number as additional % arguments for the \{chap,sec,...}entry macros which will eventually % read this. The node name is used in the pdf outlines as the % destination to jump to. % % We open the .toc file for writing here instead of at @setfilename (or % any other fixed time) so that @contents can be anywhere in the document. % But if #1 is `omit', then we don't do anything. This is used for the % table of contents chapter openings themselves. % \newif\iftocfileopened \def\omitkeyword{omit}% % \def\writetocentry#1#2#3{% \edef\writetoctype{#1}% \ifx\writetoctype\omitkeyword \else \iftocfileopened\else \immediate\openout\tocfile = \jobname.toc \global\tocfileopenedtrue \fi % \iflinks {\atdummies \edef\temp{% \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% \temp }% \fi \fi % % Tell \shipout to create a pdf destination on each page, if we're % writing pdf. These are used in the table of contents. We can't % just write one on every page because the title pages are numbered % 1 and 2 (the page numbers aren't printed), and so are the first % two pages of the document. Thus, we'd have two destinations named % `1', and two named `2'. \ifpdf \global\pdfmakepagedesttrue \fi } % These characters do not print properly in the Computer Modern roman % fonts, so we must take special care. This is more or less redundant % with the Texinfo input format setup at the end of this file. % \def\activecatcodes{% \catcode`\"=\active \catcode`\$=\active \catcode`\<=\active \catcode`\>=\active \catcode`\\=\active \catcode`\^=\active \catcode`\_=\active \catcode`\|=\active \catcode`\~=\active } % Read the toc file, which is essentially Texinfo input. \def\readtocfile{% \setupdatafile \activecatcodes \input \tocreadfilename } \newskip\contentsrightmargin \contentsrightmargin=1in \newcount\savepageno \newcount\lastnegativepageno \lastnegativepageno = -1 % Prepare to read what we've written to \tocfile. % \def\startcontents#1{% % If @setchapternewpage on, and @headings double, the contents should % start on an odd page, unlike chapters. Thus, we maintain % \contentsalignmacro in parallel with \pagealignmacro. % From: Torbjorn Granlund <tege@matematik.su.se> \contentsalignmacro \immediate\closeout\tocfile % % Don't need to put `Contents' or `Short Contents' in the headline. % It is abundantly clear what they are. \chapmacro{#1}{Yomitfromtoc}{}% % \savepageno = \pageno \begingroup % Set up to handle contents files properly. \raggedbottom % Worry more about breakpoints than the bottom. \advance\hsize by -\contentsrightmargin % Don't use the full line length. % % Roman numerals for page numbers. \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi } % redefined for the two-volume lispref. We always output on % \jobname.toc even if this is redefined. % \def\tocreadfilename{\jobname.toc} % Normal (long) toc. % \def\contents{% \startcontents{\putwordTOC}% \openin 1 \tocreadfilename\space \ifeof 1 \else \readtocfile \fi \vfill \eject \contentsalignmacro % in case @setchapternewpage odd is in effect \ifeof 1 \else \pdfmakeoutlines \fi \closein 1 \endgroup \lastnegativepageno = \pageno \global\pageno = \savepageno } % And just the chapters. \def\summarycontents{% \startcontents{\putwordShortTOC}% % \let\partentry = \shortpartentry \let\numchapentry = \shortchapentry \let\appentry = \shortchapentry \let\unnchapentry = \shortunnchapentry % We want a true roman here for the page numbers. \secfonts \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl \let\tt=\shortconttt \rm \hyphenpenalty = 10000 \advance\baselineskip by 1pt % Open it up a little. \def\numsecentry##1##2##3##4{} \let\appsecentry = \numsecentry \let\unnsecentry = \numsecentry \let\numsubsecentry = \numsecentry \let\appsubsecentry = \numsecentry \let\unnsubsecentry = \numsecentry \let\numsubsubsecentry = \numsecentry \let\appsubsubsecentry = \numsecentry \let\unnsubsubsecentry = \numsecentry \openin 1 \tocreadfilename\space \ifeof 1 \else \readtocfile \fi \closein 1 \vfill \eject \contentsalignmacro % in case @setchapternewpage odd is in effect \endgroup \lastnegativepageno = \pageno \global\pageno = \savepageno } \let\shortcontents = \summarycontents % Typeset the label for a chapter or appendix for the short contents. % The arg is, e.g., `A' for an appendix, or `3' for a chapter. % \def\shortchaplabel#1{% % This space should be enough, since a single number is .5em, and the % widest letter (M) is 1em, at least in the Computer Modern fonts. % But use \hss just in case. % (This space doesn't include the extra space that gets added after % the label; that gets put in by \shortchapentry above.) % % We'd like to right-justify chapter numbers, but that looks strange % with appendix letters. And right-justifying numbers and % left-justifying letters looks strange when there is less than 10 % chapters. Have to read the whole toc once to know how many chapters % there are before deciding ... \hbox to 1em{#1\hss}% } % These macros generate individual entries in the table of contents. % The first argument is the chapter or section name. % The last argument is the page number. % The arguments in between are the chapter number, section number, ... % Parts, in the main contents. Replace the part number, which doesn't % exist, with an empty box. Let's hope all the numbers have the same width. % Also ignore the page number, which is conventionally not printed. \def\numeralbox{\setbox0=\hbox{8}\hbox to \wd0{\hfil}} \def\partentry#1#2#3#4{\dochapentry{\numeralbox\labelspace#1}{}} % % Parts, in the short toc. \def\shortpartentry#1#2#3#4{% \penalty-300 \vskip.5\baselineskip plus.15\baselineskip minus.1\baselineskip \shortchapentry{{\bf #1}}{\numeralbox}{}{}% } % Chapters, in the main contents. \def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} % % Chapters, in the short toc. % See comments in \dochapentry re vbox and related settings. \def\shortchapentry#1#2#3#4{% \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% } % Appendices, in the main contents. % Need the word Appendix, and a fixed-size box. % \def\appendixbox#1{% % We use M since it's probably the widest letter. \setbox0 = \hbox{\putwordAppendix{} M}% \hbox to \wd0{\putwordAppendix{} #1\hss}} % \def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}} % Unnumbered chapters. \def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} \def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} % Sections. \def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} \let\appsecentry=\numsecentry \def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} % Subsections. \def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} \let\appsubsecentry=\numsubsecentry \def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} % And subsubsections. \def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} \let\appsubsubsecentry=\numsubsubsecentry \def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} % This parameter controls the indentation of the various levels. % Same as \defaultparindent. \newdimen\tocindent \tocindent = 15pt % Now for the actual typesetting. In all these, #1 is the text and #2 is the % page number. % % If the toc has to be broken over pages, we want it to be at chapters % if at all possible; hence the \penalty. \def\dochapentry#1#2{% \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip \begingroup \chapentryfonts \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup \nobreak\vskip .25\baselineskip plus.1\baselineskip } \def\dosecentry#1#2{\begingroup \secentryfonts \leftskip=\tocindent \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup} \def\dosubsecentry#1#2{\begingroup \subsecentryfonts \leftskip=2\tocindent \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup} \def\dosubsubsecentry#1#2{\begingroup \subsubsecentryfonts \leftskip=3\tocindent \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup} % We use the same \entry macro as for the index entries. \let\tocentry = \entry % Space between chapter (or whatever) number and the title. \def\labelspace{\hskip1em \relax} \def\dopageno#1{{\rm #1}} \def\doshortpageno#1{{\rm #1}} \def\chapentryfonts{\secfonts \rm} \def\secentryfonts{\textfonts} \def\subsecentryfonts{\textfonts} \def\subsubsecentryfonts{\textfonts} \message{environments,} % @foo ... @end foo. % @tex ... @end tex escapes into raw TeX temporarily. % One exception: @ is still an escape character, so that @end tex works. % But \@ or @@ will get a plain @ character. \envdef\tex{% \setupmarkupstyle{tex}% \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie \catcode `\%=14 \catcode `\+=\other \catcode `\"=\other \catcode `\|=\other \catcode `\<=\other \catcode `\>=\other \catcode`\`=\other \catcode`\'=\other \escapechar=`\\ % % ' is active in math mode (mathcode"8000). So reset it, and all our % other math active characters (just in case), to plain's definitions. \mathactive % \let\b=\ptexb \let\bullet=\ptexbullet \let\c=\ptexc \let\,=\ptexcomma \let\.=\ptexdot \let\dots=\ptexdots \let\equiv=\ptexequiv \let\!=\ptexexclam \let\i=\ptexi \let\indent=\ptexindent \let\noindent=\ptexnoindent \let\{=\ptexlbrace \let\+=\tabalign \let\}=\ptexrbrace \let\/=\ptexslash \let\*=\ptexstar \let\t=\ptext \expandafter \let\csname top\endcsname=\ptextop % outer \let\frenchspacing=\plainfrenchspacing % \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% \def\@{@}% } % There is no need to define \Etex. % Define @lisp ... @end lisp. % @lisp environment forms a group so it can rebind things, % including the definition of @end lisp (which normally is erroneous). % Amount to narrow the margins by for @lisp. \newskip\lispnarrowing \lispnarrowing=0.4in % This is the definition that ^^M gets inside @lisp, @example, and other % such environments. \null is better than a space, since it doesn't % have any width. \def\lisppar{\null\endgraf} % This space is always present above and below environments. \newskip\envskipamount \envskipamount = 0pt % Make spacing and below environment symmetrical. We use \parskip here % to help in doing that, since in @example-like environments \parskip % is reset to zero; thus the \afterenvbreak inserts no space -- but the % start of the next paragraph will insert \parskip. % \def\aboveenvbreak{{% % =10000 instead of <10000 because of a special case in \itemzzz and % \sectionheading, q.v. \ifnum \lastpenalty=10000 \else \advance\envskipamount by \parskip \endgraf \ifdim\lastskip<\envskipamount \removelastskip % it's not a good place to break if the last penalty was \nobreak % or better ... \ifnum\lastpenalty<10000 \penalty-50 \fi \vskip\envskipamount \fi \fi }} \let\afterenvbreak = \aboveenvbreak % \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will % also clear it, so that its embedded environments do the narrowing again. \let\nonarrowing=\relax % @cartouche ... @end cartouche: draw rectangle w/rounded corners around % environment contents. \font\circle=lcircle10 \newdimen\circthick \newdimen\cartouter\newdimen\cartinner \newskip\normbskip\newskip\normpskip\newskip\normlskip \circthick=\fontdimen8\circle % \def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth \def\ctr{{\hskip 6pt\circle\char'010}} \def\cbl{{\circle\char'012\hskip -6pt}} \def\cbr{{\hskip 6pt\circle\char'011}} \def\carttop{\hbox to \cartouter{\hskip\lskip \ctl\leaders\hrule height\circthick\hfil\ctr \hskip\rskip}} \def\cartbot{\hbox to \cartouter{\hskip\lskip \cbl\leaders\hrule height\circthick\hfil\cbr \hskip\rskip}} % \newskip\lskip\newskip\rskip \envdef\cartouche{% \ifhmode\par\fi % can't be in the midst of a paragraph. \startsavinginserts \lskip=\leftskip \rskip=\rightskip \leftskip=0pt\rightskip=0pt % we want these *outside*. \cartinner=\hsize \advance\cartinner by-\lskip \advance\cartinner by-\rskip \cartouter=\hsize \advance\cartouter by 18.4pt % allow for 3pt kerns on either % side, and for 6pt waste from % each corner char, and rule thickness \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip % Flag to tell @lisp, etc., not to narrow margin. \let\nonarrowing = t% % % If this cartouche directly follows a sectioning command, we need the % \parskip glue (backspaced over by default) or the cartouche can % collide with the section heading. \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi % \vbox\bgroup \baselineskip=0pt\parskip=0pt\lineskip=0pt \carttop \hbox\bgroup \hskip\lskip \vrule\kern3pt \vbox\bgroup \kern3pt \hsize=\cartinner \baselineskip=\normbskip \lineskip=\normlskip \parskip=\normpskip \vskip -\parskip \comment % For explanation, see the end of def\group. } \def\Ecartouche{% \ifhmode\par\fi \kern3pt \egroup \kern3pt\vrule \hskip\rskip \egroup \cartbot \egroup \checkinserts } % This macro is called at the beginning of all the @example variants, % inside a group. \newdimen\nonfillparindent \def\nonfillstart{% \aboveenvbreak \hfuzz = 12pt % Don't be fussy \sepspaces % Make spaces be word-separators rather than space tokens. \let\par = \lisppar % don't ignore blank lines \obeylines % each line of input is a line of output \parskip = 0pt % Turn off paragraph indentation but redefine \indent to emulate % the normal \indent. \nonfillparindent=\parindent \parindent = 0pt \let\indent\nonfillindent % \emergencystretch = 0pt % don't try to avoid overfull boxes \ifx\nonarrowing\relax \advance \leftskip by \lispnarrowing \exdentamount=\lispnarrowing \else \let\nonarrowing = \relax \fi \let\exdent=\nofillexdent } \begingroup \obeyspaces % We want to swallow spaces (but not other tokens) after the fake % @indent in our nonfill-environments, where spaces are normally % active and set to @tie, resulting in them not being ignored after % @indent. \gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}% \gdef\nonfillindentcheck{% \ifx\temp % \expandafter\nonfillindentgobble% \else% \leavevmode\nonfillindentbox% \fi% }% \endgroup \def\nonfillindentgobble#1{\nonfillindent} \def\nonfillindentbox{\hbox to \nonfillparindent{\hss}} % If you want all examples etc. small: @set dispenvsize small. % If you want even small examples the full size: @set dispenvsize nosmall. % This affects the following displayed environments: % @example, @display, @format, @lisp % \def\smallword{small} \def\nosmallword{nosmall} \let\SETdispenvsize\relax \def\setnormaldispenv{% \ifx\SETdispenvsize\smallword % end paragraph for sake of leading, in case document has no blank % line. This is redundant with what happens in \aboveenvbreak, but % we need to do it before changing the fonts, and it's inconvenient % to change the fonts afterward. \ifnum \lastpenalty=10000 \else \endgraf \fi \smallexamplefonts \rm \fi } \def\setsmalldispenv{% \ifx\SETdispenvsize\nosmallword \else \ifnum \lastpenalty=10000 \else \endgraf \fi \smallexamplefonts \rm \fi } % We often define two environments, @foo and @smallfoo. % Let's do it in one command. #1 is the env name, #2 the definition. \def\makedispenvdef#1#2{% \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}% \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}% \expandafter\let\csname E#1\endcsname \afterenvbreak \expandafter\let\csname Esmall#1\endcsname \afterenvbreak } % Define two environment synonyms (#1 and #2) for an environment. \def\maketwodispenvdef#1#2#3{% \makedispenvdef{#1}{#3}% \makedispenvdef{#2}{#3}% } % % @lisp: indented, narrowed, typewriter font; % @example: same as @lisp. % % @smallexample and @smalllisp: use smaller fonts. % Originally contributed by Pavel@xerox. % \maketwodispenvdef{lisp}{example}{% \nonfillstart \tt\setupmarkupstyle{example}% \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. \gobble % eat return } % @display/@smalldisplay: same as @lisp except keep current font. % \makedispenvdef{display}{% \nonfillstart \gobble } % @format/@smallformat: same as @display except don't narrow margins. % \makedispenvdef{format}{% \let\nonarrowing = t% \nonfillstart \gobble } % @flushleft: same as @format, but doesn't obey \SETdispenvsize. \envdef\flushleft{% \let\nonarrowing = t% \nonfillstart \gobble } \let\Eflushleft = \afterenvbreak % @flushright. % \envdef\flushright{% \let\nonarrowing = t% \nonfillstart \advance\leftskip by 0pt plus 1fill\relax \gobble } \let\Eflushright = \afterenvbreak % @raggedright does more-or-less normal line breaking but no right % justification. From plain.tex. \envdef\raggedright{% \rightskip0pt plus2em \spaceskip.3333em \xspaceskip.5em\relax } \let\Eraggedright\par \envdef\raggedleft{% \parindent=0pt \leftskip0pt plus2em \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt \hbadness=10000 % Last line will usually be underfull, so turn off % badness reporting. } \let\Eraggedleft\par \envdef\raggedcenter{% \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt \hbadness=10000 % Last line will usually be underfull, so turn off % badness reporting. } \let\Eraggedcenter\par % @quotation does normal linebreaking (hence we can't use \nonfillstart) % and narrows the margins. We keep \parskip nonzero in general, since % we're doing normal filling. So, when using \aboveenvbreak and % \afterenvbreak, temporarily make \parskip 0. % \makedispenvdef{quotation}{\quotationstart} % \def\quotationstart{% \indentedblockstart % same as \indentedblock, but increase right margin too. \ifx\nonarrowing\relax \advance\rightskip by \lispnarrowing \fi \parsearg\quotationlabel } % We have retained a nonzero parskip for the environment, since we're % doing normal filling. % \def\Equotation{% \par \ifx\quotationauthor\thisisundefined\else % indent a bit. \leftline{\kern 2\leftskip \sl ---\quotationauthor}% \fi {\parskip=0pt \afterenvbreak}% } \def\Esmallquotation{\Equotation} % If we're given an argument, typeset it in bold with a colon after. \def\quotationlabel#1{% \def\temp{#1}% \ifx\temp\empty \else {\bf #1: }% \fi } % @indentedblock is like @quotation, but indents only on the left and % has no optional argument. % \makedispenvdef{indentedblock}{\indentedblockstart} % \def\indentedblockstart{% {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip \parindent=0pt % % @cartouche defines \nonarrowing to inhibit narrowing at next level down. \ifx\nonarrowing\relax \advance\leftskip by \lispnarrowing \exdentamount = \lispnarrowing \else \let\nonarrowing = \relax \fi } % Keep a nonzero parskip for the environment, since we're doing normal filling. % \def\Eindentedblock{% \par {\parskip=0pt \afterenvbreak}% } \def\Esmallindentedblock{\Eindentedblock} % LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>} % If we want to allow any <char> as delimiter, % we need the curly braces so that makeinfo sees the @verb command, eg: % `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org % % [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. % % [Knuth] p.344; only we need to do the other characters Texinfo sets % active too. Otherwise, they get lost as the first character on a % verbatim line. \def\dospecials{% \do\ \do\\\do\{\do\}\do\$\do\&% \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% \do\<\do\>\do\|\do\@\do+\do\"% % Don't do the quotes -- if we do, @set txicodequoteundirected and % @set txicodequotebacktick will not have effect on @verb and % @verbatim, and ?` and !` ligatures won't get disabled. %\do\`\do\'% } % % [Knuth] p. 380 \def\uncatcodespecials{% \def\do##1{\catcode`##1=\other}\dospecials} % % Setup for the @verb command. % % Eight spaces for a tab \begingroup \catcode`\^^I=\active \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} \endgroup % \def\setupverb{% \tt % easiest (and conventionally used) font for verbatim \def\par{\leavevmode\endgraf}% \setupmarkupstyle{verb}% \tabeightspaces % Respect line breaks, % print special symbols as themselves, and % make each space count % must do in this order: \obeylines \uncatcodespecials \sepspaces } % Setup for the @verbatim environment % % Real tab expansion. \newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount % % We typeset each line of the verbatim in an \hbox, so we can handle % tabs. The \global is in case the verbatim line starts with an accent, % or some other command that starts with a begin-group. Otherwise, the % entire \verbbox would disappear at the corresponding end-group, before % it is typeset. Meanwhile, we can't have nested verbatim commands % (can we?), so the \global won't be overwriting itself. \newbox\verbbox \def\starttabbox{\global\setbox\verbbox=\hbox\bgroup} % \begingroup \catcode`\^^I=\active \gdef\tabexpand{% \catcode`\^^I=\active \def^^I{\leavevmode\egroup \dimen\verbbox=\wd\verbbox % the width so far, or since the previous tab \divide\dimen\verbbox by\tabw \multiply\dimen\verbbox by\tabw % compute previous multiple of \tabw \advance\dimen\verbbox by\tabw % advance to next multiple of \tabw \wd\verbbox=\dimen\verbbox \box\verbbox \starttabbox }% } \endgroup % start the verbatim environment. \def\setupverbatim{% \let\nonarrowing = t% \nonfillstart \tt % easiest (and conventionally used) font for verbatim % The \leavevmode here is for blank lines. Otherwise, we would % never \starttabox and the \egroup would end verbatim mode. \def\par{\leavevmode\egroup\box\verbbox\endgraf}% \tabexpand \setupmarkupstyle{verbatim}% % Respect line breaks, % print special symbols as themselves, and % make each space count. % Must do in this order: \obeylines \uncatcodespecials \sepspaces \everypar{\starttabbox}% } % Do the @verb magic: verbatim text is quoted by unique % delimiter characters. Before first delimiter expect a % right brace, after last delimiter expect closing brace: % % \def\doverb'{'<char>#1<char>'}'{#1} % % [Knuth] p. 382; only eat outer {} \begingroup \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] \endgroup % \def\verb{\begingroup\setupverb\doverb} % % % Do the @verbatim magic: define the macro \doverbatim so that % the (first) argument ends when '@end verbatim' is reached, ie: % % \def\doverbatim#1@end verbatim{#1} % % For Texinfo it's a lot easier than for LaTeX, % because texinfo's \verbatim doesn't stop at '\end{verbatim}': % we need not redefine '\', '{' and '}'. % % Inspired by LaTeX's verbatim command set [latex.ltx] % \begingroup \catcode`\ =\active \obeylines % % ignore everything up to the first ^^M, that's the newline at the end % of the @verbatim input line itself. Otherwise we get an extra blank % line in the output. \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% % We really want {...\end verbatim} in the body of the macro, but % without the active space; thus we have to use \xdef and \gobble. \endgroup % \envdef\verbatim{% \setupverbatim\doverbatim } \let\Everbatim = \afterenvbreak % @verbatiminclude FILE - insert text of file in verbatim environment. % \def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} % \def\doverbatiminclude#1{% {% \makevalueexpandable \setupverbatim \indexnofonts % Allow `@@' and other weird things in file names. \wlog{texinfo.tex: doing @verbatiminclude of #1^^J}% \input #1 \afterenvbreak }% } % @copying ... @end copying. % Save the text away for @insertcopying later. % % We save the uninterpreted tokens, rather than creating a box. % Saving the text in a box would be much easier, but then all the % typesetting commands (@smallbook, font changes, etc.) have to be done % beforehand -- and a) we want @copying to be done first in the source % file; b) letting users define the frontmatter in as flexible order as % possible is very desirable. % \def\copying{\checkenv{}\begingroup\scanargctxt\docopying} \def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} % \def\insertcopying{% \begingroup \parindent = 0pt % paragraph indentation looks wrong on title page \scanexp\copyingtext \endgroup } \message{defuns,} % @defun etc. \newskip\defbodyindent \defbodyindent=.4in \newskip\defargsindent \defargsindent=50pt \newskip\deflastargmargin \deflastargmargin=18pt \newcount\defunpenalty % Start the processing of @deffn: \def\startdefun{% \ifnum\lastpenalty<10000 \medbreak \defunpenalty=10003 % Will keep this @deffn together with the % following @def command, see below. \else % If there are two @def commands in a row, we'll have a \nobreak, % which is there to keep the function description together with its % header. But if there's nothing but headers, we need to allow a % break somewhere. Check specifically for penalty 10002, inserted % by \printdefunline, instead of 10000, since the sectioning % commands also insert a nobreak penalty, and we don't want to allow % a break between a section heading and a defun. % % As a further refinement, we avoid "club" headers by signalling % with penalty of 10003 after the very first @deffn in the % sequence (see above), and penalty of 10002 after any following % @def command. \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi % % Similarly, after a section heading, do not allow a break. % But do insert the glue. \medskip % preceded by discardable penalty, so not a breakpoint \fi % \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent } \def\dodefunx#1{% % First, check whether we are in the right environment: \checkenv#1% % % As above, allow line break if we have multiple x headers in a row. % It's not a great place, though. \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi % % And now, it's time to reuse the body of the original defun: \expandafter\gobbledefun#1% } \def\gobbledefun#1\startdefun{} % \printdefunline \deffnheader{text} % \def\printdefunline#1#2{% \begingroup % call \deffnheader: #1#2 \endheader % common ending: \interlinepenalty = 10000 \advance\rightskip by 0pt plus 1fil\relax \endgraf \nobreak\vskip -\parskip \penalty\defunpenalty % signal to \startdefun and \dodefunx % Some of the @defun-type tags do not enable magic parentheses, % rendering the following check redundant. But we don't optimize. \checkparencounts \endgroup } \def\Edefun{\endgraf\medbreak} % \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; % the only thing remaining is to define \deffnheader. % \def\makedefun#1{% \expandafter\let\csname E#1\endcsname = \Edefun \edef\temp{\noexpand\domakedefun \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% \temp } % \domakedefun \deffn \deffnx \deffnheader % % Define \deffn and \deffnx, without parameters. % \deffnheader has to be defined explicitly. % \def\domakedefun#1#2#3{% \envdef#1{% \startdefun \doingtypefnfalse % distinguish typed functions from all else \parseargusing\activeparens{\printdefunline#3}% }% \def#2{\dodefunx#1}% \def#3% } \newif\ifdoingtypefn % doing typed function? \newif\ifrettypeownline % typeset return type on its own line? % @deftypefnnewline on|off says whether the return type of typed functions % are printed on their own line. This affects @deftypefn, @deftypefun, % @deftypeop, and @deftypemethod. % \parseargdef\deftypefnnewline{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETtxideftypefnnl\endcsname = \empty \else\ifx\temp\offword \expandafter\let\csname SETtxideftypefnnl\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @txideftypefnnl value `\temp', must be on|off}% \fi\fi } % Untyped functions: % @deffn category name args \makedefun{deffn}{\deffngeneral{}} % @deffn category class name args \makedefun{defop}#1 {\defopon{#1\ \putwordon}} % \defopon {category on}class name args \def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } % \deffngeneral {subind}category name args % \def\deffngeneral#1#2 #3 #4\endheader{% % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}. \dosubind{fn}{\code{#3}}{#1}% \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% } % Typed functions: % @deftypefn category type name args \makedefun{deftypefn}{\deftypefngeneral{}} % @deftypeop category class type name args \makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} % \deftypeopon {category on}class type name args \def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } % \deftypefngeneral {subind}category type name args % \def\deftypefngeneral#1#2 #3 #4 #5\endheader{% \dosubind{fn}{\code{#4}}{#1}% \doingtypefntrue \defname{#2}{#3}{#4}\defunargs{#5\unskip}% } % Typed variables: % @deftypevr category type var args \makedefun{deftypevr}{\deftypecvgeneral{}} % @deftypecv category class type var args \makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} % \deftypecvof {category of}class type var args \def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } % \deftypecvgeneral {subind}category type var args % \def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% \dosubind{vr}{\code{#4}}{#1}% \defname{#2}{#3}{#4}\defunargs{#5\unskip}% } % Untyped variables: % @defvr category var args \makedefun{defvr}#1 {\deftypevrheader{#1} {} } % @defcv category class var args \makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} % \defcvof {category of}class var args \def\defcvof#1#2 {\deftypecvof{#1}#2 {} } % Types: % @deftp category name args \makedefun{deftp}#1 #2 #3\endheader{% \doind{tp}{\code{#2}}% \defname{#1}{}{#2}\defunargs{#3\unskip}% } % Remaining @defun-like shortcuts: \makedefun{defun}{\deffnheader{\putwordDeffunc} } \makedefun{defmac}{\deffnheader{\putwordDefmac} } \makedefun{defspec}{\deffnheader{\putwordDefspec} } \makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } \makedefun{defvar}{\defvrheader{\putwordDefvar} } \makedefun{defopt}{\defvrheader{\putwordDefopt} } \makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } \makedefun{defmethod}{\defopon\putwordMethodon} \makedefun{deftypemethod}{\deftypeopon\putwordMethodon} \makedefun{defivar}{\defcvof\putwordInstanceVariableof} \makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} % \defname, which formats the name of the @def (not the args). % #1 is the category, such as "Function". % #2 is the return type, if any. % #3 is the function name. % % We are followed by (but not passed) the arguments, if any. % \def\defname#1#2#3{% \par % Get the values of \leftskip and \rightskip as they were outside the @def... \advance\leftskip by -\defbodyindent % % Determine if we are typesetting the return type of a typed function % on a line by itself. \rettypeownlinefalse \ifdoingtypefn % doing a typed function specifically? % then check user option for putting return type on its own line: \expandafter\ifx\csname SETtxideftypefnnl\endcsname\relax \else \rettypeownlinetrue \fi \fi % % How we'll format the category name. Putting it in brackets helps % distinguish it from the body text that may end up on the next line % just below it. \def\temp{#1}% \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} % % Figure out line sizes for the paragraph shape. We'll always have at % least two. \tempnum = 2 % % The first line needs space for \box0; but if \rightskip is nonzero, % we need only space for the part of \box0 which exceeds it: \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip % % If doing a return type on its own line, we'll have another line. \ifrettypeownline \advance\tempnum by 1 \def\maybeshapeline{0in \hsize}% \else \def\maybeshapeline{}% \fi % % The continuations: \dimen2=\hsize \advance\dimen2 by -\defargsindent % % The final paragraph shape: \parshape \tempnum 0in \dimen0 \maybeshapeline \defargsindent \dimen2 % % Put the category name at the right margin. \noindent \hbox to 0pt{% \hfil\box0 \kern-\hsize % \hsize has to be shortened this way: \kern\leftskip % Intentionally do not respect \rightskip, since we need the space. }% % % Allow all lines to be underfull without complaint: \tolerance=10000 \hbadness=10000 \exdentamount=\defbodyindent {% % defun fonts. We use typewriter by default (used to be bold) because: % . we're printing identifiers, they should be in tt in principle. % . in languages with many accents, such as Czech or French, it's % common to leave accents off identifiers. The result looks ok in % tt, but exceedingly strange in rm. % . we don't want -- and --- to be treated as ligatures. % . this still does not fix the ?` and !` ligatures, but so far no % one has made identifiers using them :). \df \tt \def\temp{#2}% text of the return type \ifx\temp\empty\else \tclose{\temp}% typeset the return type \ifrettypeownline % put return type on its own line; prohibit line break following: \hfil\vadjust{\nobreak}\break \else \space % type on same line, so just followed by a space \fi \fi % no return type #3% output function name }% {\rm\enskip}% hskip 0.5 em of \tenrm % \boldbrax % arguments will be output next, if any. } % Print arguments in slanted roman (not ttsl), inconsistently with using % tt for the name. This is because literal text is sometimes needed in % the argument list (groff manual), and ttsl and tt are not very % distinguishable. Prevent hyphenation at `-' chars. % \def\defunargs#1{% % use sl by default (not ttsl), % tt for the names. \df \sl \hyphenchar\font=0 % % On the other hand, if an argument has two dashes (for instance), we % want a way to get ttsl. We used to recommend @var for that, so % leave the code in, but it's strange for @var to lead to typewriter. % Nowadays we recommend @code, since the difference between a ttsl hyphen % and a tt hyphen is pretty tiny. @code also disables ?` !`. \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}% #1% \sl\hyphenchar\font=45 } % We want ()&[] to print specially on the defun line. % \def\activeparens{% \catcode`\(=\active \catcode`\)=\active \catcode`\[=\active \catcode`\]=\active \catcode`\&=\active } % Make control sequences which act like normal parenthesis chars. \let\lparen = ( \let\rparen = ) % Be sure that we always have a definition for `(', etc. For example, % if the fn name has parens in it, \boldbrax will not be in effect yet, % so TeX would otherwise complain about undefined control sequence. { \activeparens \global\let(=\lparen \global\let)=\rparen \global\let[=\lbrack \global\let]=\rbrack \global\let& = \& \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} \gdef\magicamp{\let&=\amprm} } \newcount\parencount % If we encounter &foo, then turn on ()-hacking afterwards \newif\ifampseen \def\amprm#1 {\ampseentrue{\bf\ }} \def\parenfont{% \ifampseen % At the first level, print parens in roman, % otherwise use the default font. \ifnum \parencount=1 \rm \fi \else % The \sf parens (in \boldbrax) actually are a little bolder than % the contained text. This is especially needed for [ and ] . \sf \fi } \def\infirstlevel#1{% \ifampseen \ifnum\parencount=1 #1% \fi \fi } \def\bfafterword#1 {#1 \bf} \def\opnr{% \global\advance\parencount by 1 {\parenfont(}% \infirstlevel \bfafterword } \def\clnr{% {\parenfont)}% \infirstlevel \sl \global\advance\parencount by -1 } \newcount\brackcount \def\lbrb{% \global\advance\brackcount by 1 {\bf[}% } \def\rbrb{% {\bf]}% \global\advance\brackcount by -1 } \def\checkparencounts{% \ifnum\parencount=0 \else \badparencount \fi \ifnum\brackcount=0 \else \badbrackcount \fi } % these should not use \errmessage; the glibc manual, at least, actually % has such constructs (when documenting function pointers). \def\badparencount{% \message{Warning: unbalanced parentheses in @def...}% \global\parencount=0 } \def\badbrackcount{% \message{Warning: unbalanced square brackets in @def...}% \global\brackcount=0 } \message{macros,} % @macro. % To do this right we need a feature of e-TeX, \scantokens, % which we arrange to emulate with a temporary file in ordinary TeX. \ifx\eTeXversion\thisisundefined \newwrite\macscribble \def\scantokens#1{% \toks0={#1}% \immediate\openout\macscribble=\jobname.tmp \immediate\write\macscribble{\the\toks0}% \immediate\closeout\macscribble \input \jobname.tmp } \fi \def\scanmacro#1{\begingroup \newlinechar`\^^M \let\xeatspaces\eatspaces % % Undo catcode changes of \startcontents and \doprintindex % When called from @insertcopying or (short)caption, we need active % backslash to get it printed correctly. Previously, we had % \catcode`\\=\other instead. We'll see whether a problem appears % with macro expansion. --kasal, 19aug04 \catcode`\@=0 \catcode`\\=\active \escapechar=`\@ % % ... and for \example: \spaceisspace % % The \empty here causes a following catcode 5 newline to be eaten as % part of reading whitespace after a control sequence. It does not % eat a catcode 13 newline. There's no good way to handle the two % cases (untried: maybe e-TeX's \everyeof could help, though plain TeX % would then have different behavior). See the Macro Details node in % the manual for the workaround we recommend for macros and % line-oriented commands. % \scantokens{#1\empty}% \endgroup} \def\scanexp#1{% \edef\temp{\noexpand\scanmacro{#1}}% \temp } \newcount\paramno % Count of parameters \newtoks\macname % Macro name \newif\ifrecursive % Is it recursive? % List of all defined macros in the form % \definedummyword\macro1\definedummyword\macro2... % Currently is also contains all @aliases; the list can be split % if there is a need. \def\macrolist{} % Add the macro to \macrolist \def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} \def\addtomacrolistxxx#1{% \toks0 = \expandafter{\macrolist\definedummyword#1}% \xdef\macrolist{\the\toks0}% } % Utility routines. % This does \let #1 = #2, with \csnames; that is, % \let \csname#1\endcsname = \csname#2\endcsname % (except of course we have to play expansion games). % \def\cslet#1#2{% \expandafter\let \csname#1\expandafter\endcsname \csname#2\endcsname } % Trim leading and trailing spaces off a string. % Concepts from aro-bend problem 15 (see CTAN). {\catcode`\@=11 \gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} \gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} \gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} \def\unbrace#1{#1} \unbrace{\gdef\trim@@@ #1 } #2@{#1} } % Trim a single trailing ^^M off a string. {\catcode`\^^M=\other \catcode`\Q=3% \gdef\eatcr #1{\eatcra #1Q^^MQ}% \gdef\eatcra#1^^MQ{\eatcrb#1Q}% \gdef\eatcrb#1Q#2Q{#1}% } % Macro bodies are absorbed as an argument in a context where % all characters are catcode 10, 11 or 12, except \ which is active % (as in normal texinfo). It is necessary to change the definition of \ % to recognize macro arguments; this is the job of \mbodybackslash. % % Non-ASCII encodings make 8-bit characters active, so un-activate % them to avoid their expansion. Must do this non-globally, to % confine the change to the current group. % % It's necessary to have hard CRs when the macro is executed. This is % done by making ^^M (\endlinechar) catcode 12 when reading the macro % body, and then making it the \newlinechar in \scanmacro. % \def\scanctxt{% used as subroutine \catcode`\"=\other \catcode`\+=\other \catcode`\<=\other \catcode`\>=\other \catcode`\@=\other \catcode`\^=\other \catcode`\_=\other \catcode`\|=\other \catcode`\~=\other \ifx\declaredencoding\ascii \else \setnonasciicharscatcodenonglobal\other \fi } \def\scanargctxt{% used for copying and captions, not macros. \scanctxt \catcode`\\=\other \catcode`\^^M=\other } \def\macrobodyctxt{% used for @macro definitions \scanctxt \catcode`\{=\other \catcode`\}=\other \catcode`\^^M=\other \usembodybackslash } \def\macroargctxt{% used when scanning invocations \scanctxt \catcode`\\=0 } % why catcode 0 for \ in the above? To recognize \\ \{ \} as "escapes" % for the single characters \ { }. Thus, we end up with the "commands" % that would be written @\ @{ @} in a Texinfo document. % % We already have @{ and @}. For @\, we define it here, and only for % this purpose, to produce a typewriter backslash (so, the @\ that we % define for @math can't be used with @macro calls): % \def\\{\normalbackslash}% % % We would like to do this for \, too, since that is what makeinfo does. % But it is not possible, because Texinfo already has a command @, for a % cedilla accent. Documents must use @comma{} instead. % % \anythingelse will almost certainly be an error of some kind. % \mbodybackslash is the definition of \ in @macro bodies. % It maps \foo\ => \csname macarg.foo\endcsname => #N % where N is the macro parameter number. % We define \csname macarg.\endcsname to be \realbackslash, so % \\ in macro replacement text gets you a backslash. % {\catcode`@=0 @catcode`@\=@active @gdef@usembodybackslash{@let\=@mbodybackslash} @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} } \expandafter\def\csname macarg.\endcsname{\realbackslash} \def\margbackslash#1{\char`\#1 } \def\macro{\recursivefalse\parsearg\macroxxx} \def\rmacro{\recursivetrue\parsearg\macroxxx} \def\macroxxx#1{% \getargs{#1}% now \macname is the macname and \argl the arglist \ifx\argl\empty % no arguments \paramno=0\relax \else \expandafter\parsemargdef \argl;% \if\paramno>256\relax \ifx\eTeXversion\thisisundefined \errhelp = \EMsimple \errmessage{You need eTeX to compile a file with macros with more than 256 arguments} \fi \fi \fi \if1\csname ismacro.\the\macname\endcsname \message{Warning: redefining \the\macname}% \else \expandafter\ifx\csname \the\macname\endcsname \relax \else \errmessage{Macro name \the\macname\space already defined}\fi \global\cslet{macsave.\the\macname}{\the\macname}% \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% \addtomacrolist{\the\macname}% \fi \begingroup \macrobodyctxt \ifrecursive \expandafter\parsermacbody \else \expandafter\parsemacbody \fi} \parseargdef\unmacro{% \if1\csname ismacro.#1\endcsname \global\cslet{#1}{macsave.#1}% \global\expandafter\let \csname ismacro.#1\endcsname=0% % Remove the macro name from \macrolist: \begingroup \expandafter\let\csname#1\endcsname \relax \let\definedummyword\unmacrodo \xdef\macrolist{\macrolist}% \endgroup \else \errmessage{Macro #1 not defined}% \fi } % Called by \do from \dounmacro on each macro. The idea is to omit any % macro definitions that have been changed to \relax. % \def\unmacrodo#1{% \ifx #1\relax % remove this \else \noexpand\definedummyword \noexpand#1% \fi } % This makes use of the obscure feature that if the last token of a % <parameter list> is #, then the preceding argument is delimited by % an opening brace, and that opening brace is not consumed. \def\getargs#1{\getargsxxx#1{}} \def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} \def\getmacname#1 #2\relax{\macname={#1}} \def\getmacargs#1{\def\argl{#1}} % For macro processing make @ a letter so that we can make Texinfo private macro names. \edef\texiatcatcode{\the\catcode`\@} \catcode `@=11\relax % Parse the optional {params} list. Set up \paramno and \paramlist % so \defmacro knows what to do. Define \macarg.BLAH for each BLAH % in the params list to some hook where the argument si to be expanded. If % there are less than 10 arguments that hook is to be replaced by ##N where N % is the position in that list, that is to say the macro arguments are to be % defined `a la TeX in the macro body. % % That gets used by \mbodybackslash (above). % % We need to get `macro parameter char #' into several definitions. % The technique used is stolen from LaTeX: let \hash be something % unexpandable, insert that wherever you need a #, and then redefine % it to # just before using the token list produced. % % The same technique is used to protect \eatspaces till just before % the macro is used. % % If there are 10 or more arguments, a different technique is used, where the % hook remains in the body, and when macro is to be expanded the body is % processed again to replace the arguments. % % In that case, the hook is \the\toks N-1, and we simply set \toks N-1 to the % argument N value and then \edef the body (nothing else will expand because of % the catcode regime underwhich the body was input). % % If you compile with TeX (not eTeX), and you have macros with 10 or more % arguments, you need that no macro has more than 256 arguments, otherwise an % error is produced. \def\parsemargdef#1;{% \paramno=0\def\paramlist{}% \let\hash\relax \let\xeatspaces\relax \parsemargdefxxx#1,;,% % In case that there are 10 or more arguments we parse again the arguments % list to set new definitions for the \macarg.BLAH macros corresponding to % each BLAH argument. It was anyhow needed to parse already once this list % in order to count the arguments, and as macros with at most 9 arguments % are by far more frequent than macro with 10 or more arguments, defining % twice the \macarg.BLAH macros does not cost too much processing power. \ifnum\paramno<10\relax\else \paramno0\relax \parsemmanyargdef@@#1,;,% 10 or more arguments \fi } \def\parsemargdefxxx#1,{% \if#1;\let\next=\relax \else \let\next=\parsemargdefxxx \advance\paramno by 1 \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname {\xeatspaces{\hash\the\paramno}}% \edef\paramlist{\paramlist\hash\the\paramno,}% \fi\next} \def\parsemmanyargdef@@#1,{% \if#1;\let\next=\relax \else \let\next=\parsemmanyargdef@@ \edef\tempb{\eatspaces{#1}}% \expandafter\def\expandafter\tempa \expandafter{\csname macarg.\tempb\endcsname}% % Note that we need some extra \noexpand\noexpand, this is because we % don't want \the to be expanded in the \parsermacbody as it uses an % \xdef . \expandafter\edef\tempa {\noexpand\noexpand\noexpand\the\toks\the\paramno}% \advance\paramno by 1\relax \fi\next} % These two commands read recursive and nonrecursive macro bodies. % (They're different since rec and nonrec macros end differently.) % \catcode `\@\texiatcatcode \long\def\parsemacbody#1@end macro% {\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% \long\def\parsermacbody#1@end rmacro% {\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% \catcode `\@=11\relax \let\endargs@\relax \let\nil@\relax \def\nilm@{\nil@}% \long\def\nillm@{\nil@}% % This macro is expanded during the Texinfo macro expansion, not during its % definition. It gets all the arguments values and assigns them to macros % macarg.ARGNAME % % #1 is the macro name % #2 is the list of argument names % #3 is the list of argument values \def\getargvals@#1#2#3{% \def\macargdeflist@{}% \def\saveparamlist@{#2}% Need to keep a copy for parameter expansion. \def\paramlist{#2,\nil@}% \def\macroname{#1}% \begingroup \macroargctxt \def\argvaluelist{#3,\nil@}% \def\@tempa{#3}% \ifx\@tempa\empty \setemptyargvalues@ \else \getargvals@@ \fi } % \def\getargvals@@{% \ifx\paramlist\nilm@ % Some sanity check needed here that \argvaluelist is also empty. \ifx\argvaluelist\nillm@ \else \errhelp = \EMsimple \errmessage{Too many arguments in macro `\macroname'!}% \fi \let\next\macargexpandinbody@ \else \ifx\argvaluelist\nillm@ % No more arguments values passed to macro. Set remaining named-arg % macros to empty. \let\next\setemptyargvalues@ \else % pop current arg name into \@tempb \def\@tempa##1{\pop@{\@tempb}{\paramlist}##1\endargs@}% \expandafter\@tempa\expandafter{\paramlist}% % pop current argument value into \@tempc \def\@tempa##1{\longpop@{\@tempc}{\argvaluelist}##1\endargs@}% \expandafter\@tempa\expandafter{\argvaluelist}% % Here \@tempb is the current arg name and \@tempc is the current arg value. % First place the new argument macro definition into \@tempd \expandafter\macname\expandafter{\@tempc}% \expandafter\let\csname macarg.\@tempb\endcsname\relax \expandafter\def\expandafter\@tempe\expandafter{% \csname macarg.\@tempb\endcsname}% \edef\@tempd{\long\def\@tempe{\the\macname}}% \push@\@tempd\macargdeflist@ \let\next\getargvals@@ \fi \fi \next } \def\push@#1#2{% \expandafter\expandafter\expandafter\def \expandafter\expandafter\expandafter#2% \expandafter\expandafter\expandafter{% \expandafter#1#2}% } % Replace arguments by their values in the macro body, and place the result % in macro \@tempa \def\macvalstoargs@{% % To do this we use the property that token registers that are \the'ed % within an \edef expand only once. So we are going to place all argument % values into respective token registers. % % First we save the token context, and initialize argument numbering. \begingroup \paramno0\relax % Then, for each argument number #N, we place the corresponding argument % value into a new token list register \toks#N \expandafter\putargsintokens@\saveparamlist@,;,% % Then, we expand the body so that argument are replaced by their % values. The trick for values not to be expanded themselves is that they % are within tokens and that tokens expand only once in an \edef . \edef\@tempc{\csname mac.\macroname .body\endcsname}% % Now we restore the token stack pointer to free the token list registers % which we have used, but we make sure that expanded body is saved after % group. \expandafter \endgroup \expandafter\def\expandafter\@tempa\expandafter{\@tempc}% } \def\macargexpandinbody@{% %% Define the named-macro outside of this group and then close this group. \expandafter \endgroup \macargdeflist@ % First the replace in body the macro arguments by their values, the result % is in \@tempa . \macvalstoargs@ % Then we point at the \norecurse or \gobble (for recursive) macro value % with \@tempb . \expandafter\let\expandafter\@tempb\csname mac.\macroname .recurse\endcsname % Depending on whether it is recursive or not, we need some tailing % \egroup . \ifx\@tempb\gobble \let\@tempc\relax \else \let\@tempc\egroup \fi % And now we do the real job: \edef\@tempd{\noexpand\@tempb{\macroname}\noexpand\scanmacro{\@tempa}\@tempc}% \@tempd } \def\putargsintokens@#1,{% \if#1;\let\next\relax \else \let\next\putargsintokens@ % First we allocate the new token list register, and give it a temporary % alias \@tempb . \toksdef\@tempb\the\paramno % Then we place the argument value into that token list register. \expandafter\let\expandafter\@tempa\csname macarg.#1\endcsname \expandafter\@tempb\expandafter{\@tempa}% \advance\paramno by 1\relax \fi \next } % Save the token stack pointer into macro #1 \def\texisavetoksstackpoint#1{\edef#1{\the\@cclvi}} % Restore the token stack pointer from number in macro #1 \def\texirestoretoksstackpoint#1{\expandafter\mathchardef\expandafter\@cclvi#1\relax} % newtoks that can be used non \outer . \def\texinonouternewtoks{\alloc@ 5\toks \toksdef \@cclvi} % Tailing missing arguments are set to empty \def\setemptyargvalues@{% \ifx\paramlist\nilm@ \let\next\macargexpandinbody@ \else \expandafter\setemptyargvaluesparser@\paramlist\endargs@ \let\next\setemptyargvalues@ \fi \next } \def\setemptyargvaluesparser@#1,#2\endargs@{% \expandafter\def\expandafter\@tempa\expandafter{% \expandafter\def\csname macarg.#1\endcsname{}}% \push@\@tempa\macargdeflist@ \def\paramlist{#2}% } % #1 is the element target macro % #2 is the list macro % #3,#4\endargs@ is the list value \def\pop@#1#2#3,#4\endargs@{% \def#1{#3}% \def#2{#4}% } \long\def\longpop@#1#2#3,#4\endargs@{% \long\def#1{#3}% \long\def#2{#4}% } % This defines a Texinfo @macro. There are eight cases: recursive and % nonrecursive macros of zero, one, up to nine, and many arguments. % Much magic with \expandafter here. % \xdef is used so that macro definitions will survive the file % they're defined in; @include reads the file inside a group. % \def\defmacro{% \let\hash=##% convert placeholders to macro parameter chars \ifrecursive \ifcase\paramno % 0 \expandafter\xdef\csname\the\macname\endcsname{% \noexpand\scanmacro{\temp}}% \or % 1 \expandafter\xdef\csname\the\macname\endcsname{% \bgroup\noexpand\macroargctxt \noexpand\braceorline \expandafter\noexpand\csname\the\macname xxx\endcsname}% \expandafter\xdef\csname\the\macname xxx\endcsname##1{% \egroup\noexpand\scanmacro{\temp}}% \else \ifnum\paramno<10\relax % at most 9 \expandafter\xdef\csname\the\macname\endcsname{% \bgroup\noexpand\macroargctxt \noexpand\csname\the\macname xx\endcsname}% \expandafter\xdef\csname\the\macname xx\endcsname##1{% \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% \expandafter\expandafter \expandafter\xdef \expandafter\expandafter \csname\the\macname xxx\endcsname \paramlist{\egroup\noexpand\scanmacro{\temp}}% \else % 10 or more \expandafter\xdef\csname\the\macname\endcsname{% \noexpand\getargvals@{\the\macname}{\argl}% }% \global\expandafter\let\csname mac.\the\macname .body\endcsname\temp \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble \fi \fi \else \ifcase\paramno % 0 \expandafter\xdef\csname\the\macname\endcsname{% \noexpand\norecurse{\the\macname}% \noexpand\scanmacro{\temp}\egroup}% \or % 1 \expandafter\xdef\csname\the\macname\endcsname{% \bgroup\noexpand\macroargctxt \noexpand\braceorline \expandafter\noexpand\csname\the\macname xxx\endcsname}% \expandafter\xdef\csname\the\macname xxx\endcsname##1{% \egroup \noexpand\norecurse{\the\macname}% \noexpand\scanmacro{\temp}\egroup}% \else % at most 9 \ifnum\paramno<10\relax \expandafter\xdef\csname\the\macname\endcsname{% \bgroup\noexpand\macroargctxt \expandafter\noexpand\csname\the\macname xx\endcsname}% \expandafter\xdef\csname\the\macname xx\endcsname##1{% \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% \expandafter\expandafter \expandafter\xdef \expandafter\expandafter \csname\the\macname xxx\endcsname \paramlist{% \egroup \noexpand\norecurse{\the\macname}% \noexpand\scanmacro{\temp}\egroup}% \else % 10 or more: \expandafter\xdef\csname\the\macname\endcsname{% \noexpand\getargvals@{\the\macname}{\argl}% }% \global\expandafter\let\csname mac.\the\macname .body\endcsname\temp \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\norecurse \fi \fi \fi} \catcode `\@\texiatcatcode\relax \def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} % \braceorline decides whether the next nonwhitespace character is a % {. If so it reads up to the closing }, if not, it reads the whole % line. Whatever was read is then fed to the next control sequence % as an argument (by \parsebrace or \parsearg). % \def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} \def\braceorlinexxx{% \ifx\nchar\bgroup\else \expandafter\parsearg \fi \macnamexxx} % @alias. % We need some trickery to remove the optional spaces around the equal % sign. Make them active and then expand them all to nothing. % \def\alias{\parseargusing\obeyspaces\aliasxxx} \def\aliasxxx #1{\aliasyyy#1\relax} \def\aliasyyy #1=#2\relax{% {% \expandafter\let\obeyedspace=\empty \addtomacrolist{#1}% \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% }% \next } \message{cross references,} \newwrite\auxfile \newif\ifhavexrefs % True if xref values are known. \newif\ifwarnedxrefs % True if we warned once that they aren't known. % @inforef is relatively simple. \def\inforef #1{\inforefzzz #1,,,,**} \def\inforefzzz #1,#2,#3,#4**{% \putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, node \samp{\ignorespaces#1{}}} % @node's only job in TeX is to define \lastnode, which is used in % cross-references. The @node line might or might not have commas, and % might or might not have spaces before the first comma, like: % @node foo , bar , ... % We don't want such trailing spaces in the node name. % \parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} % % also remove a trailing comma, in case of something like this: % @node Help-Cross, , , Cross-refs \def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} \def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}} \let\nwnode=\node \let\lastnode=\empty % Write a cross-reference definition for the current node. #1 is the % type (Ynumbered, Yappendix, Ynothing). % \def\donoderef#1{% \ifx\lastnode\empty\else \setref{\lastnode}{#1}% \global\let\lastnode=\empty \fi } % @anchor{NAME} -- define xref target at arbitrary point. % \newcount\savesfregister % \def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} \def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} \def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} % \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an % anchor), which consists of three parts: % 1) NAME-title - the current sectioning name taken from \lastsection, % or the anchor name. % 2) NAME-snt - section number and type, passed as the SNT arg, or % empty for anchors. % 3) NAME-pg - the page number. % % This is called from \donoderef, \anchor, and \dofloat. In the case of % floats, there is an additional part, which is not written here: % 4) NAME-lof - the text as it should appear in a @listoffloats. % \def\setref#1#2{% \pdfmkdest{#1}% \iflinks {% \atdummies % preserve commands, but don't expand them \edef\writexrdef##1##2{% \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef ##1}{##2}}% these are parameters of \writexrdef }% \toks0 = \expandafter{\lastsection}% \immediate \writexrdef{title}{\the\toks0 }% \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, at \shipout }% \fi } % @xrefautosectiontitle on|off says whether @section(ing) names are used % automatically in xrefs, if the third arg is not explicitly specified. % This was provided as a "secret" @set xref-automatic-section-title % variable, now it's official. % \parseargdef\xrefautomaticsectiontitle{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETxref-automatic-section-title\endcsname = \empty \else\ifx\temp\offword \expandafter\let\csname SETxref-automatic-section-title\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @xrefautomaticsectiontitle value `\temp', must be on|off}% \fi\fi } % % @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is % the node name, #2 the name of the Info cross-reference, #3 the printed % node name, #4 the name of the Info file, #5 the name of the printed % manual. All but the node name can be omitted. % \def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} \def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} \def\ref#1{\xrefX[#1,,,,,,,]} % \newbox\toprefbox \newbox\printedrefnamebox \newbox\infofilenamebox \newbox\printedmanualbox % \def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup \unsepspaces % % Get args without leading/trailing spaces. \def\printedrefname{\ignorespaces #3}% \setbox\printedrefnamebox = \hbox{\printedrefname\unskip}% % \def\infofilename{\ignorespaces #4}% \setbox\infofilenamebox = \hbox{\infofilename\unskip}% % \def\printedmanual{\ignorespaces #5}% \setbox\printedmanualbox = \hbox{\printedmanual\unskip}% % % If the printed reference name (arg #3) was not explicitly given in % the @xref, figure out what we want to use. \ifdim \wd\printedrefnamebox = 0pt % No printed node name was explicitly given. \expandafter\ifx\csname SETxref-automatic-section-title\endcsname \relax % Not auto section-title: use node name inside the square brackets. \def\printedrefname{\ignorespaces #1}% \else % Auto section-title: use chapter/section title inside % the square brackets if we have it. \ifdim \wd\printedmanualbox > 0pt % It is in another manual, so we don't have it; use node name. \def\printedrefname{\ignorespaces #1}% \else \ifhavexrefs % We (should) know the real title if we have the xref values. \def\printedrefname{\refx{#1-title}{}}% \else % Otherwise just copy the Info node name. \def\printedrefname{\ignorespaces #1}% \fi% \fi \fi \fi % % Make link in pdf output. \ifpdf {\indexnofonts \turnoffactive \makevalueexpandable % This expands tokens, so do it after making catcode changes, so _ % etc. don't get their TeX definitions. This ignores all spaces in % #4, including (wrongly) those in the middle of the filename. \getfilename{#4}% % % This (wrongly) does not take account of leading or trailing % spaces in #1, which should be ignored. \edef\pdfxrefdest{#1}% \ifx\pdfxrefdest\empty \def\pdfxrefdest{Top}% no empty targets \else \txiescapepdf\pdfxrefdest % escape PDF special chars \fi % \leavevmode \startlink attr{/Border [0 0 0]}% \ifnum\filenamelength>0 goto file{\the\filename.pdf} name{\pdfxrefdest}% \else goto name{\pdfmkpgn{\pdfxrefdest}}% \fi }% \setcolor{\linkcolor}% \fi % % Float references are printed completely differently: "Figure 1.2" % instead of "[somenode], p.3". We distinguish them by the % LABEL-title being set to a magic string. {% % Have to otherify everything special to allow the \csname to % include an _ in the xref name, etc. \indexnofonts \turnoffactive \expandafter\global\expandafter\let\expandafter\Xthisreftitle \csname XR#1-title\endcsname }% \iffloat\Xthisreftitle % If the user specified the print name (third arg) to the ref, % print it instead of our usual "Figure 1.2". \ifdim\wd\printedrefnamebox = 0pt \refx{#1-snt}{}% \else \printedrefname \fi % % If the user also gave the printed manual name (fifth arg), append % "in MANUALNAME". \ifdim \wd\printedmanualbox > 0pt \space \putwordin{} \cite{\printedmanual}% \fi \else % node/anchor (non-float) references. % % If we use \unhbox to print the node names, TeX does not insert % empty discretionaries after hyphens, which means that it will not % find a line break at a hyphen in a node names. Since some manuals % are best written with fairly long node names, containing hyphens, % this is a loss. Therefore, we give the text of the node name % again, so it is as if TeX is seeing it for the first time. % \ifdim \wd\printedmanualbox > 0pt % Cross-manual reference with a printed manual name. % \crossmanualxref{\cite{\printedmanual\unskip}}% % \else\ifdim \wd\infofilenamebox > 0pt % Cross-manual reference with only an info filename (arg 4), no % printed manual name (arg 5). This is essentially the same as % the case above; we output the filename, since we have nothing else. % \crossmanualxref{\code{\infofilename\unskip}}% % \else % Reference within this manual. % % _ (for example) has to be the character _ for the purposes of the % control sequence corresponding to the node, but it has to expand % into the usual \leavevmode...\vrule stuff for purposes of % printing. So we \turnoffactive for the \refx-snt, back on for the % printing, back off for the \refx-pg. {\turnoffactive % Only output a following space if the -snt ref is nonempty; for % @unnumbered and @anchor, it won't be. \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi }% % output the `[mynode]' via the macro below so it can be overridden. \xrefprintnodename\printedrefname % % But we always want a comma and a space: ,\space % % output the `page 3'. \turnoffactive \putwordpage\tie\refx{#1-pg}{}% \fi\fi \fi \endlink \endgroup} % Output a cross-manual xref to #1. Used just above (twice). % % Only include the text "Section ``foo'' in" if the foo is neither % missing or Top. Thus, @xref{,,,foo,The Foo Manual} outputs simply % "see The Foo Manual", the idea being to refer to the whole manual. % % But, this being TeX, we can't easily compare our node name against the % string "Top" while ignoring the possible spaces before and after in % the input. By adding the arbitrary 7sp below, we make it much less % likely that a real node name would have the same width as "Top" (e.g., % in a monospaced font). Hopefully it will never happen in practice. % % For the same basic reason, we retypeset the "Top" at every % reference, since the current font is indeterminate. % \def\crossmanualxref#1{% \setbox\toprefbox = \hbox{Top\kern7sp}% \setbox2 = \hbox{\ignorespaces \printedrefname \unskip \kern7sp}% \ifdim \wd2 > 7sp % nonempty? \ifdim \wd2 = \wd\toprefbox \else % same as Top? \putwordSection{} ``\printedrefname'' \putwordin{}\space \fi \fi #1% } % This macro is called from \xrefX for the `[nodename]' part of xref % output. It's a separate macro only so it can be changed more easily, % since square brackets don't work well in some documents. Particularly % one that Bob is working on :). % \def\xrefprintnodename#1{[#1]} % Things referred to by \setref. % \def\Ynothing{} \def\Yomitfromtoc{} \def\Ynumbered{% \ifnum\secno=0 \putwordChapter@tie \the\chapno \else \ifnum\subsecno=0 \putwordSection@tie \the\chapno.\the\secno \else \ifnum\subsubsecno=0 \putwordSection@tie \the\chapno.\the\secno.\the\subsecno \else \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno \fi\fi\fi } \def\Yappendix{% \ifnum\secno=0 \putwordAppendix@tie @char\the\appendixno{}% \else \ifnum\subsecno=0 \putwordSection@tie @char\the\appendixno.\the\secno \else \ifnum\subsubsecno=0 \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno \else \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno \fi\fi\fi } % Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. % If its value is nonempty, SUFFIX is output afterward. % \def\refx#1#2{% {% \indexnofonts \otherbackslash \expandafter\global\expandafter\let\expandafter\thisrefX \csname XR#1\endcsname }% \ifx\thisrefX\relax % If not defined, say something at least. \angleleft un\-de\-fined\angleright \iflinks \ifhavexrefs {\toks0 = {#1}% avoid expansion of possibly-complex value \message{\linenumber Undefined cross reference `\the\toks0'.}}% \else \ifwarnedxrefs\else \global\warnedxrefstrue \message{Cross reference values unknown; you must run TeX again.}% \fi \fi \fi \else % It's defined, so just use it. \thisrefX \fi #2% Output the suffix in any case. } % This is the macro invoked by entries in the aux file. Usually it's % just a \def (we prepend XR to the control sequence name to avoid % collisions). But if this is a float type, we have more work to do. % \def\xrdef#1#2{% {% The node name might contain 8-bit characters, which in our current % implementation are changed to commands like @'e. Don't let these % mess up the control sequence name. \indexnofonts \turnoffactive \xdef\safexrefname{#1}% }% % \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref % % Was that xref control sequence that we just defined for a float? \expandafter\iffloat\csname XR\safexrefname\endcsname % it was a float, and we have the (safe) float type in \iffloattype. \expandafter\let\expandafter\floatlist \csname floatlist\iffloattype\endcsname % % Is this the first time we've seen this float type? \expandafter\ifx\floatlist\relax \toks0 = {\do}% yes, so just \do \else % had it before, so preserve previous elements in list. \toks0 = \expandafter{\floatlist\do}% \fi % % Remember this xref in the control sequence \floatlistFLOATTYPE, % for later use in \listoffloats. \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0 {\safexrefname}}% \fi } % Read the last existing aux file, if any. No error if none exists. % \def\tryauxfile{% \openin 1 \jobname.aux \ifeof 1 \else \readdatafile{aux}% \global\havexrefstrue \fi \closein 1 } \def\setupdatafile{% \catcode`\^^@=\other \catcode`\^^A=\other \catcode`\^^B=\other \catcode`\^^C=\other \catcode`\^^D=\other \catcode`\^^E=\other \catcode`\^^F=\other \catcode`\^^G=\other \catcode`\^^H=\other \catcode`\^^K=\other \catcode`\^^L=\other \catcode`\^^N=\other \catcode`\^^P=\other \catcode`\^^Q=\other \catcode`\^^R=\other \catcode`\^^S=\other \catcode`\^^T=\other \catcode`\^^U=\other \catcode`\^^V=\other \catcode`\^^W=\other \catcode`\^^X=\other \catcode`\^^Z=\other \catcode`\^^[=\other \catcode`\^^\=\other \catcode`\^^]=\other \catcode`\^^^=\other \catcode`\^^_=\other % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc. % in xref tags, i.e., node names. But since ^^e4 notation isn't % supported in the main text, it doesn't seem desirable. Furthermore, % that is not enough: for node names that actually contain a ^ % character, we would end up writing a line like this: 'xrdef {'hat % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first % argument, and \hat is not an expandable control sequence. It could % all be worked out, but why? Either we support ^^ or we don't. % % The other change necessary for this was to define \auxhat: % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter % and then to call \auxhat in \setq. % \catcode`\^=\other % % Special characters. Should be turned off anyway, but... \catcode`\~=\other \catcode`\[=\other \catcode`\]=\other \catcode`\"=\other \catcode`\_=\other \catcode`\|=\other \catcode`\<=\other \catcode`\>=\other \catcode`\$=\other \catcode`\#=\other \catcode`\&=\other \catcode`\%=\other \catcode`+=\other % avoid \+ for paranoia even though we've turned it off % % This is to support \ in node names and titles, since the \ % characters end up in a \csname. It's easier than % leaving it active and making its active definition an actual \ % character. What I don't understand is why it works in the *value* % of the xrdef. Seems like it should be a catcode12 \, and that % should not typeset properly. But it works, so I'm moving on for % now. --karl, 15jan04. \catcode`\\=\other % % Make the characters 128-255 be printing characters. {% \count1=128 \def\loop{% \catcode\count1=\other \advance\count1 by 1 \ifnum \count1<256 \loop \fi }% }% % % @ is our escape character in .aux files, and we need braces. \catcode`\{=1 \catcode`\}=2 \catcode`\@=0 } \def\readdatafile#1{% \begingroup \setupdatafile \input\jobname.#1 \endgroup} \message{insertions,} % including footnotes. \newcount \footnoteno % The trailing space in the following definition for supereject is % vital for proper filling; pages come out unaligned when you do a % pagealignmacro call if that space before the closing brace is % removed. (Generally, numeric constants should always be followed by a % space to prevent strange expansion errors.) \def\supereject{\par\penalty -20000\footnoteno =0 } % @footnotestyle is meaningful for Info output only. \let\footnotestyle=\comment {\catcode `\@=11 % % Auto-number footnotes. Otherwise like plain. \gdef\footnote{% \let\indent=\ptexindent \let\noindent=\ptexnoindent \global\advance\footnoteno by \@ne \edef\thisfootno{$^{\the\footnoteno}$}% % % In case the footnote comes at the end of a sentence, preserve the % extra spacing after we do the footnote number. \let\@sf\empty \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi % % Remove inadvertent blank space before typesetting the footnote number. \unskip \thisfootno\@sf \dofootnote }% % Don't bother with the trickery in plain.tex to not require the % footnote text as a parameter. Our footnotes don't need to be so general. % % Oh yes, they do; otherwise, @ifset (and anything else that uses % \parseargline) fails inside footnotes because the tokens are fixed when % the footnote is read. --karl, 16nov96. % \gdef\dofootnote{% \insert\footins\bgroup % We want to typeset this text as a normal paragraph, even if the % footnote reference occurs in (for example) a display environment. % So reset some parameters. \hsize=\pagewidth \interlinepenalty\interfootnotelinepenalty \splittopskip\ht\strutbox % top baseline for broken footnotes \splitmaxdepth\dp\strutbox \floatingpenalty\@MM \leftskip\z@skip \rightskip\z@skip \spaceskip\z@skip \xspaceskip\z@skip \parindent\defaultparindent % \smallfonts \rm % % Because we use hanging indentation in footnotes, a @noindent appears % to exdent this text, so make it be a no-op. makeinfo does not use % hanging indentation so @noindent can still be needed within footnote % text after an @example or the like (not that this is good style). \let\noindent = \relax % % Hang the footnote text off the number. Use \everypar in case the % footnote extends for more than one paragraph. \everypar = {\hang}% \textindent{\thisfootno}% % % Don't crash into the line above the footnote text. Since this % expands into a box, it must come within the paragraph, lest it % provide a place where TeX can split the footnote. \footstrut % % Invoke rest of plain TeX footnote routine. \futurelet\next\fo@t } }%end \catcode `\@=11 % In case a @footnote appears in a vbox, save the footnote text and create % the real \insert just after the vbox finished. Otherwise, the insertion % would be lost. % Similarly, if a @footnote appears inside an alignment, save the footnote % text to a box and make the \insert when a row of the table is finished. % And the same can be done for other insert classes. --kasal, 16nov03. % Replace the \insert primitive by a cheating macro. % Deeper inside, just make sure that the saved insertions are not spilled % out prematurely. % \def\startsavinginserts{% \ifx \insert\ptexinsert \let\insert\saveinsert \else \let\checkinserts\relax \fi } % This \insert replacement works for both \insert\footins{foo} and % \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. % \def\saveinsert#1{% \edef\next{\noexpand\savetobox \makeSAVEname#1}% \afterassignment\next % swallow the left brace \let\temp = } \def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} \def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} \def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} \def\placesaveins#1{% \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname {\box#1}% } % eat @SAVE -- beware, all of them have catcode \other: { \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) \gdef\gobblesave @SAVE{} } % initialization: \def\newsaveins #1{% \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% \next } \def\newsaveinsX #1{% \csname newbox\endcsname #1% \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts \checksaveins #1}% } % initialize: \let\checkinserts\empty \newsaveins\footins \newsaveins\margin % @image. We use the macros from epsf.tex to support this. % If epsf.tex is not installed and @image is used, we complain. % % Check for and read epsf.tex up front. If we read it only at @image % time, we might be inside a group, and then its definitions would get % undone and the next image would fail. \openin 1 = epsf.tex \ifeof 1 \else % Do not bother showing banner with epsf.tex v2.7k (available in % doc/epsf.tex and on ctan). \def\epsfannounce{\toks0 = }% \input epsf.tex \fi \closein 1 % % We will only complain once about lack of epsf.tex. \newif\ifwarnednoepsf \newhelp\noepsfhelp{epsf.tex must be installed for images to work. It is also included in the Texinfo distribution, or you can get it from ftp://tug.org/tex/epsf.tex.} % \def\image#1{% \ifx\epsfbox\thisisundefined \ifwarnednoepsf \else \errhelp = \noepsfhelp \errmessage{epsf.tex not found, images will be ignored}% \global\warnednoepsftrue \fi \else \imagexxx #1,,,,,\finish \fi } % % Arguments to @image: % #1 is (mandatory) image filename; we tack on .eps extension. % #2 is (optional) width, #3 is (optional) height. % #4 is (ignored optional) html alt text. % #5 is (ignored optional) extension. % #6 is just the usual extra ignored arg for parsing stuff. \newif\ifimagevmode \def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup \catcode`\^^M = 5 % in case we're inside an example \normalturnoffactive % allow _ et al. in names % If the image is by itself, center it. \ifvmode \imagevmodetrue \else \ifx\centersub\centerV % for @center @image, we need a vbox so we can have our vertical space \imagevmodetrue \vbox\bgroup % vbox has better behavior than vtop herev \fi\fi % \ifimagevmode \nobreak\medskip % Usually we'll have text after the image which will insert % \parskip glue, so insert it here too to equalize the space % above and below. \nobreak\vskip\parskip \nobreak \fi % % Leave vertical mode so that indentation from an enclosing % environment such as @quotation is respected. % However, if we're at the top level, we don't want the % normal paragraph indentation. % On the other hand, if we are in the case of @center @image, we don't % want to start a paragraph, which will create a hsize-width box and % eradicate the centering. \ifx\centersub\centerV\else \noindent \fi % % Output the image. \ifpdf \dopdfimage{#1}{#2}{#3}% \else % \epsfbox itself resets \epsf?size at each figure. \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi \epsfbox{#1.eps}% \fi % \ifimagevmode \medskip % space after a standalone image \fi \ifx\centersub\centerV \egroup \fi \endgroup} % @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, % etc. We don't actually implement floating yet, we always include the % float "here". But it seemed the best name for the future. % \envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} % There may be a space before second and/or third parameter; delete it. \def\eatcommaspace#1, {#1,} % #1 is the optional FLOATTYPE, the text label for this float, typically % "Figure", "Table", "Example", etc. Can't contain commas. If omitted, % this float will not be numbered and cannot be referred to. % % #2 is the optional xref label. Also must be present for the float to % be referable. % % #3 is the optional positioning argument; for now, it is ignored. It % will somehow specify the positions allowed to float to (here, top, bottom). % % We keep a separate counter for each FLOATTYPE, which we reset at each % chapter-level command. \let\resetallfloatnos=\empty % \def\dofloat#1,#2,#3,#4\finish{% \let\thiscaption=\empty \let\thisshortcaption=\empty % % don't lose footnotes inside @float. % % BEWARE: when the floats start float, we have to issue warning whenever an % insert appears inside a float which could possibly float. --kasal, 26may04 % \startsavinginserts % % We can't be used inside a paragraph. \par % \vtop\bgroup \def\floattype{#1}% \def\floatlabel{#2}% \def\floatloc{#3}% we do nothing with this yet. % \ifx\floattype\empty \let\safefloattype=\empty \else {% % the floattype might have accents or other special characters, % but we need to use it in a control sequence name. \indexnofonts \turnoffactive \xdef\safefloattype{\floattype}% }% \fi % % If label is given but no type, we handle that as the empty type. \ifx\floatlabel\empty \else % We want each FLOATTYPE to be numbered separately (Figure 1, % Table 1, Figure 2, ...). (And if no label, no number.) % \expandafter\getfloatno\csname\safefloattype floatno\endcsname \global\advance\floatno by 1 % {% % This magic value for \lastsection is output by \setref as the % XREFLABEL-title value. \xrefX uses it to distinguish float % labels (which have a completely different output format) from % node and anchor labels. And \xrdef uses it to construct the % lists of floats. % \edef\lastsection{\floatmagic=\safefloattype}% \setref{\floatlabel}{Yfloat}% }% \fi % % start with \parskip glue, I guess. \vskip\parskip % % Don't suppress indentation if a float happens to start a section. \restorefirstparagraphindent } % we have these possibilities: % @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap % @float Foo,lbl & no caption: Foo 1.1 % @float Foo & @caption{Cap}: Foo: Cap % @float Foo & no caption: Foo % @float ,lbl & Caption{Cap}: 1.1: Cap % @float ,lbl & no caption: 1.1 % @float & @caption{Cap}: Cap % @float & no caption: % \def\Efloat{% \let\floatident = \empty % % In all cases, if we have a float type, it comes first. \ifx\floattype\empty \else \def\floatident{\floattype}\fi % % If we have an xref label, the number comes next. \ifx\floatlabel\empty \else \ifx\floattype\empty \else % if also had float type, need tie first. \appendtomacro\floatident{\tie}% \fi % the number. \appendtomacro\floatident{\chaplevelprefix\the\floatno}% \fi % % Start the printed caption with what we've constructed in % \floatident, but keep it separate; we need \floatident again. \let\captionline = \floatident % \ifx\thiscaption\empty \else \ifx\floatident\empty \else \appendtomacro\captionline{: }% had ident, so need a colon between \fi % % caption text. \appendtomacro\captionline{\scanexp\thiscaption}% \fi % % If we have anything to print, print it, with space before. % Eventually this needs to become an \insert. \ifx\captionline\empty \else \vskip.5\parskip \captionline % % Space below caption. \vskip\parskip \fi % % If have an xref label, write the list of floats info. Do this % after the caption, to avoid chance of it being a breakpoint. \ifx\floatlabel\empty \else % Write the text that goes in the lof to the aux file as % \floatlabel-lof. Besides \floatident, we include the short % caption if specified, else the full caption if specified, else nothing. {% \atdummies % % since we read the caption text in the macro world, where ^^M % is turned into a normal character, we have to scan it back, so % we don't write the literal three characters "^^M" into the aux file. \scanexp{% \xdef\noexpand\gtemp{% \ifx\thisshortcaption\empty \thiscaption \else \thisshortcaption \fi }% }% \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident \ifx\gtemp\empty \else : \gtemp \fi}}% }% \fi \egroup % end of \vtop % % place the captured inserts % % BEWARE: when the floats start floating, we have to issue warning % whenever an insert appears inside a float which could possibly % float. --kasal, 26may04 % \checkinserts } % Append the tokens #2 to the definition of macro #1, not expanding either. % \def\appendtomacro#1#2{% \expandafter\def\expandafter#1\expandafter{#1#2}% } % @caption, @shortcaption % \def\caption{\docaption\thiscaption} \def\shortcaption{\docaption\thisshortcaption} \def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} \def\defcaption#1#2{\egroup \def#1{#2}} % The parameter is the control sequence identifying the counter we are % going to use. Create it if it doesn't exist and assign it to \floatno. \def\getfloatno#1{% \ifx#1\relax % Haven't seen this figure type before. \csname newcount\endcsname #1% % % Remember to reset this floatno at the next chap. \expandafter\gdef\expandafter\resetallfloatnos \expandafter{\resetallfloatnos #1=0 }% \fi \let\floatno#1% } % \setref calls this to get the XREFLABEL-snt value. We want an @xref % to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we % first read the @float command. % \def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% % Magic string used for the XREFLABEL-title value, so \xrefX can % distinguish floats from other xref types. \def\floatmagic{!!float!!} % #1 is the control sequence we are passed; we expand into a conditional % which is true if #1 represents a float ref. That is, the magic % \lastsection value which we \setref above. % \def\iffloat#1{\expandafter\doiffloat#1==\finish} % % #1 is (maybe) the \floatmagic string. If so, #2 will be the % (safe) float type for this float. We set \iffloattype to #2. % \def\doiffloat#1=#2=#3\finish{% \def\temp{#1}% \def\iffloattype{#2}% \ifx\temp\floatmagic } % @listoffloats FLOATTYPE - print a list of floats like a table of contents. % \parseargdef\listoffloats{% \def\floattype{#1}% floattype {% % the floattype might have accents or other special characters, % but we need to use it in a control sequence name. \indexnofonts \turnoffactive \xdef\safefloattype{\floattype}% }% % % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax \ifhavexrefs % if the user said @listoffloats foo but never @float foo. \message{\linenumber No `\safefloattype' floats to list.}% \fi \else \begingroup \leftskip=\tocindent % indent these entries like a toc \let\do=\listoffloatsdo \csname floatlist\safefloattype\endcsname \endgroup \fi } % This is called on each entry in a list of floats. We're passed the % xref label, in the form LABEL-title, which is how we save it in the % aux file. We strip off the -title and look up \XRLABEL-lof, which % has the text we're supposed to typeset here. % % Figures without xref labels will not be included in the list (since % they won't appear in the aux file). % \def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} \def\listoffloatsdoentry#1-title\finish{{% % Can't fully expand XR#1-lof because it can contain anything. Just % pass the control sequence. On the other hand, XR#1-pg is just the % page number, and we want to fully expand that so we can get a link % in pdf output. \toksA = \expandafter{\csname XR#1-lof\endcsname}% % % use the same \entry macro we use to generate the TOC and index. \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% \writeentry }} \message{localization,} % For single-language documents, @documentlanguage is usually given very % early, just after @documentencoding. Single argument is the language % (de) or locale (de_DE) abbreviation. % { \catcode`\_ = \active \globaldefs=1 \parseargdef\documentlanguage{\begingroup \let_=\normalunderscore % normal _ character for filenames \tex % read txi-??.tex file in plain TeX. % Read the file by the name they passed if it exists. \openin 1 txi-#1.tex \ifeof 1 \documentlanguagetrywithoutunderscore{#1_\finish}% \else \globaldefs = 1 % everything in the txi-LL files needs to persist \input txi-#1.tex \fi \closein 1 \endgroup % end raw TeX \endgroup} % % If they passed de_DE, and txi-de_DE.tex doesn't exist, % try txi-de.tex. % \gdef\documentlanguagetrywithoutunderscore#1_#2\finish{% \openin 1 txi-#1.tex \ifeof 1 \errhelp = \nolanghelp \errmessage{Cannot read language file txi-#1.tex}% \else \globaldefs = 1 % everything in the txi-LL files needs to persist \input txi-#1.tex \fi \closein 1 } }% end of special _ catcode % \newhelp\nolanghelp{The given language definition file cannot be found or is empty. Maybe you need to install it? Putting it in the current directory should work if nowhere else does.} % This macro is called from txi-??.tex files; the first argument is the % \language name to set (without the "\lang@" prefix), the second and % third args are \{left,right}hyphenmin. % % The language names to pass are determined when the format is built. % See the etex.log file created at that time, e.g., % /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log. % % With TeX Live 2008, etex now includes hyphenation patterns for all % available languages. This means we can support hyphenation in % Texinfo, at least to some extent. (This still doesn't solve the % accented characters problem.) % \catcode`@=11 \def\txisetlanguage#1#2#3{% % do not set the language if the name is undefined in the current TeX. \expandafter\ifx\csname lang@#1\endcsname \relax \message{no patterns for #1}% \else \global\language = \csname lang@#1\endcsname \fi % but there is no harm in adjusting the hyphenmin values regardless. \global\lefthyphenmin = #2\relax \global\righthyphenmin = #3\relax } % Helpers for encodings. % Set the catcode of characters 128 through 255 to the specified number. % \def\setnonasciicharscatcode#1{% \count255=128 \loop\ifnum\count255<256 \global\catcode\count255=#1\relax \advance\count255 by 1 \repeat } \def\setnonasciicharscatcodenonglobal#1{% \count255=128 \loop\ifnum\count255<256 \catcode\count255=#1\relax \advance\count255 by 1 \repeat } % @documentencoding sets the definition of non-ASCII characters % according to the specified encoding. % \parseargdef\documentencoding{% % Encoding being declared for the document. \def\declaredencoding{\csname #1.enc\endcsname}% % % Supported encodings: names converted to tokens in order to be able % to compare them with \ifx. \def\ascii{\csname US-ASCII.enc\endcsname}% \def\latnine{\csname ISO-8859-15.enc\endcsname}% \def\latone{\csname ISO-8859-1.enc\endcsname}% \def\lattwo{\csname ISO-8859-2.enc\endcsname}% \def\utfeight{\csname UTF-8.enc\endcsname}% % \ifx \declaredencoding \ascii \asciichardefs % \else \ifx \declaredencoding \lattwo \setnonasciicharscatcode\active \lattwochardefs % \else \ifx \declaredencoding \latone \setnonasciicharscatcode\active \latonechardefs % \else \ifx \declaredencoding \latnine \setnonasciicharscatcode\active \latninechardefs % \else \ifx \declaredencoding \utfeight \setnonasciicharscatcode\active \utfeightchardefs % \else \message{Unknown document encoding #1, ignoring.}% % \fi % utfeight \fi % latnine \fi % latone \fi % lattwo \fi % ascii } % A message to be logged when using a character that isn't available % the default font encoding (OT1). % \def\missingcharmsg#1{\message{Character missing in OT1 encoding: #1.}} % Take account of \c (plain) vs. \, (Texinfo) difference. \def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi} % First, make active non-ASCII characters in order for them to be % correctly categorized when TeX reads the replacement text of % macros containing the character definitions. \setnonasciicharscatcode\active % % Latin1 (ISO-8859-1) character definitions. \def\latonechardefs{% \gdef^^a0{\tie} \gdef^^a1{\exclamdown} \gdef^^a2{\missingcharmsg{CENT SIGN}} \gdef^^a3{{\pounds}} \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} \gdef^^a5{\missingcharmsg{YEN SIGN}} \gdef^^a6{\missingcharmsg{BROKEN BAR}} \gdef^^a7{\S} \gdef^^a8{\"{}} \gdef^^a9{\copyright} \gdef^^aa{\ordf} \gdef^^ab{\guillemetleft} \gdef^^ac{$\lnot$} \gdef^^ad{\-} \gdef^^ae{\registeredsymbol} \gdef^^af{\={}} % \gdef^^b0{\textdegree} \gdef^^b1{$\pm$} \gdef^^b2{$^2$} \gdef^^b3{$^3$} \gdef^^b4{\'{}} \gdef^^b5{$\mu$} \gdef^^b6{\P} % \gdef^^b7{$^.$} \gdef^^b8{\cedilla\ } \gdef^^b9{$^1$} \gdef^^ba{\ordm} % \gdef^^bb{\guillemetright} \gdef^^bc{$1\over4$} \gdef^^bd{$1\over2$} \gdef^^be{$3\over4$} \gdef^^bf{\questiondown} % \gdef^^c0{\`A} \gdef^^c1{\'A} \gdef^^c2{\^A} \gdef^^c3{\~A} \gdef^^c4{\"A} \gdef^^c5{\ringaccent A} \gdef^^c6{\AE} \gdef^^c7{\cedilla C} \gdef^^c8{\`E} \gdef^^c9{\'E} \gdef^^ca{\^E} \gdef^^cb{\"E} \gdef^^cc{\`I} \gdef^^cd{\'I} \gdef^^ce{\^I} \gdef^^cf{\"I} % \gdef^^d0{\DH} \gdef^^d1{\~N} \gdef^^d2{\`O} \gdef^^d3{\'O} \gdef^^d4{\^O} \gdef^^d5{\~O} \gdef^^d6{\"O} \gdef^^d7{$\times$} \gdef^^d8{\O} \gdef^^d9{\`U} \gdef^^da{\'U} \gdef^^db{\^U} \gdef^^dc{\"U} \gdef^^dd{\'Y} \gdef^^de{\TH} \gdef^^df{\ss} % \gdef^^e0{\`a} \gdef^^e1{\'a} \gdef^^e2{\^a} \gdef^^e3{\~a} \gdef^^e4{\"a} \gdef^^e5{\ringaccent a} \gdef^^e6{\ae} \gdef^^e7{\cedilla c} \gdef^^e8{\`e} \gdef^^e9{\'e} \gdef^^ea{\^e} \gdef^^eb{\"e} \gdef^^ec{\`{\dotless i}} \gdef^^ed{\'{\dotless i}} \gdef^^ee{\^{\dotless i}} \gdef^^ef{\"{\dotless i}} % \gdef^^f0{\dh} \gdef^^f1{\~n} \gdef^^f2{\`o} \gdef^^f3{\'o} \gdef^^f4{\^o} \gdef^^f5{\~o} \gdef^^f6{\"o} \gdef^^f7{$\div$} \gdef^^f8{\o} \gdef^^f9{\`u} \gdef^^fa{\'u} \gdef^^fb{\^u} \gdef^^fc{\"u} \gdef^^fd{\'y} \gdef^^fe{\th} \gdef^^ff{\"y} } % Latin9 (ISO-8859-15) encoding character definitions. \def\latninechardefs{% % Encoding is almost identical to Latin1. \latonechardefs % \gdef^^a4{\euro} \gdef^^a6{\v S} \gdef^^a8{\v s} \gdef^^b4{\v Z} \gdef^^b8{\v z} \gdef^^bc{\OE} \gdef^^bd{\oe} \gdef^^be{\"Y} } % Latin2 (ISO-8859-2) character definitions. \def\lattwochardefs{% \gdef^^a0{\tie} \gdef^^a1{\ogonek{A}} \gdef^^a2{\u{}} \gdef^^a3{\L} \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} \gdef^^a5{\v L} \gdef^^a6{\'S} \gdef^^a7{\S} \gdef^^a8{\"{}} \gdef^^a9{\v S} \gdef^^aa{\cedilla S} \gdef^^ab{\v T} \gdef^^ac{\'Z} \gdef^^ad{\-} \gdef^^ae{\v Z} \gdef^^af{\dotaccent Z} % \gdef^^b0{\textdegree} \gdef^^b1{\ogonek{a}} \gdef^^b2{\ogonek{ }} \gdef^^b3{\l} \gdef^^b4{\'{}} \gdef^^b5{\v l} \gdef^^b6{\'s} \gdef^^b7{\v{}} \gdef^^b8{\cedilla\ } \gdef^^b9{\v s} \gdef^^ba{\cedilla s} \gdef^^bb{\v t} \gdef^^bc{\'z} \gdef^^bd{\H{}} \gdef^^be{\v z} \gdef^^bf{\dotaccent z} % \gdef^^c0{\'R} \gdef^^c1{\'A} \gdef^^c2{\^A} \gdef^^c3{\u A} \gdef^^c4{\"A} \gdef^^c5{\'L} \gdef^^c6{\'C} \gdef^^c7{\cedilla C} \gdef^^c8{\v C} \gdef^^c9{\'E} \gdef^^ca{\ogonek{E}} \gdef^^cb{\"E} \gdef^^cc{\v E} \gdef^^cd{\'I} \gdef^^ce{\^I} \gdef^^cf{\v D} % \gdef^^d0{\DH} \gdef^^d1{\'N} \gdef^^d2{\v N} \gdef^^d3{\'O} \gdef^^d4{\^O} \gdef^^d5{\H O} \gdef^^d6{\"O} \gdef^^d7{$\times$} \gdef^^d8{\v R} \gdef^^d9{\ringaccent U} \gdef^^da{\'U} \gdef^^db{\H U} \gdef^^dc{\"U} \gdef^^dd{\'Y} \gdef^^de{\cedilla T} \gdef^^df{\ss} % \gdef^^e0{\'r} \gdef^^e1{\'a} \gdef^^e2{\^a} \gdef^^e3{\u a} \gdef^^e4{\"a} \gdef^^e5{\'l} \gdef^^e6{\'c} \gdef^^e7{\cedilla c} \gdef^^e8{\v c} \gdef^^e9{\'e} \gdef^^ea{\ogonek{e}} \gdef^^eb{\"e} \gdef^^ec{\v e} \gdef^^ed{\'{\dotless{i}}} \gdef^^ee{\^{\dotless{i}}} \gdef^^ef{\v d} % \gdef^^f0{\dh} \gdef^^f1{\'n} \gdef^^f2{\v n} \gdef^^f3{\'o} \gdef^^f4{\^o} \gdef^^f5{\H o} \gdef^^f6{\"o} \gdef^^f7{$\div$} \gdef^^f8{\v r} \gdef^^f9{\ringaccent u} \gdef^^fa{\'u} \gdef^^fb{\H u} \gdef^^fc{\"u} \gdef^^fd{\'y} \gdef^^fe{\cedilla t} \gdef^^ff{\dotaccent{}} } % UTF-8 character definitions. % % This code to support UTF-8 is based on LaTeX's utf8.def, with some % changes for Texinfo conventions. It is included here under the GPL by % permission from Frank Mittelbach and the LaTeX team. % \newcount\countUTFx \newcount\countUTFy \newcount\countUTFz \gdef\UTFviiiTwoOctets#1#2{\expandafter \UTFviiiDefined\csname u8:#1\string #2\endcsname} % \gdef\UTFviiiThreeOctets#1#2#3{\expandafter \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname} % \gdef\UTFviiiFourOctets#1#2#3#4{\expandafter \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname} \gdef\UTFviiiDefined#1{% \ifx #1\relax \message{\linenumber Unicode char \string #1 not defined for Texinfo}% \else \expandafter #1% \fi } \begingroup \catcode`\~13 \catcode`\"12 \def\UTFviiiLoop{% \global\catcode\countUTFx\active \uccode`\~\countUTFx \uppercase\expandafter{\UTFviiiTmp}% \advance\countUTFx by 1 \ifnum\countUTFx < \countUTFy \expandafter\UTFviiiLoop \fi} \countUTFx = "C2 \countUTFy = "E0 \def\UTFviiiTmp{% \xdef~{\noexpand\UTFviiiTwoOctets\string~}} \UTFviiiLoop \countUTFx = "E0 \countUTFy = "F0 \def\UTFviiiTmp{% \xdef~{\noexpand\UTFviiiThreeOctets\string~}} \UTFviiiLoop \countUTFx = "F0 \countUTFy = "F4 \def\UTFviiiTmp{% \xdef~{\noexpand\UTFviiiFourOctets\string~}} \UTFviiiLoop \endgroup \begingroup \catcode`\"=12 \catcode`\<=12 \catcode`\.=12 \catcode`\,=12 \catcode`\;=12 \catcode`\!=12 \catcode`\~=13 \gdef\DeclareUnicodeCharacter#1#2{% \countUTFz = "#1\relax %\wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}% \begingroup \parseXMLCharref \def\UTFviiiTwoOctets##1##2{% \csname u8:##1\string ##2\endcsname}% \def\UTFviiiThreeOctets##1##2##3{% \csname u8:##1\string ##2\string ##3\endcsname}% \def\UTFviiiFourOctets##1##2##3##4{% \csname u8:##1\string ##2\string ##3\string ##4\endcsname}% \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter \gdef\UTFviiiTmp{#2}% \endgroup} \gdef\parseXMLCharref{% \ifnum\countUTFz < "A0\relax \errhelp = \EMsimple \errmessage{Cannot define Unicode char value < 00A0}% \else\ifnum\countUTFz < "800\relax \parseUTFviiiA,% \parseUTFviiiB C\UTFviiiTwoOctets.,% \else\ifnum\countUTFz < "10000\relax \parseUTFviiiA;% \parseUTFviiiA,% \parseUTFviiiB E\UTFviiiThreeOctets.{,;}% \else \parseUTFviiiA;% \parseUTFviiiA,% \parseUTFviiiA!% \parseUTFviiiB F\UTFviiiFourOctets.{!,;}% \fi\fi\fi } \gdef\parseUTFviiiA#1{% \countUTFx = \countUTFz \divide\countUTFz by 64 \countUTFy = \countUTFz \multiply\countUTFz by 64 \advance\countUTFx by -\countUTFz \advance\countUTFx by 128 \uccode `#1\countUTFx \countUTFz = \countUTFy} \gdef\parseUTFviiiB#1#2#3#4{% \advance\countUTFz by "#10\relax \uccode `#3\countUTFz \uppercase{\gdef\UTFviiiTmp{#2#3#4}}} \endgroup \def\utfeightchardefs{% \DeclareUnicodeCharacter{00A0}{\tie} \DeclareUnicodeCharacter{00A1}{\exclamdown} \DeclareUnicodeCharacter{00A3}{\pounds} \DeclareUnicodeCharacter{00A8}{\"{ }} \DeclareUnicodeCharacter{00A9}{\copyright} \DeclareUnicodeCharacter{00AA}{\ordf} \DeclareUnicodeCharacter{00AB}{\guillemetleft} \DeclareUnicodeCharacter{00AD}{\-} \DeclareUnicodeCharacter{00AE}{\registeredsymbol} \DeclareUnicodeCharacter{00AF}{\={ }} \DeclareUnicodeCharacter{00B0}{\ringaccent{ }} \DeclareUnicodeCharacter{00B4}{\'{ }} \DeclareUnicodeCharacter{00B8}{\cedilla{ }} \DeclareUnicodeCharacter{00BA}{\ordm} \DeclareUnicodeCharacter{00BB}{\guillemetright} \DeclareUnicodeCharacter{00BF}{\questiondown} \DeclareUnicodeCharacter{00C0}{\`A} \DeclareUnicodeCharacter{00C1}{\'A} \DeclareUnicodeCharacter{00C2}{\^A} \DeclareUnicodeCharacter{00C3}{\~A} \DeclareUnicodeCharacter{00C4}{\"A} \DeclareUnicodeCharacter{00C5}{\AA} \DeclareUnicodeCharacter{00C6}{\AE} \DeclareUnicodeCharacter{00C7}{\cedilla{C}} \DeclareUnicodeCharacter{00C8}{\`E} \DeclareUnicodeCharacter{00C9}{\'E} \DeclareUnicodeCharacter{00CA}{\^E} \DeclareUnicodeCharacter{00CB}{\"E} \DeclareUnicodeCharacter{00CC}{\`I} \DeclareUnicodeCharacter{00CD}{\'I} \DeclareUnicodeCharacter{00CE}{\^I} \DeclareUnicodeCharacter{00CF}{\"I} \DeclareUnicodeCharacter{00D0}{\DH} \DeclareUnicodeCharacter{00D1}{\~N} \DeclareUnicodeCharacter{00D2}{\`O} \DeclareUnicodeCharacter{00D3}{\'O} \DeclareUnicodeCharacter{00D4}{\^O} \DeclareUnicodeCharacter{00D5}{\~O} \DeclareUnicodeCharacter{00D6}{\"O} \DeclareUnicodeCharacter{00D8}{\O} \DeclareUnicodeCharacter{00D9}{\`U} \DeclareUnicodeCharacter{00DA}{\'U} \DeclareUnicodeCharacter{00DB}{\^U} \DeclareUnicodeCharacter{00DC}{\"U} \DeclareUnicodeCharacter{00DD}{\'Y} \DeclareUnicodeCharacter{00DE}{\TH} \DeclareUnicodeCharacter{00DF}{\ss} \DeclareUnicodeCharacter{00E0}{\`a} \DeclareUnicodeCharacter{00E1}{\'a} \DeclareUnicodeCharacter{00E2}{\^a} \DeclareUnicodeCharacter{00E3}{\~a} \DeclareUnicodeCharacter{00E4}{\"a} \DeclareUnicodeCharacter{00E5}{\aa} \DeclareUnicodeCharacter{00E6}{\ae} \DeclareUnicodeCharacter{00E7}{\cedilla{c}} \DeclareUnicodeCharacter{00E8}{\`e} \DeclareUnicodeCharacter{00E9}{\'e} \DeclareUnicodeCharacter{00EA}{\^e} \DeclareUnicodeCharacter{00EB}{\"e} \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}} \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}} \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}} \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}} \DeclareUnicodeCharacter{00F0}{\dh} \DeclareUnicodeCharacter{00F1}{\~n} \DeclareUnicodeCharacter{00F2}{\`o} \DeclareUnicodeCharacter{00F3}{\'o} \DeclareUnicodeCharacter{00F4}{\^o} \DeclareUnicodeCharacter{00F5}{\~o} \DeclareUnicodeCharacter{00F6}{\"o} \DeclareUnicodeCharacter{00F8}{\o} \DeclareUnicodeCharacter{00F9}{\`u} \DeclareUnicodeCharacter{00FA}{\'u} \DeclareUnicodeCharacter{00FB}{\^u} \DeclareUnicodeCharacter{00FC}{\"u} \DeclareUnicodeCharacter{00FD}{\'y} \DeclareUnicodeCharacter{00FE}{\th} \DeclareUnicodeCharacter{00FF}{\"y} \DeclareUnicodeCharacter{0100}{\=A} \DeclareUnicodeCharacter{0101}{\=a} \DeclareUnicodeCharacter{0102}{\u{A}} \DeclareUnicodeCharacter{0103}{\u{a}} \DeclareUnicodeCharacter{0104}{\ogonek{A}} \DeclareUnicodeCharacter{0105}{\ogonek{a}} \DeclareUnicodeCharacter{0106}{\'C} \DeclareUnicodeCharacter{0107}{\'c} \DeclareUnicodeCharacter{0108}{\^C} \DeclareUnicodeCharacter{0109}{\^c} \DeclareUnicodeCharacter{0118}{\ogonek{E}} \DeclareUnicodeCharacter{0119}{\ogonek{e}} \DeclareUnicodeCharacter{010A}{\dotaccent{C}} \DeclareUnicodeCharacter{010B}{\dotaccent{c}} \DeclareUnicodeCharacter{010C}{\v{C}} \DeclareUnicodeCharacter{010D}{\v{c}} \DeclareUnicodeCharacter{010E}{\v{D}} \DeclareUnicodeCharacter{0112}{\=E} \DeclareUnicodeCharacter{0113}{\=e} \DeclareUnicodeCharacter{0114}{\u{E}} \DeclareUnicodeCharacter{0115}{\u{e}} \DeclareUnicodeCharacter{0116}{\dotaccent{E}} \DeclareUnicodeCharacter{0117}{\dotaccent{e}} \DeclareUnicodeCharacter{011A}{\v{E}} \DeclareUnicodeCharacter{011B}{\v{e}} \DeclareUnicodeCharacter{011C}{\^G} \DeclareUnicodeCharacter{011D}{\^g} \DeclareUnicodeCharacter{011E}{\u{G}} \DeclareUnicodeCharacter{011F}{\u{g}} \DeclareUnicodeCharacter{0120}{\dotaccent{G}} \DeclareUnicodeCharacter{0121}{\dotaccent{g}} \DeclareUnicodeCharacter{0124}{\^H} \DeclareUnicodeCharacter{0125}{\^h} \DeclareUnicodeCharacter{0128}{\~I} \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}} \DeclareUnicodeCharacter{012A}{\=I} \DeclareUnicodeCharacter{012B}{\={\dotless{i}}} \DeclareUnicodeCharacter{012C}{\u{I}} \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}} \DeclareUnicodeCharacter{0130}{\dotaccent{I}} \DeclareUnicodeCharacter{0131}{\dotless{i}} \DeclareUnicodeCharacter{0132}{IJ} \DeclareUnicodeCharacter{0133}{ij} \DeclareUnicodeCharacter{0134}{\^J} \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}} \DeclareUnicodeCharacter{0139}{\'L} \DeclareUnicodeCharacter{013A}{\'l} \DeclareUnicodeCharacter{0141}{\L} \DeclareUnicodeCharacter{0142}{\l} \DeclareUnicodeCharacter{0143}{\'N} \DeclareUnicodeCharacter{0144}{\'n} \DeclareUnicodeCharacter{0147}{\v{N}} \DeclareUnicodeCharacter{0148}{\v{n}} \DeclareUnicodeCharacter{014C}{\=O} \DeclareUnicodeCharacter{014D}{\=o} \DeclareUnicodeCharacter{014E}{\u{O}} \DeclareUnicodeCharacter{014F}{\u{o}} \DeclareUnicodeCharacter{0150}{\H{O}} \DeclareUnicodeCharacter{0151}{\H{o}} \DeclareUnicodeCharacter{0152}{\OE} \DeclareUnicodeCharacter{0153}{\oe} \DeclareUnicodeCharacter{0154}{\'R} \DeclareUnicodeCharacter{0155}{\'r} \DeclareUnicodeCharacter{0158}{\v{R}} \DeclareUnicodeCharacter{0159}{\v{r}} \DeclareUnicodeCharacter{015A}{\'S} \DeclareUnicodeCharacter{015B}{\'s} \DeclareUnicodeCharacter{015C}{\^S} \DeclareUnicodeCharacter{015D}{\^s} \DeclareUnicodeCharacter{015E}{\cedilla{S}} \DeclareUnicodeCharacter{015F}{\cedilla{s}} \DeclareUnicodeCharacter{0160}{\v{S}} \DeclareUnicodeCharacter{0161}{\v{s}} \DeclareUnicodeCharacter{0162}{\cedilla{t}} \DeclareUnicodeCharacter{0163}{\cedilla{T}} \DeclareUnicodeCharacter{0164}{\v{T}} \DeclareUnicodeCharacter{0168}{\~U} \DeclareUnicodeCharacter{0169}{\~u} \DeclareUnicodeCharacter{016A}{\=U} \DeclareUnicodeCharacter{016B}{\=u} \DeclareUnicodeCharacter{016C}{\u{U}} \DeclareUnicodeCharacter{016D}{\u{u}} \DeclareUnicodeCharacter{016E}{\ringaccent{U}} \DeclareUnicodeCharacter{016F}{\ringaccent{u}} \DeclareUnicodeCharacter{0170}{\H{U}} \DeclareUnicodeCharacter{0171}{\H{u}} \DeclareUnicodeCharacter{0174}{\^W} \DeclareUnicodeCharacter{0175}{\^w} \DeclareUnicodeCharacter{0176}{\^Y} \DeclareUnicodeCharacter{0177}{\^y} \DeclareUnicodeCharacter{0178}{\"Y} \DeclareUnicodeCharacter{0179}{\'Z} \DeclareUnicodeCharacter{017A}{\'z} \DeclareUnicodeCharacter{017B}{\dotaccent{Z}} \DeclareUnicodeCharacter{017C}{\dotaccent{z}} \DeclareUnicodeCharacter{017D}{\v{Z}} \DeclareUnicodeCharacter{017E}{\v{z}} \DeclareUnicodeCharacter{01C4}{D\v{Z}} \DeclareUnicodeCharacter{01C5}{D\v{z}} \DeclareUnicodeCharacter{01C6}{d\v{z}} \DeclareUnicodeCharacter{01C7}{LJ} \DeclareUnicodeCharacter{01C8}{Lj} \DeclareUnicodeCharacter{01C9}{lj} \DeclareUnicodeCharacter{01CA}{NJ} \DeclareUnicodeCharacter{01CB}{Nj} \DeclareUnicodeCharacter{01CC}{nj} \DeclareUnicodeCharacter{01CD}{\v{A}} \DeclareUnicodeCharacter{01CE}{\v{a}} \DeclareUnicodeCharacter{01CF}{\v{I}} \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}} \DeclareUnicodeCharacter{01D1}{\v{O}} \DeclareUnicodeCharacter{01D2}{\v{o}} \DeclareUnicodeCharacter{01D3}{\v{U}} \DeclareUnicodeCharacter{01D4}{\v{u}} \DeclareUnicodeCharacter{01E2}{\={\AE}} \DeclareUnicodeCharacter{01E3}{\={\ae}} \DeclareUnicodeCharacter{01E6}{\v{G}} \DeclareUnicodeCharacter{01E7}{\v{g}} \DeclareUnicodeCharacter{01E8}{\v{K}} \DeclareUnicodeCharacter{01E9}{\v{k}} \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}} \DeclareUnicodeCharacter{01F1}{DZ} \DeclareUnicodeCharacter{01F2}{Dz} \DeclareUnicodeCharacter{01F3}{dz} \DeclareUnicodeCharacter{01F4}{\'G} \DeclareUnicodeCharacter{01F5}{\'g} \DeclareUnicodeCharacter{01F8}{\`N} \DeclareUnicodeCharacter{01F9}{\`n} \DeclareUnicodeCharacter{01FC}{\'{\AE}} \DeclareUnicodeCharacter{01FD}{\'{\ae}} \DeclareUnicodeCharacter{01FE}{\'{\O}} \DeclareUnicodeCharacter{01FF}{\'{\o}} \DeclareUnicodeCharacter{021E}{\v{H}} \DeclareUnicodeCharacter{021F}{\v{h}} \DeclareUnicodeCharacter{0226}{\dotaccent{A}} \DeclareUnicodeCharacter{0227}{\dotaccent{a}} \DeclareUnicodeCharacter{0228}{\cedilla{E}} \DeclareUnicodeCharacter{0229}{\cedilla{e}} \DeclareUnicodeCharacter{022E}{\dotaccent{O}} \DeclareUnicodeCharacter{022F}{\dotaccent{o}} \DeclareUnicodeCharacter{0232}{\=Y} \DeclareUnicodeCharacter{0233}{\=y} \DeclareUnicodeCharacter{0237}{\dotless{j}} \DeclareUnicodeCharacter{02DB}{\ogonek{ }} \DeclareUnicodeCharacter{1E02}{\dotaccent{B}} \DeclareUnicodeCharacter{1E03}{\dotaccent{b}} \DeclareUnicodeCharacter{1E04}{\udotaccent{B}} \DeclareUnicodeCharacter{1E05}{\udotaccent{b}} \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}} \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}} \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}} \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}} \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}} \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}} \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}} \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}} \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}} \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}} \DeclareUnicodeCharacter{1E20}{\=G} \DeclareUnicodeCharacter{1E21}{\=g} \DeclareUnicodeCharacter{1E22}{\dotaccent{H}} \DeclareUnicodeCharacter{1E23}{\dotaccent{h}} \DeclareUnicodeCharacter{1E24}{\udotaccent{H}} \DeclareUnicodeCharacter{1E25}{\udotaccent{h}} \DeclareUnicodeCharacter{1E26}{\"H} \DeclareUnicodeCharacter{1E27}{\"h} \DeclareUnicodeCharacter{1E30}{\'K} \DeclareUnicodeCharacter{1E31}{\'k} \DeclareUnicodeCharacter{1E32}{\udotaccent{K}} \DeclareUnicodeCharacter{1E33}{\udotaccent{k}} \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}} \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}} \DeclareUnicodeCharacter{1E36}{\udotaccent{L}} \DeclareUnicodeCharacter{1E37}{\udotaccent{l}} \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}} \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}} \DeclareUnicodeCharacter{1E3E}{\'M} \DeclareUnicodeCharacter{1E3F}{\'m} \DeclareUnicodeCharacter{1E40}{\dotaccent{M}} \DeclareUnicodeCharacter{1E41}{\dotaccent{m}} \DeclareUnicodeCharacter{1E42}{\udotaccent{M}} \DeclareUnicodeCharacter{1E43}{\udotaccent{m}} \DeclareUnicodeCharacter{1E44}{\dotaccent{N}} \DeclareUnicodeCharacter{1E45}{\dotaccent{n}} \DeclareUnicodeCharacter{1E46}{\udotaccent{N}} \DeclareUnicodeCharacter{1E47}{\udotaccent{n}} \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}} \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}} \DeclareUnicodeCharacter{1E54}{\'P} \DeclareUnicodeCharacter{1E55}{\'p} \DeclareUnicodeCharacter{1E56}{\dotaccent{P}} \DeclareUnicodeCharacter{1E57}{\dotaccent{p}} \DeclareUnicodeCharacter{1E58}{\dotaccent{R}} \DeclareUnicodeCharacter{1E59}{\dotaccent{r}} \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}} \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}} \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}} \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}} \DeclareUnicodeCharacter{1E60}{\dotaccent{S}} \DeclareUnicodeCharacter{1E61}{\dotaccent{s}} \DeclareUnicodeCharacter{1E62}{\udotaccent{S}} \DeclareUnicodeCharacter{1E63}{\udotaccent{s}} \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}} \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}} \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}} \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}} \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}} \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}} \DeclareUnicodeCharacter{1E7C}{\~V} \DeclareUnicodeCharacter{1E7D}{\~v} \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}} \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}} \DeclareUnicodeCharacter{1E80}{\`W} \DeclareUnicodeCharacter{1E81}{\`w} \DeclareUnicodeCharacter{1E82}{\'W} \DeclareUnicodeCharacter{1E83}{\'w} \DeclareUnicodeCharacter{1E84}{\"W} \DeclareUnicodeCharacter{1E85}{\"w} \DeclareUnicodeCharacter{1E86}{\dotaccent{W}} \DeclareUnicodeCharacter{1E87}{\dotaccent{w}} \DeclareUnicodeCharacter{1E88}{\udotaccent{W}} \DeclareUnicodeCharacter{1E89}{\udotaccent{w}} \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}} \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}} \DeclareUnicodeCharacter{1E8C}{\"X} \DeclareUnicodeCharacter{1E8D}{\"x} \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}} \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}} \DeclareUnicodeCharacter{1E90}{\^Z} \DeclareUnicodeCharacter{1E91}{\^z} \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}} \DeclareUnicodeCharacter{1E93}{\udotaccent{z}} \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}} \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}} \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}} \DeclareUnicodeCharacter{1E97}{\"t} \DeclareUnicodeCharacter{1E98}{\ringaccent{w}} \DeclareUnicodeCharacter{1E99}{\ringaccent{y}} \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}} \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}} \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}} \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}} \DeclareUnicodeCharacter{1EBC}{\~E} \DeclareUnicodeCharacter{1EBD}{\~e} \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}} \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}} \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}} \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}} \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}} \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}} \DeclareUnicodeCharacter{1EF2}{\`Y} \DeclareUnicodeCharacter{1EF3}{\`y} \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}} \DeclareUnicodeCharacter{1EF8}{\~Y} \DeclareUnicodeCharacter{1EF9}{\~y} \DeclareUnicodeCharacter{2013}{--} \DeclareUnicodeCharacter{2014}{---} \DeclareUnicodeCharacter{2018}{\quoteleft} \DeclareUnicodeCharacter{2019}{\quoteright} \DeclareUnicodeCharacter{201A}{\quotesinglbase} \DeclareUnicodeCharacter{201C}{\quotedblleft} \DeclareUnicodeCharacter{201D}{\quotedblright} \DeclareUnicodeCharacter{201E}{\quotedblbase} \DeclareUnicodeCharacter{2022}{\bullet} \DeclareUnicodeCharacter{2026}{\dots} \DeclareUnicodeCharacter{2039}{\guilsinglleft} \DeclareUnicodeCharacter{203A}{\guilsinglright} \DeclareUnicodeCharacter{20AC}{\euro} \DeclareUnicodeCharacter{2192}{\expansion} \DeclareUnicodeCharacter{21D2}{\result} \DeclareUnicodeCharacter{2212}{\minus} \DeclareUnicodeCharacter{2217}{\point} \DeclareUnicodeCharacter{2261}{\equiv} }% end of \utfeightchardefs % US-ASCII character definitions. \def\asciichardefs{% nothing need be done \relax } % Make non-ASCII characters printable again for compatibility with % existing Texinfo documents that may use them, even without declaring a % document encoding. % \setnonasciicharscatcode \other \message{formatting,} \newdimen\defaultparindent \defaultparindent = 15pt \chapheadingskip = 15pt plus 4pt minus 2pt \secheadingskip = 12pt plus 3pt minus 2pt \subsecheadingskip = 9pt plus 2pt minus 2pt % Prevent underfull vbox error messages. \vbadness = 10000 % Don't be very finicky about underfull hboxes, either. \hbadness = 6666 % Following George Bush, get rid of widows and orphans. \widowpenalty=10000 \clubpenalty=10000 % Use TeX 3.0's \emergencystretch to help line breaking, but if we're % using an old version of TeX, don't do anything. We want the amount of % stretch added to depend on the line length, hence the dependence on % \hsize. We call this whenever the paper size is set. % \def\setemergencystretch{% \ifx\emergencystretch\thisisundefined % Allow us to assign to \emergencystretch anyway. \def\emergencystretch{\dimen0}% \else \emergencystretch = .15\hsize \fi } % Parameters in order: 1) textheight; 2) textwidth; % 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; % 7) physical page height; 8) physical page width. % % We also call \setleading{\textleading}, so the caller should define % \textleading. The caller should also set \parskip. % \def\internalpagesizes#1#2#3#4#5#6#7#8{% \voffset = #3\relax \topskip = #6\relax \splittopskip = \topskip % \vsize = #1\relax \advance\vsize by \topskip \outervsize = \vsize \advance\outervsize by 2\topandbottommargin \pageheight = \vsize % \hsize = #2\relax \outerhsize = \hsize \advance\outerhsize by 0.5in \pagewidth = \hsize % \normaloffset = #4\relax \bindingoffset = #5\relax % \ifpdf \pdfpageheight #7\relax \pdfpagewidth #8\relax % if we don't reset these, they will remain at "1 true in" of % whatever layout pdftex was dumped with. \pdfhorigin = 1 true in \pdfvorigin = 1 true in \fi % \setleading{\textleading} % \parindent = \defaultparindent \setemergencystretch } % @letterpaper (the default). \def\letterpaper{{\globaldefs = 1 \parskip = 3pt plus 2pt minus 1pt \textleading = 13.2pt % % If page is nothing but text, make it come out even. \internalpagesizes{607.2pt}{6in}% that's 46 lines {\voffset}{.25in}% {\bindingoffset}{36pt}% {11in}{8.5in}% }} % Use @smallbook to reset parameters for 7x9.25 trim size. \def\smallbook{{\globaldefs = 1 \parskip = 2pt plus 1pt \textleading = 12pt % \internalpagesizes{7.5in}{5in}% {-.2in}{0in}% {\bindingoffset}{16pt}% {9.25in}{7in}% % \lispnarrowing = 0.3in \tolerance = 700 \hfuzz = 1pt \contentsrightmargin = 0pt \defbodyindent = .5cm }} % Use @smallerbook to reset parameters for 6x9 trim size. % (Just testing, parameters still in flux.) \def\smallerbook{{\globaldefs = 1 \parskip = 1.5pt plus 1pt \textleading = 12pt % \internalpagesizes{7.4in}{4.8in}% {-.2in}{-.4in}% {0pt}{14pt}% {9in}{6in}% % \lispnarrowing = 0.25in \tolerance = 700 \hfuzz = 1pt \contentsrightmargin = 0pt \defbodyindent = .4cm }} % Use @afourpaper to print on European A4 paper. \def\afourpaper{{\globaldefs = 1 \parskip = 3pt plus 2pt minus 1pt \textleading = 13.2pt % % Double-side printing via postscript on Laserjet 4050 % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. % To change the settings for a different printer or situation, adjust % \normaloffset until the front-side and back-side texts align. Then % do the same for \bindingoffset. You can set these for testing in % your texinfo source file like this: % @tex % \global\normaloffset = -6mm % \global\bindingoffset = 10mm % @end tex \internalpagesizes{673.2pt}{160mm}% that's 51 lines {\voffset}{\hoffset}% {\bindingoffset}{44pt}% {297mm}{210mm}% % \tolerance = 700 \hfuzz = 1pt \contentsrightmargin = 0pt \defbodyindent = 5mm }} % Use @afivepaper to print on European A5 paper. % From romildo@urano.iceb.ufop.br, 2 July 2000. % He also recommends making @example and @lisp be small. \def\afivepaper{{\globaldefs = 1 \parskip = 2pt plus 1pt minus 0.1pt \textleading = 12.5pt % \internalpagesizes{160mm}{120mm}% {\voffset}{\hoffset}% {\bindingoffset}{8pt}% {210mm}{148mm}% % \lispnarrowing = 0.2in \tolerance = 800 \hfuzz = 1.2pt \contentsrightmargin = 0pt \defbodyindent = 2mm \tableindent = 12mm }} % A specific text layout, 24x15cm overall, intended for A4 paper. \def\afourlatex{{\globaldefs = 1 \afourpaper \internalpagesizes{237mm}{150mm}% {\voffset}{4.6mm}% {\bindingoffset}{7mm}% {297mm}{210mm}% % % Must explicitly reset to 0 because we call \afourpaper. \globaldefs = 0 }} % Use @afourwide to print on A4 paper in landscape format. \def\afourwide{{\globaldefs = 1 \afourpaper \internalpagesizes{241mm}{165mm}% {\voffset}{-2.95mm}% {\bindingoffset}{7mm}% {297mm}{210mm}% \globaldefs = 0 }} % @pagesizes TEXTHEIGHT[,TEXTWIDTH] % Perhaps we should allow setting the margins, \topskip, \parskip, % and/or leading, also. Or perhaps we should compute them somehow. % \parseargdef\pagesizes{\pagesizesyyy #1,,\finish} \def\pagesizesyyy#1,#2,#3\finish{{% \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi \globaldefs = 1 % \parskip = 3pt plus 2pt minus 1pt \setleading{\textleading}% % \dimen0 = #1\relax \advance\dimen0 by \voffset % \dimen2 = \hsize \advance\dimen2 by \normaloffset % \internalpagesizes{#1}{\hsize}% {\voffset}{\normaloffset}% {\bindingoffset}{44pt}% {\dimen0}{\dimen2}% }} % Set default to letter. % \letterpaper \message{and turning on texinfo input format.} \def^^L{\par} % remove \outer, so ^L can appear in an @comment % DEL is a comment character, in case @c does not suffice. \catcode`\^^? = 14 % Define macros to output various characters with catcode for normal text. \catcode`\"=\other \def\normaldoublequote{"} \catcode`\$=\other \def\normaldollar{$}%$ font-lock fix \catcode`\+=\other \def\normalplus{+} \catcode`\<=\other \def\normalless{<} \catcode`\>=\other \def\normalgreater{>} \catcode`\^=\other \def\normalcaret{^} \catcode`\_=\other \def\normalunderscore{_} \catcode`\|=\other \def\normalverticalbar{|} \catcode`\~=\other \def\normaltilde{~} % This macro is used to make a character print one way in \tt % (where it can probably be output as-is), and another way in other fonts, % where something hairier probably needs to be done. % % #1 is what to print if we are indeed using \tt; #2 is what to print % otherwise. Since all the Computer Modern typewriter fonts have zero % interword stretch (and shrink), and it is reasonable to expect all % typewriter fonts to have this, we can check that font parameter. % \def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} % Same as above, but check for italic font. Actually this also catches % non-italic slanted fonts since it is impossible to distinguish them from % italic fonts. But since this is only used by $ and it uses \sl anyway % this is not a problem. \def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} % Turn off all special characters except @ % (and those which the user can use as if they were ordinary). % Most of these we simply print from the \tt font, but for some, we can % use math or other variants that look better in normal text. \catcode`\"=\active \def\activedoublequote{{\tt\char34}} \let"=\activedoublequote \catcode`\~=\active \def~{{\tt\char126}} \chardef\hat=`\^ \catcode`\^=\active \def^{{\tt \hat}} \catcode`\_=\active \def_{\ifusingtt\normalunderscore\_} \let\realunder=_ % Subroutine for the previous macro. \def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } \catcode`\|=\active \def|{{\tt\char124}} \chardef \less=`\< \catcode`\<=\active \def<{{\tt \less}} \chardef \gtr=`\> \catcode`\>=\active \def>{{\tt \gtr}} \catcode`\+=\active \def+{{\tt \char 43}} \catcode`\$=\active \def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix % If a .fmt file is being used, characters that might appear in a file % name cannot be active until we have parsed the command line. % So turn them off again, and have \everyjob (or @setfilename) turn them on. % \otherifyactive is called near the end of this file. \def\otherifyactive{\catcode`+=\other \catcode`\_=\other} % Used sometimes to turn off (effectively) the active characters even after % parsing them. \def\turnoffactive{% \normalturnoffactive \otherbackslash } \catcode`\@=0 % \backslashcurfont outputs one backslash character in current font, % as in \char`\\. \global\chardef\backslashcurfont=`\\ \global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work % \realbackslash is an actual character `\' with catcode other, and % \doublebackslash is two of them (for the pdf outlines). {\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}} % In texinfo, backslash is an active character; it prints the backslash % in fixed width font. \catcode`\\=\active % @ for escape char from now on. % The story here is that in math mode, the \char of \backslashcurfont % ends up printing the roman \ from the math symbol font (because \char % in math mode uses the \mathcode, and plain.tex sets % \mathcode`\\="026E). It seems better for @backslashchar{} to always % print a typewriter backslash, hence we use an explicit \mathchar, % which is the decimal equivalent of "715c (class 7, e.g., use \fam; % ignored family value; char position "5C). We can't use " for the % usual hex value because it has already been made active. @def@normalbackslash{{@tt @ifmmode @mathchar29020 @else @backslashcurfont @fi}} @let@backslashchar = @normalbackslash % @backslashchar{} is for user documents. % On startup, @fixbackslash assigns: % @let \ = @normalbackslash % \rawbackslash defines an active \ to do \backslashcurfont. % \otherbackslash defines an active \ to be a literal `\' character with % catcode other. We switch back and forth between these. @gdef@rawbackslash{@let\=@backslashcurfont} @gdef@otherbackslash{@let\=@realbackslash} % Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of % the literal character `\'. Also revert - to its normal character, in % case the active - from code has slipped in. % {@catcode`- = @active @gdef@normalturnoffactive{% @let-=@normaldash @let"=@normaldoublequote @let$=@normaldollar %$ font-lock fix @let+=@normalplus @let<=@normalless @let>=@normalgreater @let\=@normalbackslash @let^=@normalcaret @let_=@normalunderscore @let|=@normalverticalbar @let~=@normaltilde @markupsetuplqdefault @markupsetuprqdefault @unsepspaces } } % Make _ and + \other characters, temporarily. % This is canceled by @fixbackslash. @otherifyactive % If a .fmt file is being used, we don't want the `\input texinfo' to show up. % That is what \eatinput is for; after that, the `\' should revert to printing % a backslash. % @gdef@eatinput input texinfo{@fixbackslash} @global@let\ = @eatinput % On the other hand, perhaps the file did not have a `\input texinfo'. Then % the first `\' in the file would cause an error. This macro tries to fix % that, assuming it is called before the first `\' could plausibly occur. % Also turn back on active characters that might appear in the input % file name, in case not using a pre-dumped format. % @gdef@fixbackslash{% @ifx\@eatinput @let\ = @normalbackslash @fi @catcode`+=@active @catcode`@_=@active } % Say @foo, not \foo, in error messages. @escapechar = `@@ % These (along with & and #) are made active for url-breaking, so need % active definitions as the normal characters. @def@normaldot{.} @def@normalquest{?} @def@normalslash{/} % These look ok in all fonts, so just make them not special. % @hashchar{} gets its own user-level command, because of #line. @catcode`@& = @other @def@normalamp{&} @catcode`@# = @other @def@normalhash{#} @catcode`@% = @other @def@normalpercent{%} @let @hashchar = @normalhash @c Finally, make ` and ' active, so that txicodequoteundirected and @c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we @c don't make ` and ' active, @code will not get them as active chars. @c Do this last of all since we use ` in the previous @catcode assignments. @catcode`@'=@active @catcode`@`=@active @markupsetuplqdefault @markupsetuprqdefault @c Local variables: @c eval: (add-hook 'write-file-hooks 'time-stamp) @c page-delimiter: "^\\\\message" @c time-stamp-start: "def\\\\texinfoversion{" @c time-stamp-format: "%:y-%02m-%02d.%02H" @c time-stamp-end: "}" @c End: @c vim:sw=2: @ignore arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115 @end ignore ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-actions.el����������������������������������������������������������������������0000644�0001750�0001750�00000030553�13020504332�012655� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e-actions.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; Example actions for messages, attachments (see chapter 'Actions' in the ;; manual) ;;; Code: (eval-when-compile (byte-compile-disable-warning 'cl-functions)) (require 'cl) (require 'ido) (require 'mu4e-utils) (require 'mu4e-message) (require 'mu4e-meta) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-action-count-lines (msg) "Count the number of lines in the e-mail message. Works for headers view and message-view." (message "Number of lines: %s" (shell-command-to-string (concat "wc -l < " (shell-quote-argument (mu4e-message-field msg :path)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-msg2pdf (concat mu4e-builddir "/toys/msg2pdf/msg2pdf") "Path to the msg2pdf toy.") (defun mu4e-action-view-as-pdf (msg) "Convert the message to pdf, then show it. Works for the message view." (unless (file-executable-p mu4e-msg2pdf) (mu4e-error "msg2pdf not found; please set `mu4e-msg2pdf'")) (let* ((pdf (shell-command-to-string (concat mu4e-msg2pdf " " (shell-quote-argument (mu4e-message-field msg :path)) " 2> /dev/null"))) (pdf (and pdf (> (length pdf) 5) (substring pdf 0 -1)))) ;; chop \n (unless (and pdf (file-exists-p pdf)) (mu4e-warn "Failed to create PDF file")) (find-file pdf))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~write-body-to-html (msg) "Write the body (either html or text) to a temporary file; return the filename." (let* ((html (mu4e-message-field msg :body-html)) (txt (mu4e-message-field msg :body-txt)) (tmpfile (mu4e-make-temp-file "html")) (attachments (remove-if (lambda (part) (or (null (plist-get part :attachment)) (null (plist-get part :cid)))) (mu4e-message-field msg :parts)))) (unless (or html txt) (mu4e-error "No body part for this message")) (with-temp-buffer (insert "<head><meta charset=\"UTF-8\"></head>\n") (insert (or html (concat "<pre>" txt "</pre>"))) (write-file tmpfile) ;; rewrite attachment urls (mapc (lambda (attachment) (goto-char (point-min)) (while (re-search-forward (format "src=\"cid:%s\"" (plist-get attachment :cid)) nil t) (if (plist-get attachment :temp) (replace-match (format "src=\"%s\"" (plist-get attachment :temp))) (replace-match (format "src=\"%s%s\"" temporary-file-directory (plist-get attachment :name))) (let ((tmp-attachment-name (format "%s%s" temporary-file-directory (plist-get attachment :name)))) (mu4e~proc-extract 'save (mu4e-message-field msg :docid) (plist-get attachment :index) mu4e-decryption-policy tmp-attachment-name) (mu4e-remove-file-later tmp-attachment-name))))) attachments) (save-buffer) tmpfile))) (defun mu4e-action-view-in-browser (msg) "View the body of the message in a browser. You can influence the browser to use with the variable `browse-url-generic-program', and see the discussion of privacy aspects in `(mu4e) Displaying rich-text messages'." (browse-url (concat "file://" (mu4e~write-body-to-html msg)))) (defun mu4e-action-view-with-xwidget (msg) "View the body of the message inside xwidget-webkit. This is only available in emacs 25+; also see the discussion of privacy aspects in `(mu4e) Displaying rich-text messages'." (unless (fboundp 'xwidget-webkit-browse-url) (mu4e-error "No xwidget support available")) (xwidget-webkit-browse-url (concat "file://" (mu4e~write-body-to-html msg)) t)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defconst mu4e-text2speech-command "festival --tts" "Program that speaks out text it receives on standard-input.") (defun mu4e-action-message-to-speech (msg) "Pronounce the message text using `mu4e-text2speech-command'." (unless (mu4e-message-field msg :body-txt) (mu4e-warn "No text body for this message")) (with-temp-buffer (insert (mu4e-message-field msg :body-txt)) (shell-command-on-region (point-min) (point-max) mu4e-text2speech-command))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-captured-message nil "The last-captured message (the s-expression).") (defun mu4e-action-capture-message (msg) "Remember MSG; we can create a an attachment based on this msg with `mu4e-compose-attach-captured-message'." (setq mu4e-captured-message msg) (message "Message has been captured")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-org-contacts-file nil "File to store contact information for org-contacts. Needed by `mu4e-action-add-org-contact'.") (eval-when-compile ;; silence compiler warning about free variable (unless (require 'org-capture nil 'noerror) (defvar org-capture-templates nil))) (defun mu4e-action-add-org-contact (msg) "Add an org-contact entry based on the From: address of the current message (in headers or view). You need to set `mu4e-org-contacts-file' to the full path to the file where you store your org-contacts." (unless (require 'org-capture nil 'noerror) (mu4e-error "org-capture is not available.")) (unless mu4e-org-contacts-file (mu4e-error "`mu4e-org-contacts-file' is not defined.")) (let* ((sender (car-safe (mu4e-message-field msg :from))) (name (car-safe sender)) (email (cdr-safe sender)) (blurb (format (concat "* %%?%s\n" ":PROPERTIES:\n" ":EMAIL: %s\n" ":NICK:\n" ":BIRTHDAY:\n" ":END:\n\n") (or name email "") (or email ""))) (key "mu4e-add-org-contact-key") (org-capture-templates (append org-capture-templates (list (list key "contacts" 'entry (list 'file mu4e-org-contacts-file) blurb))))) (message "%S" org-capture-templates) (when (fboundp 'org-capture) (org-capture nil key)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-action-git-apply-patch (msg) "Apply the git [patch] message." (let ((path (ido-read-directory-name "Target directory: " (car ido-work-directory-list) "~/" t))) (setf ido-work-directory-list (cons path (delete path ido-work-directory-list))) (shell-command (format "cd %s; git apply %s" path (mu4e-message-field msg :path))))) (defun mu4e-action-git-apply-mbox (msg) "Apply and commit the git [patch] MSG. If the `default-directory' matches the most recent history entry don't bother asking for the git tree again (useful for bulk actions)." (let ((cwd (car ido-work-directory-list))) (unless (and (stringp cwd) (string= default-directory cwd)) (setq cwd (ido-read-directory-name "Target directory: " cwd "~/" t)) (setf ido-work-directory-list (cons cwd (delete cwd ido-work-directory-list)))) (shell-command (format "cd %s; git am %s" (shell-quote-argument cwd) (shell-quote-argument (mu4e-message-field msg :path)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-action-tags-header "X-Keywords" "Header where tags are stored. Used by `mu4e-action-retag-message'. Make sure it is one of the headers mu recognizes for storing tags: X-Keywords, X-Label, Keywords. Also note that changing this setting on already tagged messages can lead to messages with multiple tags headers.") (defvar mu4e-action-tags-completion-list '() "List of tags to show for autocompletion in `mu4e-action-retag-message'.") (defun mu4e~contains-line-matching (regexp path) "Determine whether the file at path contains a line matching the given regexp." (with-temp-buffer (insert-file-contents path) (save-excursion (goto-char (point-min)) (if (re-search-forward regexp nil t) t nil)))) (defun mu4e~replace-first-line-matching (regexp to-string path) "Replace the first line in the file at path that matches regexp with the string replace." (with-temp-file path (insert-file-contents path) (save-excursion (goto-char (point-min)) (if (re-search-forward regexp nil t) (replace-match to-string nil nil))))) (defun mu4e-action-retag-message (msg &optional retag-arg) "Change tags of a message. Accepts a comma-separated list of additions and removals. Example: +tag,+long tag,-oldtag would add 'tag' and 'long tag', and remove 'oldtag'." (let* ( (path (mu4e-message-field msg :path)) (maildir (mu4e-message-field msg :maildir)) (oldtags (mu4e-message-field msg :tags)) (tags-completion (append mu4e-action-tags-completion-list (mapcar (lambda (tag) (format "+%s" tag)) mu4e-action-tags-completion-list) (mapcar (lambda (tag) (format "-%s" tag)) oldtags))) (retag (if retag-arg (split-string retag-arg ",") (completing-read-multiple "Tags: " tags-completion))) (header mu4e-action-tags-header) (sep (cond ((string= header "Keywords") ", ") ((string= header "X-Label") " ") ((string= header "X-Keywords") ", ") (t ", "))) (taglist (if oldtags (copy-sequence oldtags) '())) tagstr) (dolist (tag retag taglist) (cond ((string-match "^\\+\\(.+\\)" tag) (setq taglist (push (match-string 1 tag) taglist))) ((string-match "^\\-\\(.+\\)" tag) (setq taglist (delete (match-string 1 tag) taglist))) (t (setq taglist (push tag taglist))))) (setq taglist (sort (delete-dups taglist) 'string<)) (setq tagstr (mapconcat 'identity taglist sep)) (setq tagstr (replace-regexp-in-string "[\\&]" "\\\\\\&" tagstr)) (setq tagstr (replace-regexp-in-string "[/]" "\\&" tagstr)) (if (not (mu4e~contains-line-matching (concat header ":.*") path)) ;; Add tags header just before the content (mu4e~replace-first-line-matching "^$" (concat header ": " tagstr "\n") path) ;; replaces keywords, restricted to the header (mu4e~replace-first-line-matching (concat header ":.*") (concat header ": " tagstr) path)) (mu4e-message (concat "tagging: " (mapconcat 'identity taglist ", "))) (mu4e-refresh-message path maildir))) (defun mu4e-action-show-thread (msg) "Show all messages that are in the same thread as the message at point. Point remains on the message with the message-id where the action was invoked. If invoked in view-mode, continue to display the message." (let ((msgid (mu4e-message-field msg :message-id))) (when msgid (let ((mu4e-headers-show-threads t) (mu4e-headers-include-related t)) (mu4e-headers-search (format "msgid:%s" msgid) nil nil nil msgid (eq major-mode 'mu4e-view-mode)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (provide 'mu4e-actions) �����������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-proc.el�������������������������������������������������������������������������0000644�0001750�0001750�00000047205�13020504332�012162� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; mu4e-proc.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;;; Code: (require 'mu4e-vars) (require 'mu4e-utils) (require 'mu4e-meta) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; internal vars (defvar mu4e~proc-buf nil "Buffer (string) for data received from the backend.") (defconst mu4e~proc-name " *mu4e-proc*" "Name of the server process, buffer.") (defvar mu4e~proc-process nil "The mu-server process.") ;; dealing with the length cookie that precedes expressions (defconst mu4e~cookie-pre "\376" "Each expression we get from the backend (mu server) starts with a length cookie: <`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.") (defconst mu4e~cookie-post "\377" "Each expression we get from the backend (mu server) starts with a length cookie: <`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.") (defconst mu4e~cookie-matcher-rx (concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)" mu4e~cookie-post) "Regular expression matching the length cookie. Match 1 will be the length (in hex).") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defsubst mu4e~proc-send-command (frm &rest args) "Send as command to the mu server process. Start the process if needed." (unless (mu4e~proc-running-p) (mu4e~proc-start)) (let ((cmd (apply 'format frm args))) (mu4e-log 'to-server "%s" cmd) (process-send-string mu4e~proc-process (concat cmd "\n")))) (defun mu4e~proc-start () "Start the mu server process." (unless (file-executable-p mu4e-mu-binary) (mu4e-error (format "`mu4e-mu-binary' (%S) not found" mu4e-mu-binary))) (let* ((process-connection-type nil) ;; use a pipe (args '("server")) (args (append args (when mu4e-mu-home (list (concat "--muhome=" mu4e-mu-home)))))) (setq mu4e~proc-buf "") (setq mu4e~proc-process (apply 'start-process mu4e~proc-name mu4e~proc-name mu4e-mu-binary args)) ;; register a function for (:info ...) sexps (unless mu4e~proc-process (mu4e-error "Failed to start the mu4e backend")) (set-process-query-on-exit-flag mu4e~proc-process nil) (set-process-coding-system mu4e~proc-process 'binary 'utf-8-unix) (set-process-filter mu4e~proc-process 'mu4e~proc-filter) (set-process-sentinel mu4e~proc-process 'mu4e~proc-sentinel))) (defun mu4e~proc-kill () "Kill the mu server process." (let* ((buf (get-buffer mu4e~proc-name)) (proc (and (buffer-live-p buf) (get-buffer-process buf)))) (when proc (let ((delete-exited-processes t)) ;; the mu server signal handler will make it quit after 'quit' (mu4e~proc-send-command "cmd:quit")) ;; try sending SIGINT (C-c) to process, so it can exit gracefully (ignore-errors (signal-process proc 'SIGINT)))) (setq mu4e~proc-process nil mu4e~proc-buf nil)) (defun mu4e~proc-running-p () "Whether the mu process is running." (when (and mu4e~proc-process (memq (process-status mu4e~proc-process) '(run open listen connect stop))) t)) (defsubst mu4e~proc-eat-sexp-from-buf () "'Eat' the next s-expression from `mu4e~proc-buf'. Note: this is a string, not an emacs-buffer. `mu4e~proc-buf gets its contents from the mu-servers in the following form: <`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'> Function returns this sexp, or nil if there was none. `mu4e~proc-buf' is updated as well, with all processed sexp data removed." (ignore-errors ;; the server may die in the middle... ;; mu4e~cookie-matcher-rx: ;; (concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)]" mu4e~cookie-post) (let ((b (string-match mu4e~cookie-matcher-rx mu4e~proc-buf)) (sexp-len) (objcons)) (when b (setq sexp-len (string-to-number (match-string 1 mu4e~proc-buf) 16)) ;; does mu4e~proc-buf contain the full sexp? (when (>= (length mu4e~proc-buf) (+ sexp-len (match-end 0))) ;; clear-up start (setq mu4e~proc-buf (substring mu4e~proc-buf (match-end 0))) ;; note: we read the input in binary mode -- here, we take the part ;; that is the sexp, and convert that to utf-8, before we interpret ;; it. (setq objcons (read-from-string (decode-coding-string (substring mu4e~proc-buf 0 sexp-len) 'utf-8 t))) (when objcons (setq mu4e~proc-buf (substring mu4e~proc-buf sexp-len)) (car objcons))))))) (defun mu4e~proc-filter (proc str) "A process-filter for the 'mu server' output. It accumulates the strings into valid sexps by checking of the ';;eox' end-of-sexp marker, and then evaluating them. The server output is as follows: 1. an error (:error 2 :message \"unknown command\") ;; eox => this will be passed to `mu4e-error-func'. 2a. a message sexp looks something like: \( :docid 1585 :from ((\"Donald Duck\" . \"donald@example.com\")) :to ((\"Mickey Mouse\" . \"mickey@example.com\")) :subject \"Wicked stuff\" :date (20023 26572 0) :size 15165 :references (\"200208121222.g7CCMdb80690@msg.id\") :in-reply-to \"200208121222.g7CCMdb80690@msg.id\" :message-id \"foobar32423847ef23@pluto.net\" :maildir: \"/archive\" :path \"/home/mickey/Maildir/inbox/cur/1312254065_3.32282.pluto,4cd5bd4e9:2,\" :priority high :flags (new unread) :attachments ((2 \"hello.jpg\" \"image/jpeg\") (3 \"laah.mp3\" \"audio/mp3\")) :body-txt \" <message body>\" \) ;; eox => this will be passed to `mu4e-header-func'. 2b. After the list of message sexps has been returned (see 2a.), we'll receive a sexp that looks like (:found <n>) with n the number of messages found. The <n> will be passed to `mu4e-found-func'. 3. a view looks like: (:view <msg-sexp>) => the <msg-sexp> (see 2.) will be passed to `mu4e-view-func'. 4. a database update looks like: (:update <msg-sexp> :move <nil-or-t>) => the <msg-sexp> (see 2.) will be passed to `mu4e-update-func', :move tells us whether this is a move to another maildir, or merely a flag change. 5. a remove looks like: (:remove <docid>) => the docid will be passed to `mu4e-remove-func' 6. a compose looks like: (:compose <reply|forward|edit|new> [:original<msg-sexp>] [:include <attach>]) `mu4e-compose-func'." (mu4e-log 'misc "* Received %d byte(s)" (length str)) (setq mu4e~proc-buf (concat mu4e~proc-buf str)) ;; update our buffer (let ((sexp (mu4e~proc-eat-sexp-from-buf))) (with-local-quit (while sexp (mu4e-log 'from-server "%S" sexp) (cond ;; a header plist can be recognized by the existence of a :date field ((plist-get sexp :date) (funcall mu4e-header-func sexp)) ;; the found sexp, we receive after getting all the headers ((plist-get sexp :found) (funcall mu4e-found-func (plist-get sexp :found))) ;; viewing a specific message ((plist-get sexp :view) (funcall mu4e-view-func (plist-get sexp :view))) ;; receive an erase message ((plist-get sexp :erase) (funcall mu4e-erase-func)) ;; receive a :sent message ((plist-get sexp :sent) (funcall mu4e-sent-func (plist-get sexp :docid) (plist-get sexp :path))) ;; received a pong message ((plist-get sexp :pong) (funcall mu4e-pong-func (plist-get sexp :props))) ;; received a contacts message ;; note: we use 'member', to match (:contacts nil) ((plist-member sexp :contacts) (funcall mu4e-contacts-func (plist-get sexp :contacts))) ;; something got moved/flags changed ((plist-get sexp :update) (funcall mu4e-update-func (plist-get sexp :update) (plist-get sexp :move))) ;; a message got removed ((plist-get sexp :remove) (funcall mu4e-remove-func (plist-get sexp :remove))) ;; start composing a new message ((plist-get sexp :compose) (funcall mu4e-compose-func (plist-get sexp :compose) (plist-get sexp :original) (plist-get sexp :include))) ;; do something with a temporary file ((plist-get sexp :temp) (funcall mu4e-temp-func (plist-get sexp :temp) ;; name of the temp file (plist-get sexp :what) ;; what to do with it ;; (pipe|emacs|open-with...) (plist-get sexp :docid) ;; docid of the message (plist-get sexp :param)));; parameter for the action ;; get some info ((plist-get sexp :info) (funcall mu4e-info-func sexp)) ;; receive an error ((plist-get sexp :error) (funcall mu4e-error-func (plist-get sexp :error) (plist-get sexp :message))) (t (mu4e-message "Unexpected data from server [%S]" sexp))) (setq sexp (mu4e~proc-eat-sexp-from-buf)))))) ;; error codes are defined in src/mu-util ;;(defconst mu4e-xapian-empty 19 "Error code: xapian is empty/non-existent") (defun mu4e~proc-sentinel (proc msg) "Function that will be called when the mu-server process terminates." (let ((status (process-status proc)) (code (process-exit-status proc))) (setq mu4e~proc-process nil) (setq mu4e~proc-buf "") ;; clear any half-received sexps (cond ((eq status 'signal) (cond ((eq code 9) (message nil)) ;;(message "the mu server process has been stopped")) (t (error (format "mu server process received signal %d" code))))) ((eq status 'exit) (cond ((eq code 0) (message nil)) ;; don't do anything ((eq code 11) (error "Database is locked by another process")) ((eq code 15) (error "Database needs upgrade; try `mu index --rebuild' from the command line")) ((eq code 19) (error "Database empty; try indexing some messages")) (t (error "mu server process ended with exit code %d" code)))) (t (error "Something bad happened to the mu server process"))))) (defsubst mu4e~docid-msgid-param (docid-or-msgid) "Construct a backend parameter based on DOCID-OR-MSGID." (format (if (stringp docid-or-msgid) "msgid:\"%s\"" "docid:%d") docid-or-msgid)) (defun mu4e~proc-remove (docid) "Remove message identified by docid. The results are reporter through either (:update ... ) or (:error) sexp, which are handled my `mu4e-error-func', respectively." (mu4e~proc-send-command "cmd:remove docid:%d" docid)) (defun mu4e~proc-escape (str) "Escape STRING for transport -- put it in quotes, and escape existing quotation. In particular, backslashes and double-quotes." (let ((esc (replace-regexp-in-string "\\\\" "\\\\\\\\" str))) (format "\"%s\"" (replace-regexp-in-string "\"" "\\\\\"" esc)))) (defun mu4e~proc-find (query threads sortfield sortdir maxnum skip-dups include-related) "Start a database query for QUERY. If THREADS is non-nil, show results in threaded fasion, SORTFIELD is a symbol describing the field to sort by (or nil); see `mu4e~headers-sortfield-choices'. If SORT is `descending', sort Z->A, if it's `ascending', sort A->Z. MAXNUM determines the maximum number of results to return, or nil for 'unlimited'. If SKIP-DUPS is non-nil, show only one of duplicate messages (see `mu4e-headers-skip-duplicates'). If INCLUDE-RELATED is non-nil, include messages related to the messages matching the search query (see `mu4e-headers-include-related'). For each result found, a function is called, depending on the kind of result. The variables `mu4e-error-func' contain the function that will be called for, resp., a message (header row) or an error." (mu4e~proc-send-command (concat "cmd:find query:%s threads:%s sortfield:%s reverse:%s maxnum:%d " "skip-dups:%s include-related:%s") (mu4e~proc-escape query) (if threads "true" "false") ;; sortfield is e.g. ':subject'; this removes the ':' (if (null sortfield) "nil" (substring (symbol-name sortfield) 1)) ;; TODO: use ascending/descending in backend too (it's clearer than 'reverse' (if (eq sortdir 'descending) "true" "false") (if maxnum maxnum -1) (if skip-dups "true" "false") (if include-related "true" "false"))) (defun mu4e~proc-move (docid-or-msgid &optional maildir flags) "Move message identified by DOCID-OR-MSGID. At least one of MAILDIR and FLAGS should be specified. Note, even if MAILDIR is nil, this is still a move, since a change in flags still implies a change in message filename. MAILDIR (), optionally setting FLAGS (keyword argument :flags). optionally setting FLAGS in the process. If MAILDIR is nil, message will be moved within the same maildir. MAILDIR must be a maildir, that is, the part _without_ cur/ or new/ or the root-maildir-prefix. E.g. \"/archive\". This directory must already exist. The FLAGS parameter can have the following forms: 1. a list of flags such as '(passed replied seen) 2. a string containing the one-char versions of the flags, e.g. \"PRS\" 3. a delta-string specifying the changes with +/- and the one-char flags, e.g. \"+S-N\" to set Seen and remove New. The flags are any of `deleted', `flagged', `new', `passed', `replied' `seen' or `trashed', or the corresponding \"DFNPRST\" as defined in [1]. See `mu4e-string-to-flags' and `mu4e-flags-to-string'. The server reports the results for the operation through `mu4e-update-func'. If the variable `mu4e-change-filenames-when-moving' is non-nil, moving to a different maildir generates new names for the target files; this helps certain tools (such as mbsync). The results are reported through either (:update ... ) or (:error ) sexp, which are handled my `mu4e-update-func' and `mu4e-error-func', respectively." (unless (or maildir flags) (mu4e-error "At least one of maildir and flags must be specified")) (unless (or (not maildir) (file-exists-p (concat mu4e-maildir "/" maildir "/"))) (mu4e-error "Target dir does not exist")) (let* ((idparam (mu4e~docid-msgid-param docid-or-msgid)) (flagstr (when flags (concat " flags:" (if (stringp flags) flags (mu4e-flags-to-string flags))))) (path (when maildir (format " maildir:%s" (mu4e~proc-escape maildir)))) (rename (if (and maildir mu4e-change-filenames-when-moving) "true" "false"))) (mu4e~proc-send-command "cmd:move %s %s %s %s" idparam (or flagstr "") (or path "") (format "newname:%s" rename)))) (defun mu4e~proc-index (path my-addresses cleanup lazy-check) "Update the message database for filesystem PATH, which should point to some maildir directory structure. MY-ADDRESSES is a list of 'my' email addresses (see `mu4e-user-mail-address-list')." (let ((path (mu4e~proc-escape path)) (addrs (when my-addresses (mapconcat 'identity my-addresses ",")))) (if addrs (mu4e~proc-send-command "cmd:index path:%s my-addresses:%s cleanup:%s lazy-check:%s" path addrs (if cleanup "true" : "false") (if lazy-check "true")) (mu4e~proc-send-command "cmd:index path:%s" path)))) (defun mu4e~proc-add (path maildir) "Add the message at PATH to the database. With MAILDIR set to the maildir this message resides in, e.g. '/drafts'; if this works, we will receive (:info add :path <path> :docid <docid>) as well as (:update <msg-sexp>)." (mu4e~proc-send-command "cmd:add path:%s %s" (mu4e~proc-escape path) (if maildir (format "maildir:%s" (mu4e~proc-escape maildir)) ""))) (defun mu4e~proc-sent (path maildir) "Add the message at PATH to the database. With MAILDIR set to the maildir this message resides in, e.g. '/drafts'. if this works, we will receive (:info add :path <path> :docid <docid> :fcc <path>)." (mu4e~proc-send-command "cmd:sent path:%s maildir:%s" (mu4e~proc-escape path) (mu4e~proc-escape maildir))) (defun mu4e~proc-compose (type decrypt &optional docid) "Start composing a message of certain TYPE (a symbol, either `forward', `reply', `edit', `resend' or `new', based on an original message (ie, replying to, forwarding, editing, resending) with DOCID or nil for type `new'. The result will be delivered to the function registered as `mu4e-compose-func'." (unless (member type '(forward reply edit resend new)) (mu4e-error "Unsupported compose-type %S" type)) (unless (eq (null docid) (eq type 'new)) (mu4e-error "`new' implies docid not-nil, and vice-versa")) (mu4e~proc-send-command "cmd:compose type:%s docid:%d extract-encrypted:%s use-agent:true" (symbol-name type) docid (if decrypt "true" "false"))) (defun mu4e~proc-mkdir (path) "Create a new maildir-directory at filesystem PATH." (mu4e~proc-send-command "cmd:mkdir path:%s" (mu4e~proc-escape path))) (defun mu4e~proc-extract (action docid partidx decrypt &optional path what param) "Extract an attachment with index PARTIDX from message with DOCID and perform ACTION on it (as symbol, either `save', `open', `temp') which mean: * save: save the part to PARAM1 (a path) (non-optional for save)$ * open: open the part with the default application registered for doing so * temp: save to a temporary file, then respond with (:temp <path> :what <what> :param <param>)." (let ((cmd (concat "cmd:extract " (case action (save (format "action:save docid:%d index:%d path:%s extract-encrypted:%s use-agent:true" docid partidx (mu4e~proc-escape path) (if decrypt "true" "false"))) (open (format "action:open docid:%d index:%d extract-encrypted:%s use-agent:true" docid partidx (if decrypt "true" "false"))) (temp (format "action:temp docid:%d index:%d what:%s%s extract-encrypted:%s use-agent:true" docid partidx what (if param (if (stringp param) (format " param:%s" (mu4e~proc-escape param)) (format " param:%S" param)) "") (if decrypt "true" "false"))) (otherwise (mu4e-error "Unsupported action %S" action)))) )) (mu4e~proc-send-command "%s" cmd))) (defun mu4e~proc-ping () "Sends a ping to the mu server, expecting a (:pong ...) in response." (mu4e~proc-send-command "cmd:ping")) (defun mu4e~proc-contacts (personal after) "Sends the contacts command to the mu server. A (:contacts (<list>)) is expected in response. If PERSONAL is non-nil, only get personal contacts, if AFTER is non-nil, get only contacts seen AFTER (the time_t value)." (mu4e~proc-send-command "cmd:contacts personal:%s after:%d" (if personal "true" "false") (or after 0))) (defun mu4e~proc-view (docid-or-msgid &optional images decrypt) "Get one particular message based on its DOCID-OR-MSGID. Optionally, if IMAGES is non-nil, backend will any images attached to the message, and return them as temp files. The result will be delivered to the function registered as `mu4e-view-func'." (mu4e~proc-send-command "cmd:view %s extract-images:%s extract-encrypted:%s use-agent:true" (mu4e~docid-msgid-param docid-or-msgid) (if images "true" "false") (if decrypt "true" "false"))) (defun mu4e~proc-view-path (path &optional images decrypt) "View message at PATH (keyword argument). Optionally, if IMAGES is non-nil, backend will any images attached to the message, and return them as temp files. The result will be delivered to the function registered as `mu4e-view-func'." (mu4e~proc-send-command "cmd:view path:%s extract-images:%s extract-encrypted:%s use-agent:true" (mu4e~proc-escape path) (if images "true" "false") (if decrypt "true" "false"))) (provide 'mu4e-proc) ;; End of mu4e-proc.el �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-mark.el�������������������������������������������������������������������������0000644�0001750�0001750�00000043117�13020504332�012147� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; mu4e-mark.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; In this file are function related to marking messages; they assume we are ;; currently in the headers buffer. ;; Code: (require 'mu4e-proc) (require 'mu4e-utils) (require 'mu4e-message) (eval-when-compile (byte-compile-disable-warning 'cl-functions)) ;; keep byte-compiler happy (declare-function mu4e~headers-mark "mu4e-headers") (declare-function mu4e~headers-goto-docid "mu4e-headers") (declare-function mu4e-headers-next "mu4e-headers") (defcustom mu4e-headers-leave-behavior 'ask "What to do when user leaves the headers view. That is when he e.g. quits, refreshes or does a new search. Value is one of the following symbols: - `ask' ask user whether to ignore the marks - `apply' automatically apply the marks before doing anything else - `ignore' automatically ignore the marks without asking" :type '(choice (const ask :tag "ask user whether to ignore marks") (const apply :tag "apply marks without asking") (const ignore :tag "ignore marks without asking")) :group 'mu4e-headers) (defcustom mu4e-mark-execute-pre-hook nil "Hook run just *before* a mark is applied to a message. The hook function is called with two arguments, the mark being executed and the message itself.") (defvar mu4e-headers-show-target t "Whether to show targets (such as '-> delete', '-> /archive') when marking message. Normally, this is useful information for the user, however, when you often mark large numbers (thousands) of message, showing the target makes this quite a bit slower (showing the target uses an emacs feature called 'overlays', which aren't particularly fast).") ;;; insert stuff;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e~mark-map nil "Map (hash) of docid->markinfo; when a message is marked, the information is added here. markinfo is a cons cell consisting of the following: \(mark . target) where MARK is the type of mark (move, trash, delete) TARGET (optional) is the target directory (for 'move')") ;; the mark-map is specific for the current header buffer ;; currently, there can't be more than one, but we never know what will ;; happen in the future ;; the fringe is the space on the left of headers, where we put marks below some ;; handy definitions; only `mu4e-mark-fringe-len' should be change (if ever), ;; the others follow from that. (defconst mu4e~mark-fringe-len 2 "Width of the fringe for marks on the left.") (defconst mu4e~mark-fringe (make-string mu4e~mark-fringe-len ?\s) "The space on the left of message headers to put marks.") (defconst mu4e~mark-fringe-format (format "%%-%ds" mu4e~mark-fringe-len) "Format string to set a mark and leave remaining space.") (defun mu4e~mark-initialize () "Initialize the marks subsystem." (set (make-local-variable 'mu4e~mark-map) (make-hash-table))) (defun mu4e~mark-clear () "Clear the marks subsystem." (clrhash mu4e~mark-map)) (defun mu4e~mark-find-headers-buffer () "Find the headers buffer, if any." (find-if (lambda (b) (with-current-buffer b (eq major-mode 'mu4e-headers-mode))) (buffer-list))) (defmacro mu4e~mark-in-context (&rest body) "Evaluate BODY in the context of the headers buffer in case this is either a headers or view buffer." `(cond ((eq major-mode 'mu4e-headers-mode) ,@body) ((eq major-mode 'mu4e-view-mode) (when (buffer-live-p mu4e~view-headers-buffer) (let* ((msg (mu4e-message-at-point)) (docid (mu4e-message-field msg :docid))) (with-current-buffer mu4e~view-headers-buffer (if (mu4e~headers-goto-docid docid) ,@body (mu4e-error "cannot find message in headers buffer.")))))) (t ;; even in other modes (e.g. mu4e-main-mode we try to find ;; the headers buffer (let ((hbuf (mu4e~mark-find-headers-buffer))) (if (buffer-live-p hbuf) (with-current-buffer hbuf ,@body) ,@body))))) (defvar mu4e-marks '((refile :char ("r" . "▶") :prompt "refile" :dyn-target (lambda (target msg) (mu4e-get-refile-folder msg)) :action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))) (delete :char ("D" . "❌") :prompt "Delete" :show-target (lambda (target) "delete") :action (lambda (docid msg target) (mu4e~proc-remove docid))) (flag :char ("+" . "✚") :prompt "+flag" :show-target (lambda (target) "flag") :action (lambda (docid msg target) (mu4e~proc-move docid nil "+F-u-N"))) (move :char ("m" . "▷") :prompt "move" :ask-target mu4e~mark-get-move-target :action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))) (read :char ("!" . "◼") :prompt "!read" :show-target (lambda (target) "read") :action (lambda (docid msg target) (mu4e~proc-move docid nil "+S-u-N"))) (trash :char ("d" . "▼") :prompt "dtrash" :dyn-target (lambda (target msg) (mu4e-get-trash-folder msg)) :action (lambda (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "+T-N"))) (unflag :char ("-" . "➖") :prompt "-unflag" :show-target (lambda (target) "unflag") :action (lambda (docid msg target) (mu4e~proc-move docid nil "-F-N"))) (untrash :char ("=" . "▲") :prompt "=untrash" :show-target (lambda (target) "untrash") :action (lambda (docid msg target) (mu4e~proc-move docid nil "-T"))) (unread :char ("?" . "◻") :prompt "?unread" :show-target (lambda (target) "unread") :action (lambda (docid msg target) (mu4e~proc-move docid nil "-S+u-N"))) (unmark :char " " :prompt "unmark" :action (mu4e-error "No action for unmarking")) (action :char ( "a" . "◯") :prompt "action" :ask-target (lambda () (mu4e-read-option "Action: " mu4e-headers-actions)) :action (lambda (docid msg actionfunc) (save-excursion (when (mu4e~headers-goto-docid docid) (mu4e-headers-action actionfunc))))) (something :char ("*" . "✱") :prompt "*something" :action (mu4e-error "No action for deferred mark"))) "The list of all the possible marks. This is an alist mapping mark symbols to their properties. The properties are: :char (string) or (basic . fancy) The character to display in the headers view. Either a single-character string, or a dotted-pair cons cell where the second item will be used if `mu4e-use-fancy-chars' is `t', otherwise we'll use the first one. It can also be a plain string for backwards compatibility since we didn't always support `mu4e-use-fancy-chars' here. :prompt (string) The prompt to use when asking for marks (used for example when marking a whole thread) :ask-target (function returning a string) Get the target. This function run once per bulk-operation, and thus is suitable for user-interaction. If nil, the target is nil. :dyn-target (function from (TARGET MSG) to string). Compute the dynamic target. This is run once per message, which is passed as MSG. The default is to just return the target. :show-target (function from TARGET to string) How to display the target. :action (function taking (DOCID MSG TARGET)). The action to apply on the message.") (defun mu4e-mark-at-point (mark target) "Mark (or unmark) message at point. MARK specifies the mark-type. For `move'-marks and `trash'-marks the TARGET argument is non-nil and specifies to which maildir the message is to be moved/trashed. The function works in both headers buffers and message buffers. The following marks are available, and the corresponding props: MARK TARGET description ---------------------------------------------------------- `refile' y mark this message for archiving `something' n mark this message for *something* (decided later) `delete' n remove the message `flag' n mark this message for flagging `move' y move the message to some folder `read' n mark the message as read `trash' y trash the message to some folder `unflag' n mark this message for unflagging `untrash' n remove the 'trashed' flag from a message `unmark' n unmark this message `unread' n mark the message as unread `action' y mark the message for some action." (interactive) (let* ((msg (mu4e-message-at-point)) (docid (mu4e-message-field msg :docid)) ;; get a cell with the mark char and the 'target' 'move' already has a ;; target (the target folder) the other ones get a pseudo "target", as ;; info for the user. (markdesc (cdr (or (assq mark mu4e-marks) (mu4e-error "Invalid mark %S" mark)))) (get-markkar (lambda (char) (if (listp char) (if mu4e-use-fancy-chars (cdr char) (car char)) char))) (markkar (funcall get-markkar (plist-get markdesc :char))) (target (mu4e~mark-get-dyn-target mark target)) (show-fct (plist-get markdesc :show-target)) (shown-target (if show-fct (funcall show-fct target) (if target (format "%S" target))))) (unless docid (mu4e-warn "No message on this line")) (unless (eq major-mode 'mu4e-headers-mode) (mu4e-error "Not in headers-mode")) (save-excursion (when (mu4e~headers-mark docid markkar) ;; update the hash -- remove everything current, and if add the new stuff, ;; unless we're unmarking (remhash docid mu4e~mark-map) ;; remove possible overlays (remove-overlays (line-beginning-position) (line-end-position)) ;; now, let's set a mark (unless we were unmarking) (unless (eql mark 'unmark) (puthash docid (cons mark target) mu4e~mark-map) ;; when we have a target (ie., when moving), show the target folder in ;; an overlay (when (and shown-target mu4e-headers-show-target) (let* ((targetstr (propertize (concat "-> " shown-target " ") 'face 'mu4e-system-face)) ;; mu4e~headers-goto-docid docid t \will take us just after the ;; docid cookie and then we skip the mu4e~mark-fringe (start (+ (length mu4e~mark-fringe) (mu4e~headers-goto-docid docid t))) (overlay (make-overlay start (+ start (length targetstr))))) (overlay-put overlay 'display targetstr) docid))))))) (defun mu4e~mark-get-move-target () "Ask for a move target, and propose to create it if it does not exist." (interactive) ;; (mu4e-message-at-point) ;; raises error if there is none (let* ((target (mu4e-ask-maildir "Move message to: ")) (target (if (string= (substring target 0 1) "/") target (concat "/" target))) (fulltarget (concat mu4e-maildir target))) (when (or (file-directory-p fulltarget) (and (yes-or-no-p (format "%s does not exist. Create now?" fulltarget)) (mu4e~proc-mkdir fulltarget))) target))) (defun mu4e~mark-ask-target (mark) "Ask the target for MARK, if the user should be asked the target." (let ((getter (plist-get (cdr (assq mark mu4e-marks)) :ask-target))) (and getter (funcall getter)))) (defun mu4e~mark-get-dyn-target (mark target) "Get the dynamic target for MARK. The result may depend on the message at point." (let ((getter (plist-get (cdr (assq mark mu4e-marks)) :dyn-target))) (if getter (funcall getter target (mu4e-message-at-point)) target))) (defun mu4e-mark-set (mark &optional target) "Mark the header at point, or, if region is active, mark all headers in the region. Optionally, provide TARGET (for moves)." (unless target (setq target (mu4e~mark-ask-target mark))) (if (not (use-region-p)) ;; single message (mu4e-mark-at-point mark target) ;; mark all messages in the region. (save-excursion (let ((cant-go-further) (eor (region-end))) (goto-char (region-beginning)) (while (and (<= (point) eor) (not cant-go-further)) (mu4e-mark-at-point mark target) (setq cant-go-further (not (mu4e-headers-next)))))))) (defun mu4e-mark-restore (docid) "Restore the visual mark for the message with DOCID." (let ((markcell (gethash docid mu4e~mark-map))) (when markcell (save-excursion (when (mu4e~headers-goto-docid docid) (mu4e-mark-at-point (car markcell) (cdr markcell))))))) (defun mu4e~mark-get-markpair (prompt &optional allow-something) "Ask user for a mark; return (MARK . TARGET). If ALLOW-SOMETHING is non-nil, allow the 'something' pseudo mark as well." (let* ((marks (mapcar (lambda (markdescr) (cons (plist-get (cdr markdescr) :prompt) (car markdescr))) mu4e-marks)) (marks (if allow-something marks (remove-if (lambda (m) (eq 'something (cdr m))) marks))) (mark (mu4e-read-option prompt marks)) (target (mu4e~mark-ask-target mark))) (cons mark target))) (defun mu4e-mark-resolve-deferred-marks () "Check if there are any deferred ('something') marks. If there are such marks, replace them with a _real_ mark (ask the user which one)." (interactive) (mu4e~mark-in-context (let ((markpair)) (maphash (lambda (docid val) (let ((mark (car val)) (target (cdr val))) (when (eql mark 'something) (unless markpair (setq markpair (mu4e~mark-get-markpair "Set deferred mark(s) to: " nil))) (save-excursion (when (mu4e~headers-goto-docid docid) (mu4e-mark-set (car markpair) (cdr markpair))))))) mu4e~mark-map)))) (defun mu4e~mark-check-target (target) "Check if the target exists; if not, offer to create it." (let ((fulltarget (concat mu4e-maildir target))) (if (not (mu4e-create-maildir-maybe fulltarget)) (mu4e-error "Target dir %s does not exist " fulltarget) target))) (defun mu4e-mark-execute-all (&optional no-confirmation) "Execute the actions for all marked messages in this buffer. After the actions have been executed succesfully, the affected messages are *hidden* from the current header list. Since the headers are the result of a search, we cannot be certain that the messages no longer match the current one - to get that certainty, we need to rerun the search, but we don't want to do that automatically, as it may be too slow and/or break the user's flow. Therefore, we hide the message, which in practice seems to work well. If NO-CONFIRMATION is non-nil, don't ask user for confirmation." (interactive) (mu4e~mark-in-context (let ((marknum (hash-table-count mu4e~mark-map))) (if (zerop marknum) (message "Nothing is marked") (mu4e-mark-resolve-deferred-marks) (when (or no-confirmation (y-or-n-p (format "Are you sure you want to execute %d mark%s?" marknum (if (> marknum 1) "s" "")))) (maphash (lambda (docid val) (let* ((mark (car val)) (target (cdr val)) (markdescr (assq mark mu4e-marks)) (msg (save-excursion (mu4e~headers-goto-docid docid) (mu4e-message-at-point)))) ;; note: whenever you do something with the message, ;; it looses its N (new) flag (if markdescr (progn (run-hook-with-args 'mu4e-mark-execute-pre-hook mark msg) (funcall (plist-get (cdr markdescr) :action) docid msg target)) (mu4e-error "Unrecognized mark %S" mark)))) mu4e~mark-map)) (mu4e-mark-unmark-all) (message nil))))) (defun mu4e-mark-unmark-all () "Unmark all marked messages." (interactive) (mu4e~mark-in-context (when (or (null mu4e~mark-map) (zerop (hash-table-count mu4e~mark-map))) (mu4e-warn "Nothing is marked")) (maphash (lambda (docid val) (save-excursion (when (mu4e~headers-goto-docid docid) (mu4e-mark-set 'unmark)))) mu4e~mark-map) ;; in any case, clear the marks map (mu4e~mark-clear))) (defun mu4e-mark-docid-marked-p (docid) "Is the given docid marked?" (when (gethash docid mu4e~mark-map) t)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-mark-marks-num () "Return the number of marks in the current buffer." (if mu4e~mark-map (hash-table-count mu4e~mark-map) 0)) (defun mu4e-mark-handle-when-leaving () "If there are any marks in the current buffer, handle those according to the value of `mu4e-headers-leave-behavior'. This function is to be called before any further action (like searching, quitting the buffer) is taken; returning t means 'take the following action', return nil means 'don't do anything'." (mu4e~mark-in-context (let ((marknum (mu4e-mark-marks-num)) (what mu4e-headers-leave-behavior)) (unless (zerop marknum) ;; nothing to do? (when (eq what 'ask) (setq what (mu4e-read-option (format "There are %d existing mark(s); should we: " marknum) '( ("apply marks" . apply) ("ignore marks?" . ignore))))) ;; we determined what to do... now do it (when (eq what 'apply) (mu4e-mark-execute-all t)))))) (provide 'mu4e-mark) ;; End of mu4e-mark.el �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/TODO���������������������������������������������������������������������������������0000644�0001750�0001750�00000000573�12605152237�010525� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������* TODO *** mu4e-get-sub-maildirs *** extract mailing list name *** mark thread *** bounce support *** sorting *** tool bars *** refiling-by-pattern *** inspect message (muile) *** message statistics *** include exchange handling / org integration *** integrate bbdb *** forward-as-attachment *** identity support # Local Variables: # mode: org; org-startup-folded: nil # End: �������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/Makefile.in��������������������������������������������������������������������������0000644�0001750�0001750�00000105525�13021065705�012101� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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 = : build_triplet = @build@ host_triplet = @host@ subdir = mu4e ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/perlmod.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(dist_lisp_LISP) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = mu4e-meta.el texi.texi 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 = AM_V_DVIPS = $(am__v_DVIPS_@AM_V@) am__v_DVIPS_ = $(am__v_DVIPS_@AM_DEFAULT_V@) am__v_DVIPS_0 = @echo " DVIPS " $@; am__v_DVIPS_1 = AM_V_MAKEINFO = $(am__v_MAKEINFO_@AM_V@) am__v_MAKEINFO_ = $(am__v_MAKEINFO_@AM_DEFAULT_V@) am__v_MAKEINFO_0 = @echo " MAKEINFO" $@; am__v_MAKEINFO_1 = AM_V_INFOHTML = $(am__v_INFOHTML_@AM_V@) am__v_INFOHTML_ = $(am__v_INFOHTML_@AM_DEFAULT_V@) am__v_INFOHTML_0 = @echo " INFOHTML" $@; am__v_INFOHTML_1 = AM_V_TEXI2DVI = $(am__v_TEXI2DVI_@AM_V@) am__v_TEXI2DVI_ = $(am__v_TEXI2DVI_@AM_DEFAULT_V@) am__v_TEXI2DVI_0 = @echo " TEXI2DVI" $@; am__v_TEXI2DVI_1 = AM_V_TEXI2PDF = $(am__v_TEXI2PDF_@AM_V@) am__v_TEXI2PDF_ = $(am__v_TEXI2PDF_@AM_DEFAULT_V@) am__v_TEXI2PDF_0 = @echo " TEXI2PDF" $@; am__v_TEXI2PDF_1 = AM_V_texinfo = $(am__v_texinfo_@AM_V@) am__v_texinfo_ = $(am__v_texinfo_@AM_DEFAULT_V@) am__v_texinfo_0 = -q am__v_texinfo_1 = AM_V_texidevnull = $(am__v_texidevnull_@AM_V@) am__v_texidevnull_ = $(am__v_texidevnull_@AM_DEFAULT_V@) am__v_texidevnull_0 = > /dev/null am__v_texidevnull_1 = INFO_DEPS = $(srcdir)/mu4e.info am__TEXINFO_TEX_DIR = $(srcdir) DVIS = mu4e.dvi PDFS = mu4e.pdf PSS = mu4e.ps HTMLS = mu4e.html TEXINFOS = mu4e.texi TEXI2DVI = texi2dvi TEXI2PDF = $(TEXI2DVI) --pdf --batch MAKEINFOHTML = $(MAKEINFO) --html AM_MAKEINFOHTMLFLAGS = $(AM_MAKEINFOFLAGS) DVIPS = dvips 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 am__installdirs = "$(DESTDIR)$(infodir)" "$(DESTDIR)$(lispdir)" \ "$(DESTDIR)$(docdir)" 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; }; \ } dist_lispLISP_INSTALL = $(INSTALL_DATA) LISP = $(dist_lisp_LISP) am__ELFILES = mu4e-actions.el mu4e-compose.el mu4e-context.el \ mu4e-contrib.el mu4e-draft.el mu4e-headers.el mu4e-lists.el \ mu4e-main.el mu4e-mark.el mu4e-message.el mu4e-meta.el \ mu4e-proc.el mu4e-speedbar.el mu4e-utils.el mu4e-vars.el \ mu4e-view.el mu4e.el org-mu4e.el org-old-mu4e.el am__ELCFILES = $(am__ELFILES:.el=.elc) ELCFILES = $(LISP:.el=.elc) DATA = $(doc_DATA) 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 \ distdir 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 DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(mu4e_TEXINFOS) $(srcdir)/Makefile.in \ $(srcdir)/mu4e-meta.el.in $(srcdir)/texi.texi.in \ $(top_srcdir)/gtest.mk TODO texinfo.tex DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) 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" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMACS = @EMACS@ EMACSLOADPATH = @EMACSLOADPATH@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GMIME_CFLAGS = @GMIME_CFLAGS@ GMIME_LIBS = @GMIME_LIBS@ GREP = @GREP@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ GUILE_BINARY = @GUILE_BINARY@ GUILE_CFLAGS = @GUILE_CFLAGS@ GUILE_LIBS = @GUILE_LIBS@ GUILE_SITEDIR = @GUILE_SITEDIR@ GUILE_SNARF = @GUILE_SNARF@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MU_DOC_DIR = @MU_DOC_DIR@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PMCCABE = @PMCCABE@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SORT = @SORT@ STRIP = @STRIP@ VERSION = @VERSION@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ XAPIAN_CONFIG = @XAPIAN_CONFIG@ XAPIAN_CXXFLAGS = @XAPIAN_CXXFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ 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 = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_makeinfo = @have_makeinfo@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ lispdir = @lispdir@ 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@ TEST_PROGS = SUBDIRS = info_TEXINFOS = mu4e.texi mu4e_TEXINFOS = fdl.texi dist_lisp_LISP = \ mu4e-actions.el \ mu4e-compose.el \ mu4e-context.el \ mu4e-contrib.el \ mu4e-draft.el \ mu4e-headers.el \ mu4e-lists.el \ mu4e-main.el \ mu4e-mark.el \ mu4e-message.el \ mu4e-meta.el \ mu4e-proc.el \ mu4e-speedbar.el \ mu4e-utils.el \ mu4e-vars.el \ mu4e-view.el \ mu4e.el \ org-mu4e.el \ org-old-mu4e.el EXTRA_DIST = $(elisp_DATA) mu4e-about.org CLEANFILES = *.elc ${BUILT_SOURCES} doc_DATA = \ mu4e-about.org all: all-recursive .SUFFIXES: .SUFFIXES: .dvi .el .elc .html .info .pdf .ps .texi $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtest.mk $(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) --gnu mu4e/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu mu4e/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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtest.mk $(am__empty): $(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): mu4e-meta.el: $(top_builddir)/config.status $(srcdir)/mu4e-meta.el.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ texi.texi: $(top_builddir)/config.status $(srcdir)/texi.texi.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs .texi.info: $(AM_V_MAKEINFO)restore=: && backupdir="$(am__leading_dot)am$$$$" && \ am__cwd=`pwd` && $(am__cd) $(srcdir) && \ rm -rf $$backupdir && mkdir $$backupdir && \ if ($(MAKEINFO) --version) >/dev/null 2>&1; then \ for f in $@ $@-[0-9] $@-[0-9][0-9] $(@:.info=).i[0-9] $(@:.info=).i[0-9][0-9]; do \ if test -f $$f; then mv $$f $$backupdir; restore=mv; else :; fi; \ done; \ else :; fi && \ cd "$$am__cwd"; \ if $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ -o $@ $<; \ then \ rc=0; \ $(am__cd) $(srcdir); \ else \ rc=$$?; \ $(am__cd) $(srcdir) && \ $$restore $$backupdir/* `echo "./$@" | sed 's|[^/]*$$||'`; \ fi; \ rm -rf $$backupdir; exit $$rc .texi.dvi: $(AM_V_TEXI2DVI)TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ $(TEXI2DVI) $(AM_V_texinfo) --build-dir=$(@:.dvi=.t2d) -o $@ $(AM_V_texidevnull) \ $< .texi.pdf: $(AM_V_TEXI2PDF)TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ $(TEXI2PDF) $(AM_V_texinfo) --build-dir=$(@:.pdf=.t2p) -o $@ $(AM_V_texidevnull) \ $< .texi.html: $(AM_V_MAKEINFO)rm -rf $(@:.html=.htp) $(AM_V_at)if $(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ -o $(@:.html=.htp) $<; \ then \ rm -rf $@ && mv $(@:.html=.htp) $@; \ else \ rm -rf $(@:.html=.htp); exit 1; \ fi $(srcdir)/mu4e.info: mu4e.texi $(mu4e_TEXINFOS) mu4e.dvi: mu4e.texi $(mu4e_TEXINFOS) mu4e.pdf: mu4e.texi $(mu4e_TEXINFOS) mu4e.html: mu4e.texi $(mu4e_TEXINFOS) .dvi.ps: $(AM_V_DVIPS)TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ $(DVIPS) $(AM_V_texinfo) -o $@ $< uninstall-dvi-am: @$(NORMAL_UNINSTALL) @list='$(DVIS)'; test -n "$(dvidir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(dvidir)/$$f'"; \ rm -f "$(DESTDIR)$(dvidir)/$$f"; \ done uninstall-html-am: @$(NORMAL_UNINSTALL) @list='$(HTMLS)'; test -n "$(htmldir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " rm -rf '$(DESTDIR)$(htmldir)/$$f'"; \ rm -rf "$(DESTDIR)$(htmldir)/$$f"; \ done uninstall-info-am: @$(PRE_UNINSTALL) @if test -d '$(DESTDIR)$(infodir)' && $(am__can_run_installinfo); then \ list='$(INFO_DEPS)'; \ for file in $$list; do \ relfile=`echo "$$file" | sed 's|^.*/||'`; \ echo " install-info --info-dir='$(DESTDIR)$(infodir)' --remove '$(DESTDIR)$(infodir)/$$relfile'"; \ if install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$$relfile"; \ then :; else test ! -f "$(DESTDIR)$(infodir)/$$relfile" || exit 1; fi; \ done; \ else :; fi @$(NORMAL_UNINSTALL) @list='$(INFO_DEPS)'; \ for file in $$list; do \ relfile=`echo "$$file" | sed 's|^.*/||'`; \ relfile_i=`echo "$$relfile" | sed 's|\.info$$||;s|$$|.i|'`; \ (if test -d "$(DESTDIR)$(infodir)" && cd "$(DESTDIR)$(infodir)"; then \ echo " cd '$(DESTDIR)$(infodir)' && rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]"; \ rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]; \ else :; fi); \ done uninstall-pdf-am: @$(NORMAL_UNINSTALL) @list='$(PDFS)'; test -n "$(pdfdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(pdfdir)/$$f'"; \ rm -f "$(DESTDIR)$(pdfdir)/$$f"; \ done uninstall-ps-am: @$(NORMAL_UNINSTALL) @list='$(PSS)'; test -n "$(psdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(psdir)/$$f'"; \ rm -f "$(DESTDIR)$(psdir)/$$f"; \ done dist-info: $(INFO_DEPS) @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ list='$(INFO_DEPS)'; \ for base in $$list; do \ case $$base in \ $(srcdir)/*) base=`echo "$$base" | sed "s|^$$srcdirstrip/||"`;; \ esac; \ if test -f $$base; then d=.; else d=$(srcdir); fi; \ base_i=`echo "$$base" | sed 's|\.info$$||;s|$$|.i|'`; \ for file in $$d/$$base $$d/$$base-[0-9] $$d/$$base-[0-9][0-9] $$d/$$base_i[0-9] $$d/$$base_i[0-9][0-9]; do \ if test -f $$file; then \ relfile=`expr "$$file" : "$$d/\(.*\)"`; \ test -f "$(distdir)/$$relfile" || \ cp -p $$file "$(distdir)/$$relfile"; \ else :; fi; \ done; \ done mostlyclean-aminfo: -rm -rf mu4e.t2d mu4e.t2p clean-aminfo: -test -z "mu4e.dvi mu4e.pdf mu4e.ps mu4e.html" \ || rm -rf mu4e.dvi mu4e.pdf mu4e.ps mu4e.html maintainer-clean-aminfo: @list='$(INFO_DEPS)'; for i in $$list; do \ i_i=`echo "$$i" | sed 's|\.info$$||;s|$$|.i|'`; \ echo " rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]"; \ rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]; \ done .el.elc: if test "$(EMACS)" != "no"; then \ am__dir=. am__subdir_includes=''; \ case $@ in */*) \ am__dir=`echo '$@' | sed 's,/[^/]*$$,,'`; \ am__subdir_includes="-L $$am__dir -L $(srcdir)/$$am__dir"; \ esac; \ test -d "$$am__dir" || $(MKDIR_P) "$$am__dir" || exit 1; \ $(EMACS) --batch \ $(AM_ELCFLAGS) $(ELCFLAGS) \ $$am__subdir_includes -L $(builddir) -L $(srcdir) \ --eval "(defun byte-compile-dest-file (f) \"$@\")" \ --eval "(unless (byte-compile-file \"$<\") (kill-emacs 1))"; \ else :; fi install-dist_lispLISP: $(dist_lisp_LISP) $(ELCFILES) @$(NORMAL_INSTALL) @if test "$(EMACS)" != no && test -n "$(lispdir)"; then \ list='$(dist_lisp_LISP)'; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(lispdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(lispdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ $(am__strip_dir) \ echo " $(dist_lispLISP_INSTALL) '$$d$$p' '$(DESTDIR)$(lispdir)/$$f'"; \ $(dist_lispLISP_INSTALL) "$$d$$p" "$(DESTDIR)$(lispdir)/$$f" || exit $$?; \ if test -f $${p}c; then \ echo " $(dist_lispLISP_INSTALL) '$${p}c' '$(DESTDIR)$(lispdir)/$${f}c'"; \ $(dist_lispLISP_INSTALL) "$${p}c" "$(DESTDIR)$(lispdir)/$${f}c" || exit $$?; \ else : ; fi; \ done; \ else : ; fi uninstall-dist_lispLISP: @$(NORMAL_UNINSTALL) @test "$(EMACS)" != no && test -n "$(lispdir)" || exit 0; \ list='$(dist_lisp_LISP)'; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ files="$$files "`echo "$$files" | sed 's|$$|c|'`; \ dir='$(DESTDIR)$(lispdir)'; $(am__uninstall_files_from_dir) clean-lisp: -rm -f $(ELCFILES) install-docDATA: $(doc_DATA) @$(NORMAL_INSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ done uninstall-docDATA: @$(NORMAL_UNINSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) # 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" 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 distdir: $(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 @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 $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-info check-am: all-am check: check-recursive all-am: Makefile $(INFO_DEPS) $(LISP) $(ELCFILES) $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(infodir)" "$(DESTDIR)$(lispdir)" "$(DESTDIR)$(docdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done 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: -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) 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-aminfo clean-generic clean-libtool clean-lisp \ mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: $(DVIS) html: html-recursive html-am: $(HTMLS) info: info-recursive info-am: $(INFO_DEPS) install-data-am: install-dist_lispLISP install-docDATA install-info-am install-dvi: install-dvi-recursive install-dvi-am: $(DVIS) @$(NORMAL_INSTALL) @list='$(DVIS)'; test -n "$(dvidir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(dvidir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dvidir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dvidir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(dvidir)" || exit $$?; \ done install-exec-am: install-html: install-html-recursive install-html-am: $(HTMLS) @$(NORMAL_INSTALL) @list='$(HTMLS)'; list2=; test -n "$(htmldir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)'"; \ $(MKDIR_P) "$(DESTDIR)$(htmldir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p" || test -d "$$p"; then d=; else d="$(srcdir)/"; fi; \ $(am__strip_dir) \ d2=$$d$$p; \ if test -d "$$d2"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)/$$f'"; \ $(MKDIR_P) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \ echo " $(INSTALL_DATA) '$$d2'/* '$(DESTDIR)$(htmldir)/$$f'"; \ $(INSTALL_DATA) "$$d2"/* "$(DESTDIR)$(htmldir)/$$f" || exit $$?; \ else \ list2="$$list2 $$d2"; \ fi; \ done; \ test -z "$$list2" || { echo "$$list2" | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \ done; } install-info: install-info-recursive install-info-am: $(INFO_DEPS) @$(NORMAL_INSTALL) @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ list='$(INFO_DEPS)'; test -n "$(infodir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(infodir)'"; \ $(MKDIR_P) "$(DESTDIR)$(infodir)" || exit 1; \ fi; \ for file in $$list; do \ case $$file in \ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ esac; \ if test -f $$file; then d=.; else d=$(srcdir); fi; \ file_i=`echo "$$file" | sed 's|\.info$$||;s|$$|.i|'`; \ for ifile in $$d/$$file $$d/$$file-[0-9] $$d/$$file-[0-9][0-9] \ $$d/$$file_i[0-9] $$d/$$file_i[0-9][0-9] ; do \ if test -f $$ifile; then \ echo "$$ifile"; \ else : ; fi; \ done; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(infodir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(infodir)" || exit $$?; done @$(POST_INSTALL) @if $(am__can_run_installinfo); then \ list='$(INFO_DEPS)'; test -n "$(infodir)" || list=; \ for file in $$list; do \ relfile=`echo "$$file" | sed 's|^.*/||'`; \ echo " install-info --info-dir='$(DESTDIR)$(infodir)' '$(DESTDIR)$(infodir)/$$relfile'";\ install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$$relfile" || :;\ done; \ else : ; fi install-man: install-pdf: install-pdf-recursive install-pdf-am: $(PDFS) @$(NORMAL_INSTALL) @list='$(PDFS)'; test -n "$(pdfdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pdfdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pdfdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pdfdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pdfdir)" || exit $$?; done install-ps: install-ps-recursive install-ps-am: $(PSS) @$(NORMAL_INSTALL) @list='$(PSS)'; test -n "$(psdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(psdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(psdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(psdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(psdir)" || exit $$?; done installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-aminfo \ maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-aminfo mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: $(PDFS) ps: ps-recursive ps-am: $(PSS) uninstall-am: uninstall-dist_lispLISP uninstall-docDATA \ uninstall-dvi-am uninstall-html-am uninstall-info-am \ uninstall-pdf-am uninstall-ps-am .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-aminfo clean-generic clean-libtool \ clean-lisp cscopelist-am ctags ctags-am dist-info distclean \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dist_lispLISP \ install-docDATA 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-aminfo maintainer-clean-generic mostlyclean \ mostlyclean-aminfo mostlyclean-generic mostlyclean-libtool pdf \ pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-dist_lispLISP uninstall-docDATA uninstall-dvi-am \ uninstall-html-am uninstall-info-am uninstall-pdf-am \ uninstall-ps-am .PRECIOUS: Makefile # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof # 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: ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-main.el�������������������������������������������������������������������������0000644�0001750�0001750�00000020351�13021037566�012147� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e-main.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;;; Code: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (require 'smtpmail) ;; the queing stuff (silence elint) (require 'mu4e-utils) ;; utility functions (require 'mu4e-context) ;; the context (defconst mu4e~main-buffer-name " *mu4e-main*" "*internal* Name of the mu4e main view buffer.") (defvar mu4e-main-mode-map (let ((map (make-sparse-keymap))) (define-key map "b" 'mu4e-headers-search-bookmark) (define-key map "B" 'mu4e-headers-search-bookmark-edit) (define-key map "s" 'mu4e-headers-search) (define-key map "q" 'mu4e-quit) (define-key map "j" 'mu4e~headers-jump-to-maildir) (define-key map "C" 'mu4e-compose-new) (define-key map "m" 'mu4e~main-toggle-mail-sending-mode) (define-key map "f" 'smtpmail-send-queued-mail) ;; (define-key map "U" 'mu4e-update-mail-and-index) (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) ;; for terminal users (define-key map (kbd "C-c C-u") 'mu4e-update-mail-and-index) (define-key map "S" 'mu4e-interrupt-update-mail) (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) (define-key map ";" 'mu4e-context-switch) (define-key map "$" 'mu4e-show-log) (define-key map "A" 'mu4e-about) (define-key map "N" 'mu4e-news) (define-key map "H" 'mu4e-display-manual) map) "Keymap for the *mu4e-main* buffer.") (fset 'mu4e-main-mode-map mu4e-main-mode-map) (defvar mu4e-main-mode-abbrev-table nil) (define-derived-mode mu4e-main-mode special-mode "mu4e:main" "Major mode for the mu4e main screen. \\{mu4e-main-mode-map}." (use-local-map mu4e-main-mode-map) (setq truncate-lines t overwrite-mode 'overwrite-mode-binary) ;; show context in mode-string (make-local-variable 'global-mode-string) (add-to-list 'global-mode-string '(:eval (mu4e-context-label))) (set (make-local-variable 'revert-buffer-function) #'mu4e~main-view-real)) (defun mu4e~main-action-str (str &optional func-or-shortcut) "Highlight the first occurence of [.] in STR. If FUNC-OR-SHORTCUT is non-nil and if it is a function, call it when STR is clicked (using RET or mouse-2); if FUNC-OR-SHORTCUT is a string, execute the corresponding keyboard action when it is clicked." (let ((newstr (replace-regexp-in-string "\\[\\(..?\\)\\]" (lambda(m) (format "[%s]" (propertize (match-string 1 m) 'face 'mu4e-highlight-face))) str)) (map (make-sparse-keymap)) (func (if (functionp func-or-shortcut) func-or-shortcut (if (stringp func-or-shortcut) (lexical-let ((macro func-or-shortcut)) (lambda()(interactive) (execute-kbd-macro macro))))))) (define-key map [mouse-2] func) (define-key map (kbd "RET") func) (put-text-property 0 (length newstr) 'keymap map newstr) (put-text-property (string-match "\\[.+$" newstr) (- (length newstr) 1) 'mouse-face 'highlight newstr) newstr)) ;; NEW ;; This is the old `mu4e~main-view' function but without ;; buffer switching at the end. (defun mu4e~main-view-real (ignore-auto noconfirm) (let ((buf (get-buffer-create mu4e~main-buffer-name)) (inhibit-read-only t)) (with-current-buffer buf (erase-buffer) (insert "* " (propertize "mu4e - mu for emacs version " 'face 'mu4e-title-face) (propertize mu4e-mu-version 'face 'mu4e-header-key-face) ;; show some server properties; in this case; a big C when there's ;; crypto support, a big G when there's Guile support " " (propertize (concat (when (plist-get mu4e~server-props :crypto) "C") (when (plist-get mu4e~server-props :guile) "G")) 'face 'mu4e-title-face) "\n\n" (propertize " Basics\n\n" 'face 'mu4e-title-face) (mu4e~main-action-str "\t* [j]ump to some maildir\n" 'mu4e-jump-to-maildir) (mu4e~main-action-str "\t* enter a [s]earch query\n" 'mu4e-search) (mu4e~main-action-str "\t* [C]ompose a new message\n" 'mu4e-compose-new) "\n" (propertize " Bookmarks\n\n" 'face 'mu4e-title-face) ;; TODO: it's a bit uncool to hard-code the "b" shortcut... (mapconcat (lambda (bm) (mu4e~main-action-str (concat "\t* [b" (make-string 1 (mu4e-bookmark-key bm)) "] " (mu4e-bookmark-name bm)) (concat "b" (make-string 1 (mu4e-bookmark-key bm))))) (mu4e-bookmarks) "\n") "\n\n" (propertize " Misc\n\n" 'face 'mu4e-title-face) (mu4e~main-action-str "\t* [;]Switch focus\n" 'mu4e-context-switch) (mu4e~main-action-str "\t* [U]pdate email & database\n" 'mu4e-update-mail-and-index) ;; show the queue functions if `smtpmail-queue-dir' is defined (if (file-directory-p smtpmail-queue-dir) (mu4e~main-view-queue) "") "\n" (mu4e~main-action-str "\t* [N]ews\n" 'mu4e-news) (mu4e~main-action-str "\t* [A]bout mu4e\n" 'mu4e-about) (mu4e~main-action-str "\t* [H]elp\n" 'mu4e-display-manual) (mu4e~main-action-str "\t* [q]uit\n" 'mu4e-quit)) (mu4e-main-mode) ))) (defun mu4e~main-view-queue () "Display queue-related actions in the main view." (concat (mu4e~main-action-str "\t* toggle [m]ail sending mode " 'mu4e~main-toggle-mail-sending-mode) "(currently " (propertize (if smtpmail-queue-mail "queued" "direct") 'face 'mu4e-header-key-face) ")\n" (let ((queue-size (mu4e~main-queue-size))) (if (zerop queue-size) "" (mu4e~main-action-str (format "\t* [f]lush %s queued %s\n" (propertize (int-to-string queue-size) 'face 'mu4e-header-key-face) (if (> queue-size 1) "mails" "mail")) 'smtpmail-send-queued-mail))))) (defun mu4e~main-queue-size () "Return, as an int, the number of emails in the queue." (condition-case nil (with-temp-buffer (insert-file-contents (expand-file-name smtpmail-queue-index-file smtpmail-queue-dir)) (count-lines (point-min) (point-max))) (error 0))) (defun mu4e~main-view () "Create the mu4e main-view, and switch to it." (mu4e~main-view-real nil nil) (switch-to-buffer mu4e~main-buffer-name) (goto-char (point-min)) (add-to-list 'global-mode-string '(:eval (mu4e-context-label)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Interactive functions ;; NEW ;; Toggle mail sending mode without switching (defun mu4e~main-toggle-mail-sending-mode () "Toggle sending mail mode, either queued or direct." (interactive) (let ((curpos (point))) (unless (file-directory-p smtpmail-queue-dir) (mu4e-error "`smtpmail-queue-dir' does not exist")) (setq smtpmail-queue-mail (not smtpmail-queue-mail)) (message (concat "Outgoing mail will now be " (if smtpmail-queue-mail "queued" "sent directly"))) (mu4e~main-view-real nil nil) (goto-char curpos))) ;; (progn ;; (define-key mu4e-compose-mode-map (kbd "C-c m") 'mu4e~main-toggle-mail-sending-mode) ;; (define-key mu4e-view-mode-map (kbd "C-c m") 'mu4e~main-toggle-mail-sending-mode) ;; (define-key mu4e-compose-mode-map (kbd "C-c m") 'mu4e~main-toggle-mail-sending-mode) ;; (define-key mu4e-headers-mode-map (kbd "C-c m") 'mu4e~main-toggle-mail-sending-mode) ;; ) (provide 'mu4e-main) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-contrib.el����������������������������������������������������������������������0000644�0001750�0001750�00000020046�13020504332�012651� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e-contrib.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2013-2016 Dirk-Jan C. Binnema ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; Some user-contributed functions for mu4e ;; Contributed by sabof (require 'mu4e) (defun mu4e-headers-mark-all-unread-read () "Put a ! \(read) mark on all visible unread messages." (interactive) (mu4e-headers-mark-for-each-if (cons 'read nil) (lambda (msg param) (memq 'unread (mu4e-msg-field msg :flags))))) (defun mu4e-headers-flag-all-read () "Flag all visible messages as \"read\"." (interactive) (mu4e-headers-mark-all-unread-read) (mu4e-mark-execute-all t)) ;;; (defun mu4e-headers-mark-all () "Mark all messages within current query results and ask user to execute which action." (interactive) (mu4e-headers-mark-for-each-if (cons 'something nil) (lambda (msg param) t)) (mu4e-mark-execute-all)) ;;; ;;; Bookmark handlers ;; ;; Allow bookmarking a mu4e buffer in regular emacs bookmarks. ;; Probably this can be moved to mu4e-view.el. (add-hook 'mu4e-view-mode-hook #'(lambda () (set (make-local-variable 'bookmark-make-record-function) 'mu4e-view-bookmark-make-record))) ;; And this can be moved to mu4e-headers.el. (add-hook 'mu4e-headers-mode-hook #'(lambda () (set (make-local-variable 'bookmark-make-record-function) 'mu4e-view-bookmark-make-record))) (defun mu4e-view-bookmark-make-record () "Make a bookmark entry for a mu4e buffer. Note that this is an emacs bookmark, not to be confused with `mu4e-bookmarks'." (let* ((msg (mu4e-message-at-point)) (maildir (plist-get msg :maildir)) (date (format-time-string "%Y%m%d" (plist-get msg :date))) (query (format "maildir:%s date:%s" maildir date)) (docid (plist-get msg :docid)) (mode (symbol-name major-mode)) (subject (or (plist-get msg :subject) "No subject"))) `(,subject ,@(bookmark-make-record-default 'no-file 'no-context) (location . (,query . ,docid)) (mode . ,mode) (handler . mu4e-bookmark-jump)))) (defun mu4e-bookmark-jump (bookmark) "Handler function for record returned by `mu4e-view-bookmark-make-record'. BOOKMARK is a bookmark name or a bookmark record." (let* ((path (bookmark-prop-get bookmark 'location)) (mode (bookmark-prop-get bookmark 'mode)) (docid (cdr path)) (query (car path))) (call-interactively 'mu4e) (mu4e-headers-search query) (sit-for 0.5) (mu4e~headers-goto-docid docid) (mu4e~headers-highlight docid) (unless (string= mode "mu4e-headers-mode") (call-interactively 'mu4e-headers-view-message) (run-with-timer 0.1 nil (lambda (bmk) (bookmark-default-handler `("" (buffer . ,(current-buffer)) . ,(bookmark-get-bookmark-record bmk)))) bookmark)))) ;;; handling spam with Bogofilter with possibility to define it for SpamAssassin ;;; contributed by Gour ;; to add the actions to the menu, you can use something like: ;; (add-to-list 'mu4e-headers-actions ;; '("sMark as spam" . mu4e-register-msg-as-spam) t) ;; (add-to-list 'mu4e-headers-actions ;; '("hMark as ham" . mu4e-register-msg-as-ham) t) (defvar mu4e-register-as-spam-cmd nil "Command for invoking spam processor to register message as spam, for example for bogofilter, use \"/usr/bin/bogofilter -Ns < %s\" ") (defvar mu4e-register-as-ham-cmd nil "Command for invoking spam processor to register message as ham. For example for bogofile, use \"/usr/bin/bogofilter -Sn < %s\"") (defun mu4e-register-msg-as-spam (msg) "Mark message as spam." (interactive) (let* ((path (shell-quote-argument (mu4e-message-field msg :path))) (command (format mu4e-register-as-spam-cmd path))) ;; re-register msg as spam (shell-command command)) (mu4e-mark-at-point 'delete nil)) (defun mu4e-register-msg-as-ham (msg) "Mark message as ham." (interactive) (let* ((path (shell-quote-argument(mu4e-message-field msg :path))) (command (format mu4e-register-as-ham-cmd path))) ;; re-register msg as ham (shell-command command)) (mu4e-mark-at-point 'something nil)) ;; (add-to-list 'mu4e-view-actions ;; '("sMark as spam" . mu4e-view-register-msg-as-spam) t) ;; (add-to-list 'mu4e-view-actions ;; '("hMark as ham" . mu4e-view-register-msg-as-ham) t) (defun mu4e-view-register-msg-as-spam (msg) "Mark message as spam (view mode)." (interactive) (let* ((path (shell-quote-argument (mu4e-message-field msg :path))) (command (format mu4e-register-as-spam-cmd path))) (shell-command command)) (mu4e-view-mark-for-delete)) (defun mu4e-view-register-msg-as-ham (msg) "Mark message as ham (view mode)." (interactive) (let* ((path (shell-quote-argument(mu4e-message-field msg :path))) (command (format mu4e-register-as-ham-cmd path))) (shell-command command)) (mu4e-view-mark-for-something)) ;;; end of spam-filtering functions ;;; eshell functions ;; Code for 'gnus-dired-attached' modifed to run from eshell, allowing files to ;; be attached to an email via mu4e using the eshell. Does not depend on gnus. (defun eshell/mu4e-attach (&rest args) "Attach files to a mu4e message using eshell. If no mu4e buffers found, compose a new message and then attach the file." (let ((destination nil) (files-str nil) (bufs nil) ;; Remove directories from the list (files-to-attach (delq nil (mapcar (lambda (f) (if (or (not (file-exists-p f)) (file-directory-p f)) nil (expand-file-name f))) (eshell-flatten-list (reverse args)))))) ;; warn if user tries to attach without any files marked (if (null files-to-attach) (error "No files to attach") (setq files-str (mapconcat (lambda (f) (file-name-nondirectory f)) files-to-attach ", ")) (setq bufs (mu4e~active-composition-buffers)) ;; set up destination mail composition buffer (if (and bufs (y-or-n-p "Attach files to existing mail composition buffer? ")) (setq destination (if (= (length bufs) 1) (get-buffer (car bufs)) (let ((prompt (mu4e-format "%s" "Attach to buffer"))) (funcall mu4e-completing-read-function prompt bufs)))) ;; setup a new mail composition buffer (if (y-or-n-p "Compose new mail and attach this file? ") (progn (mu4e-compose-new) (setq destination (current-buffer))))) ;; if buffer was found, set buffer to destination buffer, and attach files (if (not (eq destination 'nil)) (progn (set-buffer destination) (goto-char (point-max)) ;attach at end of buffer (while files-to-attach (mml-attach-file (car files-to-attach) (or (mm-default-file-encoding (car files-to-attach)) "application/octet-stream") nil) (setq files-to-attach (cdr files-to-attach))) (message "Attached file(s) %s" files-str)) (message "No buffer to attach file to."))))) ;;; end of eshell functions (provide 'mu4e-contrib) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/texi.texi.in�������������������������������������������������������������������������0000644�0001750�0001750�00000000117�12605152237�012300� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@c the version for mu for including in texinfo docs @set mu-version @VERSION@ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-meta.el.in����������������������������������������������������������������������0000644�0001750�0001750�00000000437�13020504332�012546� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; auto-generated (defconst mu4e-mu-version "@VERSION@" "Required mu binary version; mu4e's version must agree with this.") (defconst mu4e-builddir "@abs_top_builddir@" "Top-level build directory.") (defconst mu4e-doc-dir "@MU_DOC_DIR@" "Mu4e's data-dir.") (provide 'mu4e-meta) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-headers.el����������������������������������������������������������������������0000644�0001750�0001750�00000205337�13020504332�012634� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e-headers.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; In this file are function related mu4e-headers-mode, to creating the list of ;; one-line descriptions of emails, aka 'headers' (not to be confused with ;; headers like 'To:' or 'Subject:') ;; Code: (eval-when-compile (byte-compile-disable-warning 'cl-functions)) (require 'cl) (require 'fringe) (require 'hl-line) (require 'mu4e-utils) ;; utility functions (require 'mu4e-proc) (require 'mu4e-vars) (require 'mu4e-mark) (require 'mu4e-compose) (require 'mu4e-actions) (require 'mu4e-message) ;; the headers view (defgroup mu4e-headers nil "Settings for the headers view." :group 'mu4e) (defcustom mu4e-headers-fields '( (:human-date . 12) (:flags . 6) (:mailing-list . 10) (:from . 22) (:subject . nil)) "A list of header fields to show in the headers buffer. Each element has the form (HEADER . WIDTH), where HEADER is one of the available headers (see `mu4e-header-info') and WIDTH is the respective width in characters. A width of `nil' means 'unrestricted', and this is best reserved for the rightmost (last) field. Note that emacs may become very slow with excessively long lines (1000s of characters), so if you regularly get such messages, you want to avoid fields with `nil' altogether." :type `(repeat (cons (choice ,@(mapcar (lambda (h) (list 'const :tag (plist-get (cdr h) :help) (car h))) mu4e-header-info)) (choice (integer :tag "width") (const :tag "unrestricted width" nil)))) :group 'mu4e-headers) (defcustom mu4e-headers-date-format "%x" "Date format to use in the headers view. In the format of `format-time-string'." :type 'string :group 'mu4e-headers) (defcustom mu4e-headers-time-format "%X" "Time format to use in the headers view. In the format of `format-time-string'." :type 'string :group 'mu4e-headers) (defcustom mu4e-headers-long-date-format "%c" "Date format to use in the headers view tooltip. In the format of `format-time-string'." :type 'string :group 'mu4e-headers) (defcustom mu4e-headers-visible-lines 10 "Number of lines to display in the header view when using the horizontal split-view. This includes the header-line at the top, and the mode-line." :type 'integer :group 'mu4e-headers) (defcustom mu4e-headers-visible-columns 30 "Number of columns to display for the header view when using the vertical split-view." :type 'integer :group 'mu4e-headers) (defcustom mu4e-headers-auto-update t "Whether to automatically update the current headers buffer if an indexing operation showed changes." :type 'boolean :group 'mu4e-headers) (defcustom mu4e-headers-results-limit 500 "Maximum number of results to show; this affects performance quite a bit, especially when `mu4e-headers-include-related' is non-nil. Set to -1 for no limits, and you temporarily (for one query) ignore the limit by pressing a C-u before invoking the search." :type '(choice (const :tag "Unlimited" -1) (integer :tag "Limit")) :group 'mu4e-headers) (make-obsolete-variable 'mu4e-search-results-limit 'mu4e-headers-results-limit "0.9.9.5-dev6") (defcustom mu4e-headers-skip-duplicates nil "With this option set to non-nil, show only one of duplicate messages. This is useful when you have multiple copies of the same message, which is a common occurence for example when using Gmail and offlineimap." :type 'boolean :group 'mu4e-headers) (defcustom mu4e-headers-include-related nil "With this option set to non-nil, not just return the matches for a searches, but also messages that are related (through their references) to these messages. This can be useful e.g. to include sent messages into message threads." :type 'boolean :group 'mu4e-headers) (defvar mu4e-headers-hide-predicate nil "Predicate function applied to headers before they are shown; if function is nil or evaluates to nil, show the header, otherwise don't. function takes one parameter MSG, which is the message plist for the message to be hidden or not. Example that hides all 'trashed' messages: (setq mu4e-headers-hide-predicate (lambda (msg) (member 'trashed (mu4e-message-field msg :flags)))) Note that this is merely a display filter.") (defcustom mu4e-headers-visible-flags '(draft flagged new passed replied seen trashed attach encrypted signed unread) "An ordered list of flags to show in the headers buffer. Each element is a symbol in the list (DRAFT FLAGGED NEW PASSED REPLIED SEEN TRASHED ATTACH ENCRYPTED SIGNED UNREAD)." :type '(set (const :tag "Draft" draft) (const :tag "Flagged" flagged) (const :tag "New" new) (const :tag "Passed" passed) (const :tag "Replied" replied) (const :tag "Seen" seen) (const :tag "Trashed" trashed) (const :tag "Attach" attach) (const :tag "Encrypted" encrypted) (const :tag "Signed" signed) (const :tag "Unread" unread)) :group 'mu4e-headers) (defcustom mu4e-headers-found-hook nil "Hook run just *after* all of the headers for the last search query have been received and are displayed." :type 'hook :group 'mu4e-headers) (defcustom mu4e-headers-search-bookmark-hook nil "Hook run just after we invoke a bookmarked search. This function receives the query as its parameter. The reason to use this instead of `mu4e-headers-search-hook' is if you only want to execute a hook when a search is entered via a bookmark, e.g. if you'd like to treat the bookmarks as a custom folder and change the options for the search, e.g. `mu4e-headers-show-threads', `mu4e-headers-include-related', `mu4e-headers-skip-duplicates` or `mu4e-headers-results-limit'." :type 'hook :group 'mu4e-headers) (defcustom mu4e-headers-search-hook nil "Hook run just before executing a new search operation. This function receives the query as its parameter. This is a more general hook facility than the `mu4e-headers-search-bookmark-hook'. It gets called on every executed search, not just those that are invoked via bookmarks, but also manually invoked searches." :type 'hook :group 'mu4e-headers) (defvar mu4e-headers-sort-field :date "Field to sort the headers by. Field must be a symbol, one of: :date, :subject, :size, :prio, :from, :to.") (defvar mu4e-headers-sort-direction 'descending "Direction to sort by; a symbol either `descending' (sorting Z->A) or `ascending' (sorting A->Z).") ;; marks for headers of the form; each is a cons-cell (basic . fancy) ;; each of which is basic ascii char and something fancy, respectively (defvar mu4e-headers-draft-mark '("D" . "⚒") "Draft.") (defvar mu4e-headers-flagged-mark '("F" . "✚") "Flagged.") (defvar mu4e-headers-new-mark '("N" . "✱") "New.") (defvar mu4e-headers-passed-mark '("P" . "❯") "Passed (fwd).") (defvar mu4e-headers-replied-mark '("R" . "❮") "Replied.") (defvar mu4e-headers-seen-mark '("S" . "✔") "Seen.") (defvar mu4e-headers-trashed-mark '("T" . "⏚") "Trashed.") (defvar mu4e-headers-attach-mark '("a" . "⚓") "W/ attachments.") (defvar mu4e-headers-encrypted-mark '("x" . "⚴") "Encrypted.") (defvar mu4e-headers-signed-mark '("s" . "☡") "Signed.") (defvar mu4e-headers-unread-mark '("u" . "⎕") "Unread.") ;; thread prefix marks (defvar mu4e-headers-has-child-prefix '("+" . "◼ ") "Parent.") (defvar mu4e-headers-empty-parent-prefix '("-" . "◽ ") "Orphan.") (defvar mu4e-headers-first-child-prefix '("\\" . "┗▶") "First child.") (defvar mu4e-headers-duplicate-prefix '("=" . "≡ ") "Duplicate.") (defvar mu4e-headers-default-prefix '("|" . "│ ") "Default.") (defvar mu4e-headers-actions '( ("capture message" . mu4e-action-capture-message) ("show this thread" . mu4e-action-show-thread)) "List of actions to perform on messages in the headers list. The actions are of the form (NAME SHORTCUT FUNC) where: * NAME is the name of the action (e.g. \"Count lines\") * SHORTCUT is a one-character shortcut to call this action * FUNC is a function which receives a message plist as an argument.") (defvar mu4e-headers-custom-markers '(("Older than" (lambda (msg date) (time-less-p (mu4e-msg-field msg :date) date)) (lambda () (mu4e-get-time-date "Match messages before: "))) ("Newer than" (lambda (msg date) (time-less-p date (mu4e-msg-field msg :date))) (lambda () (mu4e-get-time-date "Match messages after: "))) ("Bigger than" (lambda (msg bytes) (> (mu4e-msg-field msg :size) (* 1024 bytes))) (lambda () (read-number "Match messages bigger than (Kbytes): ")))) "List of custom markers -- functions to mark message that match some custom function. Each of the list members has the following format: (NAME PREDICATE-FUNC PARAM-FUNC) * NAME is the name of the predicate function, and the first character is the shortcut (so keep those unique). * PREDICATE-FUNC is a function that takes two parameters, MSG and (optionally) PARAM, and should return non-nil when there's a match. * PARAM-FUNC is function that is evaluated once, and its value is then passed to PREDICATE-FUNC as PARAM. This is useful for getting user-input.") (defvar mu4e-headers-show-threads t "Whether to show threads in the headers list.") (defvar mu4e-headers-full-search nil "Whether to show all results. If this is nil show results up to `mu4e-search-results-limit')") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; internal variables/constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; docid cookies (defconst mu4e~headers-docid-pre "\376" "Each header starts (invisibly) with the `mu4e~headers-docid-pre', followed by the docid, followed by `mu4e~headers-docid-post'.") (defconst mu4e~headers-docid-post "\377" "Each header starts (invisibly) with the `mu4e~headers-docid-pre', followed by the docid, followed by `mu4e~headers-docid-post'.") (defvar mu4e~headers-view-win nil "The view window connected to this headers view.") (defvar mu4e~headers-sort-field-choices '( ("date" . :date) ("from" . :from) ("maildir" . :maildir) ("prio" . :prio) ("zsize" . :size) ("subject" . :subject) ("to" . :to)) "List of cells describing the various sort-options. In the format needed for `mu4e-read-option'.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~headers-clear () "Clear the header buffer and related data structures." (when (buffer-live-p mu4e~headers-buffer) (let ((inhibit-read-only t)) (with-current-buffer mu4e~headers-buffer (setq mu4e~view-msg nil) (mu4e~mark-clear) (erase-buffer))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; handler functions ;; ;; next are a bunch of handler functions; those will be called from mu4e~proc in ;; response to output from the server process (defun mu4e~headers-view-handler (msg) "Handler function for displaying a message." (mu4e-view msg mu4e~headers-buffer)) (defun mu4e~headers-view-this-message-p (docid) "Is DOCID currently being viewed?" (let ((viewbuf (get-buffer mu4e~view-buffer-name))) (when (and viewbuf (buffer-live-p viewbuf)) (with-current-buffer viewbuf (eq docid (plist-get mu4e~view-msg :docid)))))) (defun mu4e~headers-update-handler (msg is-move) "Update handler, will be called when a message has been updated in the database. This function will update the current list of headers." (when (buffer-live-p mu4e~headers-buffer) (with-current-buffer mu4e~headers-buffer (let* ((docid (mu4e-message-field msg :docid)) (initial-message-at-point (mu4e~headers-docid-at-point)) (initial-column (current-column)) (point (mu4e~headers-docid-pos docid))) (when point ;; is the message present in this list? ;; if it's marked, unmark it now (when (mu4e-mark-docid-marked-p docid) (mu4e-mark-set 'unmark)) ;; re-use the thread info from the old one; this is needed because ;; *update* messages don't have thread info by themselves (unlike ;; search results) ;; since we still have the search results, re-use ;; those (plist-put msg :thread (mu4e~headers-field-for-docid docid :thread)) ;; first, remove the old one (otherwise, we'd have two headers with ;; the same docid... (mu4e~headers-remove-handler docid t) ;; if we're actually viewing this message (in mu4e-view mode), we ;; update it; that way, the flags can be updated, as well as the path ;; (which is useful for viewing the raw message) (when (mu4e~headers-view-this-message-p docid) (mu4e-view msg mu4e~headers-buffer)) ;; now, if this update was about *moving* a message, we don't show it ;; anymore (of course, we cannot be sure if the message really no ;; longer matches the query, but this seem a good heuristic. if it ;; was only a flag-change, show the message with its updated flags. (unless is-move (mu4e~headers-header-handler msg point)) (if (and initial-message-at-point (mu4e~headers-goto-docid initial-message-at-point)) (progn (move-to-column initial-column) (mu4e~headers-highlight initial-message-at-point)) ;; attempt to highlight the corresponding line and make it visible (mu4e~headers-highlight docid)) (run-hooks 'mu4e-msg-changed-hook)))))) (defun mu4e~headers-remove-handler (docid &optional skip-hook) "Remove handler, will be called when a message with DOCID has been removed from the database. This function will hide the removed message from the current list of headers. If the message is not present, don't do anything. If SKIP-HOOK is not nil, `mu4e-msg-changed-hook' will be invoked." (when (buffer-live-p mu4e~headers-buffer) (with-current-buffer mu4e~headers-buffer (mu4e~headers-remove-header docid t) ;; if we were viewing this message, close it now. (when (and (mu4e~headers-view-this-message-p docid) (buffer-live-p mu4e~view-buffer)) (with-current-buffer mu4e~view-buffer ;; XXX it seems this sometimes fails; investigate; ;; for now, just ignore the error (ignore-errors (kill-buffer-and-window)))) (unless skip-hook (run-hooks 'mu4e-msg-changed-hook))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defsubst mu4e~headers-contact-str (contacts) "Turn the list of contacts CONTACTS (with elements (NAME . EMAIL) into a string." (mapconcat (lambda (ct) (let ((name (car ct)) (email (cdr ct))) (or name email "?"))) contacts ", ")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defsubst mu4e~headers-thread-prefix (thread) "Calculate the thread prefix based on thread info THREAD." (when thread (let ((get-prefix (lambda (cell) (if mu4e-use-fancy-chars (cdr cell) (car cell))))) (concat (make-string (* (if (plist-get thread :empty-parent) 0 1) (plist-get thread :level)) ?\s) (cond ((plist-get thread :has-child) (funcall get-prefix mu4e-headers-has-child-prefix)) ((plist-get thread :empty-parent) (funcall get-prefix mu4e-headers-empty-parent-prefix)) ((plist-get thread :first-child) (funcall get-prefix mu4e-headers-first-child-prefix)) ((plist-get thread :duplicate) (funcall get-prefix mu4e-headers-duplicate-prefix)) (t (funcall get-prefix mu4e-headers-default-prefix))) " ")))) (defsubst mu4e~headers-flags-str (flags) "Get a display string for the flags. Note that `mu4e-flags-to-string' is for internal use only; this function is for display. (This difference is significant, since internally, the Maildir spec determines what the flags look like, while our display may be different)." (let ((str "") (get-prefix (lambda (cell) (if mu4e-use-fancy-chars (cdr cell) (car cell))))) (dolist (flag mu4e-headers-visible-flags) (when (member flag flags) (setq str (concat str (case flag ('draft (funcall get-prefix mu4e-headers-draft-mark)) ('flagged (funcall get-prefix mu4e-headers-flagged-mark)) ('new (funcall get-prefix mu4e-headers-new-mark)) ('passed (funcall get-prefix mu4e-headers-passed-mark)) ('replied (funcall get-prefix mu4e-headers-replied-mark)) ('seen (funcall get-prefix mu4e-headers-seen-mark)) ('trashed (funcall get-prefix mu4e-headers-trashed-mark)) ('attach (funcall get-prefix mu4e-headers-attach-mark)) ('encrypted (funcall get-prefix mu4e-headers-encrypted-mark)) ('signed (funcall get-prefix mu4e-headers-signed-mark)) ('unread (funcall get-prefix mu4e-headers-unread-mark))))))) str)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defconst mu4e-headers-from-or-to-prefix '("" . "To ") "Prefix for the :from-or-to field. It's a cons cell with the car element being the From: prefix, the cdr element the To: prefix.") (defsubst mu4e~headers-from-or-to (msg) "When the from address for message MSG is one of the the user's addresses, \(as per `mu4e-user-mail-address-list'), show the To address; otherwise ; show the from address; prefixed with the appropriate `mu4e-headers-from-or-to-prefix'." (let ((addr (cdr-safe (car-safe (mu4e-message-field msg :from))))) (if (mu4e-user-mail-address-p addr) (concat (cdr mu4e-headers-from-or-to-prefix) (mu4e~headers-contact-str (mu4e-message-field msg :to))) (concat (car mu4e-headers-from-or-to-prefix) (mu4e~headers-contact-str (mu4e-message-field msg :from)))))) (defsubst mu4e~headers-human-date (msg) "Show a 'human' date. If the date is today, show the time, otherwise, show the date. The formats used for date and time are `mu4e-headers-date-format' and `mu4e-headers-time-format'." (let ((date (mu4e-msg-field msg :date))) (if (equal date '(0 0 0)) "None" (let ((day1 (decode-time date)) (day2 (decode-time (current-time)))) (if (and (eq (nth 3 day1) (nth 3 day2)) ;; day (eq (nth 4 day1) (nth 4 day2)) ;; month (eq (nth 5 day1) (nth 5 day2))) ;; year (format-time-string mu4e-headers-time-format date) (format-time-string mu4e-headers-date-format date)))))) (defsubst mu4e~headers-thread-subject (msg) "Get the subject if it is the first one in a thread; otherwise, return the thread-prefix without the subject-text. In other words, show the subject of a thread only once, similar to e.g. 'mutt'." (let* ((tinfo (mu4e-message-field msg :thread)) (subj (mu4e-msg-field msg :subject))) (concat ;; prefix subject with a thread indicator (mu4e~headers-thread-prefix tinfo) (if (or (not tinfo) (zerop (plist-get tinfo :level)) (plist-get tinfo :empty-parent)) (truncate-string-to-width subj 600) "")))) (defsubst mu4e~headers-mailing-list (list) "Get some identifier for the mailing list." (if list (propertize (mu4e-get-mailing-list-shortname list) 'help-echo list) "")) (defun mu4e~headers-custom-field (msg field) "Show some custom header field, or raise an error if it is not found." (let* ((item (or (assoc field mu4e-header-info-custom) (mu4e-error "field %S not found" field))) (func (or (plist-get (cdr-safe item) :function) (mu4e-error "no :function defined for field %S %S" field (cdr item))))) (funcall func msg))) (defun mu4e~headers-field-apply-basic-properties (msg field val width) (case field (:subject (concat ;; prefix subject with a thread indicator (mu4e~headers-thread-prefix (mu4e-message-field msg :thread)) ;; "["(plist-get (mu4e-message-field msg :thread) :path) "] " ;; work-around: emacs' display gets really slow when lines are too long; ;; so limit subject length to 600 (truncate-string-to-width val 600))) (:thread-subject (mu4e~headers-thread-subject msg)) ((:maildir :path :message-id) val) ((:to :from :cc :bcc) (mu4e~headers-contact-str val)) ;; if we (ie. `user-mail-address' is the 'From', show ;; 'To', otherwise show From (:from-or-to (mu4e~headers-from-or-to msg)) (:date (format-time-string mu4e-headers-date-format val)) (:mailing-list (mu4e~headers-mailing-list val)) (:human-date (propertize (mu4e~headers-human-date msg) 'help-echo (format-time-string mu4e-headers-long-date-format (mu4e-msg-field msg :date)))) (:flags (propertize (mu4e~headers-flags-str val) 'help-echo (format "%S" val))) (:tags (propertize (mapconcat 'identity val ", "))) (:size (mu4e-display-size val)) (t (mu4e~headers-custom-field msg field)))) (defun mu4e~headers-field-truncate-to-width (_msg _field val width) "Truncate VAL to WIDTH." (if width (truncate-string-to-width val width 0 ?\s t) val)) (defvar mu4e~headers-field-handler-functions '(mu4e~headers-field-apply-basic-properties mu4e~headers-field-truncate-to-width)) (defun mu4e~headers-field-handler (f-w msg) "Create a description of the field of MSG described by F-W." (let* ((field (car f-w)) (width (cdr f-w)) (val (mu4e-message-field msg (car f-w)))) (dolist (func mu4e~headers-field-handler-functions) (setq val (funcall func msg field val width))) val)) (defvar mu4e~headers-line-handler-functions '(mu4e~headers-line-apply-flag-face)) (defun mu4e~headers-line-apply-flag-face (msg line) "Adjust LINE's face property based on FLAGS." (let* ((flags (mu4e-message-field msg :flags)) (face (cond ((memq 'trashed flags) 'mu4e-trashed-face) ((memq 'draft flags) 'mu4e-draft-face) ((or (memq 'unread flags) (memq 'new flags)) 'mu4e-unread-face) ((memq 'flagged flags) 'mu4e-flagged-face) ((memq 'replied flags) 'mu4e-replied-face) ((memq 'passed flags) 'mu4e-forwarded-face) (t 'mu4e-header-face)))) ;; hmmm, this only works with emacs 24.4+ (when (fboundp 'add-face-text-property) (add-face-text-property 0 (length line) face t line)) line)) (defun mu4e~headers-line-handler (msg line) (dolist (func mu4e~headers-line-handler-functions) (setq line (funcall func msg line))) line) ;; note: this function is very performance-sensitive (defun mu4e~headers-header-handler (msg &optional point) "Create a one line description of MSG in this buffer, at POINT, if provided, or at the end of the buffer otherwise." (unless (and mu4e-headers-hide-predicate (funcall mu4e-headers-hide-predicate msg)) (let ((docid (mu4e-message-field msg :docid)) (line (mapconcat (lambda (f-w) (mu4e~headers-field-handler f-w msg)) mu4e-headers-fields " "))) (setq line (mu4e~headers-line-handler msg line)) (mu4e~headers-add-header line docid point msg)))) (defconst mu4e~no-matches "No matching messages found") (defconst mu4e~end-of-results "End of search results") (defun mu4e~headers-found-handler (count) "Create a one line description of the number of headers found after the end of the search results." (when (buffer-live-p mu4e~headers-buffer) (with-current-buffer mu4e~headers-buffer (save-excursion (goto-char (point-max)) (let ((inhibit-read-only t) (str (if (zerop count) mu4e~no-matches mu4e~end-of-results))) (insert (propertize str 'face 'mu4e-system-face 'intangible t)) (unless (zerop count) (mu4e-message "Found %d matching message%s" count (if (= 1 count) "" "s"))))) ;; if we need to jump to some specific message, do so now (goto-char (point-min)) (when mu4e~headers-msgid-target (mu4e-headers-goto-message-id mu4e~headers-msgid-target)) (when (and mu4e~headers-view-target (mu4e-message-at-point 'noerror)) ;; view the message at point when there is one. (mu4e-headers-view-message)) (setq mu4e~headers-view-target nil mu4e~headers-msgid-target nil)) (when (mu4e~headers-docid-at-point) (mu4e~headers-highlight (mu4e~headers-docid-at-point))) ;; run-hooks (run-hooks 'mu4e-headers-found-hook))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro mu4e~headers-defun-mark-for (mark) "Define a function mu4e~headers-mark-MARK." (let ((funcname (intern (format "mu4e-headers-mark-for-%s" mark))) (docstring (format "Mark header at point with %s." mark))) `(progn (defun ,funcname () ,docstring (interactive) (mu4e-headers-mark-and-next ',mark)) (put ',funcname 'definition-name ',mark)))) (mu4e~headers-defun-mark-for refile) (mu4e~headers-defun-mark-for something) (mu4e~headers-defun-mark-for delete) (mu4e~headers-defun-mark-for flag) (mu4e~headers-defun-mark-for move) (mu4e~headers-defun-mark-for read) (mu4e~headers-defun-mark-for trash) (mu4e~headers-defun-mark-for unflag) (mu4e~headers-defun-mark-for untrash) (mu4e~headers-defun-mark-for unmark) (mu4e~headers-defun-mark-for unread) (mu4e~headers-defun-mark-for action) ;;; headers-mode and mode-map ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-headers-mode-map nil "Keymap for *mu4e-headers* buffers.") (unless mu4e-headers-mode-map (setq mu4e-headers-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) ;; for terminal users (define-key map (kbd "C-c C-u") 'mu4e-update-mail-and-index) (define-key map "s" 'mu4e-headers-search) (define-key map "S" 'mu4e-headers-search-edit) (define-key map "/" 'mu4e-headers-search-narrow) (define-key map "j" 'mu4e~headers-jump-to-maildir) (define-key map (kbd "<M-left>") 'mu4e-headers-query-prev) (define-key map (kbd "\\") 'mu4e-headers-query-prev) (define-key map (kbd "<M-right>") 'mu4e-headers-query-next) (define-key map "b" 'mu4e-headers-search-bookmark) (define-key map "B" 'mu4e-headers-search-bookmark-edit) (define-key map "O" 'mu4e-headers-change-sorting) (define-key map "P" 'mu4e-headers-toggle-threading) (define-key map "Q" 'mu4e-headers-toggle-full-search) (define-key map "W" 'mu4e-headers-toggle-include-related) (define-key map "V" 'mu4e-headers-toggle-skip-duplicates) (define-key map "q" 'mu4e~headers-quit-buffer) (define-key map "g" 'mu4e-headers-rerun-search) ;; for compatibility (define-key map "%" 'mu4e-headers-mark-pattern) (define-key map "t" 'mu4e-headers-mark-subthread) (define-key map "T" 'mu4e-headers-mark-thread) ;; navigation between messages (define-key map "p" 'mu4e-headers-prev) (define-key map "n" 'mu4e-headers-next) (define-key map (kbd "<M-up>") 'mu4e-headers-prev) (define-key map (kbd "<M-down>") 'mu4e-headers-next) (define-key map (kbd "[") 'mu4e-headers-prev-unread) (define-key map (kbd "]") 'mu4e-headers-next-unread) ;; change the number of headers (define-key map (kbd "C-+") 'mu4e-headers-split-view-grow) (define-key map (kbd "C--") 'mu4e-headers-split-view-shrink) (define-key map (kbd "<C-kp-add>") 'mu4e-headers-split-view-grow) (define-key map (kbd "<C-kp-subtract>") 'mu4e-headers-split-view-shrink) (define-key map ";" 'mu4e-context-switch) ;; switching to view mode (if it's visible) (define-key map "y" 'mu4e-select-other-view) ;; marking/unmarking ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-key map (kbd "<backspace>") 'mu4e-headers-mark-for-trash) (define-key map (kbd "d") 'mu4e-headers-mark-for-trash) (define-key map (kbd "<delete>") 'mu4e-headers-mark-for-delete) (define-key map (kbd "<deletechar>") 'mu4e-headers-mark-for-delete) (define-key map (kbd "D") 'mu4e-headers-mark-for-delete) (define-key map (kbd "m") 'mu4e-headers-mark-for-move) (define-key map (kbd "r") 'mu4e-headers-mark-for-refile) (define-key map (kbd "?") 'mu4e-headers-mark-for-unread) (define-key map (kbd "!") 'mu4e-headers-mark-for-read) (define-key map (kbd "A") 'mu4e-headers-mark-for-action) (define-key map (kbd "u") 'mu4e-headers-mark-for-unmark) (define-key map (kbd "+") 'mu4e-headers-mark-for-flag) (define-key map (kbd "-") 'mu4e-headers-mark-for-unflag) (define-key map (kbd "=") 'mu4e-headers-mark-for-untrash) (define-key map (kbd "&") 'mu4e-headers-mark-custom) (define-key map (kbd "*") 'mu4e-headers-mark-for-something) (define-key map (kbd "<kp-multiply>") 'mu4e-headers-mark-for-something) (define-key map (kbd "<insertchar>") 'mu4e-headers-mark-for-something) (define-key map (kbd "<insert>") 'mu4e-headers-mark-for-something) (define-key map (kbd "#") 'mu4e-mark-resolve-deferred-marks) (define-key map "U" 'mu4e-mark-unmark-all) (define-key map "x" 'mu4e-mark-execute-all) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-key map "a" 'mu4e-headers-action) ;; message composition (define-key map "R" 'mu4e-compose-reply) (define-key map "F" 'mu4e-compose-forward) (define-key map "C" 'mu4e-compose-new) (define-key map "E" 'mu4e-compose-edit) (define-key map (kbd "RET") 'mu4e-headers-view-message) (define-key map [mouse-2] 'mu4e-headers-view-message) (define-key map "$" 'mu4e-show-log) (define-key map "H" 'mu4e-display-manual) ;; menu (define-key map [menu-bar] (make-sparse-keymap)) (let ((menumap (make-sparse-keymap "Headers"))) (define-key map [menu-bar headers] (cons "Headers" menumap)) (define-key menumap [mu4e~headers-quit-buffer] '("Quit view" . mu4e~headers-quit-buffer)) (define-key menumap [display-help] '("Help" . mu4e-display-manual)) (define-key menumap [sepa0] '("--")) (define-key menumap [toggle-include-related] '(menu-item "Toggle related messages" mu4e-headers-toggle-include-related :button (:toggle . (and (boundp 'mu4e-headers-include-related) mu4e-headers-include-related)))) (define-key menumap [toggle-threading] '(menu-item "Toggle threading" mu4e-headers-toggle-threading :button (:toggle . (and (boundp 'mu4e-headers-show-threads) mu4e-headers-show-threads)))) (define-key menumap [sepa1] '("--")) (define-key menumap [execute-marks] '("Execute marks" . mu4e-mark-execute-all)) (define-key menumap [unmark-all] '("Unmark all" . mu4e-mark-unmark-all)) (define-key menumap [unmark] '("Unmark" . mu4e-headers-mark-for-unmark)) (define-key menumap [mark-pattern] '("Mark pattern" . mu4e-headers-mark-pattern)) (define-key menumap [mark-as-read] '("Mark as read" . mu4e-headers-mark-for-read)) (define-key menumap [mark-as-unread] '("Mark as unread" . mu4e-headers-mark-for-unread)) (define-key menumap [mark-delete] '("Mark for deletion" . mu4e-headers-mark-for-delete)) (define-key menumap [mark-trash] '("Mark for trash" . mu4e-headers-mark-for-trash)) (define-key menumap [mark-move] '("Mark for move" . mu4e-headers-mark-for-move)) (define-key menumap [sepa2] '("--")) (define-key menumap [resend] '("Resend" . mu4e-compose-resend)) (define-key menumap [forward] '("Forward" . mu4e-compose-forward)) (define-key menumap [reply] '("Reply" . mu4e-compose-reply)) (define-key menumap [compose-new] '("Compose new" . mu4e-compose-new)) (define-key menumap [sepa3] '("--")) (define-key menumap [query-next] '("Next query" . mu4e-headers-query-next)) (define-key menumap [query-prev] '("Previous query" . mu4e-headers-query-prev)) (define-key menumap [narrow-search] '("Narrow search" . mu4e-headers-search-narrow)) (define-key menumap [bookmark] '("Search bookmark" . mu4e-headers-search-bookmark)) (define-key menumap [jump] '("Jump to maildir" . mu4e~headers-jump-to-maildir)) (define-key menumap [refresh] '("Refresh" . mu4e-headers-rerun-search)) (define-key menumap [search] '("Search" . mu4e-headers-search)) (define-key menumap [sepa4] '("--")) (define-key menumap [view] '("View" . mu4e-headers-view-message)) (define-key menumap [next] '("Next" . mu4e-headers-next)) (define-key menumap [previous] '("Previous" . mu4e-headers-prev)) (define-key menumap [sepa5] '("--"))) map))) (fset 'mu4e-headers-mode-map mu4e-headers-mode-map) (defun mu4e~header-line-format () "Get the format for the header line." (let ((uparrow (if mu4e-use-fancy-chars " ▲" " ^")) (downarrow (if mu4e-use-fancy-chars " ▼" " V"))) (cons (make-string (+ mu4e~mark-fringe-len (floor (fringe-columns 'left t))) ?\s) (mapcar (lambda (item) (let* ((field (car item)) (width (cdr item)) (info (cdr (assoc field (append mu4e-header-info mu4e-header-info-custom)))) (require-full (plist-get info :require-full)) (sortable (plist-get info :sortable)) ;; if sortable, it is either t (when field is sortable itself) ;; or a symbol (if another field is used for sorting) (sortfield (when sortable (if (booleanp sortable) field sortable))) (help (plist-get info :help)) ;; triangle to mark the sorted-by column (arrow (when (and sortable (eq sortfield mu4e-headers-sort-field)) (if (eq mu4e-headers-sort-direction 'descending) downarrow uparrow))) (name (concat (plist-get info :shortname) arrow)) (map (make-sparse-keymap))) (when require-full (mu4e-error "Field %S is not supported in mu4e-headers-mode" field)) (when sortable (define-key map [header-line mouse-1] (lambda (&optional e) ;; getting the field, inspired by `tabulated-list-col-sort' (interactive "e") (let* ((obj (posn-object (event-start e))) (field (and obj (get-text-property 0 'field (car obj))))) ;; "t": if we're already sorted by field, the sort-order is ;; changed (mu4e-headers-change-sorting field t))))) (concat (propertize (if width (truncate-string-to-width name width 0 ?\s t) name) 'face (when arrow 'bold) 'help-echo help 'mouse-face (when sortable 'highlight) 'keymap (when sortable map) 'field field) " "))) mu4e-headers-fields)))) (defvar mu4e-headers-mode-abbrev-table nil) (defun mu4e~headers-do-auto-update () "Update the current headers buffer after indexing has brought some changes, `mu4e-headers-auto-update' is non-nil and there is no user-interaction ongoing." (when (and mu4e-headers-auto-update ;; must be set (zerop (mu4e-mark-marks-num)) ;; non active marks (not (active-minibuffer-window))) ;; no user input (with-current-buffer mu4e~headers-buffer (mu4e-headers-rerun-search)))) (define-derived-mode mu4e-headers-mode special-mode "mu4e:headers" "Major mode for displaying mu4e search results. \\{mu4e-headers-mode-map}." (use-local-map mu4e-headers-mode-map) (make-local-variable 'mu4e~headers-proc) (make-local-variable 'mu4e~highlighted-docid) (make-local-variable 'global-mode-string) (set (make-local-variable 'hl-line-face) 'mu4e-header-highlight-face) ;; maybe update the current headers upon indexing changes (add-hook 'mu4e-index-updated-hook 'mu4e~headers-do-auto-update nil t) (add-hook 'mu4e-index-updated-hook (lambda () (run-hooks 'mu4e-msg-changed-hook)) t t) (setq truncate-lines t buffer-undo-list t ;; don't record undo information overwrite-mode nil header-line-format (mu4e~header-line-format)) (mu4e~mark-initialize) ;; initialize the marking subsystem (hl-line-mode 1)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; highlighting (defvar mu4e~highlighted-docid nil "The highlighted docid") (defun mu4e~headers-highlight (docid) "Highlight the header with DOCID, or do nothing if it's not found. Also, unhighlight any previously highlighted headers." (with-current-buffer mu4e~headers-buffer (save-excursion ;; first, unhighlight the previously highlighted docid, if any (when (and docid mu4e~highlighted-docid (mu4e~headers-goto-docid mu4e~highlighted-docid)) (hl-line-unhighlight)) ;; now, highlight the new one (when (mu4e~headers-goto-docid docid) (hl-line-highlight))) (setq mu4e~highlighted-docid docid))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~headers-select-window () "When there is a visible window for the headers buffer, make sure to select it. This is needed when adding new headers, otherwise adding a lot of new headers looks really choppy." (let ((win (get-buffer-window mu4e~headers-buffer))) (when win (select-window win)))) ;;;; headers in the buffer are prefixed by an invisible string with the docid ;;;; followed by an EOT ('end-of-transmission', \004, ^D) non-printable ascii ;;;; character. this string also has a text-property with the docid. the former ;;;; is used for quickly finding a certain header, the latter for retrieving the ;;;; docid at point without string matching etc. (defsubst mu4e~headers-docid-cookie (docid) "Create an invisible string containing DOCID; this is to be used at the beginning of lines to identify headers." (propertize (format "%s%d%s" mu4e~headers-docid-pre docid mu4e~headers-docid-post) 'docid docid 'invisible t));; (defsubst mu4e~headers-docid-at-point (&optional point) "Get the docid for the header at POINT, or at current (point) if nil. Returns the docid, or nil if there is none." (save-excursion (when point (goto-char point)) (get-text-property (line-beginning-position) 'docid))) (defun mu4e~headers-goto-docid (docid &optional to-mark) "Go to the beginning of the line with the header with docid DOCID, or nil if it cannot be found. If the optional TO-MARK is non-nil, go to the point directly *after* the docid-cookie instead of the beginning of the line." (let ((oldpoint (point)) (newpoint)) (goto-char (point-min)) (setq newpoint (search-forward (mu4e~headers-docid-cookie docid) nil t)) (unless to-mark (if (null newpoint) (goto-char oldpoint) ;; not found; restore old pos (progn (beginning-of-line) ;; found, move to beginning of line (setq newpoint (point))))) newpoint)) ;; return the point, or nil if not found (defsubst mu4e~headers-docid-pos (docid) "Return the pos of the beginning of the line with the header with docid DOCID, or nil if it cannot be found." (let ((pos)) (save-excursion (setq pos (mu4e~headers-goto-docid docid))) pos)) (defsubst mu4e~headers-field-for-docid (docid field) "Get FIELD (a symbol, see `mu4e-headers-names') for the message with DOCID which must be present in the headers buffer." (save-excursion (when (mu4e~headers-goto-docid docid) (mu4e-message-field (mu4e-message-at-point) field)))) (defun mu4e-headers-goto-message-id (msgid) "Go to the next message with message-id MSGID. Return the message plist, or nil if not found." (mu4e-headers-find-if (lambda (msg) (let ((this-msgid (mu4e-message-field msg :message-id))) (when (and this-msgid (string= msgid this-msgid)) msg))))) ;;;; markers mark headers for (defun mu4e~headers-mark (docid mark) "(Visually) mark the header for DOCID with character MARK." (with-current-buffer mu4e~headers-buffer (let ((inhibit-read-only t) (oldpoint (point))) (unless (mu4e~headers-goto-docid docid) (mu4e-error "Cannot find message with docid %S" docid)) ;; now, we're at the beginning of the header, looking at ;; <docid>\004 ;; (which is invisible). jump past that… (unless (re-search-forward mu4e~headers-docid-post nil t) (mu4e-error "Cannot find the `mu4e~headers-docid-post' separator")) ;; clear old marks, and add the new ones. (let ((msg (get-text-property (point) 'msg))) (delete-char mu4e~mark-fringe-len) (insert (propertize (format mu4e~mark-fringe-format mark) 'face 'mu4e-header-marks-face 'docid docid 'msg msg))) (goto-char oldpoint)))) (defsubst mu4e~headers-add-header (str docid point &optional msg) "Add header STR with DOCID to the buffer at POINT if non-nil, or at (point-max) otherwise. If MSG is not nil, add it as the text-property `msg'." (when (buffer-live-p mu4e~headers-buffer) (with-current-buffer mu4e~headers-buffer (let ((inhibit-read-only t) (is-first-header (= (point-min) (point-max)))) (save-excursion (goto-char (if point point (point-max))) (insert (propertize (concat (mu4e~headers-docid-cookie docid) mu4e~mark-fringe str "\n") 'docid docid 'msg msg))))))) (defun mu4e~headers-remove-header (docid &optional ignore-missing) "Remove header with DOCID at point. When IGNORE-MISSING is non-nill, don't raise an error when the docid is not found." (with-current-buffer mu4e~headers-buffer (if (mu4e~headers-goto-docid docid) (let ((inhibit-read-only t)) (delete-region (line-beginning-position) (line-beginning-position 2))) (unless ignore-missing (mu4e-error "Cannot find message with docid %S" docid))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~headers-search-execute (expr ignore-history) "Search in the mu database for EXPR, and switch to the output buffer for the results. If IGNORE-HISTORY is true, do *not* update the query history stack." ;; note: we don't want to update the history if this query comes from ;; `mu4e~headers-query-next' or `mu4e~headers-query-prev'. (mu4e-hide-other-mu4e-buffers) (let* ((buf (get-buffer-create mu4e~headers-buffer-name)) (inhibit-read-only t) (maxnum (unless mu4e-headers-full-search mu4e-headers-results-limit))) (with-current-buffer buf (mu4e-headers-mode) (unless ignore-history ;; save the old present query to the history list (when mu4e~headers-last-query (mu4e~headers-push-query mu4e~headers-last-query 'past))) (setq mu4e~headers-buffer buf mode-name "mu4e-headers" mu4e~headers-last-query expr) (add-to-list 'global-mode-string '(:eval (concat (propertize (mu4e~quote-for-modeline mu4e~headers-last-query) 'face 'mu4e-modeline-face) " " (mu4e-context-label))))) (switch-to-buffer buf) (run-hook-with-args 'mu4e-headers-search-hook expr) (mu4e~proc-find expr mu4e-headers-show-threads mu4e-headers-sort-field mu4e-headers-sort-direction maxnum mu4e-headers-skip-duplicates mu4e-headers-include-related))) (defun mu4e~headers-redraw-get-view-window () "Close all windows, redraw the headers buffer based on the value of `mu4e-split-view', and return a window for the message view." (mu4e-hide-other-mu4e-buffers) (unless (buffer-live-p mu4e~headers-buffer) (mu4e-error "No headers buffer available")) (switch-to-buffer mu4e~headers-buffer) ;; kill the existing view win (when (buffer-live-p mu4e~view-buffer) (kill-buffer mu4e~view-buffer)) ;; get a new view window (setq mu4e~headers-view-win (let* ((new-win-func (cond ((eq mu4e-split-view 'horizontal) ;; split horizontally '(split-window-vertically mu4e-headers-visible-lines)) ((eq mu4e-split-view 'vertical) ;; split vertically '(split-window-horizontally mu4e-headers-visible-columns))))) (cond ((with-demoted-errors "Unable to split window: %S" (eval new-win-func))) (t ;; no splitting; just use the currently selected one (selected-window))))) mu4e~headers-view-win) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; search-based marking (defun mu4e-headers-for-each (func) "Call FUNC for each header, moving point to the header. FUNC receives one argument, the message s-expression for the corresponding header." (save-excursion (goto-char (point-min)) (while (search-forward mu4e~headers-docid-pre nil t) ;; not really sure why we need to jump to bol; we do need to, otherwise we ;; miss lines sometimes... (let ((msg (get-text-property (line-beginning-position) 'msg))) (when msg (funcall func msg)))))) (defun mu4e-headers-find-if (func &optional backward) "Move to the next header for which FUNC returns non-`nil', starting from the current position. FUNC receives one argument, the message s-expression for the corresponding header. If BACKWARD is non-`nil', search backwards. Returns the new position, or `nil' if nothing was found. If you want to exclude matches for the current message, you can use `mu4e-headers-find-if-next'." (let ((pos) (search-func (if backward 'search-backward 'search-forward))) (save-excursion (while (and (null pos) (funcall search-func mu4e~headers-docid-pre nil t)) ;; not really sure why we need to jump to bol; we do need to, otherwise we ;; miss lines sometimes... (let ((msg (get-text-property (line-beginning-position) 'msg))) (when (and msg (funcall func msg)) (setq pos (point)))))) (when pos (goto-char pos)))) (defun mu4e-headers-find-if-next (func &optional backwards) "Like `mu4e-headers-find-if', but do not match the current header. Move to the next or (if BACKWARDS is non-`nil') header for which FUNC returns non-`nil', starting from the current position." (let ((pos)) (save-excursion (if backwards (beginning-of-line) (end-of-line)) (setq pos (mu4e-headers-find-if func backwards))) (when pos (goto-char pos)))) (defvar mu4e~headers-regexp-hist nil "History list of regexps used.") (defun mu4e-headers-mark-for-each-if (markpair mark-pred &optional param) "Mark all headers for which predicate function MARK-PRED returns non-nil with MARKPAIR. MARK-PRED is function that receives two arguments, MSG (the message at point) and PARAM (a user-specified parameter). MARKPAIR is a cell (MARK . TARGET); see `mu4e-mark-at-point' for details about marks." (mu4e-headers-for-each (lambda (msg) (when (funcall mark-pred msg param) (mu4e-mark-at-point (car markpair) (cdr markpair)))))) (defun mu4e-headers-mark-pattern () "Ask user for a kind of mark (move, delete etc.), a field to match and a regular expression to match with. Then, mark all matching messages with that mark." (interactive) (let ((markpair (mu4e~mark-get-markpair "Mark matched messages with: " t)) (field (mu4e-read-option "Field to match: " '( ("subject" . :subject) ("from" . :from) ("to" . :to) ("cc" . :cc) ("bcc" . :bcc) ("list" . :mailing-list)))) (pattern (read-string (mu4e-format "Regexp:") nil 'mu4e~headers-regexp-hist))) (mu4e-headers-mark-for-each-if markpair (lambda (msg param) (let* ((do-mark) (value (mu4e-msg-field msg field))) (setq do-mark (if (member field '(:to :from :cc :bcc :reply-to)) (find-if (lambda (contact) (let ((name (car contact)) (email (cdr contact))) (or (and name (string-match pattern name)) (and email (string-match pattern email))))) value) (string-match pattern (or value ""))))))))) (defun mu4e-headers-mark-custom () "Mark messages based on a user-provided predicate function." (interactive) (let* ((pred (mu4e-read-option "Match function: " mu4e-headers-custom-markers)) (param (when (cdr pred) (eval (cdr pred)))) (markpair (mu4e~mark-get-markpair "Mark matched messages with: " t))) (mu4e-headers-mark-for-each-if markpair (car pred) param))) (defun mu4e~headers-get-thread-info (msg what) "Get WHAT (a symbol, either path or thread-id) for MSG." (let* ((thread (or (mu4e-message-field msg :thread) (mu4e-error "No thread info found"))) (path (or (plist-get thread :path) (mu4e-error "No threadpath found")))) (case what (path path) (thread-id (save-match-data ;; the thread id is the first segment of the thread path (when (string-match "^\\([[:xdigit:]]+\\):?" path) (match-string 1 path)))) (otherwise (mu4e-error "Not supported"))))) (defun mu4e-headers-mark-thread-using-markpair (markpair &optional subthread) "Mark the thread at point using the given markpair. If SUBTHREAD is non-nil, marking is limited to the message at point and its descendants." (let* ((mark (car markpair)) (allowed-marks (mapcar 'car mu4e-marks))) (unless (memq mark allowed-marks) (mu4e-error "The mark (%s) has to be one of: %s" mark allowed-marks))) ;; note: the tread id is shared by all messages in a thread (let* ((msg (mu4e-message-at-point)) (thread-id (mu4e~headers-get-thread-info msg 'thread-id)) (path (mu4e~headers-get-thread-info msg 'path)) (last-marked-point)) (mu4e-headers-for-each (lambda (mymsg) (let ((my-thread-id (mu4e~headers-get-thread-info mymsg 'thread-id))) (if subthread ;; subthread matching; mymsg's thread path should have path as its ;; prefix (when (string-match (concat "^" path) (mu4e~headers-get-thread-info mymsg 'path)) (mu4e-mark-at-point (car markpair) (cdr markpair)) (setq last-marked-point (point))) ;; nope; not looking for the subthread; looking for the whole thread (when (string= thread-id (mu4e~headers-get-thread-info mymsg 'thread-id)) (mu4e-mark-at-point (car markpair) (cdr markpair)) (setq last-marked-point (point))))))) (when last-marked-point (goto-char last-marked-point) (mu4e-headers-next)))) (defun mu4e-headers-mark-thread (&optional subthread markpair) "Like `mu4e-headers-mark-thread-using-markpair' but prompt for the markpair." (interactive (let* ((subthread current-prefix-arg)) (list current-prefix-arg ;; FIXME: e.g., for refiling we should evaluate this ;; for each line separately (mu4e~mark-get-markpair (if subthread "Mark subthread with: " "Mark whole thread with: ") t)))) (mu4e-headers-mark-thread-using-markpair markpair subthread)) (defun mu4e-headers-mark-subthread (&optional markpair) "Like `mu4e-mark-thread', but only for a sub-thread." (interactive) (if markpair (mu4e-headers-mark-thread t markpair) (let ((current-prefix-arg t)) (call-interactively 'mu4e-headers-mark-thread)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; the query past / present / future ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e~headers-query-past nil "Stack of queries before the present one.") (defvar mu4e~headers-query-future nil "Stack of queries after the present one.") (defvar mu4e~headers-query-stack-size 20 "Maximum size for the query stacks.") (defun mu4e~headers-push-query (query where) "Push QUERY to one of the query stacks. WHERE is a symbol telling us where to push; it's a symbol, either 'future or 'past. Functional also removes duplicats, limits the stack size." (let ((stack (case where (past mu4e~headers-query-past) (future mu4e~headers-query-future)))) ;; only add if not the same item (unless (and stack (string= (car stack) query)) (push query stack) ;; limit the stack to `mu4e~headers-query-stack-size' elements (when (> (length stack) mu4e~headers-query-stack-size) (setq stack (subseq stack 0 mu4e~headers-query-stack-size))) ;; remove all duplicates of the new element (remove-if (lambda (elm) (string= elm (car stack))) (cdr stack)) ;; update the stacks (case where (past (setq mu4e~headers-query-past stack)) (future (setq mu4e~headers-query-future stack)))))) (defun mu4e~headers-pop-query (whence) "Pop a query from the stack. WHENCE is a symbol telling us where to get it from, either `future' or `past'." (case whence (past (unless mu4e~headers-query-past (mu4e-warn "No more previous queries")) (pop mu4e~headers-query-past)) (future (unless mu4e~headers-query-future (mu4e-warn "No more next queries")) (pop mu4e~headers-query-future)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; interactive functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e~headers-search-hist nil "History list of searches.") (defvar mu4e~headers-msgid-target nil "Message-id to jump to after the search has finished.") (defvar mu4e~headers-view-target nil "Whether to automatically view (open) the target message (as per `mu4e~headers-msgid-target').") (defun mu4e-headers-search (&optional expr prompt edit ignore-history msgid show) "Search in the mu database for EXPR, and switch to the output buffer for the results. This is an interactive function which ask user for EXPR. PROMPT, if non-nil, is the prompt used by this function (default is \"Search for:\"). If EDIT is non-nil, instead of executing the query for EXPR, let the user edit the query before executing it. If IGNORE-HISTORY is true, do *not* update the query history stack. If MSGID is non-nil, attempt to move point to the first message with that message-id after searching. If SHOW is non-nil, show the message with MSGID." ;; note: we don't want to update the history if this query comes from ;; `mu4e~headers-query-next' or `mu4e~headers-query-prev'." (interactive) (let* ((prompt (mu4e-format (or prompt "Search for: "))) (expr (if edit (read-string prompt expr) (or expr (read-string prompt nil 'mu4e~headers-search-hist))))) (mu4e-mark-handle-when-leaving) (mu4e~headers-search-execute expr ignore-history) (setq mu4e~headers-msgid-target msgid mu4e~headers-view-target show))) (defun mu4e-headers-search-edit () "Edit the last search expression." (interactive) (mu4e-headers-search mu4e~headers-last-query nil t)) (defun mu4e-headers-search-bookmark (&optional expr edit) "Search using some bookmarked query EXPR. If EDIT is non-nil, let the user edit the bookmark before starting the search." (interactive) (let ((expr (or expr (mu4e-ask-bookmark (if edit "Select bookmark: " "Bookmark: "))))) (run-hook-with-args 'mu4e-headers-search-bookmark-hook expr) (mu4e-headers-search expr (when edit "Edit bookmark: ") edit))) (defun mu4e-headers-search-bookmark-edit () "Edit an existing bookmark before executing it." (interactive) (mu4e-headers-search-bookmark nil t)) (defun mu4e-headers-search-narrow (filter ) "Narrow the last search by appending search expression FILTER to the last search expression. Note that you can go back to previous query (effectively, 'widen' it), with `mu4e-headers-query-prev'." (interactive (let ((filter (read-string (mu4e-format "Narrow down to: ") nil 'mu4e~headers-search-hist nil t))) (list filter))) (unless mu4e~headers-last-query (mu4e-warn "There's nothing to filter")) (mu4e-headers-search (format "(%s) AND (%s)" mu4e~headers-last-query filter))) (defun mu4e-headers-change-sorting (&optional field dir) "Change the sorting/threading parameters. FIELD is the field to sort by; DIR is a symbol: either 'ascending, 'descending, 't (meaning: if FIELD is the same as the current sortfield, change the sort-order) or nil (ask the user)." (interactive) (let* ((field (or field (mu4e-read-option "Sortfield: " mu4e~headers-sort-field-choices))) ;; note: 'sortable' is either a boolean (meaning: if non-nil, this is ;; sortable field), _or_ another field (meaning: sort by this other field). (sortable (plist-get (cdr (assoc field mu4e-header-info)) :sortable)) ;; error check (sortable (if sortable sortable (mu4e-error "Not a sortable field"))) (sortfield (if (booleanp sortable) field sortable)) (dir (case dir ((ascending descending) dir) ;; change the sort order if field = curfield (t (if (eq sortfield mu4e-headers-sort-field) (if (eq mu4e-headers-sort-direction 'ascending) 'descending 'ascending) 'descending)) (mu4e-read-option "Direction: " '(("ascending" . 'ascending) ("descending" . 'descending)))))) (setq mu4e-headers-sort-field sortfield mu4e-headers-sort-direction dir) (mu4e-message "Sorting by %s (%s)" (symbol-name sortfield) (symbol-name mu4e-headers-sort-direction)) (mu4e-headers-rerun-search))) (defun mu4e~headers-toggle (name togglevar dont-refresh) "Toggle variable TOGGLEVAR for feature NAME. Unless DONT-REFRESH is non-nil, re-run the last search." (set togglevar (not (symbol-value togglevar))) (mu4e-message "%s turned %s%s" name (if (symbol-value togglevar) "on" "off") (if dont-refresh " (press 'g' to refresh)" "")) (unless dont-refresh (mu4e-headers-rerun-search))) (defun mu4e-headers-toggle-threading (&optional dont-refresh) "Toggle `mu4e-headers-show-threads'. With prefix-argument, do _not_ refresh the last search with the new setting for threading." (interactive "P") (mu4e~headers-toggle "Threading" 'mu4e-headers-show-threads dont-refresh)) (defun mu4e-headers-toggle-full-search (&optional dont-refresh) "Toggle `mu4e-headers-full-search'. With prefix-argument, do _not_ refresh the last search with the new setting for threading." (interactive "P") (mu4e~headers-toggle "Full-search" 'mu4e-headers-full-search dont-refresh)) (defun mu4e-headers-toggle-include-related (&optional dont-refresh) "Toggle `mu4e-headers-include-related'. With prefix-argument, do _not_ refresh the last search with the new setting for threading." (interactive "P") (mu4e~headers-toggle "Include-related" 'mu4e-headers-include-related dont-refresh)) (defun mu4e-headers-toggle-skip-duplicates (&optional dont-refresh) "Toggle `mu4e-headers-skip-duplicates'. With prefix-argument, do _not_ refresh the last search with the new setting for threading." (interactive "P") (mu4e~headers-toggle "Skip-duplicates" 'mu4e-headers-skip-duplicates dont-refresh)) (defvar mu4e~headers-loading-buf nil "A buffer for loading a message view.") (defun mu4e~headers-get-loading-buf () "Get a buffer to give feedback while loading a message view." (unless (buffer-live-p mu4e~headers-loading-buf) (setq mu4e~headers-loading-buf (get-buffer-create " *mu4e-loading*"))) (with-current-buffer mu4e~headers-loading-buf (let ((inhibit-read-only t)) (erase-buffer) (local-set-key (kbd "q") 'kill-buffer-and-window) (insert (propertize "Waiting for message..." 'face 'mu4e-system-face 'intangible t)))) mu4e~headers-loading-buf) (defun mu4e-headers-view-message () "View message at point. If there's an existing window for the view, re-use that one. If not, create a new one, depending on the value of `mu4e-split-view': if it's a symbol `horizontal' or `vertical', split the window accordingly; if it is nil, replace the current window. " (interactive) (unless (eq major-mode 'mu4e-headers-mode) (mu4e-error "Must be in mu4e-headers-mode (%S)" major-mode)) (let* ((msg (mu4e-message-at-point)) (docid (or (mu4e-message-field msg :docid) (mu4e-warn "No message at point"))) ;; decrypt (or not), based on `mu4e-decryption-policy'. (decrypt (and (member 'encrypted (mu4e-message-field msg :flags)) (if (eq mu4e-decryption-policy 'ask) (yes-or-no-p (mu4e-format "Decrypt message?")) mu4e-decryption-policy))) (viewwin (mu4e~headers-redraw-get-view-window))) (unless (window-live-p viewwin) (mu4e-error "Cannot get a message view")) (select-window viewwin) (switch-to-buffer (mu4e~headers-get-loading-buf)) (mu4e~proc-view docid mu4e-view-show-images decrypt))) (defun mu4e-headers-rerun-search () "Rerun the search for the last search expression." (interactive) ;; if possible, try to return to the same message (let* ((msg (mu4e-message-at-point t)) (msgid (and msg (mu4e-message-field msg :message-id)))) (mu4e-headers-search mu4e~headers-last-query nil nil t msgid))) (defun mu4e~headers-query-navigate (whence) "Execute the previous query from the query stacks. WHENCE determines where the query is taken from and is a symbol, either `future' or `past'." (let ((query (mu4e~headers-pop-query whence)) (where (if (eq whence 'future) 'past 'future))) (when query (mu4e~headers-push-query mu4e~headers-last-query where) (mu4e-headers-search query nil nil t)))) (defun mu4e-headers-query-next () "Execute the previous query from the query stacks." (interactive) (mu4e~headers-query-navigate 'future)) (defun mu4e-headers-query-prev () "Execute the previous query from the query stacks." (interactive) (mu4e~headers-query-navigate 'past)) ;; forget the past so we don't repeat it :/ (defun mu4e-headers-forget-queries () "Forget all the complete query history." (interactive) (setq ;; note: don't forget the present one mu4e~headers-query-past nil mu4e~headers-query-future nil) (mu4e-message "Query history cleared")) (defun mu4e~headers-move (lines) "Move point LINES lines forward (if LINES is positive) or backward (if LINES is negative). If this succeeds, return the new docid. Otherwise, return nil." (unless (eq major-mode 'mu4e-headers-mode) (mu4e-error "Must be in mu4e-headers-mode (%S)" major-mode)) (let ((succeeded (zerop (forward-line lines))) (docid (mu4e~headers-docid-at-point))) ;; move point, even if this function is called when this window is not ;; visible (when docid ;; update all windows showing the headers buffer (walk-windows (lambda (win) (when (eq (window-buffer win) mu4e~headers-buffer) (set-window-point win (point)))) nil t) ;;(set-window-point (get-buffer-window mu4e~headers-buffer t) (point)) ;; attempt to highlight the new line, display the message (mu4e~headers-highlight docid) ;; update message view if it was already showing (when (and mu4e-split-view (window-live-p mu4e~headers-view-win)) (mu4e-headers-view-message)) docid))) (defun mu4e-headers-next (&optional n) "Move point to the next message header. If this succeeds, return the new docid. Otherwise, return nil. Optionally, takes an integer N (prefix argument), to the Nth next header." (interactive "P") (mu4e~headers-move (or n 1))) (defun mu4e-headers-prev (&optional n) "Move point to the previous message header. If this succeeds, return the new docid. Otherwise, return nil. Optionally, takes an integer N (prefix argument), to the Nth previous header." (interactive "P") (mu4e~headers-move (- (or n 1)))) (defun mu4e~headers-prev-or-next-unread (backwards) "Move point to the next message that is unread (and untrashed). If BACKWARDS is non-`nil', move backwards." (interactive) (or (mu4e-headers-find-if-next (lambda (msg) (let ((flags (mu4e-message-field msg :flags))) (and (member 'unread flags) (not (member 'trashed flags))))) backwards) (mu4e-message (format "No %s unread message found" (if backwards "previous" "next"))))) (defun mu4e-headers-prev-unread () "Move point to the previous message that is unread (and untrashed)." (interactive) (mu4e~headers-prev-or-next-unread t)) (defun mu4e-headers-next-unread () "Move point to the next message that is unread (and untrashed)." (interactive) (mu4e~headers-prev-or-next-unread nil)) (defun mu4e~headers-jump-to-maildir (maildir) "Show the messages in maildir (user is prompted to ask what maildir)." (interactive (let ((maildir (mu4e-ask-maildir "Jump to maildir: "))) (list maildir))) (when maildir (mu4e-mark-handle-when-leaving) (mu4e-headers-search (format "maildir:\"%s\"" maildir)))) (defun mu4e-headers-split-view-grow (&optional n) "In split-view, grow the headers window. In horizontal split-view, increase the number of lines shown by N. In vertical split-view, increase the number of columns shown by N. If N is negative shrink the headers window. When not in split-view do nothing." (interactive "P") (let ((n (or n 1)) (hwin (get-buffer-window mu4e~headers-buffer))) (when (and (buffer-live-p mu4e~view-buffer) (window-live-p hwin)) (let ((n (or n 1))) (case mu4e-split-view ;; emacs has weird ideas about what horizontal, vertical means... (horizontal (window-resize hwin n nil) (incf mu4e-headers-visible-lines n)) (vertical (window-resize hwin n t) (incf mu4e-headers-visible-columns n))))))) (defun mu4e-headers-split-view-shrink (&optional n) "In split-view, shrink the headers window. In horizontal split-view, decrease the number of lines shown by N. In vertical split-view, decrease the number of columns shown by N. If N is negative grow the headers window. When not in split-view do nothing." (interactive "P") (mu4e-headers-split-view-grow (- (or n 1)))) (defun mu4e-headers-action (&optional actionfunc) "Ask user what to do with message-at-point, then do it. The actions are specified in `mu4e-headers-actions'. Optionally, pass ACTIONFUNC, which is a function that takes a msg-plist argument." (interactive) (let ((msg (mu4e-message-at-point)) (afunc (or actionfunc (mu4e-read-option "Action: " mu4e-headers-actions)))) (funcall afunc msg))) (defun mu4e-headers-mark-and-next (mark) "Set mark MARK on the message at point or on all messages in the region if there is a region, then move to the next message." (interactive) (mu4e-mark-set mark) (mu4e-headers-next)) (defun mu4e~headers-quit-buffer () "Quit the mu4e-headers buffer. This is a rather complex function, to ensure we don't disturb other windows." (interactive) (unless (eq major-mode 'mu4e-headers-mode) (mu4e-error "Must be in mu4e-headers-mode (%S)" major-mode)) (mu4e-mark-handle-when-leaving) (let ((curbuf (current-buffer)) (curwin (selected-window)) (headers-visible)) (walk-windows (lambda (win) (with-selected-window win ;; if we the view window connected to this one, kill it (when (and (not (one-window-p win)) (eq mu4e~headers-view-win win)) (delete-window win) (setq mu4e~headers-view-win nil))) ;; and kill any _other_ (non-selected) window that shows the current ;; buffer (when (and (eq curbuf (window-buffer win)) ;; does win show curbuf? (not (eq curwin win)) ;; it's not the curwin? (not (one-window-p))) ;; and not the last one? (delete-window win)))) ;; delete it! ;; now, all *other* windows should be gone. kill ourselves, and return ;; to the main view (kill-buffer) (mu4e~main-view))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (provide 'mu4e-headers) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/Makefile.am��������������������������������������������������������������������������0000644�0001750�0001750�00000002510�13020504332�012047� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������## Copyright (C) 2008-2016 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ## ## 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, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk SUBDIRS= info_TEXINFOS=mu4e.texi mu4e_TEXINFOS=fdl.texi dist_lisp_LISP= \ mu4e-actions.el \ mu4e-compose.el \ mu4e-context.el \ mu4e-contrib.el \ mu4e-draft.el \ mu4e-headers.el \ mu4e-lists.el \ mu4e-main.el \ mu4e-mark.el \ mu4e-message.el \ mu4e-meta.el \ mu4e-proc.el \ mu4e-speedbar.el \ mu4e-utils.el \ mu4e-vars.el \ mu4e-view.el \ mu4e.el \ org-mu4e.el \ org-old-mu4e.el EXTRA_DIST=$(elisp_DATA) mu4e-about.org CLEANFILES=*.elc ${BUILT_SOURCES} doc_DATA = \ mu4e-about.org ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/fdl.texi�����������������������������������������������������������������������������0000644�0001750�0001750�00000051030�12605152237�011467� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@c The GNU Free Documentation License. @center Version 1.2, November 2002 @c This file is intended to be included within another document, @c hence no sectioning command or @node. @display Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, 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. @end display @enumerate 0 @item PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document @dfn{free} in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of ``copyleft'', which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. @item APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The ``Document'', below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as ``you''. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A ``Modified Version'' of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A ``Secondary Section'' is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The ``Invariant Sections'' are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The ``Cover Texts'' are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A ``Transparent'' copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not ``Transparent'' is called ``Opaque''. Examples of suitable formats for Transparent copies include plain @sc{ascii} without markup, Texinfo input format, La@TeX{} input format, @acronym{SGML} or @acronym{XML} using a publicly available @acronym{DTD}, and standard-conforming simple @acronym{HTML}, PostScript or @acronym{PDF} designed for human modification. Examples of transparent image formats include @acronym{PNG}, @acronym{XCF} and @acronym{JPG}. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, @acronym{SGML} or @acronym{XML} for which the @acronym{DTD} and/or processing tools are not generally available, and the machine-generated @acronym{HTML}, PostScript or @acronym{PDF} produced by some word processors for output purposes only. The ``Title Page'' means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, ``Title Page'' means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text. A section ``Entitled XYZ'' means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as ``Acknowledgements'', ``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' of such a section when you modify the Document means that it remains a section ``Entitled XYZ'' according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. @item VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. @item COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. @item MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: @enumerate A @item Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. @item List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. @item State on the Title page the name of the publisher of the Modified Version, as the publisher. @item Preserve all the copyright notices of the Document. @item Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. @item Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. @item Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. @item Include an unaltered copy of this License. @item Preserve the section Entitled ``History'', Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled ``History'' in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. @item Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the ``History'' section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. @item For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. @item Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. @item Delete any section Entitled ``Endorsements''. Such a section may not be included in the Modified Version. @item Do not retitle any existing section to be Entitled ``Endorsements'' or to conflict in title with any Invariant Section. @item Preserve any Warranty Disclaimers. @end enumerate If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section Entitled ``Endorsements'', provided it contains nothing but endorsements of your Modified Version by various parties---for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. @item COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled ``History'' in the various original documents, forming one section Entitled ``History''; likewise combine any sections Entitled ``Acknowledgements'', and any sections Entitled ``Dedications''. You must delete all sections Entitled ``Endorsements.'' @item COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. @item AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an ``aggregate'' if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. @item TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled ``Acknowledgements'', ``Dedications'', or ``History'', the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. @item TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document 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. @item FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See @uref{http://www.gnu.org/copyleft/}. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License ``or any later version'' applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. @end enumerate @page @heading ADDENDUM: How to use this License for your documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: @smallexample @group Copyright (C) @var{year} @var{your name}. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. @end group @end smallexample If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the ``with@dots{}Texts.'' line with this: @smallexample @group with the Invariant Sections being @var{list their titles}, with the Front-Cover Texts being @var{list}, and with the Back-Cover Texts being @var{list}. @end group @end smallexample If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. @c Local Variables: @c ispell-local-pdict: "ispell-dict" @c End: ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-compose.el����������������������������������������������������������������������0000644�0001750�0001750�00000100566�13021040244�012662� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; -*-mode: emacs-lisp; tab-width: 8; indent-tabs-mode: t -*- ;; mu4e-compose.el -- part of mu4e, the mu mail user agent for emacs ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; In this file, various functions to compose/send messages, piggybacking on ;; gnus' message mode ;; Magic / Rupe Goldberg ;; 1) When we reply/forward a message, we get it from the backend, ie: ;; we send to the backend (mu4e-compose): ;; compose type:reply docid:30935 ;; backend responds with: ;; (:compose reply :original ( .... <original message> )) ;; 2) When we compose a message, message and headers are separated by ;; `mail-header-separator', ie. '--text follows this line--. We use ;; before-save-hook and after-save-hook to remove/re-add this special line, so ;; it stays in the buffer, but never hits the disk. ;; see: ;; mu4e~compose-insert-mail-header-separator ;; mu4e~compose-remove-mail-header-separator ;; ;; (maybe we can get away with remove it only just before sending? what does ;; gnus do?) ;; 3) When sending a message, we want to do a few things: ;; a) move the message from drafts to the sent folder (maybe; depends on ;; `mu4e-sent-messages-behavior') ;; b) if it's a reply, mark the replied-to message as "R", i.e. replied ;; if it's a forward, mark the forwarded message as "P", i.e. passed (forwarded) ;; c) kill all buffers looking at the sent message ;; a) is dealt with by message-mode, but we need to tell it where to move the ;; sent message. We do this by adding an Fcc: header with the target folder, ;; see `mu4e~compose-setup-fcc-maybe'. Since message-mode does not natively ;; understand maildirs, we also need to tell it what to do, so we also set ;; `message-fcc-handler-function' there. Finally, we add the the message in ;; the sent-folder to the database. ;; ;; b) this is handled in `mu4e~compose-set-parent-flag' ;; ;; c) this is handled in our handler for the `sent'-message from the backend ;; (`mu4e-sent-handler') ;; ;;; Code: (eval-when-compile (byte-compile-disable-warning 'cl-functions)) (require 'cl) (require 'message) (require 'mail-parse) (require 'smtpmail) (require 'rfc2368) (require 'mu4e-utils) (require 'mu4e-vars) (require 'mu4e-proc) (require 'mu4e-actions) (require 'mu4e-message) (require 'mu4e-draft) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Composing / Sending messages (defgroup mu4e-compose nil "Customizations for composing/sending messages." :group 'mu4e) (defcustom mu4e-sent-messages-behavior 'sent "Determines what mu4e does with sent messages. This is one of the symbols: * `sent' move the sent message to the Sent-folder (`mu4e-sent-folder') * `trash' move the sent message to the Trash-folder (`mu4e-trash-folder') * `delete' delete the sent message. Note, when using GMail/IMAP, you should set this to either `trash' or `delete', since GMail already takes care of keeping copies in the sent folder. Alternatively, `mu4e-sent-messages-behavior' can be a function which takes no arguments, and which should return on of the mentioned symbols, for example: (setq mu4e-sent-messages-behavior (lambda () (if (string= (message-sendmail-envelope-from) \"foo@example.com\") 'delete 'sent))) The various `message-' functions from `message-mode' are available for querying the message information." :type '(choice (const :tag "move message to mu4e-sent-folder" sent) (const :tag "move message to mu4e-trash-folder" trash) (const :tag "delete message" delete)) :group 'mu4e-compose) (defcustom mu4e-compose-context-policy 'ask "Policy for determining the context when composing a new message. If the value is `always-ask', ask the user unconditionally. In all other cases, if any context matches (using its match function), this context is used. Otherwise, if none of the contexts match, we have the following choices: - `pick-first': pick the first of the contexts available (ie. the default) - `ask': ask the user - `ask-if-none': ask if there is no context yet, otherwise leave it as it is - nil: return nil; leaves the current context as is. Also see `mu4e-context-policy'." :type '(choice (const :tag "Always ask what context to use" always-ask) (const :tag "Ask if none of the contexts match" ask) (const :tag "Ask when there's no context yet" ask-if-none) (const :tag "Pick the first context if none match" pick-first) (const :tag "Don't change the context when none match" nil) :safe 'symbolp :group 'mu4e-compose)) (defcustom mu4e-compose-crypto-reply-policy 'sign-and-encrypt "Policy for signing/encrypting replies to encrypted messages. We have the following choices: - `sign': sign the reply - `sign-and-encrypt': sign and encrypt the reply - `encrypt': encrypt the reply, but don't sign it. - anything else: do nothing." :type '(choice (const :tag "Sign the reply" sign) (const :tag "Sign and encrypt the reply" sign-and-encrypt) (const :tag "Encrypt the reply" encrypt) (const :tag "Don't do anything" nil) :safe 'symbolp :group 'mu4e-compose)) (defcustom mu4e-compose-format-flowed nil "Whether to compose messages to be sent as format=flowed (or with long lines if `use-hard-newlines' is set to nil). The variable `fill-flowed-encode-column' lets you customize the width beyond which format=flowed lines are wrapped." :type 'boolean :safe 'booleanp :group 'mu4e-compose) (defcustom mu4e-compose-pre-hook nil "Hook run just *before* message composition starts. If the compose-type is either 'reply' or 'forward', the variable `mu4e-compose-parent-message' points to the message replied to / being forwarded / edited, and `mu4e-compose-type' contains the type of message to be composed. Note that there is no draft message yet when this hook runs, it is meant for influencing the how mu4e constructs the draft message. If you want to do something with the draft messages after it has been constructed, `mu4e-compose-mode-hook' would be the place to do that." :type 'hook :group 'mu4e-compose) (defvar mu4e-compose-type nil "The compose-type for this buffer, which is a symbol, `new', `forward', `reply' or `edit'.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-compose-attach-captured-message () "Insert the last captured message file as an attachment. Messages are captured with `mu4e-action-capture-message'." (interactive) (unless mu4e-captured-message (mu4e-warn "No message has been captured")) (let ((path (plist-get mu4e-captured-message :path))) (unless (file-exists-p path) (mu4e-warn "Captured message file not found")) (mml-attach-file path "message/rfc822" (or (plist-get mu4e-captured-message :subject) "No subject") "attachment"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 'fcc' refers to saving a copy of a sent message to a certain folder. that's ;; what these 'Sent mail' folders are for! ;; ;; We let message mode take care of this by adding a field ;; Fcc: <full-path-to-message-in-target-folder> ;; in the "message-send-hook" (ie., just before sending). message mode will ;; then take care of the saving when the message is actually sent. ;; ;; note, where and if you make this copy depends on the value of ;; `mu4e-sent-messages-behavior'. (defun mu4e~compose-setup-fcc-maybe () "Maybe setup Fcc, based on `mu4e-sent-messages-behavior'. If needed, set the Fcc header, and register the handler function." (let* ((sent-behavior ;; Note; we cannot simply use functionp here, since at least ;; delete is a function, too... (if (member mu4e-sent-messages-behavior '(delete trash sent)) mu4e-sent-messages-behavior (if (functionp mu4e-sent-messages-behavior) (funcall mu4e-sent-messages-behavior) mu4e-sent-messages-behavior))) (mdir (case sent-behavior (delete nil) (trash (mu4e-get-trash-folder mu4e-compose-parent-message)) (sent (mu4e-get-sent-folder mu4e-compose-parent-message)) (otherwise (mu4e-error "unsupported value '%S' `mu4e-sent-messages-behavior'." mu4e-sent-messages-behavior)))) (fccfile (and mdir (concat mu4e-maildir mdir "/cur/" (mu4e~draft-message-filename-construct "S"))))) ;; if there's an fcc header, add it to the file (when fccfile (message-add-header (concat "Fcc: " fccfile "\n")) ;; sadly, we cannot define as 'buffer-local'... this will screw up gnus ;; etc. if you run it after mu4e so, (hack hack) we reset it to the old ;; handler after we've done our thing. (setq message-fcc-handler-function (lexical-let ((maildir mdir) (old-handler message-fcc-handler-function)) (lambda (file) (setq message-fcc-handler-function old-handler) ;; reset the fcc handler (write-file file) ;; writing maildirs files is easy (mu4e~proc-add file (or maildir "/")))))))) ;; update the database (defvar mu4e-compose-hidden-headers `("^References:" "^Face:" "^X-Face:" "^X-Draft-From:" "^User-agent:") "Hidden headers when composing.") (defun mu4e~compose-hide-headers () "Hide the headers as per `mu4e-compose-hidden-headers'." (let ((message-hidden-headers mu4e-compose-hidden-headers)) (message-hide-headers))) (defconst mu4e~compose-address-fields-regexp "^\\(To\\|B?Cc\\|Reply-To\\|From\\):") (defun mu4e~compose-register-message-save-hooks () "Just before saving, we remove the mail-header-separator; just after saving we restore it; thus, the separator should never appear on disk." (add-hook 'before-save-hook 'mu4e~draft-remove-mail-header-separator nil t) (add-hook 'after-save-hook (lambda () (mu4e~compose-set-friendly-buffer-name) (mu4e~draft-insert-mail-header-separator) ;; hide some headers again (mu4e~compose-hide-headers) (set-buffer-modified-p nil) (mu4e-message "Saved (%d lines)" (count-lines (point-min) (point-max))) ;; update the file on disk -- ie., without the separator (mu4e~proc-add (buffer-file-name) mu4e~draft-drafts-folder)) nil t)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; address completion; inspired by org-contacts.el and ;; https://github.com/nordlow/elisp/blob/master/mine/completion-styles-cycle.el (defun mu4e~compose-complete-handler (str pred action) (cond ((eq action nil) (try-completion str mu4e~contacts pred)) ((eq action t) (all-completions str mu4e~contacts pred)) ((eq action 'metadata) ;; our contacts are already sorted - just need to tell the ;; completion machinery not to try to undo that... '(metadata (display-sort-function . mu4e~sort-contacts-for-completion) (cycle-sort-function . mu4e~sort-contacts-for-completion))))) (defun mu4e~compose-complete-contact (&optional start) "Complete the text at START with a contact. Ie. either 'name <email>' or 'email')." (interactive) (let ((mail-abbrev-mode-regexp mu4e~compose-address-fields-regexp) (eoh ;; end-of-headers (save-excursion (goto-char (point-min)) (search-forward-regexp mail-header-separator nil t)))) ;; try to complete only when we're in the headers area, ;; looking at an address field. (when (and eoh (> eoh (point)) (mail-abbrev-in-expansion-header-p)) (let* ((end (point)) (start (or start (save-excursion (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*") (goto-char (match-end 0)) (point))))) (list start end 'mu4e~compose-complete-handler))))) (defun mu4e~compose-setup-completion () "Set up auto-completion of addresses." (set (make-local-variable 'completion-ignore-case) t) (set (make-local-variable 'completion-cycle-threshold) 7) (add-to-list (make-local-variable 'completion-styles) 'substring) (add-hook 'completion-at-point-functions 'mu4e~compose-complete-contact nil t)) (defun mu4e~remove-refs-maybe () "Remove the References: header if the In-Reply-To header is missing. This allows the user to effectively start a new message-thread by removing the In-Reply-To header." (unless (message-fetch-field "in-reply-to") (message-remove-header "References"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-compose-mode-map nil "Keymap for \"*mu4e-compose*\" buffers.") (unless mu4e-compose-mode-map (setq mu4e-compose-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) (define-key map (kbd "C-c C-u") 'mu4e-update-mail-and-index) (define-key map (kbd "C-c C-k") 'mu4e-message-kill-buffer) (define-key map (kbd "M-q") 'mu4e-fill-paragraph) map))) (defun mu4e-fill-paragraph (&optional region) "If `use-hard-newlines', takes a multi-line paragraph and makes it into a single line of text. Assume paragraphs are separated by blank lines. If `use-hard-newlines' is not enabled, this simply executes `fill-paragraph'." ;; Inspired by https://www.emacswiki.org/emacs/UnfillParagraph (interactive (progn (barf-if-buffer-read-only) '(t))) (if mu4e-compose-format-flowed (let ((fill-column (point-max)) (use-hard-newlines nil)); rfill "across" hard newlines (delete-trailing-whitespace) (fill-paragraph nil region)) (delete-trailing-whitespace) (fill-paragraph nil region))) (defun mu4e-toggle-use-hard-newlines () (interactive) (setq use-hard-newlines (not use-hard-newlines)) (if use-hard-newlines (turn-off-auto-fill) (turn-on-auto-fill))) (defun mu4e~compose-remap-faces () "Our parent `message-mode' uses font-locking for the compose buffers; lets remap its faces so it uses the ones for mu4e." ;; normal headers (face-remap-add-relative 'message-header-name '((:inherit mu4e-header-key-face))) (face-remap-add-relative 'message-header-other '((:inherit mu4e-header-value-face))) ;; special headers (face-remap-add-relative 'message-header-from '((:inherit mu4e-contact-face))) (face-remap-add-relative 'message-header-to '((:inherit mu4e-contact-face))) (face-remap-add-relative 'message-header-cc '((:inherit mu4e-contact-face))) (face-remap-add-relative 'message-header-bcc '((:inherit mu4e-contact-face))) (face-remap-add-relative 'message-header-subject '((:inherit mu4e-special-header-value-face))) ;; citation (face-remap-add-relative 'message-cited-text '((:inherit mu4e-cited-1-face)))) (defvar mu4e-compose-mode-abbrev-table nil) (define-derived-mode mu4e-compose-mode message-mode "mu4e:compose" "Major mode for the mu4e message composition, derived from `message-mode'. \\{message-mode-map}." (progn (use-local-map mu4e-compose-mode-map) (make-local-variable 'global-mode-string) (add-to-list 'global-mode-string '(:eval (mu4e-context-label))) (set (make-local-variable 'message-signature) mu4e-compose-signature) ;; set this to allow mu4e to work when gnus-agent is unplugged in gnus (set (make-local-variable 'message-send-mail-real-function) nil) (make-local-variable 'message-default-charset) ;; message-mode has font-locking, but uses its own faces. Let's ;; use the mu4e-specific ones instead (mu4e~compose-remap-faces) ;; if the default charset is not set, use UTF-8 (unless message-default-charset (setq message-default-charset 'utf-8)) ;; make sure mu4e is started in the background (ie. we don't want to error ;; out when sending the message; better to do it now if there's a problem) (mu4e~start) ;; start mu4e in background, if needed (mu4e~compose-register-message-save-hooks) ;; set the default directory to the user's home dir; this is probably more ;; useful e.g. when finding an attachment file the directory the current ;; mail files lives in... (setq default-directory (expand-file-name "~/")) ;; offer completion for e-mail addresses (when mu4e-compose-complete-addresses (mu4e~compose-setup-completion)) (when mu4e-compose-format-flowed (turn-off-auto-fill) (setq truncate-lines nil word-wrap t use-hard-newlines t) ;; Set the marks in the fringes before activating visual-line-mode (set (make-local-variable 'visual-line-fringe-indicators) '(left-curly-arrow right-curly-arrow)) (visual-line-mode t)) (when (lookup-key message-mode-map [menu-bar text]) (define-key-after (lookup-key message-mode-map [menu-bar text]) [mu4e-hard-newlines] '(menu-item "Format=flowed" mu4e-toggle-use-hard-newlines :button (:toggle . use-hard-newlines) :help "Toggle format=flowed" :visible (eq major-mode 'mu4e-compose-mode) :enable mu4e-compose-format-flowed) 'sep)) (when (lookup-key mml-mode-map [menu-bar Attachments]) (define-key-after (lookup-key mml-mode-map [menu-bar Attachments]) [mu4e-compose-attach-captured-message] '(menu-item "Attach captured message" mu4e-compose-attach-captured-message :help "Attach message captured in Headers View (with 'a c')" :visible (eq major-mode 'mu4e-compose-mode)) (quote Attach\ External...))) ;; setup the fcc-stuff, if needed (add-hook 'message-send-hook (lambda () ;; mu4e~compose-save-before-sending ;; when in-reply-to was removed, remove references as well. (when (eq mu4e-compose-type 'reply) (mu4e~remove-refs-maybe)) (when use-hard-newlines (mu4e-send-harden-newlines)) ;; for safety, always save the draft before sending (set-buffer-modified-p t) (save-buffer) (mu4e~compose-setup-fcc-maybe) (widen)) nil t) ;; when the message has been sent. (add-hook 'message-sent-hook (lambda () ;; mu4e~compose-mark-after-sending (setq mu4e-sent-func 'mu4e-sent-handler) (mu4e~proc-sent (buffer-file-name) mu4e~draft-drafts-folder)) nil t)) ;; mark these two hooks as permanent-local, so they'll survive mode-changes ;; (put 'mu4e~compose-save-before-sending 'permanent-local-hook t) (put 'mu4e~compose-mark-after-sending 'permanent-local-hook t)) (defun mu4e-send-harden-newlines () "Set the hard property to all newlines." (save-excursion (goto-char (point-min)) (while (search-forward "\n" nil t) (put-text-property (1- (point)) (point) 'hard t)))) (defconst mu4e~compose-buffer-max-name-length 30 "Maximum length of the mu4e-send-buffer-name.") (defun mu4e~compose-set-friendly-buffer-name (&optional compose-type) "Set some user-friendly buffer name based on the compose type." (let* ((subj (message-field-value "subject")) (subj (unless (and subj (string-match "^[:blank:]*$" subj)) subj)) (str (or subj (case compose-type (reply "*reply*") (forward "*forward*") (otherwise "*draft*"))))) (rename-buffer (generate-new-buffer-name (truncate-string-to-width str mu4e~compose-buffer-max-name-length nil nil t) (buffer-name))))) (defun mu4e~compose-crypto-reply (parent compose-type) "When composing a reply to an encrypted message, we can automatically encrypt that reply." (when (and (eq compose-type 'reply) (and parent (member 'encrypted (mu4e-message-field parent :flags)))) (case mu4e-compose-crypto-reply-policy (sign (mml-secure-message-sign)) (encrypt (mml-secure-message-encrypt)) (sign-and-encrypt (mml-secure-message-sign-encrypt))))) (defun* mu4e~compose-handler (compose-type &optional original-msg includes) "Create a new draft message, or open an existing one. COMPOSE-TYPE determines the kind of message to compose and is a symbol, either `reply', `forward', `edit', `resend' `new'. `edit' is for editing existing (draft) messages. When COMPOSE-TYPE is `reply' or `forward', MSG should be a message plist. If COMPOSE-TYPE is `new', ORIGINAL-MSG should be nil. Optionally (when forwarding, replying) ORIGINAL-MSG is the original message we will forward / reply to. Optionally (when forwarding) INCLUDES contains a list of (:file-name <filename> :mime-type <mime-type> :disposition <disposition>) for the attachements to include; file-name refers to a file which our backend has conveniently saved for us (as a tempfile)." ;; Run the hooks defined for `mu4e-compose-pre-hook'. If compose-type is ;; `reply', `forward' or `edit', `mu4e-compose-parent-message' points to the ;; message being forwarded or replied to, otherwise it is nil. (set (make-local-variable 'mu4e-compose-parent-message) original-msg) (put 'mu4e-compose-parent-message 'permanent-local t) ;; remember the compose-type (set (make-local-variable 'mu4e-compose-type) compose-type) (put 'mu4e-compose-type 'permanent-local t) ;; maybe switch the context (mu4e~context-autoswitch mu4e-compose-parent-message mu4e-compose-context-policy) (run-hooks 'mu4e-compose-pre-hook) ;; this opens (or re-opens) a messages with all the basic headers set. (let ((winconf (current-window-configuration))) (condition-case nil (mu4e-draft-open compose-type original-msg) (quit (set-window-configuration winconf) (mu4e-message "Operation aborted") (return-from mu4e~compose-handler)))) ;; insert mail-header-separator, which is needed by message mode to separate ;; headers and body. will be removed before saving to disk (mu4e~draft-insert-mail-header-separator) ;; maybe encrypt/sign replies (mu4e~compose-crypto-reply original-msg compose-type) ;; include files -- e.g. when forwarding a message with attachments, ;; we take those from the original. (save-excursion (goto-char (point-max)) ;; put attachments at the end (dolist (att includes) (mml-attach-file (plist-get att :file-name) (plist-get att :mime-type)))) ;; buffer is not user-modified yet (mu4e~compose-set-friendly-buffer-name compose-type) (set-buffer-modified-p nil) ;; now jump to some useful positions, and start writing that mail! (if (member compose-type '(new forward)) (message-goto-to) (message-goto-body)) ;; bind to `mu4e-compose-parent-message' of compose buffer (set (make-local-variable 'mu4e-compose-parent-message) original-msg) (put 'mu4e-compose-parent-message 'permanent-local t) ;; hide some headers (mu4e~compose-hide-headers) ;; switch on the mode (mu4e-compose-mode) ;; set mu4e-compose-type once more for this buffer, ;; we loose it after the mode-change, it seems (set (make-local-variable 'mu4e-compose-type) compose-type) (put 'mu4e-compose-type 'permanent-local t) (when mu4e-compose-in-new-frame ;; make sure to close the frame when we're done with the message these are ;; all buffer-local; (push 'delete-frame message-exit-actions) (push 'delete-frame message-postpone-actions))) (defun mu4e-sent-handler (docid path) "Handler function, called with DOCID and PATH for the just-sent message. For Forwarded ('Passed') and Replied messages, try to set the appropriate flag at the message forwarded or replied-to." (mu4e~compose-set-parent-flag path) (when (file-exists-p path) ;; maybe the draft was not saved at all (mu4e~proc-remove docid)) ;; kill any remaining buffers for the draft file, or they will hang around... ;; this seems a bit hamfisted... (dolist (buf (buffer-list)) (when (and (buffer-file-name buf) (string= (buffer-file-name buf) path)) (if message-kill-buffer-on-exit (kill-buffer buf)))) ;; now, try to go back to some previous buffer, in the order ;; view->headers->main (if (buffer-live-p mu4e~view-buffer) (switch-to-buffer mu4e~view-buffer) (if (buffer-live-p mu4e~headers-buffer) (switch-to-buffer mu4e~headers-buffer) ;; if all else fails, back to the main view (when (fboundp 'mu4e) (mu4e)))) (mu4e-message "Message sent")) (defun mu4e-message-kill-buffer () "Wrapper around `message-kill-buffer'. It restores mu4e window layout after killing the compose-buffer." (interactive) (let ((current-buffer (current-buffer))) (message-kill-buffer) ;; Compose buffer killed (when (not (equal current-buffer (current-buffer))) ;; Restore mu4e (if mu4e-compose-in-new-frame (delete-frame) (if (buffer-live-p mu4e~view-buffer) (switch-to-buffer mu4e~view-buffer) (if (buffer-live-p mu4e~headers-buffer) (switch-to-buffer mu4e~headers-buffer) ;; if all else fails, back to the main view (when (fboundp 'mu4e) (mu4e)))))))) (defun mu4e~compose-set-parent-flag (path) "Set the 'replied' \"R\" flag on messages we replied to, and the 'passed' \"F\" flag on message we have forwarded. If a message has an 'in-reply-to' header, it is considered a reply to the message with the corresponding message id. If it does not have an 'in-reply-to' header, but does have a 'references' header, it is considered to be a forward message for the message corresponding with the /last/ message-id in the references header. Now, if the message has been determined to be either a forwarded message or a reply, we instruct the server to update that message with resp. the 'P' (passed) flag for a forwarded message, or the 'R' flag for a replied message. The original messages are also marked as Seen. Function assumes that it's executed in the context of the message buffer." (let ((buf (find-file-noselect path))) (when buf (with-current-buffer buf (message-narrow-to-headers-or-head) (let ((in-reply-to (message-fetch-field "in-reply-to")) (forwarded-from) (references (message-fetch-field "references"))) (unless in-reply-to (when references (with-temp-buffer ;; inspired by `message-shorten-references'. (insert references) (goto-char (point-min)) (let ((refs)) (while (re-search-forward "<[^ <]+@[^ <]+>" nil t) (push (match-string 0) refs)) ;; the last will be the first (setq forwarded-from (first refs)))))) ;; remove the <> (when (and in-reply-to (string-match "<\\(.*\\)>" in-reply-to)) (mu4e~proc-move (match-string 1 in-reply-to) nil "+R-N")) (when (and forwarded-from (string-match "<\\(.*\\)>" forwarded-from)) (mu4e~proc-move (match-string 1 forwarded-from) nil "+P-N"))))))) (defun mu4e-compose (compose-type) "Start composing a message of COMPOSE-TYPE, where COMPOSE-TYPE is a symbol, one of `reply', `forward', `edit', `resend' `new'. All but `new' take the message at point as input. Symbol `edit' is only allowed for draft messages." (let ((msg (mu4e-message-at-point 'noerror))) ;; some sanity checks (unless (or msg (eq compose-type 'new)) (mu4e-warn "No message at point")) (unless (member compose-type '(reply forward edit resend new)) (mu4e-error "Invalid compose type '%S'" compose-type)) (when (and (eq compose-type 'edit) (not (member 'draft (mu4e-message-field msg :flags)))) (mu4e-warn "Editing is only allowed for draft messages")) ;; 'new is special, since it takes no existing message as arg; therefore, we ;; don't need to involve the backend, and call the handler *directly* (if (eq compose-type 'new) (mu4e~compose-handler 'new) ;; otherwise, we need the doc-id (let* ((docid (mu4e-message-field msg :docid)) ;; decrypt (or not), based on `mu4e-decryption-policy'. (decrypt (and (member 'encrypted (mu4e-message-field msg :flags)) (if (eq mu4e-decryption-policy 'ask) (yes-or-no-p (mu4e-format "Decrypt message?")) mu4e-decryption-policy)))) ;; if there's a visible view window, select that before starting composing ;; a new message, so that one will be replaced by the compose window. The ;; 10-or-so line headers buffer is not a good place to write it... (let ((viewwin (get-buffer-window mu4e~view-buffer))) (when (window-live-p viewwin) (select-window viewwin))) ;; talk to the backend (mu4e~proc-compose compose-type decrypt docid))))) (defun mu4e-compose-reply () "Compose a reply for the message at point in the headers buffer." (interactive) (mu4e-compose 'reply)) (defun mu4e-compose-forward () "Forward the message at point in the headers buffer." (interactive) (mu4e-compose 'forward)) (defun mu4e-compose-edit () "Edit the draft message at point in the headers buffer. This is only possible if the message at point is, in fact, a draft message." (interactive) (mu4e-compose 'edit)) (defun mu4e-compose-resend () "Resend the message at point in the headers buffer." (interactive) (mu4e-compose 'resend)) (defun mu4e-compose-new () "Start writing a new message." (interactive) (mu4e-compose 'new)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; mu4e-compose-func and mu4e-send-func are wrappers so we can set ourselves ;; as default emacs mailer (define-mail-user-agent etc.) ;;;###autoload (defun mu4e~compose-mail (&optional to subject other-headers continue switch-function yank-action send-actions return-action) "This is mu4e's implementation of `compose-mail'." ;; create a new draft message 'resetting' (as below) is not actually needed in ;; this case, but let's prepare for the re-edit case as well (mu4e~compose-handler 'new) (when (message-goto-to) ;; reset to-address, if needed (message-delete-line)) (message-add-header (concat "To: " to "\n")) (when (message-goto-subject) ;; reset subject, if needed (message-delete-line)) (message-add-header (concat "Subject: " subject "\n")) ;; add any other headers specified (when other-headers (dolist (h other-headers other-headers) (if (symbolp (car h)) (setcar h (symbol-name (car h)))) (message-add-header (concat (capitalize (car h)) ": " (cdr h) "\n" )) )) ;; yank message (if (bufferp yank-action) (list 'insert-buffer yank-action) yank-action) ;; try to put the user at some reasonable spot... (if (not to) (message-goto-to) (if (not subject) (message-goto-subject) (message-goto-body)))) ;; happily, we can re-use most things from message mode ;;;###autoload (define-mail-user-agent 'mu4e-user-agent 'mu4e~compose-mail 'message-send-and-exit 'message-kill-buffer 'message-send-hook) ;; Without this `mail-user-agent' cannot be set to `mu4e-user-agent' ;; through customize, as the custom type expects a function. Not ;; sure whether this function is actually ever used; if it is then ;; returning the symbol is probably the correct thing to do, as other ;; such functions suggest. (defun mu4e-user-agent () 'mu4e-user-agent) (defun mu4e~compose-browse-url-mail (url &optional ignored) "Adapter for `browse-url-mailto-function." (let* ((headers (rfc2368-parse-mailto-url url)) (to (cdr (assoc "To" headers))) (subject (cdr (assoc "Subject" headers))) (body (cdr (assoc "Body" headers)))) (mu4e~compose-mail to subject) (if body (progn (message-goto-body) (insert body) (if (not to) (message-goto-to) (if (not subject) (message-goto-subject) (message-goto-body))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-compose-goto-top () "Go to the beginning of the message or buffer. Go to the beginning of the message or, if already there, go to the beginning of the buffer." (interactive) (let ((old-position (point))) (message-goto-body) (when (equal (point) old-position) (goto-char (point-min))))) (define-key mu4e-compose-mode-map (vector 'remap 'beginning-of-buffer) 'mu4e-compose-goto-top) (defun mu4e-compose-goto-bottom () "Go to the end of the message or buffer. Go to the end of the message (before signature) or, if already there, go to the end of the buffer." (interactive) (let ((old-position (point)) (message-position (save-excursion (message-goto-body) (point)))) (goto-char (point-max)) (when (re-search-backward message-signature-separator message-position t) (forward-line -1)) (when (equal (point) old-position) (goto-char (point-max))))) (define-key mu4e-compose-mode-map (vector 'remap 'end-of-buffer) 'mu4e-compose-goto-bottom) (provide 'mu4e-compose) ;; Load mu4e completely even when this file was loaded through ;; autoload. (require 'mu4e) ������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e.texi����������������������������������������������������������������������������0000644�0001750�0001750�00000524122�13020504332�011570� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@documentencoding UTF-8 \input texinfo.tex @c -*-texinfo-*- @include texi.texi @c %**start of header @setfilename mu4e.info @settitle Mu4e @value{mu-version} user manual @c Use proper quote and backtick for code sections in PDF output @c Cf. Texinfo manual 14.2 @set txicodequoteundirected @set txicodequotebacktick @c %**end of header @copying Copyright @copyright{} 2012-2016 Dirk-Jan C. Binnema @quotation Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License.'' @end quotation @end copying @titlepage @title @t{Mu4e} - an e-mail client for GNU/Emacs @subtitle version @value{mu-version} @author Dirk-Jan C. Binnema @c The following two commands start the copyright page. @page @vskip 0pt plus 1filll @insertcopying @end titlepage @dircategory Emacs @direntry * mu4e: (Mu4e). An email client for GNU/Emacs. @end direntry @contents @ifnottex @node Top @top mu4e manual @end ifnottex @iftex @node Welcome to mu4e @unnumbered Welcome to mu4e @end iftex Welcome to @t{mu4e} @value{mu-version}. @t{mu4e} (@t{mu}-for-emacs) is an e-mail client for GNU-Emacs version 24.4 or higher, built on top of the @t{mu}@footnote{@url{http://www.djcbsoftware.nl/code/mu}} e-mail search engine. @t{mu4e} is optimized for fast handling of large amounts of e-mail. Some of its highlights: @itemize @item Fully search-based: there are no folders@footnote{that is, instead of folders, you use queries that match messages in a particular folder}, only queries. @item Fully documented, with example configurations @item User-interface optimized for speed, with quick key strokes for common actions @item Support for non-English languages (so ``angstrom'' matches ``Ångström'') @item Asynchronous: heavy actions don't block @t{emacs}@footnote{currently, the only exception to this is @emph{sending mail}; there are solutions for that though - see the @ref{FAQ}} @item Support for cryptography - signing, encrypting and decrypting @item Address auto-completion based on the contacts in your messages @item Extendable with your own snippets of elisp @end itemize In this manual, we go through the installation of @t{mu4e}, do some basic configuration and explain its daily use. We also show you how you can customize @t{mu4e} for your special needs. At the end of the manual, there are some example configurations, to get you up to speed quickly: @ref{Example configurations}. There's also a section with answers to frequenly asked questions, @ref{FAQ}. @menu * Introduction:: Where be begin * Getting started:: Setting things up * Main view:: The @t{mu4e} overview * Headers view:: Lists of message headers * Message view:: Viewing specific messages * Editor view:: Creating / editing messages * Searching:: Some more background on searching/queries * Marking:: Marking messages and performing actions * Contexts:: Defining contexts and switching between them * Dynamic folders:: Folders that change based on circumstances * Actions:: Defining and using custom actions * Extending mu4e:: Writing code for @t{mu4e} Appendices * Interaction with other tools:: mu4e and the rest of the world * Example configurations:: Some examples to set you up quickly * FAQ:: Common questions and answers * Tips and Tricks:: Useful tips * How it works:: Some notes about the implementation of @t{mu4e} * Logging and debugging:: How to debug problems in @t{mu4e} * GNU Free Documentation License:: The license of this manual @end menu @node Introduction @chapter Introduction Let's get started! @menu * Why another e-mail client::Aren't there enough already * Other mail clients::Where mu4e takes its inspiration * What mu4e does not do::Focus on the core-business, delegate the rest * Becoming a mu4e user::Joining the club @end menu @node Why another e-mail client @section Why another e-mail client? I (the author) spend a @emph{lot} of time dealing with e-mail, both professionally and privately. Having an efficient e-mail client is essential. Since none of the existing ones worked the way I wanted, I thought about creating my own. @command{emacs} is an integral part of my workflow, so it made a lot of sense to use it for e-mail as well. And as I had already written an e-mail search engine (@t{mu}), it seemed only logical to use that as a basis. @node Other mail clients @section Other mail clients Under the hood, @t{mu4e} is fully search-based, similar to programs like @t{notmuch}@footnote{@url{http://notmuchmail.org}} and @t{sup}@footnote{@url{http://sup.rubyforge.org/}}. However, @t{mu4e}'s user-interface is quite different. @t{mu4e}'s mail handling (deleting, moving etc.) is inspired by Wanderlustu@footnote{@url{http://www.gohome.org/wl/}} (another @t{emacs}-based e-mail client), @t{mutt}@footnote{@url{http://www.mutt.org/}} and the @t{dired} file-manager for emacs. @t{mu4e} tries to keep all the 'state' in your maildirs, so you can easily switch between clients, synchronize over @abbr{IMAP}, backup with @t{rsync} and so on. If you delete the database, you won't lose any information. @node What mu4e does not do @section What @t{mu4e} does not do There are a number of things that @t{mu4e} does @b{not} do, by design: @itemize @item @t{mu}/@t{mu4e} do @emph{not} get your e-mail messages from a mail server. That task is delegated to other tools, such as @t{offlineimap}@footnote{@url{http://offlineimap.org/}}, @t{isync/mbsync}@footnote{@url{http://isync.sourceforge.net/}} or @t{fetchmail}@footnote{@url{http://www.fetchmail.info/}}. As long as the messages end up in a maildir, @t{mu4e} and @t{mu} are happy to deal with them. @item @t{mu4e} also does @emph{not} implement sending of messages; instead, it depends on @t{smtpmail} (@inforef{Top,,smtpmail}), which is part of @command{emacs}. In addition, @t{mu4e} piggybacks on Gnus' message editor; @inforef{Top,,message}. @end itemize Thus, many of the things an e-mail client traditionally needs to do, are delegated to other tools. This leaves @t{mu4e} to concentrate on what it does best: quickly finding the mails you are looking for, and handle them as efficiently as possible. @node Becoming a mu4e user @section Becoming a @t{mu4e} user If @t{mu4e} sounds like something for you, give it a shot! We're trying hard to make it as easy as possible to set up and use; and while you can use elisp in various places to augment @t{mu4e}, a lot of knowledge about programming or elisp shouldn't be required. The idea is to provide sensible defaults, and allow for customization. When you take @t{mu4e} into use, it's a good idea to subscribe to the @t{mu}/@t{mu4e}-mailing list@footnote{@url{http://groups.google.com/group/mu-discuss}}. Sometimes, you might encounter some unexpected behavior while using @t{mu4e}. It could be a bug in @t{mu4e}, it could be an issue in other software. Or it could just be a misunderstanding. In any case, if you want to report this (either to the mailing list or to @url{https://github.com/djcb/mu/issues}, the latter is preferred), please always include the following information: @itemize @item what did you expect that should happen? what actually happened? @item can you provide some exact steps to reproduce? @item what version of mu4e and emacs were you using? What operating system? @item can you reproduce it with emacs -q and only loading mu4e? @item if the problem is related to some specific message, please include the raw message file (appropriately anonimized, of course) @end itemize @node Getting started @chapter Getting started In this chapter, we go through the installation of @t{mu4e} and its basic setup. After we have succeeded in @ref{Getting mail}, and @pxref{Indexing your messages}, we discuss the @ref{Basic configuration}. After these steps, @t{mu4e} should be ready to go! @menu * Requirements:: What is needed * Installation:: How to install @t{mu} and @t{mu4e} * Getting mail:: Getting mail from a server * Indexing your messages:: Creating and maintaining the index * Basic configuration:: Settings for @t{mu4e} * Folders:: Setting up standard folders * Retrieval and indexing:: Doing it from mu4e * Sending mail:: How to send mail * Running mu4e:: Overview of the @t{mu4e} views @end menu @node Requirements @section Requirements @t{mu}/@t{mu4e} are known to work on a wide variety of Unix- and Unix-like systems, including many Linux distributions, OS X and FreeBSD, and even on MS-Windows (with Cygwin). @command{emacs} 23 or 24 (recommended) is required, as well as Xapian@footnote{@url{http://xapian.org/}} and GMime@footnote{@url{http://spruce.sourceforge.net/gmime/}}. @t{mu} has optional support for the Guile 2.x (Scheme) programming language. There are also some GUI-tools, which require GTK+ 3.x and Webkit. If you intend to compile @t{mu} yourself, you need to have the typical development tools, such as C and C++ compilers (both @command{gcc} and @command{clang} should work), GNU Autotools and @command{make}, and the development packages for GMime, GLib and Xapian. Optionally (if you use them), you also need the development packages for GTK+, Webkit and Guile. @node Installation @section Installation @t{mu4e} is part of @t{mu} - by installing the latter, the former is installed as well. Some Linux distributions provide packaged versions of @t{mu}/@t{mu4e}; if you can use those, there is no need to compile anything yourself. However, if there are no packages for your distribution, if they are outdated, or if you want to use the latest development versions, you can follow the steps below. First, you need make sure you have the necessary dependencies; the details depend on your distribution. If you're using another distribution (or another OS), the below at least be helpful in identifying the packages to install. We provide some instructions for Debian, Ubuntu and Fedora; if those do not apply to you, you can follow either @ref{Building from a release tarball} or @ref{Building from git}. @subsection Dependencies for Debian/Ubuntu @example $ sudo apt-get install libgmime-2.6-dev libxapian-dev # if libgmime-2.6-dev is not available, try libgmime-2.4-dev # get emacs 23 or 24 if you don't have it yet $ sudo apt-get install emacs24 # optional $ sudo apt-get install guile-2.0-dev html2text xdg-utils # optional: only needed for msg2pdf and mug (toy gtk+ frontend) $ sudo apt-get install libwebkit-dev @end example @subsection Dependencies for Fedora @example $ sudo yum install gmime-devel xapian-core-devel # get emacs 23 or 24 if you don't have it yet $ sudo yum install emacs # optional $ sudo yum install html2text xdg-utils # optional: only needed for msg2pdf and mug (toy gtk+ frontend) $ sudo yum install webkitgtk3-devel @end example @subsection Building from a release tarball @anchor{Building from a release tarball} Using a release-tarball (as available from GoogleCode@footnote{@url{http://code.google.com/p/mu0/downloads/list}}, installation follows the typical steps: @example $ tar xvfz mu-<version>.tar.gz # use the specific version $ cd mu-<version> # On the BSDs: use gmake instead of make $ ./configure && make $ sudo make install @end example Xapian, GMime and their dependencies must be installed. @subsection Building from git @anchor{Building from git} Alternatively, if you build from the git repository or use a tarball like the ones that @t{github} produces, the instructions are slightly different, and require you to have @t{autotools} (Autoconf, Automake, Libtool, and friends) installed: @example # get from git (alternatively, use a github tarball) $ git clone git://github.com/djcb/mu.git $ cd mu $ autoreconf -i && ./configure && make # On the BSDs: use gmake instead of make $ sudo make install @end example (Xapian, GMime and their dependencies must be installed). After this, @t{mu} and @t{mu4e} should be installed @footnote{there's a hard dependency between versions of @t{mu4e} and @t{mu} - you cannot combine different versions} on your system, and be available from the command line in @command{emacs}. You may need to restart @command{emacs}, so it can find @t{mu4e} in its @code{load-path}. If, even after restarting, @command{emacs} cannot find @t{mu4e}, you may need to add it to your @code{load-path} explicitly; check where @t{mu4e} is installed, and add something like the following to your configuration before trying again: @lisp ;; the exact path may differ -- check it (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e") @end lisp @subsection mu4e and emacs customization There is some support for using the @command{emacs} customization system in @t{mu4e}, but for now, we recommend setting the values manually. Please refer to @ref{Example configurations} for a couple of examples of this; here we go through things step-by-step. @node Getting mail @section Getting mail In order for @t{mu} (and, by extension, @t{mu4e}) to work, you need to have your e-mail messages stored in a @emph{maildir}@footnote{@url{http://en.wikipedia.org/wiki/Maildir}; in this manual we use the term 'maildir' for both the standard and the hierarchy of maildirs that store your messages} - a specific directory structure with one-file-per-message. If you are already using a maildir, you are lucky. If not, some setup is required: @itemize @item @emph{Using an external IMAP or POP server} - if you are using an @abbr{IMAP} or @abbr{POP} server, you can use tools like @t{getmail}, @t{fetchmail}, @t{offlineimap} or @t{isync} to download your messages into a maildir (@file{~/Maildir}, often). Because it is such a common case, there is a full example of setting @t{mu4e} up with @t{offlineimap} and Gmail; @pxref{Gmail configuration}. @item @emph{Using a local mail server} - if you are using a local mail-server (such as @t{postfix} or @t{qmail}), you can teach them to deliver into a maildir as well, maybe in combination with @t{procmail}. A bit of googling should be able to provide you with the details. @end itemize @node Indexing your messages @section Indexing your messages After you have succeeded in @ref{Getting mail}, we need to @emph{index} the messages. That is - we need to scan the messages in the maildir and store the information about them in a special database. We can do that from @t{mu4e} -- @ref{Main view}, but the first time, it is a good idea to run it from the command line, which makes it easier to verify that everything works correctly. Assuming that your maildir is at @file{~/Maildir}, we issue the following command: @example $ mu index --maildir=~/Maildir @end example This should scan your @file{~/Maildir}@footnote{In most cases, you do not even need to provide the @t{--maildir=~/Maildir} since it is the default; see the @t{mu-index} man-page for details} and fill the database, and give progress information while doing so. The indexing process may take a few minutes the first time you do it (for thousands of e-mails); afterwards it is much faster, since @t{mu} only scans messages that are new or have changed. Indexing is discussed in full detail in the @t{mu-index} man-page. After the indexing process has finished, you can quickly test if everything worked, by trying some command-line searches, for example @example $ mu find hello @end example which lists all messages that match @t{hello}. For more examples of searches, see @ref{Queries}, or check the @t{mu-find} and @t{mu-easy} man pages. If all of this worked well, we are well on our way setting things up; the next step is to do some basic configuration for @t{mu4e}. @node Basic configuration @section Basic configuration Before we can start using @t{mu4e}, we need to tell @command{emacs} to load it. So, add to your @file{~/.emacs} (or its moral equivalent, such as @file{~/.emacs.d/init.el}) something like: @lisp (require 'mu4e) @end lisp If @command{emacs} complains that it cannot find @t{mu4e}, check your @code{load-path} and make sure that @t{mu4e}'s installation directory is part of it. If not, you can add it: @lisp (add-to-list 'load-path MU4E-PATH) @end lisp with @t{MU4E-PATH} replaced with the actual path. @node Folders @section Folders The next step is to tell @t{mu4e} where it can find your Maildir, and some special folders. So, for example@footnote{Note that the folders (@t{mu4e-sent-folder}, @t{mu4e-drafts-folder}, @t{mu4e-trash-folder} and @t{mu4e-refile-folder}) can also be @emph{functions} that are evaluated at runtime. This allows for dynamically changing them depending on the situation. See @ref{Dynamic folders} for details.}: @lisp ;; these are actually the defaults (setq mu4e-maildir "~/Maildir" ;; top-level Maildir mu4e-sent-folder "/sent" ;; folder for sent messages mu4e-drafts-folder "/drafts" ;; unfinished messages mu4e-trash-folder "/trash" ;; trashed messages mu4e-refile-folder "/archive") ;; saved messages @end lisp Note, @code{mu4e-maildir} takes an actual filesystem-path, the other folder names are all relative to @code{mu4e-maildir}. Also note that this must @emph{not} be a symbolic link. If you use @t{mu4e-context}, see @ref{Contexts and special folders} for what that means for these special folders. @node Retrieval and indexing @section Retrieval and indexing with mu4e As we have seen, we can do all of the mail retrieval @emph{outside} of @command{emacs}/@t{mu4e}. However, you can also do it from within @t{mu4e}. @subsection Basics To set up mail-retrieval from withing @t{mu4e}, set the variable @code{mu4e-get-mail-command} to the program or shell command you want to use for retrieving mail. You can then get your e-mail using @kbd{M-x mu4e-update-mail-and-index}, or @kbd{C-S-u} in all @t{mu4e}-views; alternatively, you can use @kbd{C-c C-u}, which may be more convenient if you use emacs in a terminal. You can interrupt the (foreground) update process with @kbd{q}. It is possible to update your mail and index periodically in the background or foreground, by setting the variable @code{mu4e-update-interval} to the number of seconds between these updates. If set to @code{nil}, it won't update at all. After you make changes to @code{mu4e-update-interval}, @t{mu4e} must be restarted before the changes take effect. By default, this will run in background and to change it to run in foreground, set @code{mu4e-index-update-in-background} to @code{nil} @subsection Handling errors during mail retrieval If the mail-retrieval process returns with a non-zero exit code, @t{mu4e} shows a warning (unless @code{mu4e-index-update-error-warning} is set to @code{nil}), but then try to index your maildirs anyway (unless @code{mu4e-index-update-error-continue} is set to @code{nil}). Reason for these defaults is that some of the mail-retrieval programs may return non-zero, even when the updating process succeeded; however, it is hard to tell such pseudo-errors from real ones like 'login failed'. If you need more refinement, it may be useful to wrap the mail-retrieval program in a shell-script, for example @t{fetchmail} returns 1 to indicate 'no mail'; we can handle that with: @lisp (setq mu4e-get-mail-command "fetchmail -v || [ $? -eq 1 ]") @end lisp A similar approach can be used with other mail retrieval programs, although not all of them have their exit codes documented. @subsection Implicit mail retrieval If you don't have a specific command for getting mail, for example because you are running your own mail-server, you can leave @code{mu4e-get-mail-command} at @t{"true"} (the default), in which case @t{mu4e} won't try to get new mail, but still re-index your messages. @subsection Speeding up indexing If you have a large number of e-mail messages in your store, (re)indexing might take a while. The defaults for indexing are to ensure the we always have correct, up-to-date information about your messages, even if other programs have modified the Maildir. The downside of this thoroughness (which is the default) is that it is relatively slow, something that can be noticeable with large e-mail corpa. For a faster approach, you can use the following: @lisp (setq mu4e-index-cleanup nil ;; don't do a full cleanup check mu4e-index-lazy-check t) ;; don't consider up-to-date dirs @end lisp In many cases, the mentioned thoroughness might not be needed, and these settings give a very significant speed-up. Note that you can of course occasionally run a thorough indexing round. For further details, please refer to the @t{mu-index} manpage. @subsection Example setup A simple setup could look something like: @lisp (setq mu4e-get-mail-command "offlineimap" ;; or fetchmail, or ... mu4e-update-interval 300) ;; update every 5 minutes @end lisp A hook @code{mu4e-update-pre-hook} is available which is run right before starting the process. That can be useful, for example, to influence, @code{mu4e-get-mail-command} based on the the current situation (location, time of day, ...). It is possible to get notifications when the indexing process does any updates - for example when receiving new mail. See @code{mu4e-index-updated-hook} and some tips on its usage in the @ref{FAQ}. @node Sending mail @section Sending mail @t{mu4e} re-uses Gnu's @code{message-mode} (@inforef{Top,,message}) for writing mail and inherits the setup for sending mail as well. For sending mail using @abbr{SMTP}, @t{mu4e} uses @t{smtpmail} (@inforef{Top,,smtpmail}). This package supports many different ways to send mail; please refer to its documentation for the details. Here, we only provide some simple examples - for more, see @ref{Example configurations}. A very minimal setup: @lisp ;; tell message-mode how to send mail (setq message-send-mail-function 'smtpmail-send-it) ;; if our mail server lives at smtp.example.org; if you have a local ;; mail-server, simply use 'localhost' here. (setq smtpmail-smtp-server "smtp.example.org") @end lisp Since @t{mu4e} (re)uses the same @t{message mode} and @t{smtpmail} that Gnus uses, many settings for those also apply to @t{mu4e}. @subsection Dealing with sent messages By default, @t{mu4e} puts a copy of messages you sent in the folder determined by @code{mu4e-sent-folder}. In some cases, this may not be what you want - for example, when using Gmail-over-@abbr{IMAP}, this interferes with Gmail's handling of the sent messages folder, and you may end up with duplicate messages. You can use the variable @code{mu4e-sent-messages-behavior} to customize what happens with sent messages. The default is the symbol @code{sent} which, as mentioned, causes the message to be copied to your sent-messages folder. Other possible values are the symbols @code{trash} (the sent message is moved to the trash-folder (@code{mu4e-trash-folder}), and @code{delete} to simply discard the sent message altogether (so Gmail can deal with it). For Gmail-over-@abbr{IMAP}, you could add the following to your settings: @verbatim ;; don't save messages to Sent Messages, Gmail/IMAP takes care of this (setq mu4e-sent-messages-behavior 'delete) @end verbatim And that's it! We should now be ready to go. For more complex needs, @code{mu4e-sent-messages-behavior} can also be a a parameter-less function that returns one of the mentioned symbols; see the built-in documentation for the variable. @node Running mu4e @section Running mu4e After following the steps in this chapter, we now (hopefully!) have a working @t{mu4e} setup. Great! In the next chapters, we walk you through the various views in @t{mu4e}. For your orientation, the diagram below shows how the views relate to each other, and the default key-bindings to navigate between them. @cartouche @verbatim [C] +--------+ [RFCE] --------> | editor | <-------- / +--------+ \ / [RFCE]^ \ / | \ +-------+ [sjbB]+---------+ [RET] +---------+ | main | <---> | headers | <----> | message | +-------+ [q] +---------+ [qbBjs] +---------+ [sjbB] ^ [.] | [q] V +-----+ | raw | +-----+ Default bindings ---------------- R: Reply s: search .: raw view (toggle) F: Forward j: jump-to-maildir q: quit C: Compose b: bookmark-search E: Edit B: edit bookmark-search @end verbatim @end cartouche @node Main view @chapter The main view After you have installed @t{mu4e} (@pxref{Getting started}), you can start it with @kbd{M-x mu4e}. @t{mu4e} does some checks to ensure everything is set up correctly, and then shows you the @t{mu4e} main view. Its major mode is @code{mu4e-main-mode}. @menu * Overview: MV Overview. What is the main view * Basic actions::What can we do * Bookmarks: MV Bookmarks. Jumping to other places * Miscellaneous::Notes @end menu @node MV Overview @section Overview The main view looks something like the following: @cartouche @verbatim * mu4e - mu for emacs version 0.X.X CG Basics * [j]ump to some maildir * enter a [s]earch query * [C]ompose a new message Bookmarks * [bu] Unread messages * [bt] Today's messages * [bw] Last 7 days * [bp] Messages with images * [bs] Sent mail * [bf] Flagged messages * [b]] Flow * [b/] Test Misc * [;]Switch focus * [U]pdate email & database * [N]ews * [A]bout mu4e * [H]elp * [q]uit @end verbatim @end cartouche In the example above, you can see the letters ``@t{CG}'', which indicate: @itemize @item @t{C}: support for decryption of encrypted messages, and verifying signatures. See @ref{MSGV Crypto} in the @ref{Message view} for details. @item @t{G}: support for the Guile 2.0 programming language @end itemize Whether you see both, one or none of these letters depends on the way @t{mu} is built. Let's walk through the menu. @node Basic actions @section Basic actions First, the @emph{Basics}: @itemize @item @t{[j]ump to some maildir}: after pressing @key{j} (``jump''), @t{mu4e} asks you for a maildir to visit. These are the maildirs you set in @ref{Basic configuration} and any of your own. If you choose @key{o} (``other'') or @key{/}, you can choose from all maildirs under @code{mu4e-maildir}. After choosing a maildir, the messages in that maildir are listed, in the @ref{Headers view}. @item @t{enter a [s]earch query}: after pressing @key{s}, @t{mu4e} asks you for a search query, and after entering one, shows the results in the @ref{Headers view}. @item @t{[C]ompose a new message}: after pressing @key{C}, you are dropped in the @ref{Editor view} to write a new message. @end itemize @node MV Bookmarks @section Bookmarks The next item in the Main view is @emph{Bookmarks}. Bookmarks are predefined queries with a descriptive name and a shortcut - in the example above, we see the default bookmarks. You can view the list of messages matching a certain bookmark by pressing @key{b} followed by the bookmark's shortcut. If you'd like to edit the bookmarked query first before invoking it, use @key{B}. Bookmarks are stored in the variable @code{mu4e-bookmarks}; you can add your own and/or replace the default ones; @xref{Bookmarks}. @node Miscellaneous @section Miscellaneous Finally, there are some @emph{Misc} (miscellaneous) actions: @itemize @item @t{[U]pdate email & database} executes the shell-command in the variable @code{mu4e-get-mail-command}, and afterwards updates the @t{mu} database; see @ref{Indexing your messages} and @ref{Getting mail} for details. @item @t{toggle [m]ail sending mode (direct)} toggles between sending mail directly, and queuing it first (for example, when you are offline), and @t{[f]lush queued mail} flushes any queued mail. This item is visible only if you have actually set up mail-queuing. @ref{Queuing mail} @item @t{[A]bout mu4e} provides general information about the program @item @t{[H]elp} shows help information for this view @item Finally, @t{[q]uit mu4e} quits your @t{mu4e}-session @end itemize @node Headers view @chapter The headers view The headers view shows the results of a query. The header-line shows the names of the fields. Below that, there is a line with those fields, for each matching message, followed by a footer line. The major-mode for the headers view is @code{mu4e-headers-mode}. @menu * Overview: HV Overview. What is the Header View * Keybindings::Do things with your keyboard * Marking: HV Marking. Selecting messages for doing things * Sorting and threading::Influencing the display * Custom headers: HV Custom headers. Adding your own headers * Actions: HV Actions. Defining and using actions * Split view::Seeing both headers and messages @end menu @node HV Overview @section Overview An example headers view: @cartouche @verbatim Date V Flgs From/To List Subject 06:32 Nu To Edmund Dantès GstDev + Re: Gstreamer-V4L... 15:08 Nu Abbé Busoni GstDev + Re: Gstreamer-V... 18:20 Nu Pierre Morrel GstDev \ Re: Gstreamer... 2013-03-18 S Jacopo EmacsUsr + emacs server on win... 2013-03-18 S Mercédès EmacsUsr \ RE: emacs server ... 2013-03-18 S Beachamp EmacsUsr + Re: Copying a whole... 22:07 Nu Albert de Moncerf EmacsUsr \ Re: Copying a who... 2013-03-18 S Gaspard Caderousse GstDev | Issue with GESSimpl... 2013-03-18 Ss Baron Danglars GuileUsr | Guile-SDL 0.4.2 ava... End of search results @end verbatim @end cartouche Some notes to explain what you see in the example: @itemize @item The fields shown in the headers view can be influenced by customizing the variable @code{mu4e-headers-fields}; see @code{mu4e-header-info} for the list of built-in fields. Apart from the built-in fields, you can also create custom fields using @code{mu4e-header-info-custom}; see @ref{HV Custom headers} for details. @item By default, the date is shown with the @t{:human-date} field, which shows the @emph{time} for today's messages, and the @emph{date} for older messages. If you want to distinguish between 'today' and 'older', you can use the @t{:date} field instead. @item You can customize the date and time formats with the variable @code{mu4e-headers-date-format} and @code{mu4e-headers-time-format}, respectively. In the example, we use @code{:human-date}, which shows when the time when the message was sent today, and the date otherwise. @item By default, the subject is shown using the @t{:subject} field; however, it is also possible to use @t{:thread-subject}, which shows the subject of a thread only once, similar to the display of the @t{mutt} e-mail client. @item The header field used for sorting is indicated by ``@t{V}'' or ``@t{^}''@footnote{or you can use little graphical triangles; see variable @code{mu4e-use-fancy-chars}}, corresponding to the sort order (descending or ascending, respectively). You can influence this by a mouse click, or @key{O}. Not all fields allow sorting. @item Instead of showing the @t{From:} and @t{To:} fields separately, you can use From/To (@t{:from-or-to} in @code{mu4e-headers-fields} as a more compact way to convey the most important information: it shows @t{From:} @emph{except} when the e-mail was sent by the user (i.e., you) - in that case it shows @t{To:} (prefixed by @t{To}@footnote{You can customize this by changing the variable @code{mu4e-headers-from-or-to-prefix} (a cons cell)}, as in the example above). To determine whether a message was sent by you, @t{mu4e} uses the variable @code{mu4e-user-mail-address-list}, a list of your e-mail addresses. @item The 'List' field shows the mailing-list a message is sent to; @code{mu4e} tries to create a convenient shortcut for the mailing-list name; the variable @code{mu4e-user-mailing-lists} can be used to add your your own shortcuts. You can use @code{mu4e-mailing-list-patterns} to to specify generic shortcuts, e.g. to shorten list names which contain dots (mu4e defaults to shortening up to the first dot): @lisp (setq mu4e-mailing-list-patterns '(``\\([-_a-z0-9.]+\\)\.lists\.company\.com''))) @end lisp @item The letters in the 'Flags' field correspond to the following: D=@emph{draft}, F=@emph{flagged} (i.e., 'starred'), N=@emph{new}, P=@emph{passed} (i.e., forwarded), R=@emph{replied}, S=@emph{seen}, T=@emph{trashed}, a=@emph{has-attachment}, x=@emph{encrypted}, s=@emph{signed}, u=@emph{unread}. The tooltip for this field also contains this information. @item The subject field also indicates the discussion threads @footnote{using Jamie Zawinski's mail threading algorithm, @url{http://www.jwz.org/doc/threading.html}}. @item The headers view is @emph{automatically updated} if any changes are found during the indexing process, and if there is no current user-interaction. If you do not want such automatic updates, set @code{mu4e-headers-auto-update} to @code{nil}. @item Just before executing a search, a hook-function @code{mu4e-headers-search-hook} is invoked, which receives the search expression as its parameter. @item Also, there is a hook-function @code{mu4e-headers-found-hook} available which is invoked just after @t{mu4e} has completed showing the messages in the headers-view. @end itemize @node Keybindings @section Keybindings Using the below key bindings, you can do various things with these messages; these actions are also listed in the @t{Headers} menu in the @command{emacs} menu bar. @verbatim key description =========================================================== n,p view the next, previous message ],[ move to the next, previous unread message y select the message view (if it's visible) RET open the message at point in the message view searching --------- s search S edit last query / narrow the search b search bookmark B edit bookmark before search j jump to maildir M-left,\ previous query M-right next query O change sort order P toggle threading Q toggle full-search V toggle skip-duplicates W toggle include-related marking ------- d mark for moving to the trash folder = mark for removing trash flag ('untrash') DEL,D mark for complete deletion m mark for moving to another maildir folder r mark for refiling +,- mark for flagging/unflagging ?,! mark message as unread, read u unmark message at point U unmark *all* messages % mark based on a regular expression T,t mark whole thread, subthread <insert>,* mark for 'something' (decide later) # resolve deferred 'something' marks x execute actions for the marked messages composition ----------- R,F,C reply/forward/compose E edit (only allowed for draft messages) misc ---- ; switch focus a execute some custom action on a header | pipe message through shell command C-+,C-- increase / decrease the number of headers shown H get help C-S-u update mail & reindex q leave the headers buffer @end verbatim @node HV Marking @section Marking You can @emph{mark} messages for a certain action, such as deletion or move. After one or more messages are marked, you can then execute (@code{mu4e-mark-execute-all}, @key{x}) these actions. This two-step mark-execute sequence is similar to what e.g. @t{dired} does. It is how @t{mu4e} tries to be as quick as possible, while avoiding accidents. The mark/unmark commands support the @emph{region} (i.e., ``selection'') -- so, for example, if you select some messages and press @key{DEL}, all messages in the region are marked for deletion. You can mark all messages that match a certain pattern with @key{%}. In addition, you can mark all messages in the current thread (@key{T}) or sub-thread (@key{t}). When you do a new search or refresh the headers buffer while you still have marked messages, you are asked what to do with those marks -- whether to @emph{apply} them before leaving, or @emph{ignore} them. This behavior can be influenced with the variable @code{mu4e-headers-leave-behavior}. For more information about marking, see @ref{Marking}. @node Sorting and threading @section Sorting and threading By default, @t{mu4e} sorts messages by date, in descending order: the most recent messages are shown at the top. In addition, the messages are @emph{threaded}, i.e., shown in the context of a discussion thread; this also affects the sort order. The header field used for sorting is indicated by ``@t{V}'' or ``@t{^}''@footnote{or you can use little graphical triangles; see variable @code{mu4e-use-fancy-chars}}, indicating the sort order (descending or ascending, respectively). You can change the sort order by clicking the corresponding field with the mouse, or with @kbd{M-x mu4e-headers-change-sorting} (@key{O}); note that not all fields can be used for sorting. You can toggle threading on/off using @kbd{M-x mu4e-headers-toggle-threading} or @key{P}. For both of these functions, unless you provide a prefix argument (@key{C-u}), the current search is updated immediately using the new parameters. You can toggle full-search (@ref{Searching}) using @kbd{M-x mu4e-headers-toggle-full-search} or @key{Q}. If you want to change the defaults for these settings, you can use the variables @code{mu4e-headers-sortfield} and @code{mu4e-headers-show-threads}. @node HV Custom headers @section Custom headers Sometimes the normal headers that @t{mu4e} offers (Date, From, To, Subject etc.) may not be enough. For these cases, @t{mu4e} offers @emph{custom headers} in both the headers-view and the message-view. You can do so by adding a description of your custom header to @code{mu4e-header-info-custom}, which is a list of custom headers. Let's look at an example -- suppose we want to add a custom header that shows the number of recipients for a message, i.e., the sum of the number of recipients in the To: and Cc: fields. Let's further suppose that our function takes a message-plist as its argument (@ref{Message functions}). @lisp (add-to-list 'mu4e-header-info-custom '(:recipnum . ( :name "Number of recipients" ;; long name, as seen in the message-view :shortname "Recip#" ;; short name, as seen in the headers view :help "Number of recipients for this message" ;; tooltip :function (lambda (msg) (format "%d" (+ (length (mu4e-message-field msg :to)) (length (mu4e-message-field msg :cc)))))))) @end lisp Or, let's get the full mailing-list name: @lisp (add-to-list 'mu4e-header-info-custom '(:full-mailing-list . ( :name "Mailing-list" ;; long name, as seen in the message-view :shortname "ML" ;; short name, as seen in the headers view :help "Full name for mailing list" ;; tooltip :function (lambda (msg) (or (mu4e-message-field msg :mailing-list) ""))))) @end lisp You can then add the custom header to your @code{mu4e-headers-fields}, just like the built-in headers. After evaluation, you headers-view should include a new header @t{Recip#} with the number of recipients, and/or @t{ML} with the full mailing-list name. This function can be used in both the headers-view and the message-view; if you need something specific for one of these, you can check for the mode in your function, or create separate functions. @node HV Actions @section Actions @code{mu4e-headers-action} (@key{a}) lets you pick custom actions to perform on the message at point. You can specify these actions using the variable @code{mu4e-headers-actions}. See @ref{Actions} for the details. @t{mu4e} defines some default actions. One of those is for @emph{capturing} a message: @key{a c} 'captures' the current message. Next, when you're editing some message, you can include the previously captured message as an attachment, using @code{mu4e-compose-attach-captured-message}. See @file{mu4e-actions.el} in the @t{mu4e} source distribution for more example actions. @node Split view @section Split view Using the @emph{Split view}, we can see the @ref{Headers view} and the @ref{Message view} next to each other, with the message selected in the former, visible in the latter. You can influence the way the splitting is done by customizing the variable @code{mu4e-split-view}. Possible values are: @itemize @item @t{horizontal} (this is the default): display the message view below the header view. Use @code{mu4e-headers-visible-lines} the set the number of lines shown (default: 8). @item @t{vertical}: display the message view on the right side of the header view. Use @code{mu4e-headers-visible-columns} to set the number of visible columns (default: 30). @item anything else: don't do any splitting @end itemize @noindent Some useful key bindings in the split view: @itemize @item @key{C-+} and @key{C--}: interactively change the number of columns or headers shown @item You can change the selected window from the headers-view to the message-view and vice-versa with @code{mu4e-select-other-view}, bound to @key{y} @end itemize @node Message view @chapter The message view After selecting a message in the @ref{Headers view}, it appears in a message view window, which shows the message headers, followed by the message body. Its major mode is @code{mu4e-view-mode}. @menu * Overview: MSGV Overview. What is the Message View * Keybindings: MSGV Keybindings. Do things with your keyboard * Attachments:: Opening and saving them * Viewing images inline::Images display inside emacs * Displaying rich-text messages::Dealing with HTML mail * Verifying signatures and decryption: MSGV Crypto. Support for cryptography * Custom headers: MSGV Custom headers. Your own headers * Actions: MSGV Actions. Defining and using actions. @end menu @node MSGV Overview @section Overview An example message view: @cartouche @verbatim From: randy@epiphyte.com To: julia@eruditorum.org Subject: Re: some pics Flags: (seen attach) Date: Mon 19 Jan 2004 09:39:42 AM EET Maildir: /inbox Attachments(2): [1]DSCN4961.JPG(1.3M), [2]DSCN4962.JPG(1.4M) Hi Julia, Some pics from our trip to Cerin Amroth. Enjoy! All the best, Randy. On Sun 21 Dec 2003 09:06:34 PM EET, Julia wrote: [....] @end verbatim @end cartouche Some notes: @itemize @item The variable @code{mu4e-view-fields} determines the header fields to be shown; see @code{mu4e-header-info} for a list of built-in fields. Apart from the built-in fields, you can also create custom fields using @code{mu4e-header-info-custom}; see @ref{MSGV Custom headers}. @item You can set the date format with the variable @code{mu4e-date-format-long}. @item By default, only the names of contacts in address fields are visible (see @code{mu4e-view-show-addresses} to change this). You can view the e-mail addresses by clicking on the name, or pressing @key{M-RET}. @item You can compose a message for the contact at point by either clicking @key{[mouse-2]} or pressing @key{C}. @item The body text can be line-wrapped using @t{visual-line-mode}. @t{mu4e} defines @key{w} to toggle between the wrapped and unwrapped state. If you want to do this automatically when viewing a message, invoke @code{visual-line-mode} in your @code{mu4e-view-mode-hook}. @item For messages that support it, you can toggle between html and text versions using @code{mu4e-view-toggle-html}, bound to @key{h}; @item You can hide cited parts in messages (the parts starting with ``@t{>}'') using @code{mu4e-view-hide-cited}, bound to @key{#}. If you want to do this automatically for every message, invoke the function in your @code{mu4e-view-mode-hook}. @item For search-related operations, see @ref{Searching}. @item You can scroll down the message using @key{SPC}; if you do this at the end of a message,it automatically takes you to the next one. If you want to prevent this behavior, set @code{mu4e-view-scroll-to-next} to @code{nil}. @end itemize @node MSGV Keybindings @section Keybindings You can find most things you can do with this message in the @emph{View} menu, or by using the keyboard; the default bindings are: @verbatim key description ============================================================== n,p view the next, previous message ],[ move to the next, previous unread message y select the headers view (if it's visible) RET scroll down M-RET open URL at point / attachment at point SPC scroll down, if at end, move to next message S-SPC scroll up searching --------- s search e edit last query / narrow the search b search bookmark B edit bookmark before search j jump to maildir M-left previous query M-right next query marking ------- d mark for moving to the trash folder = mark for removing trash flag ('untrash') DEL,D mark for complete deletion m mark for moving to another maildir folder r mark for refiling +,- mark for flagging/unflagging u unmark message at point U unmark *all* messages % mark based on a regular expression T,t mark whole thread, subthread <insert>,* mark for 'something' (decide later) # resolve deferred 'something' marks x execute actions for the marked messages composition ----------- R,F,C reply/forward/compose E edit (only allowed for draft messages) actions ------- g go to (visit) numbered URL (using `browse-url') (or: <mouse-1> or M-RET with point on url) C-u g visits multiple URLs f fetch (download )the numbered URL. C-u f fetches multiple URLs k save the numbered URL in the kill-ring. C-u k saves multiple URLs e extract (save) attachment (asks for number) (or: <mouse-2> or S-RET with point on attachment) C-u e extracts multiple attachments o open attachment (asks for number) (or: <mouse-1> or M-RET with point on attachment) a execute some custom action on the message A execute some custom action on an attachment misc ---- ; switch focus c copy address at point (with C-u copy long version) h toggle between html/text (if available) w toggle line wrapping # toggle show/hide cited parts v show details about the cryptographic signature . show the raw message view. 'q' takes you back. C-+,C-- increase / decrease the number of headers shown H get help C-S-u update mail & reindex q leave the message view @end verbatim For the marking commands, please refer to @ref{Marking messages}. @node Attachments @section Attachments By default, @t{mu4e} uses the @t{xdg-open}-program @footnote{@url{http://portland.freedesktop.org/wiki/}} or (on OS X) the @t{open} program for opening attachments. If you want to use another program, you do so by setting the @t{MU_PLAY_PROGRAM} environment variable to the program to be used. The default directory for extracting (saving) attachments is your home directory (@file{~/}); you can change this using the variable @code{mu4e-attachment-dir}, for example: @lisp (setq mu4e-attachment-dir "~/Downloads") @end lisp For more flexibility, @code{mu4e-attachment-dir} can also be a user-provided function. This function receives two parameters: the file-name and the mime-type as found in the e-mail message@footnote{sadly, often @t{application/octet-stream} is used for the mime-type, even if a better type is available} of the attachment, either or both of which can be @t{nil}. For example: @lisp (setq mu4e-attachment-dir (lambda (fname mtype) (cond ;; docfiles go to ~/Desktop ((and fname (string-match "\\.doc$" fname)) "~/Desktop") ;; ... other cases ... (t "~/Downloads")))) ;; everything else @end lisp You can extract multiple attachments at once by prefixing the extracting command by @key{C-u}; so @kbd{C-u e} asks you for a range of attachments to extract (for example, @kbd{1 3-6 8}). The range "@samp{a}" is a shortcut for @emph{all} attachments. @node Viewing images inline @section Viewing images inline It is possible to show images inline in the message view buffer if you run @command{emacs} in GUI-mode. You can enable this by setting the variable @code{mu4e-view-show-images} to @t{t}. Since @command{emacs} does not always handle images correctly, this is not enabled by default. If you are using @command{emacs} 24 with @emph{ImageMagick}@footnote{@url{http://www.imagemagick.org}} support, make sure you call @code{imagemagick-register-types} in your configuration, so it is used for images. @lisp ;; enable inline images (setq mu4e-view-show-images t) ;; use imagemagick, if available (when (fboundp 'imagemagick-register-types) (imagemagick-register-types)) @end lisp @node Displaying rich-text messages @section Displaying rich-text messages @t{mu4e} normally prefers the plain-text version for messages that consist of both a plain-text and html (rich-text) versions of the body-text. You can change this by setting @code{mu4e-view-prefer-html} to @t{t}. And you can toggle this value (globally) using @kbd{h} in the message view; this also refreshes the message with the new setting. Note, when using html-based rendering, you don't get the hyperlink shortcuts the text-version provides. If there is only an html-version, or if the plain-text version is too short in comparison with the html part@footnote{this is e.g. for the case where the text-part is only a short blurb telling you to use the html-version; see @code{mu4e-view-html-plaintext-ratio-heuristic}}, @t{mu4e} tries to convert the html into plain-text for display. With emacs 24.4 or newer, this defaults to @code{mu4e-shr2text}, which uses the built-in @t{shr} renderer. For older emacs versions, this defaults to the built-in @code{html2text} function. In practice, the latter gives much better results. If you use @code{mu4e-shr2text}, it might be useful to emulate some of the @t{shr} key bindings, with something like: @lisp (add-hook 'mu4e-view-mode-hook (lambda() ;; try to emulate some of the eww key-bindings (local-set-key (kbd "<tab>") 'shr-next-link) (local-set-key (kbd "<backtab>") 'shr-previous-link))) @end lisp If you're using a dark theme, and the messages are hard to read, it can help to change the luminosity, e.g.: @lisp (setq shr-color-visible-luminance-min 80) @end lisp If your emacs does not have @t{shr} yet, it can be useful to use a custom method. For that, you can set the variable @code{mu4e-html2text-command} to either a shell command or a function instead. @subsection Html2text commands If @code{mu4e-html2text-command} is a shell command, it is expected to take html from standard input and write plain text in @t{UTF-8} encoding on standard output. An example of such a program is the program that is actually @emph{called} @t{html2text}@footnote{@url{http://www.mbayer.de/html2text/}}. After installation, you can set it up with something like the following: @lisp (setq mu4e-html2text-command "html2text -utf8 -width 72") @end lisp An alternative to this is the Python @t{python-html2text} package; after installing that, you can tell @t{mu4e} to use it with something like: @lisp (setq mu4e-html2text-command "html2markdown | grep -v ' _place_holder;'") @end lisp On OS X, there is a program called @t{textutil} as yet another alternative: @lisp (setq mu4e-html2text-command "textutil -stdin -format html -convert txt -stdout") @end lisp @subsection Html2text functions @anchor{Html2text functions} If @code{mu4e-html2text-command} refers to an elisp function, it is expected to take the current buffer in html as input, and transform it into text (just like the @code{html2text} function). @subsection Privacy aspects @anchor{Privacy aspects} When opening your messages in a graphical browser, it may expose you doing so to the sender, due to the presence of specially crafted image URLs, or Javascript. If that is an issue, it is recommended to use a browser (or browser profile) that does not load images. The same applies to Javascript. @node MSGV Crypto @section Crypto The @t{mu4e} message view supports@footnote{Crypto-support in @t{mu4e} requires @t{mu} to have been build with crypto-support; see the @ref{FAQ}} decryption of encrypted messages, as well as verification of signatures. For signing/encrypting messages your outgoing messages, see @ref{Signing and encrypting}. Currently, only PGP/MIME is supported; PGP-inline and S/MIME are not. For all of this to work, @command{gpg-agent} must be running, and it must set the environment variable @t{GPG_AGENT_INFO}. You can check from @command{emacs} with @key{M-x getenv GPG_AGENT_INFO}. In many mainstream Linux/Unix desktop environments, everything works out-of-the-box, but if your environment does not automatically start @command{gpg-agent}, you can do so by hand: @verbatim $ eval $(gpg-agent --daemon) @end verbatim @noindent This starts the daemon, and sets the environment variable. @subsection Decryption @anchor{Decryption} If you receive messages that are encrypted (using PGP/MIME), @t{mu4e} can try to decrypt them, base on the setting of @code{mu4e-decryption-policy}. If you set it to @t{t}, @t{mu4e} attempts to decrypt messages automatically; this is the default. If you set it to @t{nil}, @t{mu4e} @emph{won't} attempt to decrypt anything. Finally, if you set it to @t{'ask}, it asks you what to do, each time an encrypted message is encountered. When opening an encrypted message, @t{mu} consults @t{gpg-agent} to see if it already has unlocked the key needed to decrypt the message; if not, it prompts you for a password (typically with a separate top-level window). This is only needed once per session. @subsection Verifying signatures @anchor{Verifying signatures} Some e-mail messages are cryptographically signed, and @t{mu4e} can check the validity of these signatures. If a message has one or more signatures, the message view shows an extra header @t{Signature:} (assuming it is part of your @code{mu4e-view-fields}), and one or more 'verdicts' of the signatures found; either @t{verified}, @t{unverified} or @t{error}. For instance: @verbatim Signature: unverified (Details) @end verbatim You can see the details of the signature verification by activating the @t{Details} or pressing @key{v}. This pops up a little window with the details of the signatures found and whether they could be verified or not. For more information, see the @command{mu-verify} manual page. @node MSGV Custom headers @section Custom headers Sometimes the normal headers that @t{mu4e} offers (Date, From, To, Subject etc.) may not be enough. For these cases, @t{mu4e} offers @emph{custom headers} in both the headers-view and the message-view. See @ref{HV Custom headers} for an example of this; the difference for the message-view is that you should add your custom header to @code{mu4e-view-fields} rather than @code{mu4e-headers-fields}. @node MSGV Actions @section Actions You can perform custom functions (``actions'') on messages and their attachments. For a general discussion on how to define your own, see see @ref{Actions}. @subsection Message actions @code{mu4e-view-action} (@key{a}) lets you pick some custom action to perform on the current message. You can specify these actions using the variable @code{mu4e-view-actions}; @t{mu4e} defines a number of example actions. @subsection Attachment actions Similarly, there is @code{mu4e-view-attachment-action} (@key{A}) for actions on attachments, which you can specify with @code{mu4e-view-attachment-actions}. @t{mu4e} predefines a number of attachment-actions: @itemize @item @t{open-with} (@key{w}): open the attachment with some arbitrary program. For example, suppose you have received a message with a picture attachment; then, @kbd{A w 1 RET gimp RET} opens that attachment in @emph{The Gimp} @item @t{pipe} (@key{|}: process the attachment with some Unix shell-pipe and see the results. Suppose you receive a patch file, and would like to get an overview of the changes, using the @t{diffstat} program. You can use something like: @kbd{A | 1 RET diffstat -b RET}. @item @command{emacs} (@key{e}): open the attachment in your running @command{emacs}. For example, if you receive some text file you'd like to open in @command{emacs}: @kbd{A e 1 RET}. @end itemize These actions all work on a @emph{temporary copy} of the attachment. @node Editor view @chapter The editor view Writing e-mail messages takes place in the Editor View. @t{mu4e}'s editor view builds on top of Gnu's @t{message-mode}. Most of the @t{message-mode} functionality is available, as well some @t{mu4e}-specifics. Its major mode is @code{mu4e-compose-mode}. @menu * Overview: EV Overview. What is the Editor view * Keybindings: EV Keybindings. Doing things with your keyboard * Address autocompletion:: Quickly entering known addresses * Compose hooks::Calling functions when composing * Signing and encrypting:: Support for cryptography * Queuing mail:: Sending mail when the time is ripe * Message signatures:: Adding your personal footer to messages * Other settings::Miscellanea @end menu @node EV Overview @section Overview @cartouche @verbatim From: Rupert the Monkey <rupert@example.com> To: Wally the Walrus <wally@example.com> Subject: Re: Eau-qui d'eau qui? --text follows this line-- On Mon 16 Jan 2012 10:18:47 AM EET, Wally the Walrus wrote: > Hi Rupert, > > Dude - how are things? > > Later -- wally. @end verbatim @end cartouche @node EV Keybindings @section Keybindings @t{mu4e}'s editor view derives from Gnu's message editor and shares most of its keybindings. Here are some of the more useful ones (you can use the menu to find more): @verbatim key description --- ----------- C-c C-c send message C-c C-d save to drafts and leave C-c C-k kill the message buffer (the message remains in the draft folder) C-c C-a attach a file (pro-tip: drag & drop works as well) (mu4e-specific) C-S-u update mail & reindex @end verbatim @node Address autocompletion @section Address autocompletion @t{mu4e} supports@footnote{@command{emacs} 23.2 or higher is required} autocompleting addresses when composing e-mail messages. @t{mu4e} uses the e-mail addresses from the messages you sent or received as the source for this. Address auto-completion is enabled by default; if you want to disable it for some reason, set @t{mu4e-compose-complete-addresses} to @t{nil}. Emacs 24 also supports cycling through the alternatives. When there are more than @emph{5} matching addresses, they are shown in a @t{*Completions*} buffer. Once the number of matches gets below this number, one is inserted in the address field and you can cycle through the alternatives using @key{TAB}. @subsection Limiting the number of addresses If you have a lot of mail, especially from mailing lists and the like, there can be a @emph{lot} of e-mail addresses, many of which may not be very useful when auto-completing. For this reason, @t{mu4e} attempts to limit the number of e-mail addresses in the completion pool by filtering out the ones that are not likely to be relevant. The following variables are available for tuning this: @itemize @item @code{mu4e-compose-complete-only-personal} - when set to @t{t}, only consider addresses that were seen in @emph{personal} messages -- that is, messages in which one of my e-mail addresses was seen in one of the address fields. This is to exclude mailing list posts. You can define what is considered 'my e-mail address' using @code{mu4e-user-mail-address-list}, a list of e-mail address (defaults to @code{user-mail-address}, and when indexing from the command line, the @t{--my-address} parameter for @t{mu index}. @item @code{mu4e-compose-complete-only-after} - only consider e-mail addresses last seen after some date. Parameter is a string, parseable by @code{org-parse-time-string}. This excludes old e-mail addresses. The default is @t{"2010-01-01"}, i.e., only consider e-mail addresses seen since the start of 2010. @item @code{mu4e-compose-complete-ignore-address-regexp} - a regular expression to filter out other 'junk' e-mail addresses; defaults to ``@t{no-?reply}''. @end itemize @node Compose hooks @section Compose hooks If you want to change some setting, or execute some custom action before message composition starts, you can define a @emph{hook function}. @t{mu4e} offers two hooks: @itemize @item @code{mu4e-compose-pre-hook}: this hook is run @emph{before} composition starts; if you are composing a @emph{reply}, @emph{forward} a message, or @emph{edit} an existing message, the variable @code{mu4e-compose-parent-message} points to the message being replied to, forwarded or edited, and you can use @code{mu4e-message-field} to get the value of various properties (and see @ref{Message functions}). @item @code{mu4e-compose-mode-hook}: this hook is run just before composition starts, when the whole buffer has already been set up. This is a good place for editing-related settings. @code{mu4e-compose-parent-message} (see above) is also at your disposal. @end itemize @noindent Let's look at some examples. First, suppose we want to set the @t{From:}-address for a reply message based on the receiver of the original: @lisp ;; 1) messages to me@@foo.example.com should be replied with From:me@@foo.example.com ;; 2) messages to me@@bar.example.com should be replied with From:me@@bar.example.com ;; 3) all other mail should use From:me@@cuux.example.com (add-hook 'mu4e-compose-pre-hook (defun my-set-from-address () "Set the From address based on the To address of the original." (let ((msg mu4e-compose-parent-message)) ;; msg is shorter... (when msg (setq user-mail-address (cond ((mu4e-message-contact-field-matches msg :to "me@@foo.example.com") "me@@foo.example.com") ((mu4e-message-contact-field-matches msg :to "me@@bar.example.com") "me@@bar.example.com") (t "me@@cuux.example.com"))))))) @end lisp Second, as mentioned, @code{mu4e-compose-mode-hook} is especially useful for editing-related settings. For example: @lisp (add-hook 'mu4e-compose-mode-hook (defun my-do-compose-stuff () "My settings for message composition." (set-fill-column 72) (flyspell-mode))) @end lisp This hook is also useful for adding headers or changing headers, since the message is fully formed when this hook runs. For example, to add a @t{Bcc:}-header, you could add something like the following, using @code{message-add-header} from @code{message-mode}. @lisp (add-hook 'mu4e-compose-mode-hook (defun my-add-bcc () "Add a Bcc: header." (save-excursion (message-add-header "Bcc: me@@example.com\n")))) @end lisp @noindent For a more general discussion about extending @t{mu4e}, see @ref{Extending mu4e}. @node Signing and encrypting @section Signing and encrypting Signing and encrypting of messages is possible using @t{emacs-mime} (@inforef{Composing,,emacs-mime}), most easily accessed through the @t{Attachments}-menu while composing a message, or with @kbd{M-x mml-secure-message-encrypt-pgp}, @kbd{M-x mml-secure-message-sign-pgp}. The support for encryption and signing is @emph{independent} of the support for their counterparts, decrypting and signature verification (as discussed in @ref{MSGV Crypto}). Even if your @t{mu4e} does not have support for the latter two, you can still sign/encrypt messages. Currently, decryption and signature verification only works for PGP/MIME; inline-PGP and S/MIME are not supported. Important note: the messages are encrypted when they are @emph{sent}: this means that draft messages are @emph{not} encrypted. So if you are using e.g. @t{offlineimap} or @t{mbsync} to synchronize with some remote IMAP-service, make sure the drafts folder is @emph{not} in the set of synchronized folders, for obvious reasons. @node Queuing mail @section Queuing mail If you cannot send mail right now, for example because you are currently offline, you can @emph{queue} the mail, and send it when you have restored your internet connection. You can control this from the @ref{Main view}. To allow for queuing, you need to tell @t{smtpmail} where you want to store the queued messages. For example: @lisp (setq smtpmail-queue-mail t ;; start in queuing mode smtpmail-queue-dir "~/Maildir/queue/cur") @end lisp For convenience, we put the queue directory somewhere in our normal maildir. If you want to use queued mail, you should create this directory before starting @t{mu4e}. The @command{mu mkdir} command may be useful here, so for example: @verbatim $ mu mkdir ~/Maildir/queue $ touch ~/Maildir/queue/.noindex @end verbatim The file created by the @command{touch} command tells @t{mu} to ignore this directory for indexing, which makes sense since it contains @t{smtpmail} meta-data rather than normal messages; see the @t{mu-mkdir} and @t{mu-index} man-pages for details. @emph{Warning}: when you switch on queued-mode, your messages @emph{won't} reach their destination until you switch it off again; so, be careful not to do this accidentally! @node Message signatures @section Message signatures Message signatures are the standard footer blobs in e-mail messages where you can put in information you want to include in every message. The text to include is set with @code{mu4e-compose-signature}. If you don't want to include this automatically with each message, you can set @code{mu4e-compose-signature-auto-include} to @code{nil}; you can then still include the signature manually, using the function @code{message-insert-signature}, typically bound to @kbd{C-c C-w}. @node Other settings @section Other settings @itemize @item If you want use @t{mu4e} as @command{emacs}' default program for sending mail, see @ref{Emacs default}. @item Normally, @t{mu4e} @emph{buries} the message buffer after sending; if you want to kill the buffer instead, add something like the following to your configuration: @lisp (setq message-kill-buffer-on-exit t) @end lisp @item If you want to exclude your own e-mail address when ``replying to all'', set @code{mu4e-compose-dont-reply-to-self} to @code{t}. In order for this to work properly you need to properly set the @code{user-mail-address} variable or in the case you use multiple e-mail addresses you need to set the @code{mu4e-user-mail-address-list} variable accordingly. @end itemize @node Searching @chapter Searching @t{mu4e} is fully search-based: even if you 'jump to a folder', you are executing a query for messages that happen to have the property of being in a certain folder (maildir). Normally, queries return up to @code{mu4e-headers-results-limit} (default: 500) results. That is usually more than enough, and makes things significantly faster. Sometimes, however, you may want to show @emph{all} results; you can enable this with @kbd{M-x mu4e-headers-toggle-full-search}, or by customizing the variable @code{mu4e-headers-full-search}. This applies to all search commands. You can also influence the sort order and whether threads are shown or not; see @ref{Sorting and threading}. @menu * Queries:: Searching for messages. * Bookmarks:: Remembering queries. * Maildir searches:: Queries for maildirs. * Other search functionality:: Some more tricks. @end menu @node Queries @section Queries @t{mu4e} queries are the same as the ones that @t{mu find} understands@footnote{with the caveat that command-line queries are subject to the shell's interpretation before @t{mu} sees them}. Let's look at some examples here, please refer to the @code{mu-find} and @code{mu-easy} man pages for details and more examples. @itemize @item Get all messages regarding @emph{bananas}: @verbatim bananas @end verbatim @item Get all messages regarding @emph{bananas} from @emph{John} with an attachment: @verbatim from:john flag:attach bananas @end verbatim @item Get all messages with subject @emph{wombat} in June 2009 @verbatim subject:wombat date:20090601..20090630 @end verbatim @item Get all messages with PDF attachments in the @t{/projects} folder @verbatim maildir:/projects mime:application/pdf @end verbatim @item Get all messages about @emph{Rupert} in the @t{/Sent Items} folder. Note that maildirs with spaces must be quoted. @verbatim maildir:"/Sent Items" rupert @end verbatim @item Get all important messages which are signed: @verbatim flag:signed prio:high @end verbatim @item Get all messages from @emph{Jim} without an attachment: @verbatim from:jim AND NOT flag:attach @end verbatim @item Get all messages with Alice in one of the contacts-fields (@t{to}, @t{from}, @t{cc}, @t{bcc}): @verbatim contact:alice @end verbatim @item Get all unread messages where the subject mentions Ångström: (search is case-insensitive and accent-insensitive, so this matches Ångström, angstrom, aNGstrøM, ...) @verbatim subject:Ångström flag:unread @end verbatim @item Get all unread messages between Mar-2002 and Aug-2003 about some bird: @verbatim date:20020301..20030831 nightingale flag:unread @end verbatim @item Get today's messages: @verbatim date:today..now @end verbatim or, unless you have a really old Xapian @verbatim date:today @end verbatim @item Get all messages we got in the last two weeks regarding @emph{emacs}: @verbatim date:2w..now emacs @end verbatim or, unless you have a really old Xapian @verbatim date:2w.. emacs @end verbatim @item Get messages from the @emph{Mu} mailing list: @verbatim list:mu-discuss.googlegroups.com @end verbatim Note - in the @ref{Headers view} you may see the 'friendly name' for a list; however, when searching you need the real name. You can see the real name for a mailing list from the friendly name's tool-tip. @item Get messages with a subject soccer, Socrates, society, ...; note that the '*'-wildcard can only appear as a term's rightmost character: @verbatim subject:soc* @end verbatim @item Get all messages @emph{not} sent to a mailing-list: @verbatim NOT flag:list @end verbatim @item Get all mails with attachments with filenames starting with @emph{pic}; note that the '*' wildcard can only appear as the term's rightmost character: @verbatim file:pic* @end verbatim @item Get all messages with PDF-attachments: @verbatim mime:application/pdf @end verbatim Get all messages with image attachments, and note that the '*' wildcard can only appear as the term's rightmost character: @verbatim mime:image/* @end verbatim @end itemize @node Bookmarks @section Bookmarks If you have queries that you use often, you may want to store them as @emph{bookmarks}. Bookmark searches are available in the main view (@pxref{Main view}), header view (@pxref{Headers view}), and message view (@pxref{Message view}), using (by default) the key @key{b} (@kbd{M-x mu4e-search-bookmark}), or @key{B} (@kbd{M-x mu4e-search-bookmark-edit}) which lets you edit the bookmark first. @subsection Setting up bookmarks @t{mu4e} provides a number of default bookmarks. Their definition is instructive: @lisp (defvar mu4e-bookmarks `( ,(make-mu4e-bookmark :name "Unread messages" :query "flag:unread AND NOT flag:trashed" :key ?u) ,(make-mu4e-bookmark :name "Today's messages" :query "date:today..now" :key ?t) ,(make-mu4e-bookmark :name "Last 7 days" :query "date:7d..now" :key ?w) ,(make-mu4e-bookmark :name "Messages with images" :query "mime:image/*" :key ?p)) "A list of pre-defined queries. Each query is represented by a mu4e-bookmark structure with parameters @t{:name} with the name of the bookmark, @t{:query} with the query expression (a query string or an s-expression that evaluates to query string) and a @t{:key}, which is the shortcut-key for the query. An older form of bookmark, a 3-item list with (QUERY DESCRIPTION KEY) is still recognized as well, for backward-compatibility.") @end lisp You can replace these or add your own items, by putting in your configuration (@file{~/.emacs}) something like: @lisp (add-to-list 'mu4e-bookmarks (make-mu4e-bookmark :name "Big messages" :query "size:5M..500M" :key ?b)) @end lisp This prepends your bookmark to the list, and assigns the key @key{b} to it. If you want to @emph{append} your bookmark, you can use @code{t} as the third argument to @code{add-to-list}. In the various @t{mu4e} views, pressing @key{b} lists all the bookmarks defined in the echo area, with the shortcut key highlighted. So, to invoke the bookmark we just defined (to get the list of "Big Messages"), all you need to type is @kbd{bb}. @subsection Lisp expressions as bookmarks Instead of using strings, it is also possible to use Lisp expressions as bookmarks. The only requirement is that they evaluate to a query string. For example, to get all the messages that are at most a week old in your inbox: @lisp (add-to-list 'mu4e-bookmarks (make-mu4e-bookmark :name "Inbox messages in the last 7 days" :query (concat "maildir:/inbox AND date:" (format-time-string "%Y%m%d" (subtract-time (current-time) (days-to-time 7)))) :key ?w) t) @end lisp @subsection Editing bookmarks before searching There is also @kbd{M-x mu4e-headers-search-bookmark-edit} (key @key{B}), which lets you edit the bookmarked query before invoking it. This can be useful if you have many similar queries, but need to change some parameter. For example, you could have a bookmark @samp{"date:today..now AND "}@footnote{Not a valid search query by itself}, which limits any result to today's messages. @node Maildir searches @section Maildir searches Maildir searches are quite similar to bookmark searches (see @ref{Bookmarks}), with the difference being that the target is always a maildir -- maildir queries provide a 'traditional' folder-like interface to a search-based e-mail client. By default, maildir searches are available in the @ref{Main view}, @ref{Headers view}, and @ref{Message view}, with the key @key{j} (@code{mu4e-jump-to-maildir}). @subsection Setting up maildir shortcuts You can search for maildirs like can for any other message property (e.g. with a query like @t{maildir:/myfolder}), but since it is so common, @t{mu4e} offers a shortcut for this. For this to work, you need to set the variable @code{mu4e-maildir-shortcuts} to the list of maildirs you want to have quick access to, for example: @lisp (setq mu4e-maildir-shortcuts '( ("/inbox" . ?i) ("/archive" . ?a) ("/lists" . ?l) ("/work" . ?w) ("/sent" . ?s))) @end lisp This sets @key{i} as a shortcut for the @t{/inbox} folder -- effectively a query @t{maildir:/inbox}. There is a special shortcut @key{o} or @key{/} for @emph{other} (so don't use those for your own shortcuts!), which allows you to choose from @emph{all} maildirs that you have. There is support for autocompletion; note that the list of maildirs is determined when @t{mu4e} starts; if there are changes in the maildirs while @t{mu4e} is running, you need to restart @t{mu4e}. Each of the folder names is relative to your top-level maildir directory; so if you keep your mail in @file{~/Maildir}, @file{/inbox} would refer to @file{~/Maildir/inbox}. With these shortcuts, you can jump around your maildirs (folders) very quickly - for example, getting to the @t{/lists} folder only requires you to type @kbd{jl}, then change to @t{/work} with @kbd{jw}. While in queries you need to quote folder names (maildirs) with spaces in them, you should @emph{not} quote them when used in @code{mu4e-maildir-shortcuts}, since @t{mu4e} does that automatically for you. The very same shortcuts are used by @kbd{M-x mu4e-mark-for-move} (default shortcut @key{m}); so, for example, if you want to move a message to the @t{/archive} folder, you can do so by typing @kbd{ma}. @node Other search functionality @section Other search functionality @subsection Navigating through search queries You can navigate through previous/next queries using @code{mu4e-headers-query-prev} and @code{mu4e-headers-query-next}, which are bound to @key{M-left} and @key{M-right}, similar to what some web browsers do. @t{mu4e} tries to be smart and not record duplicate queries. Also, the number of queries remembered has a fixed limit, so @t{mu4e} won't use too much memory, even if used for a long time. However, if you want to forget previous/next queries, you can use @kbd{M-x mu4e-headers-forget-queries}. @subsection Narrowing search results It can be useful to narrow existing search results, that is, to add some clauses to the current query to match fewer messages. For example, suppose you're looking at some mailing list, perhaps by jumping to a maildir (@kbd{M-x mu4e-headers-jump-to-maildir}, @key{j}) or because you followed some bookmark (@kbd{M-x mu4e-headers-search-bookmark}, @key{b}). Now, you want to narrow things down to only those messages that have attachments. This is when @kbd{M-x mu4e-headers-search-narrow} (@key{/}) comes in handy. It asks for an additional search pattern, which is appended to the current search query, in effect getting you the subset of the currently shown headers that also match this extra search pattern. @key{\} takes you back to the previous query, so, effectively 'widens' the search. Technically, narrowing the results of query @t{x} with expression @t{y} implies doing a search @t{(x) AND (y)}. Note that messages that were not in your original search results because of @code{mu4e-headers-results-limit} may show up in the narrowed query. @subsection Including related messages @anchor{Including related messages} It can be useful to not only show the messages that directly match a certain query, but also include messages that are related to these messages. That is, messages that belong to the same discussion threads are included in the results, just like e.g. Gmail does it. You can enable this behavior by setting @code{mu4e-headers-include-related} to @code{t}, and you can toggle between including/not-including with @key{W}. Be careful though when e.g. deleting ranges of messages from a certain folder -- the list may now also include messages from @emph{other} folders. @subsection Skipping duplicates @anchor{Skipping duplicates} Another useful feature is skipping of @emph{duplicate messages}. When you have copies of messages, there's usually little value in including more than one in search results. A common reason for having multiple copies of messages is the combination of Gmail and @t{offlineimap}, since that is the way the labels / virtual folders in Gmail are represented. You can enable skipping duplicates by setting @code{mu4e-headers-skip-duplicates} to @code{t}, and you can toggle between the skipping/not skipping with @key{V}. Note, messages are considered duplicates when they have the same @t{Message-Id}. @node Marking @chapter Marking In @t{mu4e}, the common way to do things with messages is a two-step process - first you @emph{mark} them for a certain action, then you @emph{execute} (@key{x}) those marks. This is similar to the way @t{dired} operates. Marking can happen in both the @ref{Headers view} and the @ref{Message view}. @menu * Marking messages::Selecting message do something with them * What to mark for::What can we do with them * Executing the marks::Do it * Leaving the headers buffer::Handling marks automatically when leaving * Built-in marking functions::Helper functions for dealing with them * Custom mark functions::Define your own mark function * Adding a new kind of mark::Adding your own marks @end menu @node Marking messages @section Marking messages There are multiple ways to mark messages: @itemize @item @emph{message at point}: you can put a mark on the message-at-point in either the @ref{Headers view} or @ref{Message view} @item @emph{region}: you can put a mark on all messages in the current region (selection) in the @ref{Headers view} @item @emph{pattern}: you can put a mark on all messages in the @ref{Headers view} matching a certain pattern with @kbd{M-x mu4e-headers-mark-pattern} (@key{%}) @item @emph{thread/subthread}: You can put a mark on all the messages in the thread/subthread at point with @kbd{M-x mu4e-headers-mark-thread} and @kbd{M-x mu4e-headers-mark-subthread}, respectively @end itemize @node What to mark for @section What to mark for @t{mu4e} supports a number of marks: @cartouche @verbatim mark for/as | keybinding | description -------------+-------------+------------------------------ 'something' | *, <insert> | mark now, decide later delete | D, <delete> | delete flag | + | mark as 'flagged' ('starred') move | m | move to some maildir read | ! | mark as read refile | r | mark for refiling trash | d | move to the trash folder untrash | = | remove 'trash' flag unflag | - | remove 'flagged' mark unmark | u | remove mark at point unmark all | U | remove all marks unread | ? | marks as unread action | a | apply some action @end verbatim @end cartouche After marking a message, the left-most columns in the headers view indicate the kind of mark. This is informative, but if you mark many (say, thousands) messages, this slows things down significantly@footnote{this uses an @command{emacs} feature called @emph{overlays}, which are slow when used a lot in a buffer}. For this reason, you can disable this by setting @code{mu4e-headers-show-target} to @code{nil}. @t{something} is a special kind of mark; you can use it to mark messages for 'something', and then decide later what the 'something' should be@footnote{This kind of 'deferred marking' is similar to the facility in @t{dired}, @t{midnight commander} (@url{http://www.midnight-commander.org/}) and the like, and uses the same key binding (@key{insert}).} Later, you can set the actual mark using @kbd{M-x mu4e-mark-resolve-deferred-marks} (@key{#}). Alternatively, @t{mu4e} will ask you when you try to execute the marks (@key{x}). @node Executing the marks @section Executing the marks After you have marked some messages, you can execute them with @key{x} (@kbd{M-x mu4e-mark-execute-all}). A hook, @code{mu4e-mark-execute-pre-hook}, is available which is run right before execution of each mark. The hook is called with two arguments, the mark and the message itself. @node Leaving the headers buffer @section Leaving the headers buffer When you quit or update a headers buffer that has marked messages (for example, by doing a new search), @t{mu4e} asks you what to do with them, depending on the value of the variable @code{mu4e-headers-leave-behavior} -- see its documentation. @node Built-in marking functions @section Built-in marking functions Some examples of @t{mu4e}'s built-in marking functions. @itemize @item @emph{Mark the message at point for trashing}: press @key{d} @item @emph{Mark all messages in the buffer as unread}: press @kbd{C-x h o} @item @emph{Delete the messages in the current thread}: press @kbd{T D} @item @emph{Mark messages with a subject matching ``hello'' for flagging}: press @kbd{% s hello RET}. @end itemize @node Custom mark functions @section Custom mark functions Sometimes, the built-in functions to mark messages may not be sufficient for your needs. For this, @t{mu4e} offers an easy way to define your own custom mark functions. You can choose one of the custom marker functions by pressing @key{&} in the @ref{Headers view} and @ref{Message view}. Custom mark functions are to be appended to the list @code{mu4e-headers-custom-markers}. Each of the elements of this list ('markers') is a list with two or three elements: @enumerate @item The name of the marker - a short string describing this marker. The first character of this string determines its shortcut, so these should be unique. If necessary, simply prefix the name with a unique character. @item a predicate function, taking two arguments @var{msg} and @var{param}. @var{msg} is the message plist (see @ref{Message functions}) and @var{param} is a parameter provided by the third of the marker elements (see the next item). The predicate function should return non-@t{nil} if the message matches. @item (optionally) a function that is evaluated once, and the result is passed as a parameter to the predicate function. This is useful when user-input is needed. @end enumerate Let's look at an example: suppose we want to match all messages that have more than @emph{n} recipients -- we could do this with the following recipe: @lisp (add-to-list 'mu4e-headers-custom-markers '("More than n recipients" (lambda (msg n) (> (+ (length (mu4e-message-field msg :to)) (length (mu4e-message-field msg :cc))) n)) (lambda () (read-number "Match messages with more recipients than: "))) t) @end lisp After evaluating this expression, you can use it by pressing @key{&} in the headers buffer to select a custom marker function, and then @key{M} to choose this particular one (@t{M} because it is the first character of the description). As you can see, it's not very hard to define simple functions to match messages. There are more examples in the defaults for @code{mu4e-headers-custom-markers}; see @file{mu4e-headers.el} and see @ref{Extending mu4e} for general information about writing your own functions. @node Adding a new kind of mark @section Adding a new kind of mark It is possible to configure new marks. To do so one can add entries in the list @code{mu4e-marks}. Such an element must have the following form: @lisp (SYMBOL :char STRING :prompt STRING :ask-target (lambda () TARGET) :dyn-target (lambda (TARGET MSG) DYN-TARGET) :show-target (lambda (DYN-TARGET) STRING) :action (lambda (DOCID MSG DYN-TARGET) nil)) @end lisp The symbol can be any symbol, except for 'unmark and 'something, which are reserved. The rest is a plist with the following elements: @itemize @item @code{:char} -- the character to display in the headers view. @item @code{:prompt} -- the prompt to use when asking for marks (used for example when marking a whole thread). @item @code{:ask-target} -- a function run once per bulk-operation, and thus suitable for querying the user about a target for move-like marks. If nil, the TARGET passed to @code{:dyn-target} is nil. @item @code{:dyn-target} -- a function run once per message (The message is passed as MSG to the function). This function allows to compute a per-message target, for refile-like marks. If nil, the DYN-TARGET passed to the @code{:action} is the TARGET obtained as above. @item @code{:show-target} -- how to display the target in the headers view. If @code{:show-target} is nil the DYN-TARGET is shown (and DYN-TARGET must be a string). @item @code{:action} -- the action to apply on the message when the mark is executed. @end itemize As an example, suppose we would like to add a mark for tagging messages (gmail-style), then we can run the following code (after loading mu4e): @lisp (add-to-list 'mu4e-marks '(tag :char "g" :prompt "gtag" :ask-target (lambda () (read-string "What tag do you want to add?")) :action (lambda (docid msg target) (mu4e-action-retag-message msg (concat "+" target))))) @end lisp As another example, suppose we would like to ``archive and mark read'' a message (gmail-style), then we can run the following code (after loading mu4e): @lisp (add-to-list 'mu4e-marks '(archive :char "A" :prompt "Archive" :show-target (lambda (target) "archive") :action (lambda (docid msg target) ;; must come before proc-move since retag runs ;; 'sed' on the file (mu4e-action-retag-message msg "-\\Inbox") (mu4e~proc-move docid nil "+S-u-N")))) @end lisp Adding to @code{mu4e-marks} list allows to use the mark in bulk operations (for example when tagging a whole thread), but does not bind the mark to a key to use at the top-level. This must be done separately. In our example: @lisp (mu4e~headers-defun-mark-for tag) (mu4e~headers-defun-mark-for archive) (define-key mu4e-headers-mode-map (kbd "g") 'mu4e-headers-mark-for-tag) (define-key mu4e-headers-mode-map (kbd "A") 'mu4e-headers-mark-for-archive) @end lisp @node Contexts @chapter Contexts @menu * What are contexts::Defining the concept * Context policies::How to determine the current context * Contexts and special folders::Using context variables to determine them * Contexts example::How to define contexts * Some context tricks::Other thing to do with contexts @end menu It can be useful to switch between different sets of settings in @t{mu4e}; a typical example is the case where you have different e-mail accounts for private and work email, each with their own values for folders, e-mail addresses, mailservers and so on. The @code{mu4e-context} system is a @t{mu4e}-specific mechanism to allow for that; users can be define different @i{contexts} corresponding with groups of setting and either manually switch between them, or let @t{mu4e} determine the right context when composing a message based on some user-provided function. Note that there are a number of existing ways to switch accounts in @t{mu4e}, for example using the method described in the @ref{Tips and Tricks} section of this manual. Those still work - but the new mechanism has the benefit of being a core part of @code{mu4e}, thus allowing for deeper integration. @node What are contexts @section What are contexts Let's see what's contained in a context. Most of it is optional. A @code{mu4e-context} is Lisp object with the following members: @itemize @item @t{name}: the name of the context, e.g. @t{work} or @t{private} @item @t{vars}: an association-list (alist) of variable settings for this account. @item @t{enter-func}: an (optional) function that takes no parameter and is invoked when entering the context. You can use this for extra setup etc. @item @t{leave-func}: an (optional) function that takes no parameter and is invoked when leaving the context. You can use this for clearing things up. @item @t{match-func}: an (optional) function that takes an @t{MSG} message plist as argument, and returns non-@t{nil} if this context matches the situation. @t{mu4e} uses the first context that matches, in a couple of situations: @itemize @item when starting @t{mu4e} to determine the starting context; in this case, @t{MSG} is nil. You can use e.g. the host you're running or the time of day to determine which context matches. @item before replying to or forwarding a message with the given message plist as parameter, or @t{nil} when composing a brand new message. The function should return @t{t} when this context is the right one for this message, or @t{nil} otherwise. @item when determining the target folders for deleting, refiling etc; see @ref{Contexts and special folders}. @end itemize @end itemize @t{mu4e} uses a variable @code{mu4e-contexts}, which is a list of such objects. @node Context policies @section Context policies When you have defined contexts and you start @t{mu4e} it decides which context to use based on the variable @code{mu4e-context-policy}; similarly, when you compose a new message, the context is determined using @code{mu4e-compose-context-policy}. For both of these, you can choose one of the following policies: @itemize @item a symbol @code{always-ask}: unconditionally ask the user what context to pick. @end itemize The other choices @b{only apply if none of the contexts match} (i.e., none of the contexts' match-functions returns @code{t}). We have the following options: @itemize @item a symbol @code{ask}: ask the user if @t{mu4e} can't figure things out the context by itself (through the match-function). This is a good policy if there are no match functions, or if the match functions don't cover all cases. @item a symbol @code{ask-if-none}: if there's already a context, don't change it; otherwise, ask the user. @item a symbol @code{pick-first}: pick the first (default) context. This is a good choice if you want to specify context for special case, and fall back to the first one if none match. @item @code{nil}: don't change the context; this is useful if you don't change contexts very often, and e.g. manually changes contexts with @kbd{M-x mu4e-context-switch}. @end itemize @node Contexts and special folders @section Contexts and special folders As we discussed in @ref{Folders} and @ref{Dynamic folders}, @t{mu4e} recognizes a number of special folders: @code{mu4e-sent-folder}, @code{mu4e-drafts-folder}, @code{mu4e-trash-folder} and @code{mu4e-refile-folder}. When you have a headers-buffer with messages that belong to different contexts (say, a few different accounts), it is desirable for each of them to use the specific folders for their own context - so, for instance, if you trash a message, it needs to go to the trash-folder for the account it belongs to, which is not necessarily the current context. To make this easy to do, whenever @t{mu4e} needs to know the value for such a special folder for a given message, it tries to determine the appropriate context using @code{mu4e-context-determine} (and policy @t{nil}; see @ref{Context policies}). If it finds a matching context, it let-binds the @code{vars} for that account, and then determines the value for the folder. It does not, however, call the @code{enter-func} or @code{leave-func}, since we are not really switching context. In practice, this means that as long as each of the accounts has a good @t{match-func}, all message operations automatically find the appropriate folders. @node Contexts example @section Example Let's explain how contexts work by looking at an example. We define two contexts, 'Private' and 'Work' for a fictional user @emph{Alice Derleth}. Note that in this case, we automatically switch to the first context when starting; see the discussion in the previous section. @lisp (setq mu4e-contexts `( ,(make-mu4e-context :name "Private" :enter-func (lambda () (mu4e-message "Entering Private context")) :leave-func (lambda () (mu4e-message "Leaving Private context")) ;; we match based on the contact-fields of the message :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg :to "aliced@@home.example.com"))) :vars '( ( user-mail-address . "aliced@@home.example.com" ) ( user-full-name . "Alice Derleth" ) ( mu4e-compose-signature . (concat "Alice Derleth\n" "Lauttasaari, Finland\n")))) ,(make-mu4e-context :name "Work" :enter-func (lambda () (mu4e-message "Switch to the Work context")) ;; no leave-func ;; we match based on the contact-fields of the message :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg :to "aderleth@@miskatonic.example.com"))) :vars '( ( user-mail-address . "aderleth@@miskatonic.example.com" ) ( user-full-name . "Alice Derleth" ) ( mu4e-compose-signature . (concat "Prof. Alice Derleth\n" "Miskatonic University, Dept. of Occult Sciences\n")))) ,(make-mu4e-context :name "Cycling" :enter-func (lambda () (mu4e-message "Switch to the Cycling context")) ;; no leave-func ;; we match based on the maildir of the message; assume all ;; cycling-related messages go into the /cycling maildir :match-func (lambda (msg) (when msg (string= (mu4e-message-field msg :maildir) "/cycling"))) :vars '( ( user-mail-address . "aderleth@@example.com" ) ( user-full-name . "AliceD" ) ( mu4e-compose-signature . nil))))) ;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should ;; guess or ask the correct context, e.g. ;; start with the first (default) context; ;; default is to ask-if-none (ask when there's no context yet, and none match) ;; (setq mu4e-context-policy 'pick-first) ;; compose with the current context is no context matches; ;; default is to ask ;; (setq mu4e-compose-context-policy nil) @end lisp A couple of notes about this example: @itemize @item You can manually switch the focus use @code{M-x mu4e-context-switch}, by default bound to @kbd{;} in headers, view and main mode. The current focus appears in the mode-line. @item Normally, @code{M-x mu4e-context-switch} does not call the enter or leave functions if the 'new' context is the same as the old one. However, with a prefix-argument (@kbd{C-u}), you can force @t{mu4e} to invoke those function even in that case. @item The function @code{mu4e-context-current} returns the current-context; the current context is also visible in the mode-line when in headers, view or main mode. @item You can set any kind of variable; including settings for mail servers etc. However, settings such as @code{mu4e-maildir} and @code{mu4e-mu-home} are not changeable after they have been set without quitting @t{mu4e} first. @item @code{leave-func} (if defined) for the context we are leaving, is invoked before the @code{enter-func} (if defined) of the context we are entering. @item @code{enter-func} (if defined) is invoked before setting the variables. @item @code{match-func} (if defined) is invoked just before @code{mu4e-compose-pre-hook}. @item See the variables @code{mu4e-context-policy} and @code{mu4e-compose-context-policy} to tweak what @t{mu4e} should do when no context matches (or if you always want to be asked). @item Finally, be careful to get the quotations right -- backticks, single quotes and commas and note the '.' between variable name and its value. @end itemize @node Some context tricks @section Some context tricks It is possible to automatically fill @code{mu4e-user-address-list} by concatenating the @code{user-mail-address} fields of all contexts: @lisp ;; This sets `mu4e-user-mail-address-list' to the concatenation of all ;; `user-mail-address' values for all contexts. If you have other mail ;; addresses as well, you'll need to add those manually. (setq mu4e-user-mail-address-list (delq nil (mapcar (lambda (context) (when (mu4e-context-vars context) (cdr (assq 'user-mail-address (mu4e-context-vars context))))) mu4e-contexts))) @end lisp @node Dynamic folders @chapter Dynamic folders In @ref{Folders}, we explained how you can set up @t{mu4e}'s special folders: @lisp (setq mu4e-sent-folder "/sent" ;; sent messages mu4e-drafts-folder "/drafts" ;; unfinished messages mu4e-trash-folder "/trash" ;; trashed messages mu4e-refile-folder "/archive") ;; saved messages01 @end lisp In some cases, having such static folders may not suffice - perhaps you want to change the folders depending on the context. For example, the folder for refiling could vary, based on the sender of the message. To make this possible, instead of setting the standard folders to a string, you can set them to be a @emph{function} that takes a message as its parameter, and returns the desired folder name. This chapter shows you how to do that. For a more general discussion of how to extend @t{mu4e} and writing your own functions, see @ref{Extending mu4e}. If you use @t{mu4e-context}, see @ref{Contexts and special folders} for what that means for these special folders. @menu * Smart refiling:: Automatically choose the target folder * Other dynamic folders:: Flexible folders for sent, trash, drafts @end menu @node Smart refiling @section Smart refiling When refiling messages, perhaps to archive them, it can be useful to have different target folders for different messages, based on some property of those message -- smart refiling. To accomplish this, we can set the refiling folder (@code{mu4e-refile-folder}) to a function that returns the actual refiling folder for the particular message. An example should clarify this: @lisp (setq mu4e-refile-folder (lambda (msg) (cond ;; messages to the mu mailing list go to the /mu folder ((mu4e-message-contact-field-matches msg :to "mu-discuss@@googlegroups.com") "/mu") ;; messages sent directly to me go to /archive ;; also `mu4e-user-mail-address-p' can be used ((mu4e-message-contact-field-matches msg :to "me@@example.com") "/private") ;; messages with football or soccer in the subject go to /football ((string-match "football\\|soccer" (mu4e-message-field msg :subject)) "/football") ;; messages sent by me go to the sent folder ((find-if (lambda (addr) (mu4e-message-contact-field-matches msg :from addr)) mu4e-user-mail-address-list) mu4e-sent-folder) ;; everything else goes to /archive ;; important to have a catch-all at the end! (t "/archive")))) @end lisp @noindent This can be very powerful; you can select some messages in the headers view, then press @key{r}, and have them all marked for refiling to their particular folders. Some notes: @itemize @item We set @code{mu4e-refile-folder} to an anonymous (@t{lambda}) function. This function takes one argument, a message plist@footnote{a property list describing a message}. The plist corresponds to the message at point. See @ref{Message functions} for a discussion on how to deal with them. @item In our function, we use a @t{cond} control structure; the function returns the first of the clauses that matches. It's important to make the last clause a catch-all, so we always return @emph{some} folder. @item We use the convenience function @code{mu4e-message-contact-field-matches}, which evaluates to @code{t} if any of the names or e-mail addresses in a contact field (in this case, the @t{To:}-field) matches the regular expression. With @t{mu4e} version 0.9.16 or newer, the contact field can in fact be a list instead of a single value, such as @code{'(:to :cc)'} @end itemize @node Other dynamic folders @section Other dynamic folders Using the same mechanism, you can create dynamic sent-, trash-, and drafts-folders. The message-parameter you receive for the sent and drafts folder is the @emph{original} message, that is, the message you reply to, or forward, or edit. If there is no such message (for example when composing a brand new message) the message parameter is @t{nil}. Let's look at an example. Suppose you want a different trash folder for work-email. You can achieve this with something like: @lisp (setq mu4e-trash-folder (lambda (msg) ;; the 'and msg' is to handle the case where msg is nil (if (and msg (mu4e-message-contact-field-matches msg :to "me@@work.example.com")) "/trash-work" "/trash"))) @end lisp @noindent Good to remember: @itemize @item The @var{msg} parameter you receive in the function refers to the @emph{original message}, that is, the message being replied to or forwarded. When re-editing a message, it refers to the message being edited. When you compose a totally new message, the @var{msg} parameter is @code{nil}. @item When re-editing messages, the value of @code{mu4e-drafts-folder} is ignored. @end itemize @node Actions @chapter Actions @t{mu4e} lets you define custom actions for messages in the @ref{Headers view} and for both messages and attachments in the @ref{Message view}. Custom actions allow you to easily extend @t{mu4e} for specific needs -- for example, marking messages as spam in a spam filter or applying an attachment with a source code patch. You can invoke the actions with key @key{a} for actions on messages, and key @key{A} for actions on attachments. For general information extending @t{mu4e} and writing your own functions, see @ref{Extending mu4e}. @menu * Defining actions::How to create an action * Headers view actions::Doing things with message headers * Message view actions::Doing things with messages * Attachment actions::Doing things with attachments * Example actions::Some more examples @end menu @node Defining actions @section Defining actions Defining a new custom action comes down to writing an elisp-function to do the work. Functions that operate on messages receive a @var{msg} parameter, which corresponds to the message at point. Something like: @lisp (defun my-action-func (msg) "Describe my message function." ;; do stuff ) @end lisp @noindent Messages that operate on attachments receive a @var{msg} parameter, which corresponds to the message at point, and an @var{attachment-num}, which is the number of the attachment as seen in the message view. An attachment function looks like: @lisp (defun my-attachment-action-func (msg attachment-num) "Describe my attachment function." ;; do stuff ) @end lisp @noindent After you have defined your function, you can add it to the list of actions@footnote{Instead of defining the functions separately, you can obviously also add a @code{lambda}-function directly to the list; however, separate functions are easier to change}, either @code{mu4e-headers-actions}, @code{mu4e-view-actions} or @code{mu4e-view-attachment-actions}. The format@footnote{Note, the format of the actions has changed since version 0.9.8.4, and you must change your configuration to use the new format; @t{mu4e} warns you when you are using the old format.} of each action is a cons-cell, @code{(DESCRIPTION . VALUE)}; see below for some examples. If your shortcut is not also the first character of the description, simply prefix the description with that character. Let's look at some examples. @node Headers view actions @section Headers view actions Suppose we want to inspect the number of recipients for a message in the @ref{Headers view}. We add the following to our configuration: @lisp (defun show-number-of-recipients (msg) "Display the number of recipients for the message at point." (message "Number of recipients: %d" (+ (length (mu4e-message-field msg :to)) (length (mu4e-message-field msg :cc))))) ;; define 'N' (the first letter of the description) as the shortcut ;; the 't' argument to add-to-list puts it at the end of the list (add-to-list 'mu4e-headers-actions '("Number of recipients" . show-number-of-recipients) t) @end lisp After evaluating this, @kbd{a N} in the headers view shows the number of recipients for the message at point. @node Message view actions @section Message view actions As another example, suppose we would like to search for messages by the sender of the message at point: @lisp (defun search-for-sender (msg) "Search for messages sent by the sender of the message at point." (mu4e-headers-search (concat "from:" (cdar (mu4e-message-field msg :from))))) ;; define 'x' as the shortcut (add-to-list 'mu4e-view-actions '("xsearch for sender" . search-for-sender) t) @end lisp @indent If you wonder why we use @code{cdar}, remember that the @t{From:}-field is a list of @code{(NAME . EMAIL)} cells; thus, @code{cdar} gets us the e-mail address of the first in the list. @t{From:}-fields rarely contain multiple cells. @node Attachment actions @section Attachment actions Finally, let's define an attachment action. As mentioned, attachment-action functions receive @emph{2} arguments, the message and the attachment number to use. The following example action counts the number of lines in an attachment, and defines @key{n} as its shortcut key (the @key{n} is prefixed to the description). @lisp (defun count-lines-in-attachment (msg attachnum) "Count the number of lines in an attachment." (mu4e-view-pipe-attachment msg attachnum "wc -l")) ;; defining 'n' as the shortcut (add-to-list 'mu4e-view-attachment-actions '("ncount lines" . count-lines-in-attachment) t) @end lisp @node Example actions @section Example actions @t{mu4e} includes a number of example actions in the file @file{mu4e-actions.el} in the source distribution (see @kbd{C-h f mu4e-action-TAB}). For example, for viewing messages in an external web browser, or listening to a message's body-text using text-to-speech. @node Extending mu4e @chapter Extending mu4e @t{mu4e} is designed to be easily extendible - that is, write your own emacs-lisp to make @t{mu4e} behave exactly as you want. Here, we provide some guidelines for doing so. @menu * Extension points::Where to hook into mu4e * Available functions::General helper functions * Message functions::Working with messages * Contact functions::Working with contacts * Utility functions::Miscellaneous helpers @end menu @node Extension points @section Extension points There are a number of places where @t{mu4e} lets you plug in your own functions: @itemize @item Custom functions for message headers in the message-view and headers-view - see @ref{HV Custom headers}, @ref{MSGV Custom headers} @item Using message-specific folders for drafts, trash, sent messages and refiling, based on a function - see @ref{Dynamic folders} @item Using an attachment-specific download-directory - see the variable @code{mu4e-attachment-dir}. @item Apply a function to a message in the headers view - see @ref{Headers view actions} @item Apply a function to a message in the message view - see @ref{Message view actions} @item Add a new kind of mark for use in the headers view - see @ref{Adding a new kind of mark} @item Apply a function to an attachment - see @ref{Attachment actions} @item Custom function to mark certain messages - see @ref{Custom mark functions} @item Using various @emph{mode}-hooks, @code{mu4e-compose-pre-hook} (see @ref{Compose hooks}), @code{mu4e-index-updated-hook} (see @ref{FAQ}) @end itemize @noindent You can also write your own functions without using the above. If you want to do so, key useful functions are @code{mu4e-message-at-point} (see below), @code{mu4e-headers-for-each} (to iterate over all headers, see its docstring) and @code{mu4e-view-for-each-part} (to iterate over all parts/attachments, see its docstring). There is also @code{mu4e-view-for-each-uri} to iterate of all the URIs in the current message. Another useful function is @code{mu4e-headers-find-if} which searches for a message matching a certain pattern; again, see its docstring. @node Available functions @section Available functions The whole of @t{mu4e} consists of hundreds of elisp functions. However, the majority of those are for @emph{internal} use only; you can recognize them easily, because they all start with @code{mu4e~}. These function make all kinds of assumptions, and they are subject to change, and should therefore @emph{not} be used. The same is true for @emph{variables} that start with @code{mu4e~}; don't touch them. Let me repeat that: @verbatim Do not use mu4e~... functions or variables! @end verbatim @noindent In addition, you should use functions in the right context; functions that start with @t{mu4e-view-} are only applicable to the message view, while functions starting with @t{mu4e-headers-} are only applicable to the headers view. Functions without such prefixes are applicable everywhere. @node Message functions @section Message functions Many functions in @t{mu4e} deal with message plists (property lists). They contain information about messages, such as sender and recipient, subject, date and so on. To deal with these plists, there are a number of @code{mu4e-message-} functions (in @file{mu4e-message.el}), such as @code{mu4e-message-field} and @code{mu4e-message-at-point}, and a shortcut to combine the two, @code{mu4e-message-field-at-point}. For example, to get the subject of the message at point, in either the headers view or the message view, you could write: @lisp (mu4e-message-field (mu4e-message-at-point) :subject) @end lisp @noindent Note that: @itemize @item The contact fields (To, From, Cc, Bcc) are lists of cons-pairs @code{(name . email)}; @code{name} may be @code{nil}. So, for example: @lisp (mu4e-message-field some-msg :to) ;; => (("Jack" . "jack@@example.com") (nil . "foo@@example.com")) @end lisp If you are only looking for a match in this list (e.g., ``Is Jack one of the recipients of the message?''), there is a convenience function @code{mu4e-message-contact-field-matches} to make this easy. @item The message body is only available in the message view, not in the headers view. @end itemize Note that in headers-mode, you only have access to a reduced message plist, without the information about the message-body or mime-parts; @t{mu4e} does this for performance reasons. And even in view-mode, you do not have access to arbitrary message-headers. However, it is possible to get the information indirectly, using the raw-message and some third-party tool like @t{procmail}'s @t{formail}: @lisp (defun my-mu4e-any-message-field-at-point (hdr) "Quick & dirty way to get an arbitrary header HDR at point. Requires the 'formail' tool from procmail." (replace-regexp-in-string "\n$" "" (shell-command-to-string (concat "formail -x " hdr " -c < " (shell-quote-argument (mu4e-message-field-at-point :path)))))) @end lisp @node Contact functions @section Contact functions It can sometimes be useful to rewrite the contact information that @t{mu4e} provides, for example to convert them to some standardized format, or to fix spelling errors. And sometimes, you may want to remove certain contacts altogether. For this, @t{mu4e} provides @code{mu4e-contact-rewrite-function}, which passes each contact to a user-provided function, which is expected to return either the possibly rewritten contact or @code{nil} to remove the contact from the list - note that the latter can also be achieved using @code{mu4e-compose-complete-ignore-address-regexp}. Each of the contacts are property-lists ('plists'), with properties @code{:name} (which may be @code{nil}), and @code{:mail}, and a number of other properties which you should return unchanged. Let's look at an example: @lisp (defun my-rewrite-function (contact) (let ((name (or (plist-get contact :name) "")) (mail (plist-get contact :mail))) (cond ;; jonh smiht --> John Smith ((string= "jonh smiht" name) (plist-put contact :name "John C. Smith") contact) ;; remove evilspammer from the contacts list ((string= "evilspammer@@example.com" mail) nil) ;; others stay as the are (t contact)))) (setq mu4e-contact-rewrite-function 'my-rewrite-function) @end lisp This function is called for each of your contacts. @node Utility functions @section Utility functions @file{mu4e-utils} contains a number of utility functions; we list a few here; see their docstrings for the details: @itemize @item @code{mu4e-read-option}: read one option from a list. For example: @lisp (mu4e-read-option "Choose an animal: " '(("Monkey" . monkey) ("Gnu" . gnu) ("xMoose" . moose))) @end lisp The user is presented with: @example Choose an animal: [M]onkey, [G]nu, [x]Moose @end example @item @code{mu4e-ask-maildir}: ask for a maildir; try one of the shortcuts (@code{mu4e-maildir-shortcuts}), or the full set of available maildirs. @item @code{mu4e-running-p}: return @code{t} if the @t{mu4e} process is running, @code{nil} otherwise. @item @code{(mu4e-user-mail-address-p addr)}: return @code{t} if @var{addr} is one of the user's e-mail addresses (as per @code{mu4e-user-mail-address-list}). @item @code{mu4e-log} logs to the @t{mu4e} debugging log if it is enabled; see @code{mu4e-toggle-logging}. @item @code{mu4e-message}, @code{mu4e-warning}, @code{mu4e-error} are the @t{mu4e} equivalents of the normal elisp @code{message}, @code{user-error}@footnote{@code{user-error} only appears in @command{emacs} 24.2 and later; in older versions it falls back to @code{error}} and @code{error} functions. @end itemize @node Interaction with other tools @appendix Interaction with other tools In this chapter, we discuss some ways in ways in which @t{mu4e} can cooperate with other tools. @menu * Emacs default::Making mu4e the default emacs e-mail program * Org-mode links::Adding mu4e to your organized life * Org-contacts::Hooking up with org-contacts * BBDB::Hooking up with the Insidious Big Brother Database * Sauron::Getting new mail notifications with Sauron * Speedbar::A special frame with your folders * Mu-cite:: Fancy citation engine * Dired:: Attaching files using @t{dired} @end menu @node Emacs default @section Emacs default @command{emacs} allows you to select an e-mail program as the default program it uses when you press @key{C-x m} (@code{compose-mail}), call @code{report-emacs-bug} and so on. If you want to use @t{mu4e} for this, you can do so by adding the following to your configuration: @lisp (setq mail-user-agent 'mu4e-user-agent) @end lisp @node Org-mode links @section Org-mode links It can be useful to include links to e-mail messages or even search queries in your org-mode files. @t{mu4e} supports this with the @t{org-mu4e} module; you can set it up by adding it to your configuration, which expects org-mode 8.x. or higher@footnote{If you have an older version, you can try using @t{org-old-mu4e} instead} @lisp (require 'org-mu4e) @end lisp @noindent After this, you can use the normal @t{org-mode} mechanisms to store links: @kbd{M-x org-store-link} stores a link to a particular message when you are in @ref{Message view}. When you are in @ref{Headers view}, @kbd{M-x org-store-link} links to the @emph{query} if @code{org-mu4e-link-query-in-headers-mode} is non-@code{nil}, and to the particular message otherwise (which is the default). You can insert this link later with @kbd{M-x org-insert-link}. From @t{org-mode}, you can go to the query or message the link points to with either @kbd{M-x org-agenda-open-link} in agenda buffers, or @kbd{M-x org-open-at-point} elsewhere - both typically bound to @kbd{C-c C-o}. You can also directly @emph{capture} such links - for example, to add e-mail messages to your todo-list. For that, @t{org-mu4e} has a function @code{org-mu4e-store-and-capture}. This captures the message-at-point (or header - see the discussion on @code{org-mu4e-link-query-in-headers-mode} above), then calls org-mode's capture functionality. You can add some specific capture-template for this, for example, to add a message to your todo-list, and set a deadline for processing it within two days, you could add this to @code{org-capture-templates}: @lisp ("P" "process-soon" entry (file+headline "todo.org" "Todo") "* TODO %a %?\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))") @end lisp If you use the functionality a lot, you may want to define key-bindings for that in headers and view mode: @lisp (define-key mu4e-headers-mode-map (kbd "C-c c") 'org-mu4e-store-and-capture) (define-key mu4e-view-mode-map (kbd "C-c c") 'org-mu4e-store-and-capture) @end lisp @node Org-contacts @section Org-contacts Note, @t{mu4e} supports built-in address autocompletion; @ref{Address autocompletion}, and that is the recommended way to do this. However, it is also possible to manage your addresses with @t{org-mode}, using @t{org-contacts}@footnote{@url{http://julien.danjou.info/software/org-contacts.el}}. @t{mu4e-actions} defines a useful action (@ref{Actions}) for adding a contact based on the @t{From:}-address in the message at point. To enable this, add to your configuration something like: @lisp (setq mu4e-org-contacts-file <full-path-to-your-org-contacts-file>) (add-to-list 'mu4e-headers-actions '("org-contact-add" . mu4e-action-add-org-contact) t) (add-to-list 'mu4e-view-actions '("org-contact-add" . mu4e-action-add-org-contact) t) @end lisp @noindent After this, you should be able to add contacts using @key{a o} in the headers view and the message view, using the @t{org-capture} mechanism. Note, the shortcut character @key{o} is due to the first character of @t{org-contact-add}. @node BBDB @section BBDB Note, @t{mu4e} supports built-in address autocompletion; @ref{Address autocompletion}, and that is the recommended way to do this. However, it is also possible to manage your addresses with the current (2015-06-23) development release of @t{BBDB}, or releases of @t{BBDB} after 3.1.2.@footnote{@url{http://savannah.nongnu.org/projects/bbdb/}}. To enable BBDB, add to your @file{~/.emacs} (or its moral equivalent, such as @file{~/.emacs.d/init.el}) the following @emph{after} the @code{(require 'mu4e)} line: @lisp ;; Load BBDB (Method 1) (require 'bbdb-loaddefs) ;; OR (Method 2) ;; (require 'bbdb-loaddefs "/path/to/bbdb/lisp/bbdb-loaddefs.el") ;; OR (Method 3) ;; (autoload 'bbdb-insinuate-mu4e "bbdb-mu4e") ;; (bbdb-initialize 'message 'mu4e) (setq bbdb-mail-user-agent 'mu4e-user-agent) (setq mu4e-view-mode-hook 'bbdb-mua-auto-update visual-line-mode) (setq mu4e-compose-complete-addresses nil) (setq bbdb-mua-pop-up t) (setq bbdb-mua-pop-up-window-size 5) (setq mu4e-view-show-addresses t) @end lisp @noindent After this, you should be able to: @itemize @item In mu4e-view mode, add the sender of the email to BBDB with @key{C-u :} @item Tab-complete addresses from BBDB when composing emails @item View the BBDB contact while viewing a message @end itemize @node Sauron @section Sauron The @command{emacs}-package @t{sauron}@footnote{Sauron can be found at @url{https://github.com/djcb/sauron}, or in the Marmalade package-repository at @url{http://http://marmalade-repo.org/}} (by the same author) can be used to get notifications about new mails. If you run something like the below script from your @t{crontab} (or have some other way of having it execute every @emph{n} minutes), you receive notifications in the @t{sauron}-buffer when new messages arrive. @verbatim #!/bin/sh # the mu binary MU=mu # put the path to your Inbox folder here CHECKDIR="/home/$LOGNAME/Maildir/Inbox" sauron_msg () { DBUS_COOKIE="/home/$LOGNAME/.sauron-dbus" if test "x$DBUS_SESSION_BUS_ADDRESS" = "x"; then if test -e $DBUS_COOKIE; then export DBUS_SESSION_BUS_ADDRESS="`cat $DBUS_COOKIE`" fi fi if test -n "x$DBUS_SESSION_BUS_ADDRESS"; then dbus-send --session \ --dest="org.gnu.Emacs" \ --type=method_call \ "/org/gnu/Emacs/Sauron" \ "org.gnu.Emacs.Sauron.AddMsgEvent" \ string:shell uint32:3 string:"$1" fi } # # -mmin -5: consider only messages that were created / changed in the # the last 5 minutes # for f in `find $CHECKDIR -mmin -5 -a -type f`; do subject=`$MU view $f | grep '^Subject:' | sed 's/^Subject://'` sauron_msg "mail: $subject" done @end verbatim @noindent You might want to put: @lisp (setq sauron-dbus-cookie t) @end lisp @noindent in your setup, to allow the script to find the D-Bus session bus, even when running outside its session. @node Speedbar @section Speedbar @code{speedbar} is an @command{emacs}-extension that shows navigational information for an @command{emacs} buffer in a separate frame. Using @code{mu4e-speedbar}, @t{mu4e} lists your bookmarks and maildir folders and allows for one-click access to them. @t{mu4e} loads @t{mu4e-speedbar} automatically; all you need to do to activate it is @kbd{M-x speedbar}. Then, when then switching to the @ref{Main view}, the speedbar-frame is updated with your bookmarks and maildirs. For speed reasons, the list of maildirs is determined when @t{mu4e} starts; if the list of maildirs changes while @t{mu4e} is running, you need to restart @t{mu4e} to have those changes reflected in the speedbar and in other places that use this list, such as auto-completion when jumping to a maildir. @code{mu4e-speedbar} was contributed by @emph{Antono Vasiljev}. @node Mu-cite @section Mu-cite @t{mu-cite}@footnote{Note, despite its name, @t{mu-cite} is a project unconnected to @t{mu}/@t{mu4e}} is a package to control the way message citations look like (i.e., the message you responded to when you reply to them or forward them), with its latest version available at @url{http://www.jpl.org/elips/mu/}. After installing @t{mu-cite}, you can use something like the following to make it work with @t{mu4e}: @lisp (require 'mu-cite) (setq mu4e-compose-cite-function 'mu-cite-original) (setq mu-cite-top-format '("On " date ", " from " wrote:\n\n")) (setq mu-cite-prefix-format '(" > ")) @end lisp @node Dired @section Dired It is possible to attach files to @t{mu4e} messages using @t{dired} (@inforef{Dired,,emacs}), using the following steps (based on a post on the @t{mu-discuss} mailing list by @emph{Stephen Eglen}). To prepare for this, you need a special version of the @code{gnus-dired-mail-buffers} function so it understands @t{mu4e} buffers as well; so put in your configuration: @lisp (require 'gnus-dired) ;; make the `gnus-dired-mail-buffers' function also work on ;; message-mode derived modes, such as mu4e-compose-mode (defun gnus-dired-mail-buffers () "Return a list of active message buffers." (let (buffers) (save-current-buffer (dolist (buffer (buffer-list t)) (set-buffer buffer) (when (and (derived-mode-p 'message-mode) (null message-sent-message-via)) (push (buffer-name buffer) buffers)))) (nreverse buffers))) (setq gnus-dired-mail-mode 'mu4e-user-agent) (add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode) @end lisp Then, mark the file(s) in @t{dired} you would like to attach and press @t{C-c RET C-a}, and you'll be asked whether to attach them to an existing message, or create a new one. @node Example configurations @appendix Example configurations In this chapter, we show some example configurations. While it is very useful to see some working settings, we'd like to warn against blindly copying such things. @menu * Minimal configuration::Simplest configuration to get you going * Longer configuration::A more extensive setup * Gmail configuration::GMail-specific setup * Other settings:CONF Other settings. Some other useful configuration @end menu @node Minimal configuration @section Minimal configuration An (almost) minimal configuration for @t{mu4e} might look like this - as you see most is commented-out. @lisp ;; example configuration for mu4e ;; make sure mu4e is in your load-path (require 'mu4e) ;; Only needed if your maildir is _not_ ~/Maildir ;; Must be a real dir, not a symlink ;;(setq mu4e-maildir "/home/user/Maildir") ;; these must start with a "/", and must exist ;; (i.e.. /home/user/Maildir/sent must exist) ;; you use e.g. 'mu mkdir' to make the Maildirs if they don't ;; already exist ;; below are the defaults; if they do not exist yet, mu4e offers to ;; create them. they can also functions; see their docstrings. ;; (setq mu4e-sent-folder "/sent") ;; (setq mu4e-drafts-folder "/drafts") ;; (setq mu4e-trash-folder "/trash") ;; smtp mail setting; these are the same that `gnus' uses. (setq message-send-mail-function 'smtpmail-send-it smtpmail-default-smtp-server "smtp.example.com" smtpmail-smtp-server "smtp.example.com" smtpmail-local-domain "example.com") @end lisp @node Longer configuration @section Longer configuration A somewhat longer configuration, showing some more things that you can customize. @lisp ;; example configuration for mu4e (require 'mu4e) ;; path to our Maildir directory (setq mu4e-maildir "/home/user/Maildir") ;; the next are relative to `mu4e-maildir' ;; instead of strings, they can be functions too, see ;; their docstring or the chapter 'Dynamic folders' (setq mu4e-sent-folder "/sent" mu4e-drafts-folder "/drafts" mu4e-trash-folder "/trash") ;; the maildirs you use frequently; access them with 'j' ('jump') (setq mu4e-maildir-shortcuts '(("/archive" . ?a) ("/inbox" . ?i) ("/work" . ?w) ("/sent" . ?s))) ;; a list of user's e-mail addresses (setq mu4e-user-mail-address-list '("foo@@bar.example.com" "cuux@@example.com") ;; the headers to show in the headers list -- a pair of a field ;; and its width, with `nil' meaning 'unlimited' ;; (better only use that for the last field. ;; These are the defaults: (setq mu4e-headers-fields '( (:date . 25) ;; alternatively, use :human-date (:flags . 6) (:from . 22) (:subject . nil))) ;; alternatively, use :thread-subject ;; program to get mail; alternatives are 'fetchmail', 'getmail' ;; isync or your own shellscript. called when 'U' is pressed in ;; main view. ;; If you get your mail without an explicit command, ;; use "true" for the command (this is the default) (setq mu4e-get-mail-command "offlineimap") ;; general emacs mail settings; used when composing e-mail ;; the non-mu4e-* stuff is inherited from emacs/message-mode (setq mu4e-reply-to-address "foo@@bar.example.com" user-mail-address "foo@@bar.example.com" user-full-name "Foo X. Bar") (setq mu4e-compose-signature "Foo X. Bar\nhttp://www.example.com\n") ;; smtp mail setting (setq message-send-mail-function 'smtpmail-send-it smtpmail-default-smtp-server "smtp.example.com" smtpmail-smtp-server "smtp.example.com" smtpmail-local-domain "example.com" ;; if you need offline mode, set these -- and create the queue dir ;; with 'mu mkdir', i.e.. mu mkdir /home/user/Maildir/queue smtpmail-queue-mail nil smtpmail-queue-dir "/home/user/Maildir/queue/cur") ;; don't keep message buffers around (setq message-kill-buffer-on-exit t) @end lisp @node Gmail configuration @section Gmail configuration @emph{Gmail} is a popular e-mail provider; let's see how we can make it work with @t{mu4e}. Since we are using @abbr{IMAP}, you must enable that in the Gmail web interface (in the settings, under the ``Forwarding and POP/IMAP''-tab). Gmail users may also be interested in @ref{Including related messages, skipping duplicates}. @subsection Setting up offlineimap First of all, we need a program to get the e-mail from Gmail to our local machine; for this we use @t{offlineimap}; on Debian (and derivatives like Ubuntu), this is as easy as: @verbatim $ sudo apt-get install offlineimap @end verbatim while on Fedora (and similar) you need: @verbatim $ sudo yum install offlineimap @end verbatim Then, we can configure @t{offlineimap} by editing @file{~/.offlineimaprc}: @verbatim [general] accounts = Gmail maxsyncaccounts = 3 [Account Gmail] localrepository = Local remoterepository = Remote [Repository Local] type = Maildir localfolders = ~/Maildir [Repository Remote] type = IMAP remotehost = imap.gmail.com remoteuser = USERNAME@gmail.com remotepass = PASSWORD ssl = yes maxconnections = 1 realdelete = no @end verbatim Obviously, you need to replace @t{USERNAME} and @t{PASSWORD} with your actual Gmail username and password. After this, you should be able to download your mail: @verbatim $ offlineimap OfflineIMAP 6.3.4 Copyright 2002-2011 John Goerzen & contributors. Licensed under the GNU GPL v2+ (v2 or any later version). Account sync Gmail: ***** Processing account Gmail Copying folder structure from IMAP to Maildir Establishing connection to imap.gmail.com:993. Folder sync [Gmail]: Syncing INBOX: IMAP -> Maildir Syncing [Gmail]/All Mail: IMAP -> Maildir Syncing [Gmail]/Drafts: IMAP -> Maildir Syncing [Gmail]/Sent Mail: IMAP -> Maildir Syncing [Gmail]/Spam: IMAP -> Maildir Syncing [Gmail]/Starred: IMAP -> Maildir Syncing [Gmail]/Trash: IMAP -> Maildir Account sync Gmail: ***** Finished processing account Gmail @end verbatim We can now run @command{mu} to make sure things work: @verbatim $ mu index mu: indexing messages under /home/foo/Maildir [/home/foo/.mu/xapian] | processing mail; processed: 520; updated/new: 520, cleaned-up: 0 mu: elapsed: 3 second(s), ~ 173 msg/s mu: cleaning up messages [/home/foo/.mu/xapian] / processing mail; processed: 520; updated/new: 0, cleaned-up: 0 mu: elapsed: 0 second(s) @end verbatim We can run both the @t{offlineimap} and the @t{mu index} from within @t{mu4e}, but running it from the command line makes it a bit easier to troubleshoot as we are setting things up. Note: when using encryption, you probably do @emph{not} want to synchronize your Drafts-folder, since it contains the unencrypted messages. You can use OfflineIMAP's @t{folderfilter} for that. @subsection Settings Next step: let's make a @t{mu4e} configuration for this: @lisp (require 'mu4e) ;; default ;; (setq mu4e-maildir "~/Maildir") (setq mu4e-drafts-folder "/[Gmail].Drafts") (setq mu4e-sent-folder "/[Gmail].Sent Mail") (setq mu4e-trash-folder "/[Gmail].Trash") ;; don't save message to Sent Messages, Gmail/IMAP takes care of this (setq mu4e-sent-messages-behavior 'delete) ;; (See the documentation for `mu4e-sent-messages-behavior' if you have ;; additional non-Gmail addresses and want assign them different ;; behavior.) ;; setup some handy shortcuts ;; you can quickly switch to your Inbox -- press ``ji'' ;; then, when you want archive some messages, move them to ;; the 'All Mail' folder by pressing ``ma''. (setq mu4e-maildir-shortcuts '( ("/INBOX" . ?i) ("/[Gmail].Sent Mail" . ?s) ("/[Gmail].Trash" . ?t) ("/[Gmail].All Mail" . ?a))) ;; allow for updating mail using 'U' in the main view: (setq mu4e-get-mail-command "offlineimap") ;; something about ourselves (setq user-mail-address "USERNAME@@gmail.com" user-full-name "Foo X. Bar" mu4e-compose-signature (concat "Foo X. Bar\n" "http://www.example.com\n")) ;; sending mail -- replace USERNAME with your gmail username ;; also, make sure the gnutls command line utils are installed ;; package 'gnutls-bin' in Debian/Ubuntu (require 'smtpmail) (setq message-send-mail-function 'smtpmail-send-it starttls-use-gnutls t smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil)) smtpmail-auth-credentials '(("smtp.gmail.com" 587 "USERNAME@@gmail.com" nil)) smtpmail-default-smtp-server "smtp.gmail.com" smtpmail-smtp-server "smtp.gmail.com" smtpmail-smtp-service 587) ;; alternatively, for emacs-24 you can use: ;;(setq message-send-mail-function 'smtpmail-send-it ;; smtpmail-stream-type 'starttls ;; smtpmail-default-smtp-server "smtp.gmail.com" ;; smtpmail-smtp-server "smtp.gmail.com" ;; smtpmail-smtp-service 587) ;; don't keep message buffers around (setq message-kill-buffer-on-exit t) @end lisp And that's it -- put the above in your @file{~/.emacs}, change @t{USERNAME} etc. to your own, and restart @command{emacs}, and run @kbd{M-x mu4e}. @node CONF Other settings @section Other settings Finally, here are some more settings that are useful, but not enabled by default for various reasons. @lisp ;; use 'fancy' non-ascii characters in various places in mu4e (setq mu4e-use-fancy-chars t) ;; save attachment to my desktop (this can also be a function) (setq mu4e-attachment-dir "~/Desktop") ;; attempt to show images when viewing messages (setq mu4e-view-show-images t) @end lisp @node FAQ @appendix FAQ - Frequently Asked Questions In this chapter we list a number of actual and anticipated questions and their answers. @menu * General::General questions and answers about mu4e * Retrieving mail::Getting mail and indexing * Reading messages::Dealing with incoming messages * Writing messages::Dealing with outgoing messages * Known issues::Limitations we know about @end menu @node General @section General @enumerate @item @emph{Does @t{mu4e} provide context-sensitive help information?} Yes - pressing @key{H} should take you to the right section in this manual. @item @emph{How can I quickly delete/move/trash a lot of messages?} You can select ('mark' in @command{emacs}-speak) the messages like you would select text in a buffer; the actions you then take (e.g., @key{DEL} for delete, @key{m} for move and @key{t} for trash) apply to all selected messages. You can also use functions like @code{mu4e-headers-mark-thread} (@key{T}), @code{mu4e-headers-mark-subthread} (@key{t}) to mark whole threads at the same time, and @code{mu4e-headers-mark-pattern} (@key{%}) to mark all messages matching a certain regular expression. @item @emph{@t{mu4e} seems to return a subset of all matches - how can I get all?} For speed reasons, @t{mu4e} returns only up to the value of the variable @code{mu4e-search-result-limit} (default: 500) matches. To show @emph{all}, use @kbd{M-x mu4e-headers-toggle-full-search} (@key{Q}), or customize the variable @code{mu4e-headers-full-search}. This applies to all search commands. @item @emph{Can I automatically apply the marks on messages when leaving the headers buffer?} Yes you can -- see the documentation for the variable @t{mu4e-headers-leave-behavior}. @item @emph{How can I set @t{mu4e} as the default e-mail client in @command{emacs}?} See @ref{Emacs default}. @item @emph{Can @t{mu4e} use some fancy Unicode characters instead of these boring plain-ASCII ones?} Glad you asked! Yes, if you set @code{mu4e-use-fancy-chars} to @t{t}, @t{mu4e} uses such fancy characters in a number of places. Since not all fonts include all characters, you may want to install the @t{unifont} and/or @t{symbola} fonts on your system. @item @emph{Can I start @t{mu4e} in the background?} Yes - if you provide a prefix-argument (@key{C-u}), @t{mu4e} starts, but does not show the main-window. @item @emph{Does @t{mu4e} support searching for CJK (Chinese-Japanese-Korean) characters?} Yes, if you have @t{Xapian} 1.2.8 or newer, and set the environment variable @t{XAPIAN_CJK_NGRAM} to non-empty before indexing, both when using @t{mu} from the command-line and from @t{mu4e}. @item @emph{How can I customize the function to select a folder?} The @t{mu4e-completing-read-function} variable can be customized to select a folder in any way. The variable can be set to a function that receives five arguments, following @t{completing-read}. The default value is @code{ido-completing-read}; to use emacs's default behaviour, set the variable to @code{completing-read}. Helm users can use the same value, and by enabling @code{helm-mode} use helm-style completion. @item @emph{I have a lot of Maildir folders, so regenerating them each time makes things slow. What can I do?} Set @code{mu4e-cache-maildir-list} to @code{t} (but make sure to read its docstring). @end enumerate @node Retrieving mail @section Retrieving mail @enumerate @item @emph{How can I get notifications when receiving mail?} There is @code{mu4e-index-updated-hook}, which gets triggered when the indexing process triggered sees an update (not just new mail though). To use this hook, put something like the following in your setup (assuming you have @t{aplay} and some soundfile, change as needed): @lisp (add-hook 'mu4e-index-updated-hook (defun new-mail-sound () (shell-command "aplay ~/Sounds/boing.wav&"))) @end lisp @item @emph{It seems my headers-buffer is automatically updated when new messages are found during the indexing process -- can I disable this somehow?} Yes - set @code{mu4e-headers-auto-update} to @code{nil}. @item @emph{I don't use @t{offlineimap}, @t{fetchmail} etc., I get my mail through my own mailserver. What should I use for @code{mu4e-get-mail-command}}? Use the literal string @t{"true"} (or don't do anything, it's the default) which then uses @t{/bin/true} (a command that does nothing and always succeeds). This makes getting mail a no-op, but the messages are still re-indexed. @item @emph{How can I re-index my messages without getting new mail?} Use @kbd{M-x mu4e-update-index} @item @emph{When I try to run @t{mu index} while @t{mu4e} is running I get errors like:} @verbatim mu: mu_store_new_writable: xapian error 'Unable to get write lock on ~/.mu/xapian: already locked @end verbatim @emph{What to do about this?} You get this error because the underlying Xapian database is locked by some other process; it can be opened only once in read-write mode. There is not much @t{mu4e} can do about this, but if is another @command{mu} instance that is holding the lock, you can ask it to (gracefully) terminate: @verbatim pkill -2 -u $UID mu # send SIGINT sleep 1 mu index @end verbatim @t{mu4e} automatically restarts @t{mu} when it needs it. In practice, this seems to work quite well. @item @emph{I don't like the @t{Indexing...} messages that the indexing process gives me. Can I turn them off?}. Yes: set the variable @code{mu4e-hide-index-messages} to non-@t{nil}. @item @emph{Some IMAP-synchronization programs such as @t{mbsync} (but not @t{offlineimap}) don't like it when message files do not change their names when they are moved to different folders. Can @t{mu4e} somehow accommodate this?} Yes - you can set the variable @code{mu4e-change-filenames-when-moving} to non-nil. @item @emph{@command{offlineimap} uses IMAP's UTF-7 for encoding non-ascii folder names, while @t{mu} expects UTF-8 (so, e.g. @t{/まりも えお}@footnote{some Japanese characters} becomes @t{/&MH4wijCCMEgwSg-}). How can I make @t{mu4e} display such folders correctly?} This is best solved by telling @command{offlineimap} to use UTF-8 instead -- see @url{https://github.com/djcb/mu/issues/68#issuecomment-8598652}. @end enumerate @node Reading messages @section Reading messages @enumerate @item @emph{How can I view attached images in my message view buffers?} See @ref{Viewing images inline}. @item @emph{How can I word-wrap long lines in when viewing a message?} You can toggle between wrapped and non-wrapped states using @key{w}. If you want to do this automatically, invoke @code{visual-line-mode} in your @code{mu4e-view-mode-hook}. @item @emph{How can I perform custom actions on messages and attachments?} See @ref{Actions}. @item @emph{Does @t{mu4e} support crypto (i.e., decrypting messages and verifying signatures)?} Yes -- if @t{mu} was built with @t{GMime} 2.6 or later, it is possible to do both (note, only PGP/MIME is supported). In the @ref{Main view} the support is indicated by a big letter @t{C} on the right hand side of the @t{mu4e} version. See @ref{Decryption} and @ref{Verifying signatures}. For encryption and signing messages, see @ref{Writing messages}. @item @emph{How can I prevent @t{mu4e} from automatically marking messages as 'read' when i read them?} Set @code{mu4e-view-auto-mark-as-read} to @code{nil}. @item @emph{Does @t{mu4e} support including all related messages in a thread, like Gmail does?} Yes -- see @ref{Including related messages}. @item @emph{There seem to be a lot of duplicate messages -- how can I get rid of them?} See @ref{Skipping duplicates}. @item @emph{How can I use the @t{eww} browser to view rich-text messages?} With a new enough emacs, this happens automatically. See @ref{Html2text functions} for some details. @item @emph{Some messages are almost unreadable in emacs - can I view them in an external web browser?} Indeed, airlines often send messages that heavily depend on html and are hard to digest inside emacs. Fortunately, there's an @emph{action} (@ref{Message view actions}) defined for this. Simply add to your configuration: @lisp (add-to-list 'mu4e-view-actions '("ViewInBrowser" . mu4e-action-view-in-browser) t) @end lisp Now, when viewing such a difficult message, type @kbd{aV}, and the message opens inside a webbrowser. You can influence the browser with @code{browse-url-generic-program}; and see @ref{Privacy aspects}. @item @emph{How can read encrypted messages that I sent?}. Since you do not own the recipient's key you typically cannot read those mails - so the trick is to encrypt outgoing mails with your key, too. This can be automated by adding the following snippet to your configuration (courtesy of user @t{kpachnis}): @lisp (require 'epg-config) (setq mml2015-use 'epg epg-user-id "gpg_key_id" mml2015-encrypt-to-self t mml2015-sign-with-sender t) @end lisp @item @emph{view-as-pdf seems to hang for some e-mails - what can I do about that?} Short answer: install @t{nspluginwrapper}. Longer answer: @t{mu} comes with @t{msg2pdf}, which is a program used to convert the messages to pdf, and which depends on WebKit, which in some cases needs @t{nspluginwrapper} and it waits for a long time if it's not there. So, installing @t{nspluginwrapper} prevents that. @item @emph{Can I 'bounce' or 'resend' messages?} Yes - it is possible to edit a (copy of) an existing message and then send it, using @code{M-x mu4e-compose-resend}. This gives you a raw copy of the message, including all headers, encoded parts and so on. Reason for this is that for resending, it is important not to change anything (except perhaps for the 'To:' address when bouncing); since we cannot losslessly decode an existing message, you get the raw version. @end enumerate @node Writing messages @section Writing messages @enumerate @item @emph{What's the deal with replies to messages I wrote myself?} Like many other mail-clients, @t{mu4e} treats replies to messages you wrote yourself as special -- these message keep the same @t{To:} and @t{Cc:} as the original message. This is to ease the common case of following up to a message you wrote earlier. @item @emph{How can I automatically set the @t{From:}-address for a reply-message, based on some field in the original?} See @ref{Compose hooks}. @item @emph{And what about customizable folders for draft messages, sent messages, trashed messages, based on e.g. the @t{From:} header?} See @ref{Dynamic folders}. @item @emph{Can I define aliases for (groups of) e-mail addresses?} Sure - see @ref{(emacs) Mail Aliases}. @item @emph{How can I automatically add some header to an outgoing message?} Once more, see @ref{Compose hooks}. @item @emph{How can I influence the way the original message looks when replying or forwarding?} Since @code{mu4e-compose-mode} derives from @code{message-mode}, you can re-use many of its facilities. @inforef{Insertion Variables,,message}. @item @emph{How can I easily include attachments in the messages I write?} You can drag-and-drop from your desktop; alternatively, you can use @t{dired} -- see @ref{Dired}. @item @emph{@t{mu4e} seems to remove myself from the @t{Cc:}-list; how can I prevent that?} Set @code{mu4e-compose-keep-self-cc} to @t{t} in your configuration. @item @emph{How can I start a new message-thread from a reply?} Remove the @t{In-Reply-To} header, and @t{mu4e} automatically removes the (hidden) @t{References} header as well when sending it. This makes the message show up as a top-level message rather than as a response. @item @emph{How can I attach an existing message?} Use @code{mu4e-action-capture-message} (i.e., @kbd{a c} in the headers view) to 'capture' the to-be-attached message, then when editing the message, use @kbd{M-x mu4e-compose-attach-captured-message}. @item @emph{How can I sign or encrypt messages?} You can do so using @command{emacs}' MIME-support -- check the @t{Attachments}-menu while composing a message. Also see @ref{Signing and encrypting}. @item @emph{Can I use @t{BBDB} with @t{mu4e}?} Yes, with the current (2015-06-23) development release of BBDB @url{http://savannah.nongnu.org/projects/bbdb/}, or releases of BBDB after 3.1.2. @ref{BBDB}. @item @emph{After sending some messages, it seems the buffer for these messages stay around. How can I get rid of those?} @lisp (setq message-kill-buffer-on-exit t) @end lisp @item @emph{Sending big messages is slow and blocks emacs - what can I do about it?} For this, there's @url{https://github.com/jwiegley/emacs-async} (also available from the Emacs package repository); add the following snippet to your configuration: @lisp (require 'smtpmail-async) (setq send-mail-function 'async-smtpmail-send-it message-send-mail-function 'async-smtpmail-send-it) @end lisp With this, messages are sent using background emacs-instance. A word of warning though, this tends to not be as reliable as sending the message in the normal, synchronous fashion, and people have reported silent failures, where mail sending fails for some reason without any indication of that. You can check the progress of the background by checking the @t{*Messages*}-buffer, which should show something like: @verbatim Delivering message to "William Shakespeare" <will@example.com>... Mark set Saving file /home/djcb/Maildir/sent/cur/20130706-044350-darklady:2,S... Wrote /home/djcb/Maildir/sent/cur/20130706-044350-darklady:2,S Sending...done @end verbatim The first and final messages are the most important, and there may be considerable time between them, depending on the size of the message. @item @emph{Is it possible to compose messages in a separate frame?} Yes - set the variable @code{mu4e-compose-in-new-frame} to @code{t}. @item @emph{How can I apply format=flowed to my outgoing messages, enabling receiving clients that support this feature to reflow my paragraphs?} Plain text emails with @t{Content-Type: text/plain; format=flowed} can be reflowed (i.e. line endings removed, paragraphs refilled) by receiving clients that support this standard. Clients that don't support this, show them as is, which means this feature is truly non-invasive. Here's an explanatory blog post which also shows why this is a desirable feature: @url{https://mathiasbynens.be/notes/gmail-plain-text} (if you don't have it, your mails mostly look quite bad especially on mobile devices) and here's the RFC with all the details: @url{http://www.ietf.org/rfc/rfc2646.txt}. Since version 0.9.17, @t{mu4e} send emails with @t{format=flowed} by setting @lisp (setq mu4e-compose-format-flowed t) @end lisp in your Emacs init file (@file{~/.emacs} or @file{~/.emacs.d/init.el}). The transformation of your message into the proper format is done at the time of sending. In order to happen properly, you should write each paragraph of your message of as a long line (i.e. without carriage return). If you introduce unwanted newlines in your paragraph, use @kbd{M-q} to reformat it as a single line. If you want to send the message with paragraphs on single lines but without @t{format=flowed} (because, say, the receiver does not understand the latter as it is the case for Google or Github), use @kbd{M-x use-hard-newlines} (to turn @code{use-hard-newlines} off) or uncheck the box @t{format=flowed} in the @t{Text} menu when composing a message. @end enumerate @node Known issues @section Known issues Although they are not really @emph{questions}, we end this chapter with a list of known issues and/or missing features in @t{mu4e}. Thus, users won't have to search in vain for things that are not there (yet), and the author can use it as a todo-list. @itemize @item @emph{mu4e does not work well if the @command{emacs} language environment is not UTF-8}; so, if you encounter problems with encodings, be sure to have @code{(set-language-environment "UTF-8")} in your @file{~/.emacs}. @item @emph{Thread handling is incomplete.} While threads are calculated and are visible in the headers buffer, you cannot collapse/open them. @item @emph{The key-bindings are @emph{somewhat} hard-coded.} That is, the main menu assumes the default key-bindings, as do the clicks-on-bookmarks. @item @emph{The @t{emacs} front-end of the @t{notmuch} e-mail indexer conflicts with @t{mu4e}}. @t{notmuch} running in parallel with @t{mu4e} leads to @verbatim error in process filter: mu4e-error-handler: Error 70: cannot read ~/Maildir/... @end verbatim when sending a reply to some e-mail. This seems to be caused by @t{notmuch} changing the name of the original message file while @t{mu4e} is working on it. To prevent this, deactivate @t{notmuch} in your Emacs setup. @item @emph{The PDF-version of the manual does not show any of the non-ASCII characters} - this is because the @t{texi2pdf} documentation system does not support those. There is not much we can do about that. @end itemize For a more complete list, please refer to the issues-list in the github-repository. @node Tips and Tricks @appendix Tips and Tricks @menu * Fancy characters:: Non-ascii characters in the UI * Multiple accounts:: (Obsolete) the old way to deal with multiple accounts * Refiling messages:: Moving message to some archive folder * Saving outgoing messages:: Automatically save sent messages * Confirmation before sending:: Check messages before sending @end menu @node Fancy characters @section Fancy characters When using 'fancy characters' (@code{mu4e-use-fancy-chars}) with the @emph{Inconsolata}-font (and likely others as well), the display may be slightly off; the reason for this issue is that Inconsolata does not contain the glyphs for the 'fancy' arrows and the glyphs that are used as replacements are too high. To fix this, you can use something like the following workaround (in your @t{.emacs}-file): @lisp (if (equal window-system 'x) (progn (set-fontset-font "fontset-default" 'unicode "Dejavu Sans Mono") (set-face-font 'default "Inconsolata-10"))) @end lisp Other fonts with good support for Unicode are @t{unifont} and @t{symbola}. For a more complete solution, but with greater overhead, you can also try the @emph{unicode-fonts} package: @lisp (require 'unicode-fonts) (require 'persistent-soft) ; To cache the fonts and reduce load time (unicode-fonts-setup) @end lisp @node Multiple accounts @section Multiple accounts @b{Note}: - for @t{mu4e} version 0.9.16 and higher, the recommended way to deal with multiple accounts is through @t{mu4e}'s built-in @ref{Contexts} system. For older versions, the below still works. Using mu4e with multiple email accounts is fairly easy. Although variables such as @code{user-mail-address}, @code{mu4e-sent-folder}, @code{message-*}, @code{smtpmail-*}, etc. typically only take one value, it is easy to change their values using @code{mu4e-compose-pre-hook}. The setup described here is one way of doing this (though certainly not the only way). This setup assumes that you have multiple mail accounts under @code{mu4e-maildir}. As an example, we'll use @t{~/Maildir/Account1} and @t{~/Maildir/Account2}, but the setup works just as well if @code{mu4e-maildir} points to something else. First, you need to make sure that all variables that you wish to change based on user account are set to some initial value. So set up your environment with e.g., your main account: @lisp (setq mu4e-sent-folder "/Account1/Saved Items" mu4e-drafts-folder "/Account1/Drafts" user-mail-address "my.address@@account1.example.com" smtpmail-default-smtp-server "smtp.account1.example.com" smtpmail-local-domain "account1.example.com" smtpmail-smtp-server "smtp.account1.example.com" smtpmail-stream-type 'starttls smtpmail-smtp-service 25) @end lisp Then create a variable @code{my-mu4e-account-alist}, which should contain a list for each of your accounts. Each list should start with the account name, (which @emph{must} be identical to the account's directory name under @t{~/Maildir}), followed by @code{(variable value)} pairs: @lisp (defvar my-mu4e-account-alist '(("Account1" (mu4e-sent-folder "/Account1/Saved Items") (mu4e-drafts-folder "/Account1/Drafts") (user-mail-address "my.address@@account1.example.com") (smtpmail-default-smtp-server "smtp.account1.example.com") (smtpmail-local-domain "account1.example.com") (smtpmail-smtp-user "username1") (smtpmail-smtp-server "smtp.account1.example.com") (smtpmail-stream-type starttls) (smtpmail-smtp-service 25)) ("Account2" (mu4e-sent-folder "/Account2/Saved Items") (mu4e-drafts-folder "/Account2/Drafts") (user-mail-address "my.address@@account2.example.com") (smtpmail-default-smtp-server "smtp.account2.example.com") (smtpmail-local-domain "account2.example.com") (smtpmail-smtp-user "username2") (smtpmail-smtp-server "smtp.account2.example.com") (smtpmail-stream-type starttls) (smtpmail-smtp-service 587)))) @end lisp You can put any variable you want in the account lists, just make sure that you put in @emph{all} the variables that differ for each account. Variables that do not differ need not be included. For example, if you use the same smtp server for both accounts, you don't need to include the smtp-related variables in @code{my-mu4e-account-alist}. Note that some SMTP servers (such as Gmail) require the SMTP username to match the user mail address. In this case, your mail is appear to originate from whichever SMTP account you use. Thus unless you are certain your SMTP server does not have this requirement, you should generally use different SMTP account credentials for each mail account. Now, the following function can be used to select an account and set the variables in @code{my-mu4e-account-alist} to the correct values: @lisp (defun my-mu4e-set-account () "Set the account for composing a message." (let* ((account (if mu4e-compose-parent-message (let ((maildir (mu4e-message-field mu4e-compose-parent-message :maildir))) (string-match "/\\(.*?\\)/" maildir) (match-string 1 maildir)) (completing-read (format "Compose with account: (%s) " (mapconcat #'(lambda (var) (car var)) my-mu4e-account-alist "/")) (mapcar #'(lambda (var) (car var)) my-mu4e-account-alist) nil t nil nil (caar my-mu4e-account-alist)))) (account-vars (cdr (assoc account my-mu4e-account-alist)))) (if account-vars (mapc #'(lambda (var) (set (car var) (cadr var))) account-vars) (error "No email account found")))) @end lisp This function then needs to be added to @code{mu4e-compose-pre-hook}: @lisp (add-hook 'mu4e-compose-pre-hook 'my-mu4e-set-account) @end lisp This way, @code{my-mu4e-set-account} is called every time you edit a message. If you compose a new message, it simply asks you for the account you wish to send the message from (TAB completion works). If you're replying or forwarding a message, or editing an existing draft, the account is chosen automatically, based on the first component of the maildir of the message being replied to, forwarded or edited (i.e., the directory under @t{~/Maildir}). @node Refiling messages @section Refiling messages By setting @code{mu4e-refile-folder} to a function, you can dynamically determine where messages are to be refiled. If you want to do this based on the subject of a message, you can use a function that matches the subject against a list of regexes in the following way. First, set up a variable @code{my-mu4e-subject-alist} containing regexes plus associated mail folders: @lisp (defvar my-mu4e-subject-alist '(("kolloqui\\(um\\|a\\)" . "/Kolloquium") ("Calls" . "/Calls") ("Lehr" . "/Lehre") ("webseite\\|homepage\\|website" . "/Webseite")) "List of subjects and their respective refile folders.") @end lisp Now you can use the following function to automatically refile messages based on their subject line: @lisp (defun my-mu4e-refile-folder-function (msg) "Set the refile folder for MSG." (let ((subject (mu4e-message-field msg :subject)) (folder (or (cdar (member* subject my-mu4e-subject-alist :test #'(lambda (x y) (string-match (car y) x)))) "/General"))) folder)) @end lisp Note the @t{"/General"} folder: it is the default folder in case the subject does not match any of the regexes in @code{my-mu4e-subject-alist}. In order to make this work, you'll of course need to set @code{mu4e-refile-folder} to this function: @lisp (setq mu4e-refile-folder 'my-mu4e-refile-folder-function) @end lisp If you have multiple accounts, you can accommodate them as well: @lisp (defun my-mu4e-refile-folder-function (msg) "Set the refile folder for MSG." (let ((maildir (mu4e-message-field msg :maildir)) (subject (mu4e-message-field msg :subject)) folder) (cond ((string-match "Account1" maildir) (setq folder (or (catch 'found (dolist (mailing-list my-mu4e-mailing-lists) (if (mu4e-message-contact-field-matches msg :to (car mailing-list)) (throw 'found (cdr mailing-list))))) "/Account1/General"))) ((string-match "Gmail" maildir) (setq folder "/Gmail/All Mail")) ((string-match "Account2" maildir) (setq folder (or (cdar (member* subject my-mu4e-subject-alist :test #'(lambda (x y) (string-match (car y) x)))) "/Account2/General")))) folder)) @end lisp This function actually uses different methods to determine the refile folder, depending on the account: For @emph{Account2}, it uses @code{my-mu4e-subject-alist}, for the @emph{Gmail} account it simply uses the folder "All Mail". For Account1, it uses another method: it files the message based on the mailing list to which it was sent. This requires another variable: @lisp (defvar my-mu4e-mailing-lists '(("mu-discuss@@googlegroups.com" . "/Account1/mu4e") ("pandoc-discuss@@googlegroups.com" . "/Account1/Pandoc") ("auctex@@gnu.org" . "/Account1/AUCTeX")) "List of mailing list addresses and folders where their messages are saved.") @end lisp @node Saving outgoing messages @section Saving outgoing messages Like @code{mu4e-refile-folder}, the variable @code{mu4e-sent-folder} can also be set to a function, in order to dynamically determine the save folder. One might, for example, wish to automatically put messages going to mailing lists into the trash (because you'll receive them back from the list anyway). If you have set up the variable @code{my-mu4e-mailing-lists} as mentioned, you can use the following function to determine a save folder: @lisp (defun my-mu4e-sent-folder-function (msg) "Set the sent folder for the current message." (let ((from-address (message-field-value "From")) (to-address (message-field-value "To"))) (cond ((string-match "my.address@@account1.example.com" from-address) (if (member* to-address my-mu4e-mailing-lists :test #'(lambda (x y) (string-match (car y) x))) "/Trash" "/Account1/Sent")) ((string-match "my.address@@gmail.com" from-address) "/Gmail/Sent Mail") (t (mu4e-ask-maildir-check-exists "Save message to maildir: "))))) @end lisp Note that this function doesn't use @code{(mu4e-message-field msg :maildir)} to determine which account the message is being sent from. The reason is that that the function in @code{mu4e-sent-folder} is called when you send the message, but before mu4e has created the message struct from the compose buffer, so that @code{mu4e-message-field} cannot be used. Instead, the function uses @code{message-field-value}, which extracts the values of the headers in the compose buffer. This means that it is not possible to extract the account name from the message's maildir, so instead the from address is used to determine the account. Again, the function shows three different possibilities: for the first account (@t{my.address@@account1.example.com}) it uses @code{my-mu4e-mailing-lists} again to determine if the message goes to a mailing list. If so, the message is put in the trash folder, if not, it is saved in @t{/Account1/Sent}. For the second (Gmail) account, sent mail is simply saved in the Sent Mail folder. If the from address is not associated with Account1 or with the Gmail account, the function uses @code{mu4e-ask-maildir-check-exists} to ask the user for a maildir to save the message in. @node Confirmation before sending @section Confirmation before sending To protect yourself from sending messages too hastily, you can add a final confirmation, which you can of course make as elaborate as you wish. @lisp (add-hook 'message-send-hook (lambda () (unless (yes-or-no-p "Sure you want to send this?") (signal 'quit nil)))) @end lisp @node How it works @appendix How it works While perhaps not interesting for all users of @t{mu4e}, some curious souls may want to know how @t{mu4e} does its job. @menu * High-level overview::How the pieces go together * mu server::The mu process running in the background * Reading from the server::Processing responses from the server * The message s-expression::What messages look like from the inside @end menu @node High-level overview @section High-level overview At a high level, we can summarize the structure of the @t{mu4e} system using some ascii-art: @cartouche @example +---------+ | emacs | | +------+ +----| mu4e | --> send mail (smtpmail) +------+ | A V | ---/ search, view, move mail +---------+ \ | mu | +---------+ | A V | +---------+ | Maildir | <--- receive mail (fetchmail, +---------+ offlineimap, ...) @end example @end cartouche In words: @itemize @item Your e-mail messages are stored in a Maildir-directory (typically, @file{~/Maildir} and its subdirectories), and new mail comes in using tools like @t{fetchmail}, @t{offlineimap}, or through a local mail server. @item @t{mu} indexes these messages periodically, so you can quickly search for them. @t{mu} can run in a special @t{server}-mode, where it provides services to client software. @item @t{mu4e}, which runs inside @command{emacs} is such a client; it communicates with @command{mu} (in its @t{server}-mode) to search for messages, and manipulate them. @item @t{mu4e} uses the facilities offered by @command{emacs} (the Gnus message editor and @t{smtpmail}) to send messages. @end itemize @node mu server @section @t{mu server} @t{mu4e} is based on the @t{mu} e-mail searching/indexer. The latter is a C-program; there are different ways to communicate with a client that is emacs-based. One way to implement this, would be to call the @t{mu} command-line tool with some parameters and then parse the output. In fact, that was the first approach -- @t{mu4e} would invoke e.g., @t{mu find} and process the output in @command{emacs}. However, with this approach, we need to load the entire e-mail @emph{Xapian} database (in which the message is stored) for each invocation. Wouldn't it be nicer to keep a running @t{mu} instance around? Indeed, it would - and thus, the @t{mu server} sub-command was born. Running @t{mu server} starts a simple shell, in which you can give commands to @command{mu}, which then spits out the results/errors. @command{mu server} is not meant for humans, but it can be used manually, which is great for debugging. @node Reading from the server @section Reading from the server In the design, the next question was what format @t{mu} should use for its output for @t{mu4e} (@command{emacs}) to process. Some other programs use @abbr{JSON} here, but it seemed easier (and possibly, more efficient) just to talk to @command{emacs} in its native language: @emph{s-expressions}, and interpret those using the @command{emacs}-function @code{read-from-string}. See @ref{The message s-expression} for details on the format. So, now let's look how we process the data from @t{mu server} in @command{emacs}. We'll leave out a lot of detail, @t{mu4e}-specifics, and look at a bit more generic approach. The first thing to do is to create a process (for example, with @code{start-process}), and then register a filter function for it, which is invoked whenever the process has some data for us. Something like: @lisp (let ((proc (start-process <arguments>))) (set-process-filter proc 'my-process-filter) (set-process-sentinel proc 'my-process-sentinel)) @end lisp Note, the process sentinel is invoked when the process is terminated -- so there you can clean things up. The function @code{my-process-filter} is a user-defined function that takes the process and the chunk of output as arguments; in @t{mu4e} it looks something like (pseudo-lisp): @lisp (defun my-process-filter (proc str) ;; mu4e-buf: a global string variable to which data gets appended ;; as we receive it (setq mu4e-buf (concat mu4e-buf str)) (when <we-have-received-a-full-expression> <eat-expression-from mu4e-buf> <evaluate-expression>)) @end lisp @code{<evaluate-expression>} de-multiplexes the s-expression we got. For example, if the s-expression looks like an e-mail message header, it is processed by the header-handling function, which appends it to the header list. If the s-expression looks like an error message, it is reported to the user. And so on. The language between frontend and backend is documented in the @t{mu-server} man-page. @t{mu4e} can log these communications; you can use @kbd{M-x mu4e-toggle-logging} to turn logging on and off, and you can view the log using @kbd{M-x mu4e-show-log} (@key{$}). @node The message s-expression @section The message s-expression A typical message s-expression looks something like the following: @lisp (:docid 32461 :from (("Nikola Tesla" . "niko@@example.com")) :to (("Thomas Edison" . "tom@@example.com")) :cc (("Rupert The Monkey" . "rupert@@example.com")) :subject "RE: what about the 50K?" :date (20369 17624 0) :size 4337 :message-id "C8233AB82D81EE81AF0114E4E74@@123213.mail.example.com" :path "/home/tom/Maildir/INBOX/cur/133443243973_1.10027.atlas:2,S" :maildir "/INBOX" :priority normal :flags (seen) :parts ( (:index 1 :mime-type "text/plain" :size 12345 :attachment nil) (:index 2 :name "photo.jpg" :mime-type "image/jpeg" :size 147331 :attachment t) (:index 3 :name "book.pdf" :mime-type "application/pdf" :size 192220 :attachment t)) :references ("C8384574032D81EE81AF0114E4E74@@123213.mail.example.com" "38203498230942D81EE81AF0114E4E74@@123213.mail.example.com") :in-reply-to "38203498230942D81EE81AF0114E4E74@@123213.mail.example.com" :body-txt "Hi Tom, .... ") @end lisp This s-expression forms a property list (@t{plist}), and we can get values from it using @t{plist-get}; for example @code{(plist-get msg :subject)} would get you the message subject. However, it's better to use the function @code{mu4e-message-field} to shield you from some of the implementation details that are subject to change; and see the other convenience functions in @file{mu4e-message.el}. Some notes on the format: @itemize @item The address fields are @emph{lists} of pairs @code{(name . email)}, where @t{name} can be nil. @item The date is in format @command{emacs} uses (for example in @code{current-time}).@footnote{Emacs 32-bit integers have only 29 bits available for the actual number; the other bits are use by @command{emacs} for internal purposes. Therefore, we need to split @t{time_t} in two numbers.} @item Attachments are a list of elements with fields @t{:index} (the number of the MIME-part), @t{:name} (the file name, if any), @t{:mime-type} (the MIME-type, if any) and @t{:size} (the size in bytes, if any). @item Messages in the @ref{Headers view} come from the database and do not have @t{:attachments}. @t{:body-txt} or @t{:body-html} fields. Message in the @ref{Message view} use the actual message file, and do include these fields. @end itemize @subsection Example: ping-pong As an example of the communication between @t{mu4e} and @command{mu}, let's look at the @t{ping-pong}-sequence. When @t{mu4e} starts, it sends a command @t{ping} to the @t{mu server} backend, to learn about its version. @t{mu server} then responds with a @t{pong} s-expression to provide this information (this is implemented in @file{mu-cmd-server.c}). We start this sequence when @t{mu4e} is invoked (when the program is started). It calls @t{mu4e-proc-ping}, and registers a (lambda) function for @t{mu4e-proc-pong-func}, to handle the response. @verbatim -> cmd:ping <- (pong "mu" :version "x.x.x" :doccount 10000) @end verbatim When we receive such a @t{pong} (in @file{mu4e-proc.el}), the lambda function we registered is called, and it compares the version we got from the @t{pong} with the version we expected, and raises an error, if they differ. @node Logging and debugging @appendix Logging and debugging As explained in @ref{How it works}, @t{mu4e} communicates with its backend (@t{mu server}) by sending commands and receiving responses (s-expressions). For debugging purposes, it can be very useful to see this data. For this reason, @t{mu4e} can log all these messages. Note that the 'protocol' is documented to some extent in the @t{mu-server} manpage. You can enable (and disable) logging with @kbd{M-x mu4e-toggle-logging}. The log-buffer is called @t{*mu4e-log*}, and in the @ref{Main view}, @ref{Headers view} and @ref{Message view}, there's a keybinding @key{$} that takes you there. You can quit it by pressing @key{q}. Logging can be a bit resource-intensive, so you may not want to leave it on all the time. By default, the log only maintains the most recent 1200 lines. @t{mu} itself keeps a log as well, you can find this it in @t{<MUHOME>/log/mu.log}, typically @t{~/.mu/log/mu.log}. @node GNU Free Documentation License @appendix GNU Free Documentation License @include fdl.texi @bye ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-view.el�������������������������������������������������������������������������0000644�0001750�0001750�00000164704�13020504332�012175� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e-view.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; In this file we define mu4e-view-mode (+ helper functions), which is used for ;; viewing e-mail messages ;;; Code: (require 'mu4e-utils) ;; utility functions (require 'mu4e-vars) (require 'mu4e-mark) (require 'mu4e-proc) (require 'mu4e-compose) (require 'mu4e-actions) (require 'mu4e-message) (require 'comint) (require 'browse-url) (require 'button) (require 'epa) (require 'epg) (require 'thingatpt) (require 'calendar) (eval-when-compile (byte-compile-disable-warning 'cl-functions)) (require 'cl) ;; the message view (defgroup mu4e-view nil "Settings for the message view." :group 'mu4e) (defcustom mu4e-view-fields '(:from :to :cc :subject :flags :date :maildir :mailing-list :tags :attachments :signature :decryption) "Header fields to display in the message view buffer. For the complete list of available headers, see `mu4e-header-info'." :type (list 'symbol) :group 'mu4e-view) (defcustom mu4e-view-show-addresses nil "Whether to initially show full e-mail addresses for contacts in address fields, rather than only their names." :type 'boolean :group 'mu4e-view) (make-obsolete-variable 'mu4e-view-wrap-lines nil "0.9.9-dev7") (make-obsolete-variable 'mu4e-view-hide-cited nil "0.9.9-dev7") (defcustom mu4e-view-date-format "%c" "Date format to use in the message view. In the format of `format-time-string'." :type 'string :group 'mu4e-view) (defcustom mu4e-view-image-max-width 800 "The maximum width for images to display. This is only effective if you're using an emacs with Imagemagick support, and `mu4e-view-show-images' is non-nil." :group 'mu4e-view) (defcustom mu4e-view-image-max-height 600 "The maximum height for images to display. This is only effective if you're using an emacs with Imagemagick support, and `mu4e-view-show-images' is non-nil." :group 'mu4e-view) (defcustom mu4e-view-scroll-to-next t "If non-nil, move to the next message when calling `mu4e-view-scroll-up-or-next' (typically bound to SPC) when at the end of a message. Otherwise, don't move to the next message.") (defcustom mu4e-save-multiple-attachments-without-asking nil "If non-nil, saving multiple attachments asks once for a directory and saves all attachments in the chosen directory." :type 'boolean :group 'mu4e-view) (defvar mu4e-view-actions '( ("capture message" . mu4e-action-capture-message) ("view as pdf" . mu4e-action-view-as-pdf) ("show this thread" . mu4e-action-show-thread)) "List of actions to perform on messages in view mode. The actions are of the form: (NAME FUNC) where: * NAME is the name of the action (e.g. \"Count lines\") * FUNC is a function which receives a message plist as an argument. The first letter of NAME is used as a shortcut character.") (defvar mu4e-view-attachment-actions '( ("wopen-with" . mu4e-view-open-attachment-with) ("ein-emacs" . mu4e-view-open-attachment-emacs) ("dimport-in-diary" . mu4e-view-import-attachment-diary) ("|pipe" . mu4e-view-pipe-attachment)) "List of actions to perform on message attachments. The actions are cons-cells of the form: (NAME . FUNC) where: * NAME is the name of the action (e.g. \"Count lines\") * FUNC is a function which receives two arguments: the message plist and the attachment number. The first letter of NAME is used as a shortcut character.") (defvar mu4e-view-fill-headers t "If non-nil, automatically fill the headers when viewing them.") (defvar mu4e-view-header-field-keymap (let ((map (make-sparse-keymap))) (define-key map [mouse-1] 'mu4e~view-header-field-fold) (define-key map (kbd "TAB") 'mu4e~view-header-field-fold) map) "Keymap used for header fields.") (defvar mu4e-view-contacts-header-keymap (let ((map (make-sparse-keymap))) (define-key map [mouse-2] 'mu4e~view-compose-contact) (define-key map "C" 'mu4e~view-compose-contact) (define-key map "c" 'mu4e~view-copy-contact) map) "Keymap used for the contacts in the header fields.") (defvar mu4e-view-clickable-urls-keymap (let ((map (make-sparse-keymap))) (define-key map [mouse-1] 'mu4e~view-browse-url-from-binding) (define-key map [?\M-\r] 'mu4e~view-browse-url-from-binding) map) "Keymap used for the urls inside the body.") (defvar mu4e-view-attachments-header-keymap (let ((map (make-sparse-keymap))) (define-key map [mouse-1] 'mu4e~view-open-attach-from-binding) (define-key map [?\M-\r] 'mu4e~view-open-attach-from-binding) (define-key map [mouse-2] 'mu4e~view-save-attach-from-binding) (define-key map (kbd "<S-return>") 'mu4e~view-save-attach-from-binding) map) "Keymap used in the \"Attachements\" header field.") (defcustom mu4e-view-auto-mark-as-read t "Automatically mark messages are 'read' when you read them. This is typically the expected behavior, but can be turned off, for example when using a read-only file-system." :type 'boolean :group 'mu4e-view) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e~view-cited-hidden nil "Whether cited lines are hidden.") (defvar mu4e~view-link-map nil "A map of some number->url so we can jump to url by number.") (defvar mu4e~path-parent-docid-map (make-hash-table :test 'equal) "A map of msg paths --> parent-docids. This is to determine what is the parent docid for embedded message extracted at some path.") (defvar mu4e~view-attach-map nil "A mapping of user-visible attachment number to the actual part index.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-view-message-with-message-id (msgid) "View message with message-id MSGID. This (re)creates a headers-buffer with a search for MSGID, then open a view for that message." (mu4e-headers-search (concat "msgid:" msgid) nil nil t msgid t)) (define-obsolete-function-alias 'mu4e-view-message-with-msgid 'mu4e-view-message-with-message-id "0.9.17") (defun mu4e~view-custom-field (msg field) "Show some custom header field, or raise an error if it is not found." (let* ((item (or (assoc field mu4e-header-info-custom) (mu4e-error "field %S not found" field))) (func (or (plist-get (cdr-safe item) :function) (mu4e-error "no :function defined for field %S %S" field (cdr item))))) (funcall func msg))) (defun mu4e-view-message-text (msg) "Return the message to display (as a string), based on the MSG plist." (concat (mapconcat (lambda (field) (let ((fieldval (mu4e-message-field msg field))) (case field (:subject (mu4e~view-construct-header field fieldval)) (:path (mu4e~view-construct-header field fieldval)) (:maildir (mu4e~view-construct-header field fieldval)) (:user-agent (mu4e~view-construct-header field fieldval)) ((:flags :tags) (mu4e~view-construct-flags-tags-header field fieldval)) ;; contact fields (:to (mu4e~view-construct-contacts-header msg field)) (:from (mu4e~view-construct-contacts-header msg field)) (:cc (mu4e~view-construct-contacts-header msg field)) (:bcc (mu4e~view-construct-contacts-header msg field)) ;; if we (`user-mail-address' are the From, show To, otherwise, ;; show From (:from-or-to (let* ((from (mu4e-message-field msg :from)) (from (and from (cdar from)))) (if (mu4e-user-mail-address-p from) (mu4e~view-construct-contacts-header msg :to) (mu4e~view-construct-contacts-header msg :from)))) ;; date (:date (let ((datestr (when fieldval (format-time-string mu4e-view-date-format fieldval)))) (if datestr (mu4e~view-construct-header field datestr) ""))) ;; size (:size (mu4e~view-construct-header field (mu4e-display-size fieldval))) (:mailing-list (mu4e~view-construct-header field fieldval)) (:message-id (mu4e~view-construct-header field fieldval)) ;; attachments (:attachments (mu4e~view-construct-attachments-header msg)) ;; pgp-signatures (:signature (mu4e~view-construct-signature-header msg)) ;; pgp-decryption (:decryption (mu4e~view-construct-decryption-header msg)) (t (mu4e~view-construct-header field (mu4e~view-custom-field msg field)))))) mu4e-view-fields "") "\n" (let* ((prefer-html (cond ((eq mu4e~view-html-text 'html) t) ((eq mu4e~view-html-text 'text) nil) (t mu4e-view-prefer-html))) (body (mu4e-message-body-text msg prefer-html))) (setq mu4e~view-html-text nil) (when (fboundp 'add-face-text-property) (add-face-text-property 0 (length body) 'mu4e-view-body-face t body)) body))) (defun mu4e~view-embedded-winbuf () "Get a buffer (shown in a window) for the embedded message." (let* ((buf (get-buffer-create mu4e~view-embedded-buffer-name)) (win (or (get-buffer-window buf) (split-window-vertically)))) (select-window win) (switch-to-buffer buf))) (defun mu4e~delete-all-overlays () "`delete-all-overlays' with compatibility fallback." (if (functionp 'delete-all-overlays) (delete-all-overlays) (remove-overlays))) (defun mu4e-view (msg headersbuf) "Display the message MSG in a new buffer, and keep in sync with HDRSBUF. 'In sync' here means that moving to the next/previous message in the the message view affects HDRSBUF, as does marking etc. As a side-effect, a message that is being viewed loses its 'unread' marking if it still had that." (let* ((embedded ;; is it as an embedded msg (ie. message/rfc822 att)? (when (gethash (mu4e-message-field msg :path) mu4e~path-parent-docid-map) t)) (buf (if embedded (mu4e~view-embedded-winbuf) (get-buffer-create mu4e~view-buffer-name)))) ;; note: mu4e~view-mark-as-read-maybe will pseudo-recursively call mu4e-view ;; again by triggering mu4e~view again as it marks the message as read (with-current-buffer buf (switch-to-buffer buf) (setq mu4e~view-msg msg) (when (or embedded (not (mu4e~view-mark-as-read-maybe msg))) (let ((inhibit-read-only t)) (erase-buffer) (mu4e~delete-all-overlays) (insert (mu4e-view-message-text msg)) (goto-char (point-min)) (mu4e~fontify-cited) (mu4e~fontify-signature) (mu4e~view-make-urls-clickable) (mu4e~view-show-images-maybe msg) (setq mu4e~view-buffer buf mu4e~view-headers-buffer headersbuf) (when embedded (local-set-key "q" 'kill-buffer-and-window)) (mu4e-view-mode)))))) (defun mu4e~view-get-property-from-event (prop) "Get the property PROP at point, or the location of the mouse. The action is chosen based on the `last-command-event'. Meant to be evoked from interactive commands." (if (and (eventp last-command-event) (mouse-event-p last-command-event)) (let ((posn (event-end last-command-event))) (when (numberp (posn-point posn)) (get-text-property (posn-point posn) prop (window-buffer (posn-window posn))) )) (get-text-property (point) prop))) (defun mu4e~view-construct-header (field val &optional dont-propertize-val) "Return header field FIELD (as in `mu4e-header-info') with value VAL if VAL is non-nil. If DONT-PROPERTIZE-VAL is non-nil, do not add text-properties to VAL." (let* ((info (cdr (assoc field (append mu4e-header-info mu4e-header-info-custom)))) (key (plist-get info :name)) (val (if val (propertize val 'field 'mu4e-header-field-value 'front-sticky '(field)))) (help (plist-get info :help))) (if (and val (> (length val) 0)) (with-temp-buffer (insert (propertize (concat key ":") 'field 'mu4e-header-field-key 'front-sticky '(field) 'keymap mu4e-view-header-field-keymap 'face 'mu4e-header-key-face 'help-echo help) " " (if dont-propertize-val val (propertize val 'face 'mu4e-header-value-face)) "\n") (when mu4e-view-fill-headers ;; temporarily set the fill column <margin> positions to the right, so ;; we can indent the following lines correctly (let* ((margin 1) (fill-column (max (- fill-column margin) 0))) (fill-region (point-min) (point-max)) (goto-char (point-min)) (while (and (zerop (forward-line 1)) (not (looking-at "^$"))) (indent-to-column margin)))) (buffer-string)) ""))) (defun mu4e~view-header-field-fold () "Fold/unfold headers' value if there are more than one line." (interactive) (let ((name-pos (field-beginning)) (value-pos (1+ (field-end)))) (if (and name-pos value-pos (eq (get-text-property name-pos 'field) 'mu4e-header-field-key)) (save-excursion (let* ((folded)) (mapc (lambda (o) (when (overlay-get o 'mu4e~view-header-field-folded) (delete-overlay o) (setq folded t))) (overlays-at value-pos)) (unless folded (let* ((o (make-overlay value-pos (field-end value-pos))) (vals (split-string (field-string value-pos) "\n" t)) (val (if (= (length vals) 1) (car vals) (concat (substring (car vals) 0 -3) "...")))) (overlay-put o 'mu4e~view-header-field-folded t) (overlay-put o 'display val)))))))) (defun mu4e~view-compose-contact (&optional point) "Compose a message for the address at point." (interactive) (unless (get-text-property (or point (point)) 'email) (mu4e-error "No address at point")) (mu4e~compose-mail (get-text-property (or point (point)) 'long))) (defun mu4e~view-copy-contact (&optional full) "Compose a message for the address at (point)." (interactive "P") (let ((email (get-text-property (point) 'email)) (long (get-text-property (point) 'long))) (unless email (mu4e-error "No address at point")) (kill-new (if full long email)) (mu4e-message "Address copied."))) (defun mu4e~view-construct-contacts-header (msg field) "Add a header for a contact field (ie., :to, :from, :cc, :bcc)." (mu4e~view-construct-header field (mapconcat (lambda(c) (let* ((name (when (car c) (replace-regexp-in-string "[[:cntrl:]]" "" (car c)))) (email (when (cdr c) (replace-regexp-in-string "[[:cntrl:]]" "" (cdr c)))) (short (or name email)) ;; name may be nil (long (if name (format "%s <%s>" name email) email))) (propertize (if mu4e-view-show-addresses long short) 'long long 'short short 'email email 'keymap mu4e-view-contacts-header-keymap 'face 'mu4e-contact-face 'mouse-face 'highlight 'help-echo (format "<%s>\n%s" email "[mouse-2] or C to compose a mail for this recipient")))) (mu4e-message-field msg field) ", ") t)) (defun mu4e~view-construct-flags-tags-header (field val) "Construct a Flags: header." (mu4e~view-construct-header field (mapconcat (lambda (flag) (propertize (if (symbolp flag) (symbol-name flag) flag) 'face 'mu4e-special-header-value-face)) val (propertize ", " 'face 'mu4e-header-value-face)) t)) (defun mu4e~view-construct-signature-header (msg) "Construct a Signature: header, if there are any signed parts." (let* ((parts (mu4e-message-field msg :parts)) (verdicts (remove-if 'null (mapcar (lambda (part) (mu4e-message-part-field part :signature)) parts))) (val (when verdicts (mapconcat (lambda (v) (propertize (symbol-name v) 'face (if (eq v 'verified) 'mu4e-ok-face 'mu4e-warning-face))) verdicts ", "))) (btn (when val (with-temp-buffer (insert-text-button "Details" 'action (lambda (b) (mu4e-view-verify-msg-popup (button-get b 'msg)))) (buffer-string)))) (val (when val (concat val " (" btn ")")))) (mu4e~view-construct-header :signature val t))) (defun mu4e~view-construct-decryption-header (msg) "Construct a Decryption: header, if there are any encrypted parts." (let* ((parts (mu4e-message-field msg :parts)) (verdicts (remove-if 'null (mapcar (lambda (part) (mu4e-message-part-field part :decryption)) parts))) (succeeded (remove-if (lambda (v) (eq v 'failed)) verdicts)) (failed (remove-if (lambda (v) (eq v 'succeeded)) verdicts)) (succ (when succeeded (propertize (concat (number-to-string (length succeeded)) " part(s) decrypted") 'face 'mu4e-ok-face))) (fail (when failed (propertize (concat (number-to-string (length failed)) " part(s) failed") 'face 'mu4e-warning-face))) (val (concat succ fail))) (mu4e~view-construct-header :decryption val t))) (defun mu4e~view-open-attach-from-binding () "Open the attachement at point, or click location." (interactive) (let* (( msg (mu4e~view-get-property-from-event 'mu4e-msg)) ( attnum (mu4e~view-get-property-from-event 'mu4e-attnum))) (when (and msg attnum) (mu4e-view-open-attachment msg attnum)))) (defun mu4e~view-save-attach-from-binding () "Save the attachement at point, or click location." (interactive) (let* (( msg (mu4e~view-get-property-from-event 'mu4e-msg)) ( attnum (mu4e~view-get-property-from-event 'mu4e-attnum))) (when (and msg attnum) (mu4e-view-save-attachment-single msg attnum)))) (defun mu4e~view-construct-attachments-header (msg) "Display attachment information; the field looks like something like: :parts ((:index 1 :name \"1.part\" :mime-type \"text/plain\" :type (leaf) :attachment nil :size 228) (:index 2 :name \"analysis.doc\" :mime-type \"application/msword\" :type (leaf attachment) :attachment nil :size 605196))" (setq mu4e~view-attach-map ;; buffer local (make-hash-table :size 64 :weakness nil)) (let* ((id 0) (attachments ;; we only list parts that look like attachments, ie. that have a ;; non-nil :attachment property; we record a mapping between ;; user-visible numbers and the part indices (remove-if-not (lambda (part) (let* ((mtype (or (mu4e-message-part-field part :mime-type) "application/octet-stream")) (attachtype (mu4e-message-part-field part :type)) (isattach (or ;; we consider parts marked either ;; "attachment" or "inline" as attachment. (member 'attachment attachtype) ;; list inline parts as attachment (so they can be ;; saved), unless they are text/plain, which are ;; usually just message footers in mailing lists (and (member 'inline attachtype) (not (string-match "^text/plain" mtype)))))) (or ;; remove if it's not an attach *or* if it's an ;; image/audio/application type (but not a signature) isattach (string-match "^\\(image\\|audio\\)" mtype) (string= "message/rfc822" mtype) (string= "text/calendar" mtype) (and (string-match "^application" mtype) (not (string-match "signature" mtype)))))) (mu4e-message-field msg :parts))) (attstr (mapconcat (lambda (part) (let ((index (mu4e-message-part-field part :index)) (name (mu4e-message-part-field part :name)) (size (mu4e-message-part-field part :size))) (incf id) (puthash id index mu4e~view-attach-map) (concat (propertize (format "[%d]" id) 'face 'mu4e-attach-number-face) (propertize name 'face 'mu4e-link-face 'keymap mu4e-view-attachments-header-keymap 'mouse-face 'highlight 'help-echo (concat "[mouse-1] or [M-RET] opens the attachment\n" "[mouse-2] or [S-RET] offers to save it") 'mu4e-msg msg 'mu4e-attnum id ) (when (and size (> size 0)) (propertize (format "(%s)" (mu4e-display-size size)) 'face 'mu4e-header-key-face))))) attachments ", "))) (when attachments (mu4e~view-construct-header :attachments attstr t)))) (defun mu4e-view-for-each-part (msg func) "Apply FUNC to each part in MSG. FUNC should be a function taking two arguments: 1. the message MSG, and 2. a plist describing the attachment. The plist looks like: (:index 1 :name \"test123.doc\" :mime-type \"application/msword\" :attachment t :size 1234)." (dolist (part (mu4e-msg-field msg :parts)) (funcall func msg part))) (defvar mu4e-view-mode-map nil "Keymap for \"*mu4e-view*\" buffers.") (unless mu4e-view-mode-map (setq mu4e-view-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "C-S-u") 'mu4e-update-mail-and-index) (define-key map (kbd "C-c C-u") 'mu4e-update-mail-and-index) (define-key map "q" 'mu4e~view-quit-buffer) ;; note, 'z' is by-default bound to 'bury-buffer' ;; but that's not very useful in this case (define-key map "z" 'ignore) (define-key map "s" 'mu4e-headers-search) (define-key map "S" 'mu4e-view-search-edit) (define-key map "/" 'mu4e-view-search-narrow) (define-key map (kbd "<M-left>") 'mu4e-headers-query-prev) (define-key map (kbd "<M-right>") 'mu4e-headers-query-next) (define-key map "b" 'mu4e-headers-search-bookmark) (define-key map "B" 'mu4e-headers-search-bookmark-edit) (define-key map "%" 'mu4e-view-mark-pattern) (define-key map "t" 'mu4e-view-mark-subthread) (define-key map "T" 'mu4e-view-mark-thread) (define-key map "v" 'mu4e-view-verify-msg-popup) (define-key map "j" 'mu4e~headers-jump-to-maildir) (define-key map "g" 'mu4e-view-go-to-url) (define-key map "k" 'mu4e-view-save-url) (define-key map "f" 'mu4e-view-fetch-url) (define-key map "F" 'mu4e-compose-forward) (define-key map "R" 'mu4e-compose-reply) (define-key map "C" 'mu4e-compose-new) (define-key map "E" 'mu4e-compose-edit) (define-key map "." 'mu4e-view-raw-message) (define-key map "|" 'mu4e-view-pipe) (define-key map "a" 'mu4e-view-action) (define-key map ";" 'mu4e-context-switch) ;; toggle header settings (define-key map "O" 'mu4e-headers-change-sorting) (define-key map "P" 'mu4e-headers-toggle-threading) (define-key map "Q" 'mu4e-headers-toggle-full-search) (define-key map "W" 'mu4e-headers-toggle-include-related) ;; change the number of headers (define-key map (kbd "C-+") 'mu4e-headers-split-view-grow) (define-key map (kbd "C--") 'mu4e-headers-split-view-shrink) (define-key map (kbd "<C-kp-add>") 'mu4e-headers-split-view-grow) (define-key map (kbd "<C-kp-subtract>") 'mu4e-headers-split-view-shrink) ;; intra-message navigation (define-key map (kbd "SPC") 'mu4e-view-scroll-up-or-next) (define-key map (kbd "<home>") 'beginning-of-buffer) (define-key map (kbd "<end>") 'end-of-buffer) (define-key map (kbd "RET") 'mu4e-scroll-up) (define-key map (kbd "<backspace>") 'mu4e-scroll-down) ;; navigation between messages (define-key map "p" 'mu4e-view-headers-prev) (define-key map "n" 'mu4e-view-headers-next) ;; the same (define-key map (kbd "<M-down>") 'mu4e-view-headers-next) (define-key map (kbd "<M-up>") 'mu4e-view-headers-prev) (define-key map (kbd "[") 'mu4e-view-headers-prev-unread) (define-key map (kbd "]") 'mu4e-view-headers-next-unread) ;; switching to view mode (if it's visible) (define-key map "y" 'mu4e-select-other-view) ;; attachments (define-key map "e" 'mu4e-view-save-attachment) (define-key map "o" 'mu4e-view-open-attachment) (define-key map "A" 'mu4e-view-attachment-action) ;; marking/unmarking (define-key map "d" 'mu4e-view-mark-for-trash) (define-key map (kbd "<delete>") 'mu4e-view-mark-for-delete) (define-key map (kbd "<deletechar>") 'mu4e-view-mark-for-delete) (define-key map (kbd "D") 'mu4e-view-mark-for-delete) (define-key map (kbd "m") 'mu4e-view-mark-for-move) (define-key map (kbd "r") 'mu4e-view-mark-for-refile) (define-key map (kbd "?") 'mu4e-view-mark-for-unread) (define-key map (kbd "!") 'mu4e-view-mark-for-read) (define-key map (kbd "+") 'mu4e-view-mark-for-flag) (define-key map (kbd "-") 'mu4e-view-mark-for-unflag) (define-key map (kbd "=") 'mu4e-view-mark-for-untrash) (define-key map (kbd "&") 'mu4e-view-mark-custom) (define-key map (kbd "*") 'mu4e-view-mark-for-something) (define-key map (kbd "<kp-multiply>") 'mu4e-view-mark-for-something) (define-key map (kbd "<insert>") 'mu4e-view-mark-for-something) (define-key map (kbd "<insertchar>") 'mu4e-view-mark-for-something) (define-key map (kbd "#") 'mu4e-mark-resolve-deferred-marks) ;; misc (define-key map "w" 'visual-line-mode) (define-key map "#" 'mu4e-view-toggle-hide-cited) (define-key map "h" 'mu4e-view-toggle-html) (define-key map (kbd "M-q") 'mu4e-view-fill-long-lines) ;; next 3 only warn user when attempt in the message view (define-key map "u" 'mu4e-view-unmark) (define-key map "U" 'mu4e-view-unmark-all) (define-key map "x" 'mu4e-view-marked-execute) (define-key map "$" 'mu4e-show-log) (define-key map "H" 'mu4e-display-manual) ;; menu (define-key map [menu-bar] (make-sparse-keymap)) (let ((menumap (make-sparse-keymap "View"))) (define-key map [menu-bar headers] (cons "View" menumap)) (define-key menumap [quit-buffer] '("Quit view" . mu4e~view-quit-buffer)) (define-key menumap [display-help] '("Help" . mu4e-display-manual)) (define-key menumap [sepa0] '("--")) (define-key menumap [wrap-lines] '("Toggle wrap lines" . visual-line-mode)) (define-key menumap [toggle-html] '("Toggle view-html" . mu4e-view-toggle-html)) (define-key menumap [hide-cited] '("Toggle hide cited" . mu4e-view-toggle-hide-cited)) (define-key menumap [raw-view] '("View raw message" . mu4e-view-raw-message)) (define-key menumap [pipe] '("Pipe through shell" . mu4e-view-pipe)) ;; (define-key menumap [inspect] ;; '("Inspect with guile" . mu4e-inspect-message)) (define-key menumap [sepa8] '("--")) (define-key menumap [open-att] '("Open attachment" . mu4e-view-open-attachment)) (define-key menumap [extract-att] '("Extract attachment" . mu4e-view-save-attachment)) (define-key menumap [save-url] '("Save URL to kill-ring" . mu4e-view-save-url)) (define-key menumap [fetch-url] '("Fetch URL" . mu4e-view-fetch-url)) (define-key menumap [goto-url] '("Visit URL" . mu4e-view-go-to-url)) (define-key menumap [sepa1] '("--")) (define-key menumap [mark-delete] '("Mark for deletion" . mu4e-view-mark-for-delete)) (define-key menumap [mark-trash] '("Mark for trash" . mu4e-view-mark-for-trash)) (define-key menumap [mark-move] '("Mark for move" . mu4e-view-mark-for-move)) (define-key menumap [sepa2] '("--")) (define-key menumap [resend] '("Resend" . mu4e-compose-resend)) (define-key menumap [forward] '("Forward" . mu4e-compose-forward)) (define-key menumap [reply] '("Reply" . mu4e-compose-reply)) (define-key menumap [compose-new] '("Compose new" . mu4e-compose-new)) (define-key menumap [sepa3] '("--")) (define-key menumap [query-next] '("Next query" . mu4e-headers-query-next)) (define-key menumap [query-prev] '("Previous query" . mu4e-headers-query-prev)) (define-key menumap [narrow-search] '("Narrow search" . mu4e-headers-search-narrow)) (define-key menumap [bookmark] '("Search bookmark" . mu4e-headers-search-bookmark)) (define-key menumap [jump] '("Jump to maildir" . mu4e~headers-jump-to-maildir)) (define-key menumap [search] '("Search" . mu4e-headers-search)) (define-key menumap [sepa4] '("--")) (define-key menumap [next] '("Next" . mu4e-view-headers-next)) (define-key menumap [previous] '("Previous" . mu4e-view-headers-prev))) map))) (fset 'mu4e-view-mode-map mu4e-view-mode-map) (defcustom mu4e-view-mode-hook nil "Hook run when entering Mu4e-View mode." :options '(turn-on-visual-line-mode) :type 'hook :group 'mu4e-view) (defvar mu4e-view-mode-abbrev-table nil) (define-derived-mode mu4e-view-mode special-mode "mu4e:view" "Major mode for viewing an e-mail message in mu4e. \\{mu4e-view-mode-map}." (use-local-map mu4e-view-mode-map) (make-local-variable 'mu4e~view-headers-buffer) (make-local-variable 'mu4e~view-msg) (make-local-variable 'mu4e~view-link-map) (make-local-variable 'mu4e~view-attach-map) (make-local-variable 'mu4e~view-cited-hidden) ;; make permanent too, so they'll survive changing the mode (put 'mu4e~view-link-map 'permanent-local t) (put 'mu4e~view-attach-map 'permanent-local t) ;; show context in mode-string (make-local-variable 'global-mode-string) (add-to-list 'global-mode-string '(:eval (mu4e-context-label))) (setq buffer-undo-list t);; don't record undo info ;; autopair mode gives error when pressing RET ;; turn it off (when (boundp 'autopair-dont-activate) (setq autopair-dont-activate t))) (defun mu4e~view-mark-as-read-maybe (msg) "Clear the message MSG New/Unread status and set it to Seen. If the message is not New/Unread, do nothing. Evaluates to t if it triggers any changes, nil otherwise. If this function does any changes, it triggers a refresh." (when (and mu4e-view-auto-mark-as-read msg) (let ((flags (mu4e-message-field msg :flags)) (msgid (mu4e-message-field msg :message-id)) (docid (mu4e-message-field msg :docid))) ;; attached (embedded) messages don't have docids; leave them alone if it is a new message (when (and docid (or (member 'unread flags) (member 'new flags))) ;; mark /all/ messages with this message-id as read, so all copies of ;; this message will be marked as read. (mu4e~proc-move msgid nil "+S-u-N") t)))) (defun mu4e~view-browse-url-func (url) "Return a function that executes `browse-url' with URL. What browser is called is depending on `browse-url-browser-function' and `browse-url-mailto-function'." (save-match-data (if (string-match "^mailto:" url) (lexical-let ((url url)) (lambda () (interactive) (mu4e~compose-browse-url-mail url))) (lexical-let ((url url)) (lambda () (interactive) (browse-url url)))))) (defun mu4e~view-browse-url-from-binding (&optional url) "View in browser the url at point, or click location. If the optional argument URL is provided, browse that instead. If the url is mailto link, start writing an email to that address." (interactive) (let* (( url (or url (mu4e~view-get-property-from-event 'mu4e-url)))) (when url (if (string-match-p "^mailto:" url) (mu4e~compose-browse-url-mail url) (browse-url url))))) (defun mu4e~view-show-images-maybe (msg) "Show attached images, if `mu4e-show-images' is non-nil." (when (and (display-images-p) mu4e-view-show-images) (mu4e-view-for-each-part msg (lambda (msg part) (when (string-match "^image/" (or (mu4e-message-part-field part :mime-type) "application/object-stream")) (let ((imgfile (mu4e-message-part-field part :temp))) (when (and imgfile (file-exists-p imgfile)) (save-excursion (goto-char (point-max)) (mu4e-display-image imgfile mu4e-view-image-max-width mu4e-view-image-max-height))))))))) (defvar mu4e~view-beginning-of-url-regexp "https?\\://\\|mailto:" "Regexp that matches the beginning of http:/https:/mailto: URLs; match-string 1 will contain the matched URL, if any.") ;; this is fairly simplistic... (defun mu4e~view-make-urls-clickable () "Turn things that look like URLs into clickable things. Also number them so they can be opened using `mu4e-view-go-to-url'." (let ((num 0)) (save-excursion (setq mu4e~view-link-map ;; buffer local (make-hash-table :size 32 :weakness nil)) (goto-char (point-min)) (while (re-search-forward mu4e~view-beginning-of-url-regexp nil t) (let ((bounds (thing-at-point-bounds-of-url-at-point))) (when bounds (let* ((url (thing-at-point-url-at-point)) (ov (make-overlay (car bounds) (cdr bounds)))) (puthash (incf num) url mu4e~view-link-map) (add-text-properties (car bounds) (cdr bounds) `(face mu4e-link-face mouse-face highlight mu4e-url ,url keymap ,mu4e-view-clickable-urls-keymap help-echo "[mouse-1] or [M-RET] to open the link")) (overlay-put ov 'after-string (propertize (format "[%d]" num) 'face 'mu4e-url-number-face))))))))) (defun mu4e~view-hide-cited () "Toggle hiding of cited lines in the message body." (save-excursion (let ((inhibit-read-only t)) (goto-char (point-min)) (flush-lines mu4e-cited-regexp) (setq mu4e~view-cited-hidden t)))) (defmacro mu4e~view-in-headers-context (&rest body) "Evaluate BODY in the context of the headers buffer connected to this view." `(progn (unless (buffer-live-p mu4e~view-headers-buffer) (mu4e-error "no headers-buffer connected")) (let* ((msg (mu4e-message-at-point)) (docid (mu4e-message-field msg :docid)) (curwin (selected-window))) (unless docid (mu4e-error "message without docid: action is not possible.")) (with-current-buffer mu4e~view-headers-buffer (when (get-buffer-window) (select-window (get-buffer-window))) (if (mu4e~headers-goto-docid docid) ,@body (mu4e-error "cannot find message in headers buffer.")))))) (defun mu4e-view-headers-next (&optional n) "Move point to the next message header in the headers buffer connected with this message view. If this succeeds, return the new docid. Otherwise, return nil. Optionally, takes an integer N (prefix argument), to the Nth next header." (interactive "P") (mu4e~view-in-headers-context (mu4e~headers-move (or n 1)))) (defun mu4e-view-headers-prev (&optional n) "Move point to the previous message header in the headers buffer connected with this message view. If this succeeds, return the new docid. Otherwise, return nil. Optionally, takes an integer N (prefix argument), to the Nth previous header." (interactive "P") (mu4e~view-in-headers-context (mu4e~headers-move (- (or n 1))))) (defun mu4e~view-prev-or-next-unread (backwards) "Move point to the next or previous (when BACKWARDS is non-`nil') unread message header in the headers buffer connected with this message view. If this succeeds, return the new docid. Otherwise, return nil." (mu4e~view-in-headers-context (mu4e~headers-prev-or-next-unread backwards)) (mu4e-select-other-view) (mu4e-headers-view-message)) (defun mu4e-view-headers-prev-unread () "Move point to the previous unread message header in the headers buffer connected with this message view. If this succeeds, return the new docid. Otherwise, return nil." (interactive) (mu4e~view-prev-or-next-unread t)) (defun mu4e-view-headers-next-unread () "Move point to the next unread message header in the headers buffer connected with this message view. If this succeeds, return the new docid. Otherwise, return nil." (interactive) (mu4e~view-prev-or-next-unread nil)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Interactive functions (defun mu4e-view-toggle-hide-cited () "Toggle hiding of cited lines in the message body." (interactive) (if mu4e~view-cited-hidden (mu4e-view-refresh) (mu4e~view-hide-cited))) (defvar mu4e~view-html-text nil "Should we prefer html or text just this once? A symbol `text' or `html' or nil.") (defun mu4e-view-toggle-html () "Toggle html-display of the message body (if any)." (interactive) (setq mu4e~view-html-text (if mu4e~message-body-html 'text 'html)) (mu4e-view-refresh)) (defun mu4e-view-refresh () "Redisplay the current message." (interactive) (mu4e-view mu4e~view-msg mu4e~view-headers-buffer) (setq mu4e~view-cited-hidden nil)) (defun mu4e-view-action (&optional msg) "Ask user for some action to apply on MSG, then do it. If MSG is nil apply action to message returned bymessage-at-point. The actions are specified in `mu4e-view-actions'." (interactive) (let* ((msg (or msg (mu4e-message-at-point))) (actionfunc (mu4e-read-option "Action: " mu4e-view-actions))) (funcall actionfunc msg))) (defun mu4e-view-mark-pattern () "Ask user for a kind of mark (move, delete etc.), a field to match and a regular expression to match with. Then, mark all matching messages with that mark." (interactive) (mu4e~view-in-headers-context (mu4e-headers-mark-pattern))) (defun mu4e-view-mark-thread (&optional markpair) "Ask user for a kind of mark (move, delete etc.), and apply it to all messages in the thread at point in the headers view. The optional MARKPAIR can also be used to provide the mark selection." (interactive) (mu4e~view-in-headers-context (if markpair (mu4e-headers-mark-thread nil markpair) (call-interactively 'mu4e-headers-mark-thread)))) (defun mu4e-view-mark-subthread (&optional markpair) "Ask user for a kind of mark (move, delete etc.), and apply it to all messages in the subthread at point in the headers view. The optional MARKPAIR can also be used to provide the mark selection." (interactive) (mu4e~view-in-headers-context (if markpair (mu4e-headers-mark-subthread markpair) (mu4e-headers-mark-subthread)))) (defun mu4e-view-search-narrow () "Run `mu4e-headers-search-narrow' in the headers buffer." (interactive) (mu4e~view-in-headers-context (call-interactively 'mu4e-headers-search-narrow))) (defun mu4e-view-search-edit () "Run `mu4e-headers-search-edit' in the headers buffer." (interactive) (mu4e~view-in-headers-context (mu4e-headers-search-edit))) (defun mu4e-mark-region-code () "Highlight region marked with `message-mark-inserted-region'. Add this function to `mu4e-view-mode-hook' to enable this feature." (require 'message) (let (beg end ov-beg ov-end ov-inv) (save-excursion (goto-char (point-min)) (while (re-search-forward (concat "^" message-mark-insert-begin) nil t) (setq ov-beg (match-beginning 0) ov-end (match-end 0) ov-inv (make-overlay ov-beg ov-end) beg ov-end) (overlay-put ov-inv 'invisible t) (when (re-search-forward (concat "^" message-mark-insert-end) nil t) (setq ov-beg (match-beginning 0) ov-end (match-end 0) ov-inv (make-overlay ov-beg ov-end) end ov-beg) (overlay-put ov-inv 'invisible t)) (when (and beg end) (let ((ov (make-overlay beg end))) (overlay-put ov 'face 'mu4e-region-code)) (setq beg nil end nil)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Wash functions (defun mu4e-view-fill-long-lines () "Fill lines that are wider than the window width or `fill-column'." (interactive) (with-current-buffer mu4e~view-buffer (save-excursion (let ((inhibit-read-only t) (width (window-width (get-buffer-window (current-buffer))))) (save-restriction (message-goto-body) (while (not (eobp)) (end-of-line) (when (>= (current-column) (min fill-column width)) (narrow-to-region (min (1+ (point)) (point-max)) (point-at-bol)) (let ((goback (point-marker))) (fill-paragraph nil) (goto-char (marker-position goback))) (widen)) (forward-line 1))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; attachment handling (defun mu4e~view-get-attach-num (prompt msg &optional multi) "Ask the user with PROMPT for an attachment number for MSG, and ensure it is valid. The number is [1..n] for attachments \[0..(n-1)] in the message. If MULTI is nil, return the number for the attachment; otherwise (MULTI is non-nil), accept ranges of attachment numbers, as per `mu4e-split-ranges-to-numbers', and return the corresponding string." (let* ((count (hash-table-count mu4e~view-attach-map)) (def)) (when (zerop count) (mu4e-error "No attachments for this message")) (if (not multi) (if (= count 1) (read-number (mu4e-format "%s: " prompt) 1) (read-number (mu4e-format "%s (1-%d): " prompt count))) (progn (setq def (if (= count 1) "1" (format "1-%d" count))) (read-string (mu4e-format "%s (default %s): " prompt def) nil nil def))))) (defun mu4e~view-get-attach (msg attnum) "Return the attachment plist in MSG corresponding to attachment number ATTNUM." (let* ((partid (gethash attnum mu4e~view-attach-map)) (attach (find-if (lambda (part) (eq (mu4e-message-part-field part :index) partid)) (mu4e-message-field msg :parts)))) (or attach (mu4e-error "Not a valid attachment")))) (defun mu4e~view-request-attachment-path (fname path) "Ask the user where to save FNAME (default is PATH/FNAME)." (let ((fpath (expand-file-name (read-file-name (mu4e-format "Save as ") path nil nil fname) path))) (if (file-directory-p fpath) (expand-file-name fname fpath) fpath))) (defun mu4e~view-request-attachments-dir (path) "Ask the user where to save multiple attachments (default is PATH)." (let ((fpath (expand-file-name (read-directory-name (mu4e-format "Save in directory ") path nil nil nil) path))) (if (file-directory-p fpath) fpath))) (defun mu4e-view-save-attachment-single (&optional msg attnum) "Save attachment number ATTNUM from MSG. If MSG is nil use the message returned by `message-at-point'. If ATTNUM is nil ask for the attachment number." (interactive) (let* ((msg (or msg (mu4e-message-at-point))) (attnum (or attnum (mu4e~view-get-attach-num "Attachment to save" msg))) (att (mu4e~view-get-attach msg attnum)) (fname (plist-get att :name)) (mtype (plist-get att :mime-type)) (path (concat (mu4e~get-attachment-dir fname mtype) "/")) (index (plist-get att :index)) (retry t) (fpath)) (while retry (setq fpath (mu4e~view-request-attachment-path fname path)) (setq retry (and (file-exists-p fpath) (not (y-or-n-p (mu4e-format "Overwrite '%s'?" fpath)))))) (mu4e~proc-extract 'save (mu4e-message-field msg :docid) index mu4e-decryption-policy fpath))) (defun mu4e-view-save-attachment-multi (&optional msg) "Offer to save multiple email attachments from the current message. Default is to save all messages, [1..n], where n is the number of attachments. You can type multiple values separated by space, e.g. 1 3-6 8 will save attachments 1,3,4,5,6 and 8. Furthermore, there is a shortcut \"a\" which so means all attachments, but as this is the default, you may not need it." (interactive) (let* ((msg (or msg (mu4e-message-at-point))) (attachstr (mu4e~view-get-attach-num "Attachment number range (or 'a' for 'all')" msg t)) (count (hash-table-count mu4e~view-attach-map)) (attachnums (mu4e-split-ranges-to-numbers attachstr count))) (if mu4e-save-multiple-attachments-without-asking (let* ((path (concat (mu4e~get-attachment-dir) "/")) (attachdir (mu4e~view-request-attachments-dir path))) (dolist (num attachnums) (let* ((att (mu4e~view-get-attach msg num)) (fname (plist-get att :name)) (index (plist-get att :index)) (retry t) fpath) (while retry (setq fpath (expand-file-name (concat attachdir fname) path)) (setq retry (and (file-exists-p fpath) (not (y-or-n-p (mu4e-format "Overwrite '%s'?" fpath)))))) (mu4e~proc-extract 'save (mu4e-message-field msg :docid) index mu4e-decryption-policy fpath)))) (dolist (num attachnums) (mu4e-view-save-attachment-single msg num))))) (defun mu4e-view-save-attachment (&optional multi) "Offer to save attachment(s). If MULTI (prefix-argument) is nil, save a single one, otherwise, offer to save a range of attachments." (interactive "P") (if multi (mu4e-view-save-attachment-multi) (mu4e-view-save-attachment-single))) (defun mu4e-view-open-attachment (&optional msg attnum) "Open attachment number ATTNUM from MSG. If MSG is nil use the message returned by `message-at-point'. If ATTNUM is nil ask for the attachment number." (interactive) (let* ((msg (or msg (mu4e-message-at-point))) (attnum (or attnum (mu4e~view-get-attach-num "Attachment to open" msg))) (att (or (mu4e~view-get-attach msg attnum))) (index (plist-get att :index)) (docid (mu4e-message-field msg :docid)) (mimetype (plist-get att :mime-type))) (if (and mimetype (string= mimetype "message/rfc822")) ;; special handling for message-attachments; we open them in mu4e. we also ;; send the docid as parameter (4th arg); we'll get this back from the ;; server, and use it to determine the parent message (ie., the current ;; message) when showing the embedded message/rfc822, and return to the ;; current message when quitting that one. (mu4e~view-temp-action docid index "mu4e" docid) ;; otherwise, open with the default program (handled in mu-server (mu4e~proc-extract 'open docid index mu4e-decryption-policy)))) (defun mu4e~view-temp-action (docid index what &optional param) "Open attachment INDEX for message with DOCID, and invoke ACTION." (interactive) (mu4e~proc-extract 'temp docid index mu4e-decryption-policy nil what param )) (defvar mu4e~view-open-with-hist nil "History list for the open-with argument.") (defun mu4e-view-open-attachment-with (msg attachnum &optional cmd) "Open MSG's attachment ATTACHNUM with CMD. If CMD is nil, ask user for it." (let* ((att (mu4e~view-get-attach msg attachnum)) (cmd (or cmd (read-string (mu4e-format "Shell command to open it with: ") nil 'mu4e~view-open-with-hist))) (index (plist-get att :index))) (mu4e~view-temp-action (mu4e-message-field msg :docid) index "open-with" cmd))) (defvar mu4e~view-pipe-hist nil "History list for the pipe argument.") (defun mu4e-view-pipe-attachment (msg attachnum &optional pipecmd) "Feed MSG's attachment ATTACHNUM through pipe PIPECMD. If PIPECMD is nil, ask user for it." (let* ((att (mu4e~view-get-attach msg attachnum)) (pipecmd (or pipecmd (read-string (mu4e-format "Pipe: ") nil 'mu4e~view-pipe-hist))) (index (plist-get att :index))) (mu4e~view-temp-action (mu4e-message-field msg :docid) index "pipe" pipecmd))) (defun mu4e-view-open-attachment-emacs (msg attachnum) "Open MSG's attachment ATTACHNUM in the current emacs instance." (let* ((att (mu4e~view-get-attach msg attachnum)) (index (plist-get att :index))) (mu4e~view-temp-action (mu4e-message-field msg :docid) index "emacs"))) (defun mu4e-view-import-attachment-diary (msg attachnum) "Open MSG's attachment ATTACHNUM in the current emacs instance." (interactive) (let* ((att (mu4e~view-get-attach msg attachnum)) (index (plist-get att :index))) (mu4e~view-temp-action (mu4e-message-field msg :docid) index "diary"))) (defun mu4e-view-attachment-action (&optional msg) "Ask user what to do with attachments in MSG If MSG is nil use the message returned by `message-at-point'. The actions are specified in `mu4e-view-attachment-actions'." (interactive) (let* ((msg (or msg (mu4e-message-at-point))) (actionfunc (mu4e-read-option "Action on attachment: " mu4e-view-attachment-actions)) (attnum (mu4e~view-get-attach-num "Which attachment" msg))) (when (and actionfunc attnum) (funcall actionfunc msg attnum)))) ;; handler-function to handle the response we get from the server when we ;; want to do something with one of the attachments. (defun mu4e~view-temp-handler (path what docid param) "Handler function for doing things with temp files (ie., attachments) in response to a (mu4e~proc-extract 'temp ... )." (cond ((string= what "open-with") ;; 'param' will be the program to open-with (start-process "*mu4e-open-with-proc*" "*mu4e-open-with*" param path)) ((string= what "pipe") ;; 'param' will be the pipe command, path the infile for this (mu4e-process-file-through-pipe path param)) ;; if it's mu4e, it's some embedded message; 'param' may contain the docid ;; of the parent message. ((string= what "mu4e") ;; remember the mapping path->docid, which maps the path of the embedded ;; message to the docid of its parent (puthash path docid mu4e~path-parent-docid-map) (mu4e~proc-view-path path mu4e-view-show-images mu4e-decryption-policy)) ((string= what "emacs") (find-file path) ;; make the buffer read-only since it usually does not make ;; sense to edit the temp buffer; use C-x C-q if you insist... (setq buffer-read-only t)) ((string= what "diary") (icalendar-import-file path diary-file)) (t (mu4e-error "Unsupported action %S" what)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-view-mark-custom () "Run some custom mark function." (mu4e~view-in-headers-context (mu4e-headers-mark-custom))) (defun mu4e~view-split-view-p () "Return t if we're in split-view, nil otherwise." (member mu4e-split-view '(horizontal vertical))) (defun mu4e-view-scroll-up-or-next () "Scroll-up the current message. If `mu4e-view-scroll-to-next' is non-nil, and we can't scroll-up anymore, go the next message." (interactive) (condition-case nil (scroll-up) (error (when mu4e-view-scroll-to-next (mu4e-view-headers-next))))) (defun mu4e-scroll-up () "Scroll text of selected window up one line." (interactive) (scroll-up 1)) (defun mu4e-scroll-down () "Scroll text of selected window down one line." (interactive) (scroll-down 1)) (defun mu4e-view-unmark-all () "If we're in split-view, unmark all messages. Otherwise, warn user that unmarking only works in the header list." (interactive) (if (mu4e~view-split-view-p) (mu4e~view-in-headers-context (mu4e-mark-unmark-all)) (mu4e-message "Unmarking needs to be done in the header list view"))) (defun mu4e-view-unmark () "If we're in split-view, unmark message at point. Otherwise, warn user that unmarking only works in the header list." (interactive) (if (mu4e~view-split-view-p) (mu4e-view-mark-for-unmark) (mu4e-message "Unmarking needs to be done in the header list view"))) (defmacro mu4e~view-defun-mark-for (mark) "Define a function mu4e-view-mark-for-MARK." (let ((funcname (intern (format "mu4e-view-mark-for-%s" mark))) (docstring (format "Mark the current message for %s." mark))) `(progn (defun ,funcname () ,docstring (interactive) (mu4e~view-in-headers-context (mu4e-headers-mark-and-next ',mark))) (put ',funcname 'definition-name ',mark)))) (mu4e~view-defun-mark-for move) (mu4e~view-defun-mark-for trash) (mu4e~view-defun-mark-for refile) (mu4e~view-defun-mark-for delete) (mu4e~view-defun-mark-for flag) (mu4e~view-defun-mark-for unflag) (mu4e~view-defun-mark-for unmark) (mu4e~view-defun-mark-for something) (mu4e~view-defun-mark-for read) (mu4e~view-defun-mark-for unread) (defun mu4e-view-marked-execute () "Execute the marks." (interactive) (mu4e~view-in-headers-context (mu4e-mark-execute-all))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; URL handling (defun mu4e~view-get-urls-num (prompt &optional multi) "Ask the user with PROMPT for an URL number for MSG, and ensure it is valid. The number is [1..n] for URLs \[0..(n-1)] in the message. If MULTI is nil, return the number for the URL; otherwise (MULTI is non-nil), accept ranges of URL numbers, as per `mu4e-split-ranges-to-numbers', and return the corresponding string." (let* ((count (hash-table-count mu4e~view-link-map)) (def)) (when (zerop count) (mu4e-error "No links for this message")) (if (not multi) (if (= count 1) (read-number (mu4e-format "%s: " prompt) 1) (read-number (mu4e-format "%s (1-%d): " prompt count))) (progn (setq def (if (= count 1) "1" (format "1-%d" count))) (read-string (mu4e-format "%s (default %s): " prompt def) nil nil def))))) (defun mu4e-view-go-to-url (&optional multi) "Offer to go to url(s). If MULTI (prefix-argument) is nil, go to a single one, otherwise, offer to go to a range of urls." (interactive "P") (mu4e~view-handle-urls "URL to visit" multi (lambda (url) (mu4e~view-browse-url-from-binding url)))) (defun mu4e-view-save-url (&optional multi) "Offer to save urls(s) to the kill-ring. If MULTI (prefix-argument) is nil, save a single one, otherwise, offer to save a range of URLs." (interactive "P") (mu4e~view-handle-urls "URL to save" multi (lambda (url) (kill-new url) (mu4e-message "Saved %s to the kill-ring" url)))) (defun mu4e-view-fetch-url (&optional multi) "Offer to fetch (download) urls(s). If MULTI (prefix-argument) is nil, download a single one, otherwise, offer to fetch a range of URLs. The urls are fetched to `mu4e-attachment-dir'." (interactive "P") (mu4e~view-handle-urls "URL to fetch" multi (lambda (url) (let ((target (concat (mu4e~get-attachment-dir url) "/" (file-name-nondirectory url)))) (url-copy-file url target) (mu4e-message "Fetched %s -> %s" url target))))) (defun mu4e~view-handle-urls (prompt multi urlfunc) "If MULTI is nil, apply URLFUNC to a single uri, otherwise, apply it to a range of uris. PROMPT is the query to present to the user." (interactive "P") (if multi (mu4e~view-handle-multi-urls prompt urlfunc) (mu4e~view-handle-single-url prompt urlfunc))) (defun mu4e~view-handle-single-url (prompt urlfunc &optional num) "Apply URLFUNC to url NUM in the current message, prompting the user with PROMPT." (interactive) (let* ((num (or num (mu4e~view-get-urls-num prompt))) (url (gethash num mu4e~view-link-map))) (unless url (mu4e-warn "Invalid number for URL")) (funcall urlfunc url))) (defun mu4e~view-handle-multi-urls (prompt urlfunc) "Apply URLFUNC to a a range of urls in the current message, prompting the user with PROMPT. Default is to aplly it to all URLs, [1..n], where n is the number of urls. You can type multiple values separated by space, e.g. 1 3-6 8 will visit urls 1,3,4,5,6 and 8. Furthermore, there is a shortcut \"a\" which means all urls, but as this is the default, you may not need it." (interactive) (let* ((linkstr (mu4e~view-get-urls-num "URL number range (or 'a' for 'all')" t)) (count (hash-table-count mu4e~view-link-map)) (linknums (mu4e-split-ranges-to-numbers linkstr count))) (dolist (num linknums) (mu4e~view-handle-single-url prompt urlfunc num)))) (defun mu4e-view-for-each-uri (func) "Execute FUNC (which receives a uri) for each uri in the current message." (maphash (lambda (num uri) (funcall func uri)) mu4e~view-link-map)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defconst mu4e~view-raw-buffer-name " *mu4e-raw-view*" "*internal* Name for the raw message view buffer") (defun mu4e-view-raw-message () "Display the raw contents of message at point in a new buffer." (interactive) (let ((path (mu4e-message-field-at-point :path)) (buf (get-buffer-create mu4e~view-raw-buffer-name))) (unless (and path (file-readable-p path)) (mu4e-error "Not a readable file: %S" path)) (with-current-buffer buf (let ((inhibit-read-only t)) (erase-buffer) (insert-file-contents path) (view-mode) (goto-char (point-min)))) (switch-to-buffer buf))) (defun mu4e-view-pipe (cmd) "Pipe the message at point through shell command CMD, and display the results." (interactive "sShell command: ") (let ((path (mu4e-message-field (mu4e-message-at-point) :path))) (mu4e-process-file-through-pipe path cmd))) (defconst mu4e~verify-buffer-name " *mu4e-verify*") (defun mu4e-view-verify-msg-popup (&optional msg) "Pop-up a little signature verification window for (optional) MSG or message-at-point." (interactive) (let* ((msg (or msg (mu4e-message-at-point))) (path (mu4e-message-field msg :path)) (cmd (format "%s verify --verbose %s %s" mu4e-mu-binary (shell-quote-argument path) (if mu4e-decryption-policy "--decrypt --use-agent" ""))) (output (shell-command-to-string cmd)) ;; create a new one (buf (get-buffer-create mu4e~verify-buffer-name)) (win (or (get-buffer-window buf) (split-window-vertically (- (window-height) 6))))) (with-selected-window win (let ((inhibit-read-only t)) ;; (set-window-dedicated-p win t) (switch-to-buffer buf) (erase-buffer) (insert output) (goto-char (point-min)) (local-set-key "q" 'kill-buffer-and-window)) (setq buffer-read-only t)) (select-window win))) (defun mu4e~view-quit-buffer () "Quit the mu4e-view buffer. This is a rather complex function, to ensure we don't disturb other windows." (interactive) (unless (eq major-mode 'mu4e-view-mode) (mu4e-error "Must be in mu4e-view-mode (%S)" major-mode)) (let ((curbuf (current-buffer)) (curwin (selected-window)) (headers-win)) (walk-windows (lambda (win) ;; check whether the headers buffer window is visible (when (eq mu4e~view-headers-buffer (window-buffer win)) (setq headers-win win)) ;; and kill any _other_ (non-selected) window that shows the current ;; buffer (when (and (eq curbuf (window-buffer win)) ;; does win show curbuf? (not (eq curwin win)) ;; but it's not the curwin? (not (one-window-p))) ;; and not the last one on the frame? (delete-window win)))) ;; delete it! ;; now, all *other* windows should be gone. ;; if the headers view is also visible, kill ourselves + window; otherwise ;; switch to the headers view (if (window-live-p headers-win) ;; headers are visible (progn (kill-buffer-and-window) ;; kill the view win (setq mu4e~headers-view-win nil) (select-window headers-win)) ;; and switch to the headers win... ;; headers are not visible... (progn (kill-buffer) (setq mu4e~headers-view-win nil) (when (buffer-live-p mu4e~view-headers-buffer) (switch-to-buffer mu4e~view-headers-buffer)))))) (provide 'mu4e-view) ;; end of mu4e-view ������������������������������������������������������������mu-0.9.18/mu4e/mu4e-utils.el������������������������������������������������������������������������0000644�0001750�0001750�00000140237�13020504332�012356� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e-utils.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Copyright (C) 2013 Tibor Simko ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; Utility functions used in the mu4e ;;; Code: (eval-when-compile (byte-compile-disable-warning 'cl-functions)) (require 'cl) (eval-when-compile (require 'org nil 'noerror)) (require 'mu4e-vars) (require 'mu4e-meta) (require 'mu4e-lists) (require 'doc-view) ;; keep the byte-compiler happy (declare-function mu4e~proc-mkdir "mu4e-proc") (declare-function mu4e~proc-ping "mu4e-proc") (declare-function mu4e~proc-contacts "mu4e-proc") (declare-function mu4e~proc-kill "mu4e-proc") (declare-function mu4e~proc-index "mu4e-proc") (declare-function mu4e~proc-add "mu4e-proc") (declare-function mu4e~proc-mkdir "mu4e-proc") (declare-function mu4e~proc-running-p "mu4e-proc") (declare-function mu4e~context-autoswitch "mu4e-context") (declare-function mu4e-context-determine "mu4e-context") (declare-function mu4e-context-vars "mu4e-context") (declare-function show-all "org") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; the following is taken from org.el; we copy it here since we don't want to ;; depend on org-mode directly (it causes byte-compilation errors) TODO: a ;; cleaner solution.... (defconst mu4e~ts-regexp0 (concat "\\(\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)" "\\( +[^]+0-9>\r\n -]+\\)?\\( +\\([0-9]\\{1,2\\}\\):" "\\([0-9]\\{2\\}\\)\\)?\\)") "Regular expression matching time strings for analysis. This one does not require the space after the date, so it can be used on a string that terminates immediately after the date.") (defun mu4e-parse-time-string (s &optional nodefault) "Parse the standard Org-mode time string. This should be a lot faster than the normal `parse-time-string'. If time is not given, defaults to 0:00. However, with optional NODEFAULT, hour and minute fields will be nil if not given." (if (string-match mu4e~ts-regexp0 s) (list 0 (if (or (match-beginning 8) (not nodefault)) (string-to-number (or (match-string 8 s) "0"))) (if (or (match-beginning 7) (not nodefault)) (string-to-number (or (match-string 7 s) "0"))) (string-to-number (match-string 4 s)) (string-to-number (match-string 3 s)) (string-to-number (match-string 2 s)) nil nil nil) (mu4e-error "Not a standard mu4e time string: %s" s))) (defun mu4e-user-mail-address-p (addr) "If ADDR is one of user's e-mail addresses return t, nil otherwise. User's addresses are set in `mu4e-user-mail-address-list'. Case insensitive comparison is used." (when (and addr mu4e-user-mail-address-list (find addr mu4e-user-mail-address-list :test (lambda (s1 s2) (eq t (compare-strings s1 nil nil s2 nil nil t))))) t)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro with~mu4e-context-vars (context &rest body) "Evaluate BODY, with variables let-bound for CONTEXT (if any). `funcall'." (declare (indent 2)) `(let* ((vars (and ,context (mu4e-context-vars ,context)))) (progv ;; XXX: perhaps use eval's lexical environment instead of progv? (mapcar (lambda(cell) (car cell)) vars) (mapcar (lambda(cell) (cdr cell)) vars) (eval ,@body)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; the standard folders can be functions too (defun mu4e~get-folder (foldervar msg) "Within the mu-context of MSG, get message folder FOLDERVAR. If FOLDER is a string, return it, if it is a function, evaluate this function with MSG as parameter (which may be `nil'), and return the result." (unless (member foldervar '(mu4e-sent-folder mu4e-drafts-folder mu4e-trash-folder mu4e-refile-folder)) (mu4e-error "Folder must be one of mu4e-(sent|drafts|trash|refile)-folder")) ;; get the value with the vars for the relevants context let-bound (with~mu4e-context-vars (mu4e-context-determine msg nil) (let* ((folder (symbol-value foldervar)) (val (cond ((stringp folder) folder) ((functionp folder) (funcall folder msg)) (t (mu4e-error "unsupported type for %S" folder))))) (or val (mu4e-error "%S evaluates to nil" foldervar))))) (defun mu4e-get-drafts-folder (&optional msg) "Get the sent folder. See `mu4e-drafts-folder'." (mu4e~get-folder 'mu4e-drafts-folder msg)) (defun mu4e-get-refile-folder (&optional msg) "Get the folder for refiling. See `mu4e-refile-folder'." (mu4e~get-folder 'mu4e-refile-folder msg)) (defun mu4e-get-sent-folder (&optional msg) "Get the sent folder. See `mu4e-sent-folder'." (mu4e~get-folder 'mu4e-sent-folder msg)) (defun mu4e-get-trash-folder (&optional msg) "Get the sent folder. See `mu4e-trash-folder'." (mu4e~get-folder 'mu4e-trash-folder msg)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-remove-file-later (filename) "Remove FILENAME in a few seconds." (lexical-let ((filename filename)) (run-at-time "10 sec" nil (lambda () (ignore-errors (delete-file filename)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-make-temp-file (ext) "Create a temporary file with extension EXT. The file will self-destruct in a few seconds, enough to open it in another program." (let ((tmpfile (make-temp-file "mu4e-" nil (concat "." ext)))) (mu4e-remove-file-later tmpfile) tmpfile)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; mu4e-attachment-dir is either a string or a function that takes a filename ;; and the mime-type as argument, either (or both) which can be nil (defun mu4e~get-attachment-dir (&optional fname mimetype) "Get the directory for saving attachments from `mu4e-attachment-dir' (which can be either a string or a function, see its docstring)." (let ((dir (cond ((stringp mu4e-attachment-dir) mu4e-attachment-dir) ((functionp mu4e-attachment-dir) (funcall mu4e-attachment-dir fname mimetype)) (t (mu4e-error "unsupported type for mu4e-attachment-dir" ))))) (if dir (expand-file-name dir) (mu4e-error (mu4e-error "mu4e-attachment-dir evaluates to nil"))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~guess-maildir (path) "Guess the maildir for some path, or nil if cannot find it." (let ((idx (string-match mu4e-maildir path))) (when (and idx (zerop idx)) (replace-regexp-in-string mu4e-maildir "" (expand-file-name (concat path "/../..")))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-create-maildir-maybe (dir) "Offer to create maildir DIR if it does not exist yet. Return t if the dir already existed, or an attempt has been made to create it -- we cannot be sure creation succeeded here, since this is done asynchronously. Otherwise, return nil. NOte, DIR has to be an absolute path." (if (and (file-exists-p dir) (not (file-directory-p dir))) (mu4e-error "%s exists, but is not a directory." dir)) (cond ((file-directory-p dir) t) ((yes-or-no-p (mu4e-format "%s does not exist yet. Create now?" dir)) (mu4e~proc-mkdir dir) t) (t nil))) (defun mu4e-format (frm &rest args) "Create [mu4e]-prefixed string based on format FRM and ARGS." (concat "[" (propertize "mu4e" 'face 'mu4e-title-face) "] " (apply 'format frm args))) (defun mu4e-message (frm &rest args) "Like `message', but prefixed with mu4e. If we're waiting for user-input or if there's some message in the echo area, don't show anything." (unless (or (active-minibuffer-window)) (message "%s" (apply 'mu4e-format frm args)))) (defun mu4e-index-message (frm &rest args) "Like `mu4e-message', but specifically for index-messages. Doesn't display anything if `mu4e-hide-index-messages' is non-nil. " (unless mu4e-hide-index-messages (apply 'mu4e-message frm args))) (defun mu4e-error (frm &rest args) "Create [mu4e]-prefixed error based on format FRM and ARGS. Does a local-exit and does not return, and raises a debuggable (backtrace) error." (mu4e-log 'error (apply 'mu4e-format frm args)) (error "%s" (apply 'mu4e-format frm args))) ;; the user-error function is only available in emacs-trunk (unless (fboundp 'user-error) (defalias 'user-error 'error)) (defun mu4e-warn (frm &rest args) "Create [mu4e]-prefixed warning based on format FRM and ARGS. Does a local-exit and does not return. In emacs versions below 24.2, the functions is the same as `mu4e-error'." (mu4e-log 'error (apply 'mu4e-format frm args)) (user-error "%s" (apply 'mu4e-format frm args))) (defun mu4e~read-char-choice (prompt choices) "Read and return one of CHOICES, prompting for PROMPT. Any input that is not one of CHOICES is ignored. This mu4e's version of `read-char-choice', that becomes case-insentive after trying an exact match." (let ((choice) (chosen) (inhibit-quit nil)) (while (not chosen) (message nil);; this seems needed... (setq choice (read-char-exclusive prompt)) (setq chosen (or (member choice choices) (member (downcase choice) choices) (member (upcase choice) choices)))) (car chosen))) (defun mu4e-read-option (prompt options) "Ask user for an option from a list on the input area. PROMPT describes a multiple-choice question to the user. OPTIONS describe the options, and is a list of cells describing particular options. Cells have the following structure: (OPTIONSTRING . RESULT) where OPTIONSTRING is a non-empty string describing the option. The first character of OPTIONSTRING is used as the shortcut, and obviously all shortcuts must be different, so you can prefix the string with an uniquifying character. The options are provided as a list for the user to choose from; user can then choose by typing CHAR. Example: (mu4e-read-option \"Choose an animal: \" '((\"Monkey\" . monkey) (\"Gnu\" . gnu) (\"xMoose\" . moose))) User now will be presented with a list: \"Choose an animal: [M]onkey, [G]nu, [x]Moose\". Function will return the cdr of the list element." (let* ((prompt (mu4e-format "%s" prompt)) (chosen) (optionsstr (mapconcat (lambda (option) ;; try to detect old-style options, and warn (when (characterp (car-safe (cdr-safe option))) (mu4e-error (concat "Please use the new format for options/actions; " "see the manual"))) (let* ((kar (substring (car option) 0 1)) (val (cdr option))) (concat "[" (propertize kar 'face 'mu4e-highlight-face) "]" (substring (car option) 1)))) options ", ")) (response (mu4e~read-char-choice (concat prompt optionsstr " [" (propertize "C-g" 'face 'mu4e-highlight-face) " to cancel]") ;; the allowable chars (map 'list (lambda(elm) (string-to-char (car elm))) options))) (chosen (find-if (lambda (option) (eq response (string-to-char (car option)))) options))) (if chosen (cdr chosen) (mu4e-warn "Unknown shortcut '%c'" response)))) (defun mu4e~get-maildirs-1 (path mdir) "Get maildirs under path, recursively, as a list of relative paths." (let ((dirs) (dentries (ignore-errors (directory-files-and-attributes (concat path mdir) nil "^[^.]\\|\\.[^.][^.]" t)))) (dolist (dentry dentries) (when (and (booleanp (cadr dentry)) (cadr dentry)) (if (file-accessible-directory-p (concat mu4e-maildir "/" mdir "/" (car dentry) "/cur")) (setq dirs (cons (concat mdir (car dentry)) dirs))) (unless (member (car dentry) '("cur" "new" "tmp")) (setq dirs (append dirs (mu4e~get-maildirs-1 path (concat mdir (car dentry) "/"))))))) dirs)) (defvar mu4e-cache-maildir-list nil "Whether to cache the list of maildirs; set it to t if you find that generating the list on the fly is too slow. If you do, you can set `mu4e-maildir-list' to nil to force regenerating the cache the next time `mu4e-get-maildirs' gets called.") (defvar mu4e-maildir-list nil "Cached list of maildirs.") (defun mu4e-get-maildirs () "Get maildirs under `mu4e-maildir', recursively, as a list of relative paths (ie., /archive, /sent etc.). Most of the work is done in `mu4e-get-maildirs-1'. Note, these results are /cached/, so the list of maildirs will not change until you restart mu4e." (unless mu4e-maildir (mu4e-error "`mu4e-maildir' is not defined")) (unless (and mu4e-maildir-list mu4e-cache-maildir-list) (setq mu4e-maildir-list (sort (append (when (file-accessible-directory-p (concat mu4e-maildir "/cur")) '("/")) (mu4e~get-maildirs-1 mu4e-maildir "/")) (lambda (s1 s2) (string< (downcase s1) (downcase s2)))))) mu4e-maildir-list) (defun mu4e-ask-maildir (prompt) "Ask the user for a shortcut (using PROMPT) as defined in `mu4e-maildir-shortcuts', then return the corresponding folder name. If the special shortcut 'o' (for _o_ther) is used, or if `mu4e-maildir-shortcuts' is not defined, let user choose from all maildirs under `mu4e-maildir'." (let ((prompt (mu4e-format "%s" prompt))) (if (not mu4e-maildir-shortcuts) (funcall mu4e-completing-read-function prompt (mu4e-get-maildirs)) (let* ((mlist (append mu4e-maildir-shortcuts '(("ther" . ?o)))) (fnames (mapconcat (lambda (item) (concat "[" (propertize (make-string 1 (cdr item)) 'face 'mu4e-highlight-face) "]" (car item))) mlist ", ")) (kar (read-char (concat prompt fnames)))) (if (member kar '(?/ ?o)) ;; user chose 'other'? (funcall mu4e-completing-read-function prompt (mu4e-get-maildirs) nil nil "/") (or (car-safe (find-if (lambda (item) (= kar (cdr item))) mu4e-maildir-shortcuts)) (mu4e-warn "Unknown shortcut '%c'" kar))))))) (defun mu4e-ask-maildir-check-exists (prompt) "Like `mu4e-ask-maildir', but check for existence of the maildir, and offer to create it if it does not exist yet." (let* ((mdir (mu4e-ask-maildir prompt)) (fullpath (concat mu4e-maildir mdir))) (unless (file-directory-p fullpath) (and (yes-or-no-p (mu4e-format "%s does not exist. Create now?" fullpath)) (mu4e~proc-mkdir fullpath))) mdir)) (defstruct mu4e-bookmark "A mu4e bookmarl object with the following members: - `name': the user-visible name of the bookmark - `key': a single key to search for this bookmark - `query': the query for this bookmark. Either a literal string or a function that evaluates to a string." name ;; name/description of the bookmark query ;; a query (a string or a function evaluation to string) key ;; key to activate the bookmark ) (defun mu4e-bookmarks () "Get `mu4e-bookmarks' in the (new) format, converting from the old format if needed." (map 'list (lambda (item) (if (mu4e-bookmark-p item) item ;; already in the right format (if (and (listp item) (= (length item) 3)) (make-mu4e-bookmark :name (nth 1 item) :query (nth 0 item) :key (nth 2 item)) (mu4e-error "Invalid bookmark in mu4e-bookmarks")))) mu4e-bookmarks)) (defun mu4e-ask-bookmark (prompt &optional kar) "Ask the user for a bookmark (using PROMPT) as defined in `mu4e-bookmarks', then return the corresponding query." (unless (mu4e-bookmarks) (mu4e-error "No bookmarks defined")) (let* ((prompt (mu4e-format "%s" prompt)) (bmarks (mapconcat (lambda (bm) (concat "[" (propertize (make-string 1 (mu4e-bookmark-key bm)) 'face 'mu4e-highlight-face) "]" (mu4e-bookmark-name bm))) (mu4e-bookmarks) ", ")) (kar (read-char (concat prompt bmarks)))) (mu4e-get-bookmark-query kar))) (defun mu4e-get-bookmark-query (kar) "Get the corresponding bookmarked query for shortcut character KAR, or raise an error if none is found." (let* ((chosen-bm (or (find-if (lambda (bm) (= kar (mu4e-bookmark-key bm))) (mu4e-bookmarks)) (mu4e-warn "Unknown shortcut '%c'" kar))) (expr (mu4e-bookmark-query chosen-bm)) (query (eval expr))) (if (stringp query) query (mu4e-warn "Expression must evaluate to query string ('%S')" expr)))) (defun mu4e-bookmark-define (query name key) "Define a bookmark for QUERY with name NAME and shortcut-character KEY in the list of `mu4e-bookmarks'. This replaces any existing bookmark with KEY." (setq mu4e-bookmarks (remove-if (lambda (bm) (= (mu4e-bookmark-key bm) key)) (mu4e-bookmarks))) (add-to-list 'mu4e-bookmarks (make-mu4e-bookmark :name name :query query :key key) t)) ;;; converting flags->string and vice-versa ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~flags-to-string-raw (flags) "Convert a list of flags into a string as seen in Maildir message files; flags are symbols draft, flagged, new, passed, replied, seen, trashed and the string is the concatenation of the uppercased first letters of these flags, as per [1]. Other flags than the ones listed here are ignored. Also see `mu4e-flags-to-string'. \[1\]: http://cr.yp.to/proto/maildir.html" (when flags (let ((kar (case (car flags) ('draft ?D) ('flagged ?F) ('new ?N) ('passed ?P) ('replied ?R) ('seen ?S) ('trashed ?T) ('attach ?a) ('encrypted ?x) ('signed ?s) ('unread ?u)))) (concat (and kar (string kar)) (mu4e~flags-to-string-raw (cdr flags)))))) (defun mu4e-flags-to-string (flags) "Remove duplicates and sort the output of `mu4e~flags-to-string-raw'." (concat (sort (remove-duplicates (append (mu4e~flags-to-string-raw flags) nil)) '>))) (defun mu4e~string-to-flags-1 (str) "Convert a string with message flags as seen in Maildir messages into a list of flags in; flags are symbols draft, flagged, new, passed, replied, seen, trashed and the string is the concatenation of the uppercased first letters of these flags, as per [1]. Other letters than the ones listed here are ignored. Also see `mu4e-flags-to-string'. \[1\]: http://cr.yp.to/proto/maildir.html." (when (/= 0 (length str)) (let ((flag (case (string-to-char str) (?D 'draft) (?F 'flagged) (?P 'passed) (?R 'replied) (?S 'seen) (?T 'trashed)))) (append (when flag (list flag)) (mu4e~string-to-flags-1 (substring str 1)))))) (defun mu4e-string-to-flags (str) "Convert a string with message flags as seen in Maildir messages into a list of flags in; flags are symbols draft, flagged, new, passed, replied, seen, trashed and the string is the concatenation of the uppercased first letters of these flags, as per [1]. Other letters than the ones listed here are ignored. Also see `mu4e-flags-to-string'. \[1\]: http://cr.yp.to/proto/maildir.html " ;; "Remove duplicates from the output of `mu4e~string-to-flags-1'" (remove-duplicates (mu4e~string-to-flags-1 str))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-display-size (size) "Get a string representation of SIZE (in bytes)." (cond ((>= size 1000000) (format "%2.1fM" (/ size 1000000.0))) ((and (>= size 1000) (< size 1000000)) (format "%2.1fK" (/ size 1000.0))) ((< size 1000) (format "%d" size)) (t (propertize "?" 'face 'mu4e-system-face)))) (defun mu4e-display-manual () "Display the mu4e manual page for the current mode. Or go to the top level if there is none." (interactive) (info (case major-mode ('mu4e-main-mode "(mu4e)Main view") ('mu4e-headers-mode "(mu4e)Headers view") ('mu4e-view-mode "(mu4e)Message view") (t "mu4e")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-last-query () "Get the most recent query or nil if there is none." (when (buffer-live-p mu4e~headers-buffer) (with-current-buffer mu4e~headers-buffer mu4e~headers-last-query))) (defun mu4e-select-other-view () "When the headers view is selected, select the message view (if that has a live window), and vice versa." (interactive) (let* ((other-buf (cond ((eq major-mode 'mu4e-headers-mode) mu4e~view-buffer) ((eq major-mode 'mu4e-view-mode) mu4e~headers-buffer))) (other-win (and other-buf (get-buffer-window other-buf)))) (if (window-live-p other-win) (select-window other-win) (mu4e-message "No window to switch to")))) (defconst mu4e-output-buffer-name "*mu4e-output*" "*internal* Name of the mu4e output buffer.") (defun mu4e-process-file-through-pipe (path pipecmd) "Process file at PATH through a pipe with PIPECMD." (let ((buf (get-buffer-create mu4e-output-buffer-name))) (with-current-buffer buf (let ((inhibit-read-only t)) (erase-buffer) (call-process-shell-command pipecmd path t t) (view-mode))) (switch-to-buffer buf))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e~lists-hash nil "Hashtable of mailing-list-id => shortname, based on `mu4e~mailing-lists' and `mu4e-user-mailing-lists'.") (defun mu4e-get-mailing-list-shortname (list-id) "Get the shortname for a mailing-list with list-id LIST-ID. based on `mu4e~mailing-lists', `mu4e-user-mailing-lists', and `mu4e-mailing-list-patterns'." (unless mu4e~lists-hash (setq mu4e~lists-hash (make-hash-table :test 'equal)) (dolist (cell mu4e~mailing-lists) (puthash (car cell) (cdr cell) mu4e~lists-hash)) (dolist (cell mu4e-user-mailing-lists) (puthash (car cell) (cdr cell) mu4e~lists-hash))) (or (gethash list-id mu4e~lists-hash) (and (boundp 'mu4e-mailing-list-patterns) (cl-member-if (lambda (pattern) (string-match pattern list-id)) mu4e-mailing-list-patterns) (match-string 1 list-id)) ;; if it's not in the db, take the part until the first dot if there is one; ;; otherwise just return the whole thing (if (string-match "\\([^.]*\\)\\." list-id) (match-string 1 list-id) list-id))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-index-updated-hook nil "Hook run when the indexing process had one or more updated messages. This can be used as a simple way to invoke some action when new messages appear, but note that an update in the index does not necessarily mean a new message.") (defvar mu4e-msg-changed-hook nil "Hook run when there is a message changed in db. For new messages, it depends on `mu4e-index-updated-hook'. This can be used as a simple way to invoke some action when a message changed.") ;; some handler functions for server messages ;; (defun mu4e-info-handler (info) "Handler function for (:info ...) sexps received from the server process." (let ((type (plist-get info :info))) (cond ((eq type 'add) t) ;; do nothing ((eq type 'index) (if (eq (plist-get info :status) 'running) (mu4e-index-message "Indexing... processed %d, updated %d" (plist-get info :processed) (plist-get info :updated)) (progn (mu4e-index-message "Indexing completed; processed %d, updated %d, cleaned-up %d" (plist-get info :processed) (plist-get info :updated) (plist-get info :cleaned-up)) (unless (zerop (plist-get info :updated)) (run-hooks 'mu4e-index-updated-hook))))) ((plist-get info :message) (mu4e-index-message "%s" (plist-get info :message)))))) (defun mu4e-error-handler (errcode errmsg) "Handler function for showing an error." ;; don't use mu4e-error here; it's running in the process filter context (case errcode (4 (user-error "No matches for this search query.")) (t (error "Error %d: %s" errcode errmsg)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; RFC2822 handling of phrases in mail-addresses ;;; The optional display-name contains a phrase, it sits before the angle-addr ;;; as specified in RFC2822 for email-addresses in header fields. ;;; contributed by jhelberg (defun mu4e~rfc822-phrase-type (ph) "Return either atom, quoted-string, a corner-case or nil. This checks for empty string first. Then quotes around the phrase (returning 'rfc822-quoted-string). Then whether there is a quote inside the phrase (returning 'rfc822-containing-quote). The reverse of the RFC atext definition is then tested. If it matches, nil is returned, if not, it is an 'rfc822-atom, which is returned." (cond ((= (length ph) 0) 'rfc822-empty) ((= (aref ph 0) ?\") (if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph) 'rfc822-quoted-string 'rfc822-containing-quote)) ; starts with quote, but doesn't end with one ((string-match-p "[\"]" ph) 'rfc822-containing-quote) ((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil) (t 'rfc822-atom))) (defun mu4e~rfc822-quoteit (ph) "Quote RFC822 phrase only if necessary. Atoms and quoted strings don't need quotes. The rest do. In case a phrase contains a quote, it will be escaped." (let ((type (mu4e~rfc822-phrase-type ph))) (cond ((eq type 'rfc822-atom) ph) ((eq type 'rfc822-quoted-string) ph) ((eq type 'rfc822-containing-quote) (format "\"%s\"" (replace-regexp-in-string "\"" "\\\\\"" ph))) (t (format "\"%s\"" ph))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defsubst mu4e~process-contact (contact) "Process CONTACT, and either return nil when it should not be included, or (rfc822-string . CONTACT) otherwise." (when mu4e-contact-rewrite-function (setq contact (funcall mu4e-contact-rewrite-function contact))) (when contact (let ((name (plist-get contact :name)) (mail (plist-get contact :mail)) (ignore-rx (or mu4e-compose-complete-ignore-address-regexp "$^"))) (when (and mail (not (string-match ignore-rx mail))) (cons (if name (format "%s <%s>" (mu4e~rfc822-quoteit name) mail) mail) contact))))) (defun mu4e~sort-contacts (contacts) "Destructively sort contacts (only for cycling) in order of 'mostly likely contact'.t See the code for the detail" (let* ((now (+ (float-time) 3600)) ;; allow for clock diffs (recent (- (float-time) (* 15 24 3600)))) (sort* contacts (lambda (c1 c2) (let* ( (c1 (cdr c1)) (c2 (cdr c2)) (personal1 (plist-get c1 :personal)) (personal2 (plist-get c2 :personal)) ;; note: freq, tstamp can only be missing if the rewrite ;; function removed them. If the rewrite function changed the ;; contact somehow, we guess it's important. (freq1 (or (plist-get c1 :freq) 500)) (freq2 (or (plist-get c2 :freq) 500)) (tstamp1 (or (plist-get c1 :tstamp) now)) (tstamp2 (or (plist-get c2 :tstamp) now))) ;; only one is personal? if so, that one comes first (if (not (equal personal1 personal2)) (if personal1 t nil) ;; only one is recent? that one comes first (if (not (equal (> tstamp1 recent) (> tstamp2 recent))) (> tstamp1 tstamp2) ;; otherwise, use the frequency (> freq1 freq2)))))))) (defun mu4e~sort-contacts-for-completion (contacts) "Takes CONTACTS, which is a list of RFC-822 addresses, and sort them based on the ranking in `mu4e~contacts.'" (sort* contacts (lambda (c1 c2) (let ((rank1 (gethash c1 mu4e~contacts)) (rank2 (gethash c2 mu4e~contacts))) (< rank1 rank2))))) ;; start and stopping (defun mu4e~fill-contacts (contact-data) "We receive a list of contacts, which each contact of the form (:me NAME :mail EMAIL :tstamp TIMESTAMP :freq FREQUENCY) and fill the hash `mu4e~contacts-for-completion' with it, with each contact mapped to an integer for their ranking. This is used by the completion function in mu4e-compose." (let ((contacts) (rank 0)) (dolist (contact contact-data) (let ((contact-maybe (mu4e~process-contact contact))) ;; note, this gives cells (rfc822-address . contact) (when contact-maybe (push contact-maybe contacts)))) (setq contacts (mu4e~sort-contacts contacts)) ;; now, we have our nicely sorted list, map them to a list ;; of increasing integers. We use that map in the composer ;; to sort them there. It would have been so much easier if emacs ;; allowed us to use the sorted-list as-is, but no such luck. (setq mu4e~contacts (make-hash-table :test 'equal :weakness nil :size (length contacts))) (dolist (contact contacts) (puthash (car contact) rank mu4e~contacts) (incf rank)) (mu4e-index-message "Contacts received: %d" (hash-table-count mu4e~contacts)))) (defun mu4e~check-requirements () "Check for the settings required for running mu4e." (unless (>= emacs-major-version 23) (mu4e-error "Emacs >= 23.x is required for mu4e")) (when mu4e~server-props (let ((version (plist-get mu4e~server-props :version))) (unless (string= version mu4e-mu-version) (mu4e-error "mu server has version %s, but we need %s" version mu4e-mu-version)))) (unless (and mu4e-mu-binary (file-executable-p mu4e-mu-binary)) (mu4e-error "Please set `mu4e-mu-binary' to the full path to the mu binary.")) (unless mu4e-maildir (mu4e-error "Please set `mu4e-maildir' to the full path to your Maildir directory.")) ;; expand mu4e-maildir, mu4e-attachment-dir (setq mu4e-maildir (expand-file-name mu4e-maildir)) (unless (mu4e-create-maildir-maybe mu4e-maildir) (mu4e-error "%s is not a valid maildir directory" mu4e-maildir)) (dolist (var '(mu4e-sent-folder mu4e-drafts-folder mu4e-trash-folder)) (unless (and (boundp var) (symbol-value var)) (mu4e-error "Please set %S" var)) (unless (functionp (symbol-value var)) ;; functions are okay, too (let* ((dir (symbol-value var)) (path (concat mu4e-maildir dir))) (unless (string= (substring dir 0 1) "/") (mu4e-error "%S must start with a '/'" dir)) (unless (mu4e-create-maildir-maybe path) (mu4e-error "%s (%S) does not exist" path var)))))) (defun mu4e-running-p () "Whether mu4e is running. Checks whether the server process is live." (mu4e~proc-running-p)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; starting / getting mail / updating the index ;; ;; (defvar mu4e~update-timer nil "The mu4e update timer.") (defconst mu4e~update-name "*mu4e-update*" "Name of the process and buffer to update mail.") (defconst mu4e~update-buffer-height 8 "Height of the mu4e message retrieval/update buffer.") (defvar mu4e~get-mail-ask-password "mu4e get-mail: Enter password: " "Query string for `mu4e-get-mail-command' password.") (defvar mu4e~get-mail-password-regexp "^Remote: Enter password: $" "Regexp to match a password query in the `mu4e-get-mail-command' output.") (defun mu4e~request-contacts () "If `mu4e-compose-complete-addresses' is non-nil, get/update the list of contacts we use for autocompletion; otherwise, do nothing." (when mu4e-compose-complete-addresses (setq mu4e-contacts-func 'mu4e~fill-contacts) (mu4e~proc-contacts mu4e-compose-complete-only-personal (when mu4e-compose-complete-only-after (float-time (apply 'encode-time (mu4e-parse-time-string mu4e-compose-complete-only-after))))))) (defun mu4e~start (&optional func) "If `mu4e-contexts' have been defined, but we don't have a context yet, switch to the matching one, or none matches, the first. If mu4e is already running, execute function FUNC (if non-nil). Otherwise, check various requirements, then start mu4e. When successful, call FUNC (if non-nil) afterwards." ;; if we're already running, simply go to the main view (if (mu4e-running-p) ;; already running? (when func (funcall func)) ;; yes! run func if defined (progn ;; no! try to set a context, do some checks, set up pong handler and ping ;; the server maybe switch the context (mu4e~context-autoswitch nil mu4e-context-policy) (lexical-let ((func func)) (mu4e~check-requirements) ;; set up the 'pong' handler func (setq mu4e-pong-func (lambda (props) (setq mu4e~server-props props) ;; save props from the server (let ((version (plist-get props :version)) (doccount (plist-get props :doccount))) (mu4e~check-requirements) (when func (funcall func)) (when (and mu4e-update-interval (null mu4e~update-timer)) (setq mu4e~update-timer (run-at-time 0 mu4e-update-interval (lambda () (mu4e-update-mail-and-index mu4e-index-update-in-background))))) (mu4e-message "Started mu4e with %d message%s in store" doccount (if (= doccount 1) "" "s")))))) ;; wake up server (mu4e~proc-ping) ;; maybe request the list of contacts, automatically refresh after ;; reindexing (mu4e~request-contacts) (add-hook 'mu4e-index-updated-hook 'mu4e~request-contacts)))) (defun mu4e-clear-caches () "Clear any cached resources." (setq mu4e-maildir-list nil mu4e~contacts nil)) (defun mu4e~stop () "Stop the mu4e session." (when mu4e~update-timer (cancel-timer mu4e~update-timer) (setq mu4e~update-timer nil)) (mu4e-clear-caches) (mu4e~proc-kill) ;; kill all main/view/headers buffer (mapcar (lambda (buf) (with-current-buffer buf (when (member major-mode '(mu4e-headers-mode mu4e-view-mode mu4e-main-mode)) (kill-buffer)))) (buffer-list))) (defvar mu4e~progress-reporter nil "Internal, the progress reporter object.") (defun mu4e~get-mail-process-filter (proc msg) "Filter the output of `mu4e-get-mail-command'. Currently the filter only checks if the command asks for a password by matching the output against `mu4e~get-mail-password-regexp'. The messages are inserted into the process buffer. Also scrolls to the final line, and update the progress throbber." (when mu4e~progress-reporter (progress-reporter-update mu4e~progress-reporter)) (when (string-match mu4e~get-mail-password-regexp msg) (if (process-get proc 'x-interactive) (process-send-string proc (concat (read-passwd mu4e~get-mail-ask-password) "\n")) ;; TODO kill process? (mu4e-error "Unrecognized password request"))) (when (process-buffer proc) (let ((inhibit-read-only t) (procwin (get-buffer-window (process-buffer proc)))) ;; Insert at end of buffer. Leave point alone. (with-current-buffer (process-buffer proc) (goto-char (point-max)) (if (string-match ".*\r\\(.*\\)" msg) (progn ;; kill even with \r (end-of-line) (let ((end (point))) (beginning-of-line) (delete-region (point) end)) (insert (match-string 1 msg))) (insert msg))) ;; Auto-scroll unless user is interacting with the window. (when (and (window-live-p procwin) (not (eq (selected-window) procwin))) (with-selected-window procwin (goto-char (point-max))))))) (defun mu4e-update-index () "Update the mu4e index." (interactive) (unless mu4e-maildir (mu4e-error "`mu4e-maildir' is not defined")) (mu4e~proc-index mu4e-maildir mu4e-user-mail-address-list mu4e-index-cleanup mu4e-index-lazy-check)) (defvar mu4e~update-buffer nil "Internal, store the buffer of the update process when updating.") (define-derived-mode mu4e~update-mail-mode special-mode "mu4e:update" "Major mode used for retrieving new e-mail messages in `mu4e'.") (define-key mu4e~update-mail-mode-map (kbd "q") 'mu4e-interrupt-update-mail) (defun mu4e~temp-window (buf height) "Create a temporary window with HEIGHT at the bottom of the frame to display buffer BUF." (let ((win (split-window (frame-root-window) (- (window-height (frame-root-window)) height)))) (set-window-buffer win buf) (set-window-dedicated-p win t) win)) (defun mu4e~update-sentinel-func (proc msg) "Sentinel function for the update process." (when mu4e~progress-reporter (progress-reporter-done mu4e~progress-reporter) (setq mu4e~progress-reporter nil)) (let* ((status (process-status proc)) (code (process-exit-status proc)) (maybe-error (or (not (eq status 'exit)) (/= code 0))) (buf (and (buffer-live-p mu4e~update-buffer) mu4e~update-buffer)) (win (and buf (get-buffer-window buf)))) (unless mu4e-hide-index-messages (message nil)) (if maybe-error (progn (when mu4e-index-update-error-warning (mu4e-message "Update process returned with non-zero exit code") (sit-for 5)) (when mu4e-index-update-error-continue (mu4e-update-index))) (mu4e-update-index)) (if (window-live-p win) (with-selected-window win (kill-buffer-and-window)) (when (buffer-live-p buf) (kill-buffer buf))))) ;; complicated function, as it: ;; - needs to check for errors ;; - (optionally) pop-up a window ;; - (optionally) check password requests (defun mu4e~update-mail-and-index-real (run-in-background) "Get a new mail by running `mu4e-get-mail-command'. If RUN-IN-BACKGROUND is non-nil (or called with prefix-argument), run in the background; otherwise, pop up a window." (let* ((process-connection-type t) (proc (start-process-shell-command "mu4e-update" " *mu4e-update*" mu4e-get-mail-command)) (buf (process-buffer proc)) (win (or run-in-background (mu4e~temp-window buf mu4e~update-buffer-height)))) (setq mu4e~update-buffer buf) (when (window-live-p win) (with-selected-window win ;; ;;(switch-to-buffer buf) ;; (set-window-dedicated-p win t) (erase-buffer) (insert "\n") ;; FIXME -- needed so output start (mu4e~update-mail-mode))) (setq mu4e~progress-reporter (unless mu4e-hide-index-messages (make-progress-reporter (mu4e-format "Retrieving mail...")))) (set-process-sentinel proc 'mu4e~update-sentinel-func) ;; if we're running in the foreground, handle password requests (unless run-in-background (process-put proc 'x-interactive (not run-in-background)) (set-process-filter proc 'mu4e~get-mail-process-filter)))) (defun mu4e-update-mail-and-index (run-in-background) "Get a new mail by running `mu4e-get-mail-command'. If run-in-background is non-nil (or called with prefix-argument), run in the background; otherwise, pop up a window." (interactive "P") (unless mu4e-get-mail-command (mu4e-error "`mu4e-get-mail-command' is not defined")) (if (and (buffer-live-p mu4e~update-buffer) (process-live-p (get-buffer-process mu4e~update-buffer))) (mu4e-message "Update process is already running") (progn (run-hooks 'mu4e-update-pre-hook) (mu4e~update-mail-and-index-real run-in-background)))) (defun mu4e-interrupt-update-mail () "Stop the update process by sending SIGINT to it." (interactive) (let* ((proc (and (buffer-live-p mu4e~update-buffer) (get-buffer-process mu4e~update-buffer)))) (when (process-live-p proc) (interrupt-process proc t)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; logging / debugging (defvar mu4e~log-max-lines 1200 "*internal* Last <n> number of lines to keep around in the buffer.") (defconst mu4e~log-buffer-name "*mu4e-log*" "*internal* Name of the logging buffer.") (defun mu4e-log (type frm &rest args) "Write a message of TYPE with format-string FRM and ARGS in *mu4e-log* buffer, if the variable mu4e-debug is non-nil. Type is either 'to-server, 'from-server or 'misc. This function is meant for debugging." (when mu4e-debug (with-current-buffer (get-buffer-create mu4e~log-buffer-name) (view-mode) (setq buffer-undo-list t) (let* ((inhibit-read-only t) (tstamp (propertize (format-time-string "%Y-%m-%d %T" (current-time)) 'face 'font-lock-string-face)) (msg-face (case type (from-server 'font-lock-type-face) (to-server 'font-lock-function-name-face) (misc 'font-lock-variable-name-face) (error 'font-lock-warning-face) (otherwise (mu4e-error "Unsupported log type")))) (msg (propertize (apply 'format frm args) 'face msg-face))) (goto-char (point-max)) (insert tstamp (case type (from-server " <- ") (to-server " -> ") (error " !! ") (otherwise " ")) msg "\n") ;; if `mu4e-log-max-lines is specified and exceeded, clearest the oldest ;; lines (when (numberp mu4e~log-max-lines) (let ((lines (count-lines (point-min) (point-max)))) (when (> lines mu4e~log-max-lines) (goto-char (point-max)) (forward-line (- mu4e~log-max-lines lines)) (beginning-of-line) (delete-region (point-min) (point))))))))) (defun mu4e-toggle-logging () "Toggle between enabling/disabling debug-mode (in debug-mode, mu4e logs some of its internal workings to a log-buffer. See `mu4e-visit-log'." (interactive) (mu4e-log 'misc "logging disabled") (setq mu4e-debug (not mu4e-debug)) (mu4e-message "debug logging has been %s" (if mu4e-debug "enabled" "disabled")) (mu4e-log 'misc "logging enabled")) (defun mu4e-show-log () "Visit the mu4e debug log." (interactive) (let ((buf (get-buffer mu4e~log-buffer-name))) (unless (buffer-live-p buf) (mu4e-warn "No debug log available")) (switch-to-buffer buf))) (defun mu4e-split-ranges-to-numbers (str n) "Convert STR containing attachment numbers into a list of numbers. STR is a string; N is the highest possible number in the list. This includes expanding e.g. 3-5 into 3,4,5. If the letter \"a\" ('all')) is given, that is expanded to a list with numbers [1..n]." (let ((str-split (split-string str)) beg end list) (dolist (elem str-split list) ;; special number "a" converts into all attachments 1-N. (when (equal elem "a") (setq elem (concat "1-" (int-to-string n)))) (if (string-match "\\([0-9]+\\)-\\([0-9]+\\)" elem) ;; we have found a range A-B, which needs converting ;; into the numbers A, A+1, A+2, ... B. (progn (setq beg (string-to-number (match-string 1 elem)) end (string-to-number (match-string 2 elem))) (while (<= beg end) (add-to-list 'list beg 'append) (setq beg (1+ beg)))) ;; else just a number (add-to-list 'list (string-to-number elem) 'append))) ;; Check that all numbers are valid. (mapc #'(lambda (x) (cond ((> x n) (mu4e-warn "Attachment %d bigger than maximum (%d)" x n)) ((< x 1) (mu4e-warn "Attachment number must be greater than 0 (%d)" x)))) list))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar mu4e-imagemagick-identify "identify" "Name/path of the Imagemagick 'identify' program.") (defun mu4e-display-image (imgpath &optional maxwidth maxheight) "Display image IMG at point; optionally specify MAXWIDTH and MAXHEIGHT. Function tries to use imagemagick if available (ie., emacs was compiled with inmagemagick support); otherwise MAXWIDTH and MAXHEIGHT are ignored." (let* ((have-im (and (fboundp 'imagemagick-types) (imagemagick-types))) ;; hmm, should check for specific type (identify (and have-im maxwidth (executable-find mu4e-imagemagick-identify))) (props (and identify (shell-command-to-string (format "%s -format '%%w' %s" identify (shell-quote-argument imgpath))))) (width (and props (string-to-number props))) (img (if have-im (if (> (or width 0) (or maxwidth 0)) (create-image imgpath 'imagemagick nil :width maxwidth) (create-image imgpath 'imagemagick)) (create-image imgpath)))) (when img (save-excursion (insert "\n") (let ((size (image-size img))) ;; inspired by gnus.. (insert-char ?\n (max 0 (round (- (window-height) (or maxheight (cdr size)) 1) 2))) (insert-char ?\. (max 0 (round (- (window-width) (or maxwidth (car size))) 2))) (insert-image img)))))) (defun mu4e-hide-other-mu4e-buffers () "Bury mu4e-buffers (main, headers, view) (and delete all windows displaying it). Do _not_ bury the current buffer, though." (interactive) (let ((curbuf (current-buffer))) ;; note: 'walk-windows' does not seem to work correctly when modifying ;; windows; therefore, the doloops here (dolist (frame (frame-list)) (dolist (win (window-list frame nil)) (with-current-buffer (window-buffer win) (unless (eq curbuf (current-buffer)) (when (member major-mode '(mu4e-headers-mode mu4e-view-mode)) (when (eq t (window-deletable-p win)) (delete-window win))))))) t)) (defun mu4e-get-time-date (prompt) "Determine the emacs time value for the time/date entered by user after PROMPT. Formats are all that are accepted by `parse-time-string'." (let ((timestr (read-string (mu4e-format "%s" prompt)))) (apply 'encode-time (mu4e-parse-time-string timestr)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-derived-mode mu4e-org-mode org-mode "mu4e:org" "Major mode for mu4e documents, derived from `org-mode'.") (defun mu4e-info (path) "Show a buffer with the information (an org-file) at PATH." (interactive) (unless (file-exists-p path) (mu4e-error "Cannot find %s" path)) (lexical-let ((curbuf (current-buffer))) (find-file path) (mu4e-org-mode) (setq buffer-read-only t) (define-key mu4e-org-mode-map (kbd "q") (lambda () (interactive) (bury-buffer) (switch-to-buffer curbuf))))) (defun mu4e-about () "Show the mu4e 'about' page." (interactive) (mu4e-info (concat mu4e-doc-dir "/mu4e-about.org"))) (defun mu4e-news () "Show the mu4e 'about' page." (interactive) (mu4e-info (concat mu4e-doc-dir "/NEWS.org"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-refresh-message (path maildir) "Re-parse message at PATH and MAILDIR; if this works, we will receive (:info add :path <path> :docid <docid>) as well as (:update <msg-sexp>)." (mu4e~proc-add path maildir)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~fontify-cited () "Colorize message content based on the citation level. This is used in the view and compose modes." (save-excursion (goto-char (point-min)) (when (search-forward-regexp "^\n" nil t) ;; search the first empty line (while (re-search-forward mu4e-cited-regexp nil t) (let* ((level (string-width (replace-regexp-in-string "[^>]" "" (match-string 0)))) (face (unless (zerop level) (intern-soft (format "mu4e-cited-%d-face" level))))) (when face (add-text-properties (line-beginning-position 1) (line-end-position 1) `(face ,face)))))))) (defun mu4e~fontify-signature () "Give the message signatures a distinctive color. This is used in the view and compose modes." (let ((inhibit-read-only t)) (save-excursion ;; give the footer a different color... (goto-char (point-min)) (let ((p (search-forward "^-- *$" nil t))) (when p (add-text-properties p (point-max) '(face mu4e-footer-face))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~quote-for-modeline (str) "Quote a string to be used literally in the modeline." (replace-regexp-in-string "%" "%%" str t t)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~active-composition-buffers () "Return all active mu4e composition buffers" (let (buffers) (save-excursion (dolist (buffer (buffer-list t)) (set-buffer buffer) (when (eq major-mode 'mu4e-compose-mode) (push (buffer-name buffer) buffers)))) (nreverse buffers))) (provide 'mu4e-utils) ;;; End of mu4e-utils.el �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/org-mu4e.el��������������������������������������������������������������������������0000644�0001750�0001750�00000030443�13020504332�012002� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; org-mu4e -- Support for links to mu4e messages/queries from within ;;; org-mode, and for writing message in org-mode, sending them as ;;; rich-text ;; ;; Copyright (C) 2012-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Keywords: outlines, hypermedia, calendar, mail ;; Version: 0.0 ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of 1the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;;; Code: ;; The expect version here is org 8.x ;; the 'noerror is just to make sure bytecompilations does not break... ;; FIXME: find a better solution (require 'org nil 'noerror) (require 'org-exp nil 'noerror) (eval-when-compile (require 'cl)) (require 'mu4e) (defgroup org-mu4e nil "Settings for the org-mode related functionality in mu4e." :group 'mu4e :group 'org) (defvar org-mu4e-link-query-in-headers-mode nil "If non-nil, `org-store-link' in `mu4e-headers-mode' links to the the current query; otherwise, it links to the message at point.") (defcustom org-mu4e-link-desc-func (lambda (msg) (or (plist-get msg :subject) "No subject")) "Function that takes a msg and returns a string for the description part of an org-mode link. Example usage: (defun my-link-descr (msg) (let ((subject (or (plist-get msg :subject) \"No subject\")) (date (or (format-time-string mu4e-headers-date-format (mu4e-msg-field msg :date)) \"No date\"))) (concat subject \" \" date))) (setq org-mu4e-link-desc-func 'my-link-descr)" :type 'function :group 'org-mu4e) (defun org-mu4e-store-link () "Store a link to a mu4e query or message." (when (member major-mode '(mu4e-headers-mode mu4e-view-mode)) (if (and (eq major-mode 'mu4e-headers-mode) org-mu4e-link-query-in-headers-mode) ;; storing links to queries (let* ((query (mu4e-last-query)) desc link) (org-store-link-props :type "mu4e" :query query) (setq desc (concat "mu4e:query:" query) link desc) (org-add-link-props :link link :description desc) link) ;; storing links to messages (let* ((msg (mu4e-message-at-point)) (msgid (or (plist-get msg :message-id) "<none>")) (from (or (plist-get msg :from) '(("none" . "none")))) (fromname (car (car from))) (fromaddress (cdr (car from))) (to (or (plist-get msg :to) '(("none" . "none")))) (toname (car (car to))) (toaddress (cdr (car to))) (fromto (if (mu4e-user-mail-address-p fromaddress) (format "to %s <%s>" toname toaddress) (format "from %s <%s>" fromname fromaddress))) (date (plist-get msg :date)) (date-ts (format-time-string (org-time-stamp-format t) date)) (date-ts-ia (format-time-string (org-time-stamp-format t t) date)) (subject (or (plist-get msg :subject) "<none>")) link) (org-store-link-props :type "mu4e" :link link :message-id msgid) (setq link (concat "mu4e:msgid:" msgid)) (org-add-link-props :link link :to (format "%s <%s>" toname toaddress) :toname toname :toaddress toaddress :from (format "%s <%s>" fromname fromaddress) :fromname fromname :fromaddress fromaddress :fromto fromto :date date-ts-ia :date-timestamp date-ts :date-timestamp-inactive date-ts-ia :subject subject :description (funcall org-mu4e-link-desc-func msg)) link)))) ;; org-add-link-type is obsolete as of org-mode 9. ;; Instead we will use the org-link-set-parameters method (if (fboundp 'org-link-set-parameters) (org-link-set-parameters "mu4e" :follow #'org-mu4e-open :store #'org-mu4e-store-link) (org-add-link-type "mu4e" 'org-mu4e-open) (add-hook 'org-store-link-functions 'org-mu4e-store-link)) (defun org-mu4e-open (path) "Open the mu4e message (for paths starting with 'msgid:') or run the query (for paths starting with 'query:')." (cond ((string-match "^msgid:\\(.+\\)" path) (mu4e-view-message-with-message-id (match-string 1 path))) ((string-match "^query:\\(.+\\)" path) (mu4e-headers-search (match-string 1 path) current-prefix-arg)) (t (mu4e-error "mu4e: unrecognized link type '%s'" path)))) (defun org-mu4e-store-and-capture () "Store a link to the current message or query (depending on `org-mu4e-link-query-in-headers-mode', and capture it with org-mode)." (interactive) (org-mu4e-store-link) (org-capture)) ;;; editing with org-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; below, some functions for the org->html conversion ;; based on / inspired by Eric Schulte's org-mime.el ;; Homepage: http://orgmode.org/worg/org-contrib/org-mime.php ;; ;; EXPERIMENTAL (defun org~mu4e-mime-file (ext path id) "Create a file for an attachment." (format (concat "<#part type=\"%s\" filename=\"%s\" " "disposition=inline id=\"<%s>\">\n<#/part>\n") ext path id)) (defun org~mu4e-mime-multipart (plain html &optional images) "Create a multipart/alternative with text/plain and text/html alternatives. If the html portion of the message includes images, wrap the html and images in a multipart/related part." (concat "<#multipart type=alternative><#part type=text/plain>" plain (when images "<#multipart type=related>") "<#part type=text/html>" html images (when images "<#/multipart>\n") "<#/multipart>\n")) (defun org~mu4e-mime-replace-images (str current-file) "Replace images in html files with cid links." (let (html-images) (cons (replace-regexp-in-string ;; replace images in html "src=\"\\([^\"]+\\)\"" (lambda (text) (format "src=\"cid:%s\"" (let* ((url (and (string-match "src=\"\\([^\"]+\\)\"" text) (match-string 1 text))) (path (expand-file-name url (file-name-directory current-file))) (ext (file-name-extension path)) (id (replace-regexp-in-string "[\/\\\\]" "_" path))) (add-to-list 'html-images (org~mu4e-mime-file (concat "image/" ext) path id)) id))) str) html-images))) (defun org~mu4e-mime-convert-to-html () "Convert the current body to html." (unless (fboundp 'org-export-string-as) (mu4e-error "require function 'org-export-string-as not found.")) (let* ((begin (save-excursion (goto-char (point-min)) (search-forward mail-header-separator))) (end (point-max)) (raw-body (buffer-substring begin end)) (tmp-file (make-temp-name (expand-file-name "mail" temporary-file-directory))) ;; because we probably don't want to skip part of our mail (org-export-skip-text-before-1st-heading nil) ;; because we probably don't want to export a huge style file (org-export-htmlize-output-type 'inline-css) ;; makes the replies with ">"s look nicer (org-export-preserve-breaks t) ;; dvipng for inline latex because MathJax doesn't work in mail (org-export-with-LaTeX-fragments (if (executable-find "dvipng") 'dvipng (mu4e-message "Cannot find dvipng, ignore inline LaTeX") nil)) ;; to hold attachments for inline html images (html-and-images (org~mu4e-mime-replace-images (org-export-string-as raw-body 'html t) tmp-file)) (html-images (cdr html-and-images)) (html (car html-and-images))) (delete-region begin end) (save-excursion (goto-char begin) (newline) (insert (org~mu4e-mime-multipart raw-body html (mapconcat 'identity html-images "\n")))))) ;; next some functions to make the org/mu4e-compose-mode switch as smooth as ;; possible. (defun org~mu4e-mime-decorate-headers () "Make the headers visually distinctive (org-mode)." (save-excursion (goto-char (point-min)) (let* ((eoh (when (search-forward mail-header-separator) (match-end 0))) (olay (make-overlay (point-min) eoh))) (when olay (overlay-put olay 'face 'font-lock-comment-face))))) (defun org~mu4e-mime-undecorate-headers () "Don't make the headers visually distinctive. \(well, mu4e-compose-mode will take care of that)." (save-excursion (goto-char (point-min)) (let* ((eoh (when (search-forward mail-header-separator) (match-end 0)))) (remove-overlays (point-min) eoh)))) (defvar org-mu4e-convert-to-html nil "Whether to do automatic org-mode => html conversion when sending messages.") (defun org~mu4e-mime-convert-to-html-maybe () "Convert to html if `org-mu4e-convert-to-html' is non-nil. This function is called when sending a message (from `message-send-hook') and, if non-nil, will send the message as the rich-text version of the what is assumed to be an org-mode body." (when org-mu4e-convert-to-html (mu4e-message "Converting to html") (org~mu4e-mime-convert-to-html))) (defun org~mu4e-mime-switch-headers-or-body () "Switch the buffer to either mu4e-compose-mode (when in headers) or org-mode (when in the body)." (interactive) (let* ((sepapoint (save-excursion (goto-char (point-min)) (search-forward-regexp mail-header-separator nil t)))) ;; only do stuff when the sepapoint exist; note that after sending the ;; message, this function maybe called on a message with the sepapoint ;; stripped. This is why we don't use `message-point-in-header'. (when sepapoint (cond ;; we're in the body, but in mu4e-compose-mode? ;; if so, switch to org-mode ((and (> (point) sepapoint) (eq major-mode 'mu4e-compose-mode)) (org-mode) (add-hook 'before-save-hook (lambda () (mu4e-error "Switch to mu4e-compose-mode (M-m) before saving.")) nil t) (org~mu4e-mime-decorate-headers) (local-set-key (kbd "M-m") (lambda (keyseq) (interactive "kEnter mu4e-compose-mode key sequence: ") (let ((func (lookup-key mu4e-compose-mode-map keyseq))) (if func (funcall func) (insert keyseq)))))) ;; we're in the headers, but in org-mode? ;; if so, switch to mu4e-compose-mode ((and (<= (point) sepapoint) (eq major-mode 'org-mode)) (org~mu4e-mime-undecorate-headers) (mu4e-compose-mode) (add-hook 'message-send-hook 'org~mu4e-mime-convert-to-html-maybe nil t))) ;; and add the hook (add-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t t)))) (defun org-mu4e-compose-org-mode () "Pseudo-Minor mode for mu4e-compose-mode, to edit the message body using org-mode." (interactive) (unless (member major-mode '(org-mode mu4e-compose-mode)) (mu4e-error "Need org-mode or mu4e-compose-mode")) ;; we can check if we're already in org-mu4e-compose-mode by checking if the ;; post-command-hook is set; hackish...but a buffer-local variable does not ;; seem to survive buffer switching (if (not (member 'org~mu4e-mime-switch-headers-or-body post-command-hook)) (progn (org~mu4e-mime-switch-headers-or-body) (mu4e-message (concat "org-mu4e-compose-org-mode enabled; " "press M-m before issuing message-mode commands"))) (progn ;; otherwise, remove crap (remove-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t) (org~mu4e-mime-undecorate-headers) ;; shut off org-mode stuff (mu4e-compose-mode) (message "org-mu4e-compose-org-mode disabled")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (provide 'org-mu4e) ;;; org-mu4e.el ends here �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-vars.el�������������������������������������������������������������������������0000644�0001750�0001750�00000075655�13020504332�012204� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e-vars.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;;; Code: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Customization (require 'mu4e-meta) (require 'message) (defgroup mu4e nil "mu4e - mu for emacs" :group 'mail) (defcustom mu4e-mu-home nil "Location of the mu homedir, or nil for the default." :group 'mu4e :type '(choice (const :tag "Default location" nil) (directory :tag "Specify location")) :safe 'stringp) (defcustom mu4e-mu-binary (executable-find "mu") "Name of the mu-binary to use. If it cannot be found in your PATH, you can specify the full path." :type 'file :group 'mu4e :safe 'stringp) (defcustom mu4e-maildir (expand-file-name "~/Maildir") "The file system path to your Maildir. Must not be a symbolic link." :type 'directory :safe 'stringp :group 'mu4e) (defcustom mu4e-get-mail-command "true" "Shell command to run to retrieve new mail. Common values are \"offlineimap\", \"fetchmail\" or \"mbsync\", but arbitrary shell-commands can be used. When set to the literal string \"true\" (the default), the command simply finishes succesfully (running the 'true' command) without retrieving any mail. This can be useful when mail is already retrieved in another way." :type 'string :group 'mu4e :safe 'stringp) (defcustom mu4e-index-update-error-warning t "Whether to display warnings when we the retrieval process (as per `mu4e-get-mail-command') finished with a non-zero exit code." :type 'boolean :group 'mu4e :safe 'booleanp) (defcustom mu4e-index-update-error-continue t "Whether to continue with indexing when we the retrieval process (as per `mu4e-get-mail-command') finished with a non-zero exit code." :type 'boolean :group 'mu4e :safe 'booleanp) (defcustom mu4e-index-update-in-background t "Whether to run the automatic mail retrieval in the background." :type 'boolean :group 'mu4e :safe 'booleanp) (defcustom mu4e-index-cleanup t "Whether to run a cleanup phase after indexing -- that is, see if the is a message in the filesystem for each file in the message store. Having this option as `t' ensures that no non-existing mesages are shown but can also be quite slow with large message stores." :type 'boolean :group 'mu4e :safe 'booleanp) (defcustom mu4e-index-lazy-check nil "Whether to only use a 'lazy check' to decide whether a message needs (re)indexing or not. When this is set to `t', mu only uses the directory timestamps to decide on whether it needs to check the messages beneath it, which would miss messages that are modified outside mu. On the other hand, it's significantly faster." :type 'boolean :group 'mu4e :safe 'booleanp) (defcustom mu4e-update-interval nil "Number of seconds between automatic calls to retrieve mail and update the database. If nil, don't update automatically. Note, changes in `mu4e-update-interval' only take effect after restarting mu4e." :type '(choice (const :tag "No automatic update" nil) (integer :tag "Seconds")) :group 'mu4e :safe 'integerp) (defvar mu4e-update-pre-hook nil "Hook run just *before* the mail-retrieval / database updating process starts. You can use this hook for example to `mu4e-get-mail-command' with some specific setting.") (defcustom mu4e-hide-index-messages nil "Whether to hide the \"Indexing...\" messages, or any messages relating to updated contacts." :type 'boolean :group 'mu4e) (defcustom mu4e-change-filenames-when-moving nil "When moving messages to different folders, normally mu/mu4e keep the base filename the same (the flags-part of the filename may change still). With this option set to non-nil, mu4e instead changes the filename. This latter behavior works better with some IMAP-synchronization programs such as mbsync; the default works better with e.g. offlineimap." :type 'boolean :group 'mu4e :safe 'booleanp) (defcustom mu4e-attachment-dir (expand-file-name "~/") "Default directory for saving attachments. This can be either a string (a file system path), or a function that takes a filename and the mime-type as arguments, and returns the attachment dir. See Info node `(mu4e) Attachments' for details." :type 'directory :group 'mu4e :safe 'stringp) (defcustom mu4e-user-mail-address-list `(,user-mail-address) "List of e-mail addresses to consider 'my email addresses'. I.e. addresses whose presence in an email imply that it is a personal message. Note that the local part (the part before '@') of e-mail addresses is case-sensitive, as per RFC531. In practice however, most of the mail systems do not distinguish addresses based on the case, so the emails in this list will be matched case-insensitively." :type '(repeat (string :tag "Address")) :group 'mu4e) ;; don't use the older vars anymore (make-obsolete-variable 'mu4e-user-mail-address-regexp 'mu4e-user-mail-address-list "0.9.9.x") (make-obsolete-variable 'mu4e-my-email-addresses 'mu4e-user-mail-address-list "0.9.9.x") (defcustom mu4e-use-fancy-chars nil "Whether to use fancy (Unicode) characters for marks and threads. You can customize the exact fancy characters used with `mu4e-marks' and various `mu4e-headers-..-mark' and `mu4e-headers..-prefix' variables." :type 'boolean :group 'mu4e) (defcustom mu4e-date-format-long "%c" "Date format to use in the message view, in the format of `format-time-string'." :type 'string :group 'mu4e) (defvar mu4e-debug nil "When set to non-nil, log debug information to the *mu4e-log* buffer.") (defcustom mu4e-bookmarks '( ("flag:unread AND NOT flag:trashed" "Unread messages" ?u) ("date:today..now" "Today's messages" ?t) ("date:7d..now" "Last 7 days" ?w) ("mime:image/*" "Messages with images" ?p)) "A list of pre-defined queries. These will show up in the main screen. Each of the list elements is a three-element list of the form (QUERY DESCRIPTION KEY), where QUERY is a string with a mu query, DESCRIPTION is a short description of the query (this will show up in the UI), and KEY is a shortcut key for the query." :type '(repeat (list (string :tag "Query") (string :tag "Description") character)) :group 'mu4e) (defvar mu4e-bookmarks `( ,(make-mu4e-bookmark :name "Unread messages" :query "flag:unread AND NOT flag:trashed" :key ?u) ,(make-mu4e-bookmark :name "Today's messages" :query "date:today..now" :key ?t) ,(make-mu4e-bookmark :name "Last 7 days" :query "date:7d..now" :key ?w) ,(make-mu4e-bookmark :name "Messages with images" :query "mime:image/*" :key ?p)) "A list of pre-defined queries. Each query is represented by a mu4e-bookmark structure with parameters @t{:name} with the name of the bookmark, @t{:query} with the query expression (a query string or an s-expression that evaluates to query string) and a @t{:key}, which is the shortcut-key for the query. An older form of bookmark, a 3-item list with (QUERY DESCRIPTION KEY) is still recognized as well, for backward-compatibility.") (defcustom mu4e-split-view 'horizontal "How to show messages / headers. A symbol which is either: * `horizontal': split horizontally (headers on top) * `vertical': split vertically (headers on the left). * anything else: don't split (show either headers or messages, not both) Also see `mu4e-headers-visible-lines' and `mu4e-headers-visible-columns'." :type '(choice (const :tag "Split horizontally" horizontal) (const :tag "Split vertically" vertical) (const :tag "Don't split" nil)) :group 'mu4e-headers) (defcustom mu4e-view-show-images nil "Whether to automatically display attached images in the message view buffer." :type 'boolean :group 'mu4e-view) (make-obsolete-variable 'mu4e-show-images 'mu4e-view-show-images "0.9.9.x") (defcustom mu4e-confirm-quit t "Whether to confirm to quit mu4e." :type 'boolean :group 'mu4e) (defcustom mu4e-cited-regexp "^\\(\\([[:alpha:]]+\\)\\|\\( *\\)\\)\\(\\(>+ ?\\)+\\)" "Regular expression that determines whether a line is a citation. This recognizes lines starting with numbers of '>' and spaces as well as citations of the type \"John> ... \"." :type 'string :group 'mu4e) (defcustom mu4e-completing-read-function 'ido-completing-read "Function to be used to receive input from the user with completion. This is used to receive the name of the maildir to switch to via `mu4e~headers-jump-to-maildir'. Suggested possible values are: * `completing-read': built-in completion method * `ido-completing-read': dynamic completion within the minibuffer." :type 'function :options '(completing-read ido-completing-read) :group 'mu4e) (defcustom mu4e-context-policy 'ask-if-none "The policy to determine the context when entering the mu4e main view. If the value is `always-ask', ask the user unconditionally. In all other cases, if any context matches (using its match function), this context is used. Otherwise, if none of the contexts match, we have the following choices: - `pick-first': pick the first of the contexts available (ie. the default) - `ask': ask the user - `ask-if-none': ask if there is no context yet, otherwise leave it as it is - nil: return nil; leaves the current context as is. Also see `mu4e-compose-context-policy'." :type '(choice (const :tag "Always ask what context to use, even if one matches" always-ask) (const :tag "Ask if none of the contexts match" ask) (const :tag "Ask when there's no context yet" ask-if-none) (const :tag "Pick the first context if none match" pick-first) (const :tag "Don't change the context when none match" nil)) :group 'mu4e) ;; crypto (defgroup mu4e-crypto nil "Crypto-related settings." :group 'mu4e) (defcustom mu4e-auto-retrieve-keys nil "Attempt to automatically retrieve public keys when needed." :type 'boolean :group 'mu4e-crypto) (defcustom mu4e-decryption-policy t "Policy for dealing with encrypted parts. The setting is a symbol: * t: try to decrypt automatically * `ask': ask before decrypting anything * nil: don't try to decrypt anything." :type '(choice (const :tag "Try to decrypt automatically" t) (const :tag "Ask before decrypting anything" ask) (const :tag "Don't try to decrypt anything" nil)) :group 'mu4e-crypto) ;; completion; we put them here rather than in mu4e-compose, as mu4e-utils needs ;; the variables. (defgroup mu4e-compose nil "Message-composition related settings." :group 'mu4e) ;; address completion (defcustom mu4e-compose-complete-addresses t "Whether to do auto-completion of e-mail addresses." :type 'boolean :group 'mu4e-compose) (defcustom mu4e-compose-complete-only-personal nil "Whether to consider only 'personal' e-mail addresses, i.e. addresses from messages where user was explicitly in one of the address fields (this excludes mailing list messages). See `mu4e-user-mail-address-list' and the mu-index manpage for details for details (in particular, how to define your own e-mail addresses)." :type 'boolean :group 'mu4e-compose) (defcustom mu4e-compose-complete-only-after "2010-01-01" "Consider only contacts last seen after this date. Date must be a string, in a format parseable by `org-parse-time-string'. This excludes really old contacts. Set to nil to not have any time-based restriction." :type 'string :group 'mu4e-compose) ;;; names and mail-addresses can be mapped onto their canonical ;;; counterpart. use the customizeable function ;;; mu4e-canonical-contact-function to do that. below the identity ;;; function for mapping a contact onto the canonical one. (defun mu4e-contact-identity (contact) "This returns the name and the mail-address of a contact. It is used as the identity function for converting contacts to their canonical counterpart; useful as an example." (let ((name (plist-get contact :name)) (mail (plist-get contact :mail))) (list :name name :mail mail))) (defcustom mu4e-contact-rewrite-function nil "Either nil or a function to be used for when processing contacts and rewrite them or remove them altogether. If the function receives the contact as a list of the form (:name NAME :mail EMAIL ... other properties ... ) (other properties may be there as well) The function should return either: - nil: remove this contact, or - the rewritten cell, or - the existing cell as-is For rewriting, it is recommended to use `plist-put' to set the changed parameters, so the other properties stay in place. Those are needed for sorting the contacts." :type 'function :group 'mu4e-compose) (defcustom mu4e-compose-complete-ignore-address-regexp "no-?reply" "Ignore any e-mail addresses for completion if they match this regexp." :type 'string :group 'mu4e-compose) (defcustom mu4e-compose-reply-to-address nil "The Reply-To address (if this, for some reason, is not equal to the From: address.)" :type 'string :group 'mu4e-compose) ;; backward compatibility (make-obsolete-variable 'mu4e-reply-to-address 'mu4e-compose-reply-to-address "v0.9.9") (defcustom mu4e-compose-keep-self-cc nil "Non-nil means your e-mail address is kept on the CC list when replying to messages." :type 'boolean :group 'mu4e-compose) (defvar mu4e-compose-parent-message nil "The parent message plist. This is the message being replied to, forwarded or edited; used in `mu4e-compose-pre-hook'. For new messages, it is nil.") ;; Folders (defgroup mu4e-folders nil "Special folders." :group 'mu4e) (defcustom mu4e-drafts-folder "/drafts" "Your folder for draft messages, relative to `mu4e-maildir'. e.g. \"/drafts\". Instead of a string, may also be a function that takes a message (a msg plist, see `mu4e-message-get-field'), and returns a folder. Note, the message parameter refers to the original message being replied to / being forwarded / re-edited and is nil otherwise. `mu4e-drafts-folder' is only evaluated once." :type '(choice (string :tag "Folder name") (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-refile-folder "/archive" "Your folder for refiling messages, relative to `mu4e-maildir', e.g. \"/Archive\". Instead of a string, may also be a function that takes a message (a msg plist, see `mu4e-message-get-field'), and returns a folder. Note that the message parameter refers to the message-at-point." :type '(choice (string :tag "Folder name") (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-sent-folder "/sent" "Your folder for sent messages, relative to `mu4e-maildir', e.g. \"/Sent Items\". Instead of a string, may also be a function that takes a message (a msg plist, see `mu4e-message-get-field'), and returns a folder. Note that the message parameter refers to the original message being replied to / being forwarded / re-edited, and is nil otherwise." :type '(choice (string :tag "Folder name") (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-trash-folder "/trash" "Your folder for trashed messages, relative to `mu4e-maildir', e.g. \"/trash\". Instead of a string, may also be a function that takes a message (a msg plist, see `mu4e-message-get-field'), and returns a folder. When using `mu4e-trash-folder' in the headers view (when marking messages for trash). Note that the message parameter refers to the message-at-point. When using it when composing a message (see `mu4e-sent-messages-behavior'), this refers to the original message being replied to / being forwarded / re-edited, and is nil otherwise." :type '(choice (string :tag "Folder name") (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-maildir-shortcuts nil "A list of maildir shortcuts. This makes it possible to quickly go to a particular maildir (folder), or quickly moving messages to them (e.g., for archiving or refiling). The list contains elements of the form (maildir . shortcut), where MAILDIR is a maildir (such as \"/archive/\"), and shortcut is a single character. You can use these shortcuts in the headers and view buffers, for example with `mu4e-mark-for-move-quick' (or 'm', by default) or `mu4e-jump-to-maildir' (or 'j', by default), followed by the designated shortcut character for the maildir. Unlike in search queries, folder names with spaces in them must NOT be quoted, since mu4e does this automatically for you." :type '(repeat (cons (string :tag "Maildir") character)) :group 'mu4e-folders) ;; Faces (defgroup mu4e-faces nil "Type faces (fonts) used in mu4e." :group 'mu4e :group 'faces) (defface mu4e-unread-face '((t :inherit font-lock-keyword-face :bold t)) "Face for an unread message header." :group 'mu4e-faces) (defface mu4e-moved-face '((t :inherit font-lock-comment-face :slant italic)) "Face for a message header that has been moved to some folder. \(It's still visible in the search results, since we cannot be sure it no longer matches)." :group 'mu4e-faces) (defface mu4e-trashed-face '((t :inherit font-lock-comment-face :strike-through t)) "Face for an message header in the trash folder." :group 'mu4e-faces) (defface mu4e-draft-face '((t :inherit font-lock-string-face)) "Face for a draft message header I.e. a message with the draft flag set." :group 'mu4e-faces) (defface mu4e-flagged-face '((t :inherit font-lock-constant-face :bold t)) "Face for a flagged message header." :group 'mu4e-faces) (defface mu4e-replied-face '((t :inherit font-lock-builtin-face :bold nil)) "Face for a replied message header." :group 'mu4e-faces) (defface mu4e-forwarded-face '((t :inherit font-lock-builtin-face :bold nil)) "Face for a passed (forwarded) message header." :group 'mu4e-faces) (defface mu4e-header-face '((t :inherit default)) "Face for a header without any special flags." :group 'mu4e-faces) (defface mu4e-header-title-face '((t :inherit font-lock-type-face)) "Face for a header title in the headers view." :group 'mu4e-faces) (defface mu4e-header-highlight-face '((t :inherit region :weight bold :underline t)) "Face for the header at point." :group 'mu4e-faces) (defface mu4e-header-marks-face '((t :inherit font-lock-preprocessor-face)) "Face for the mark in the headers list." :group 'mu4e-faces) (defface mu4e-header-key-face '((t :inherit message-header-name :bold t)) "Face for a header key (such as \"Foo\" in \"Subject:\ Foo\")." :group 'mu4e-faces) (defface mu4e-header-value-face '((t :inherit font-lock-type-face)) "Face for a header value (such as \"Re: Hello!\")." :group 'mu4e-faces) (defface mu4e-special-header-value-face '((t :inherit font-lock-builtin-face)) "Face for special header values." :group 'mu4e-faces) (defface mu4e-link-face '((t :inherit link)) "Face for showing URLs and attachments in the message view." :group 'mu4e-faces) (defface mu4e-contact-face '((t :inherit font-lock-variable-name-face)) "Face for showing URLs and attachments in the message view." :group 'mu4e-faces) (defface mu4e-highlight-face '((t :inherit highlight)) "Face for highlighting things." :group 'mu4e-faces) (defface mu4e-title-face '((t :inherit font-lock-type-face :bold t)) "Face for a header title in the headers view." :group 'mu4e-faces) (defface mu4e-context-face '((t :inherit mu4e-title-face :bold t)) "Face for displaying the context in the modeline." :group 'mu4e-faces) (defface mu4e-modeline-face '((t :inherit font-lock-string-face :bold t)) "Face for the query in the mode-line." :group 'mu4e-faces) (defface mu4e-view-body-face '((t :inherit default)) "Face for the body in the message-view." :group 'mu4e-faces) (defface mu4e-footer-face '((t :inherit font-lock-comment-face)) "Face for message footers (signatures)." :group 'mu4e-faces) (defface mu4e-url-number-face '((t :inherit font-lock-constant-face :bold t)) "Face for the number tags for URLs." :group 'mu4e-faces) (defface mu4e-attach-number-face '((t :inherit font-lock-variable-name-face :bold t)) "Face for the number tags for attachments." :group 'mu4e-faces) (defface mu4e-cited-1-face '((t :inherit font-lock-builtin-face :bold nil :italic t)) "Face for cited message parts (level 1)." :group 'mu4e-faces) (defface mu4e-cited-2-face '((t :inherit font-lock-preprocessor-face :bold nil :italic t)) "Face for cited message parts (level 2)." :group 'mu4e-faces) (defface mu4e-cited-3-face '((t :inherit font-lock-variable-name-face :bold nil :italic t)) "Face for cited message parts (level 3)." :group 'mu4e-faces) (defface mu4e-cited-4-face '((t :inherit font-lock-keyword-face :bold nil :italic t)) "Face for cited message parts (level 4)." :group 'mu4e-faces) (defface mu4e-cited-5-face '((t :inherit font-lock-comment-face :bold nil :italic t)) "Face for cited message parts (level 5)." :group 'mu4e-faces) (defface mu4e-cited-6-face '((t :inherit font-lock-comment-delimiter-face :bold nil :italic t)) "Face for cited message parts (level 6)." :group 'mu4e-faces) (defface mu4e-cited-7-face '((t :inherit font-lock-type-face :bold nil :italic t)) "Face for cited message parts (level 7)." :group 'mu4e-faces) (defface mu4e-system-face '((t :inherit font-lock-comment-face :slant italic)) "Face for system message (such as the footers for message headers)." :group 'mu4e-faces) (defface mu4e-ok-face '((t :inherit font-lock-comment-face :bold t :slant normal)) "Face for things that are okay." :group 'mu4e-faces) (defface mu4e-warning-face '((t :inherit font-lock-warning-face :bold t :slant normal)) "Face for warnings / error." :group 'mu4e-faces) (defface mu4e-compose-separator-face '((t :inherit message-separator :slant italic)) "Face for the separator between headers / message in mu4e-compose-mode." :group 'mu4e-faces) (defface mu4e-compose-header-face '((t :inherit message-separator :slant italic)) "Face for the separator between headers / message in mu4e-compose-mode." :group 'mu4e-faces) (defface mu4e-region-code '((t (:background "DarkSlateGray"))) "Face for highlighting marked region in mu4e-view buffer." :group 'mu4e-faces) ;; headers info (defconst mu4e-header-info '( (:attachments . ( :name "Attachments" :shortname "Atts" :help "Message attachments" :require-full t :sortable nil)) (:bcc . ( :name "Bcc" :shortname "Bcc" :help "Blind Carbon-Copy recipients for the message" :sortable t)) (:cc . ( :name "Cc" :shortname "Cc" :help "Carbon-Copy recipients for the message" :sortable t)) (:date . ( :name "Date" :shortname "Date" :help "Date/time when the message was written" :sortable t)) (:human-date . ( :name "Date" :shortname "Date" :help "Date/time when the message was written." :sortable :date)) (:flags . ( :name "Flags" :shortname "Flgs" :help "Flags for the message" :sortable nil)) (:from . ( :name "From" :shortname "From" :help "The sender of the message" :sortable t)) (:from-or-to . ( :name "From/To" :shortname "From/To" :help "Sender of the message if it's not me; otherwise the recipient" :sortable nil)) (:maildir . ( :name "Maildir" :shortname "Maildir" :help "Maildir for this message" :sortable t)) (:mailing-list . ( :name "List" :shortname "List" :help "Mailing list for this message" :sortable nil)) (:message-id . ( :name "Message-Id" :shortname "MsgID" :help "Message-Id for this message" :sortable nil)) (:path . ( :name "Path" :shortname "Path" :help "Full filesystem path to the message" :sortable t)) (:signature . ( :name "Signature" :shortname "Sgn" :help "Check for the cryptographic signature" :require-full t :sortable nil)) (:decryption . ( :name "Decryption" :shortname "Dec" :help "Check the cryptographic decryption status" :require-full t :sortable nil)) (:size . ( :name "Size" :shortname "Size" :help "Size of the message" :sortable t)) (:subject . ( :name "Subject" :shortname "Subject" :help "Subject of the message" :sortable t)) (:tags . ( :name "Tags" :shortname "Tags" :help "Tags for the message" :sortable nil)) (:thread-subject . ( :name "Subject" :shortname "Subject" :help "Subject of the thread" :sortable :subject)) (:to . ( :name "To" :shortname "To" :help "Recipient of the message" :sortable t)) (:user-agent . ( :name "User-Agent" :shortname "UA" :help "Program used for writing this message" :require-full t :sortable t))) "An alist of all possible header fields and information about them. This is used in the user-interface (the column headers in the header list, and the fields the message view). Most fields should be self-explanatory. A special one is `:from-or-to', which is equal to `:from' unless `:from' matches one of the addresses in `mu4e-user-mail-address-list', in which case it will be equal to `:to'. Furthermore, the property `:sortable' determines whether we can sort by this field. This can be either a boolean (nil or t), or a symbol for /another/ field. For example, the `:human-date' field uses `:date' for that. Fields with which have the property `:require-full' set to non-nil require a full message; in practice this means that you cannot use such fieds as part of `mu4e-headers-fields', but only in `mu4e-view-fields.' Note, `:sortable' is not supported for custom header fields.") (defvar mu4e-header-info-custom '( (:recipnum . ( :name "Number of recipients" :shortname "Recip#" :help "Number of recipients for this message" :function (lambda (msg) (format "%d" (+ (length (mu4e-message-field msg :to)) (length (mu4e-message-field msg :cc)))))))) "A list of custom (user-defined) headers. The format is similar to `mu4e-header-info', but adds a :function property, which should point to a function that takes a message p-list as argument, and returns a string. See the default value of `mu4e-header-info-custom for an example.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; run-time vars used in multiple places ;; headers (defconst mu4e~headers-buffer-name "*mu4e-headers*" "Name of the buffer for message headers.") (defvar mu4e~headers-buffer nil "Buffer for message headers.") ; view (defconst mu4e~view-buffer-name "*mu4e-view*" "Name for the message view buffer.") (defconst mu4e~view-embedded-buffer-name " *mu4e-embedded-view*" "Name for the embedded message view buffer.") (defvar mu4e~view-buffer nil "The view buffer.") (defvar mu4e~view-msg nil "The message being viewed in view mode.") (defvar mu4e~view-headers-buffer nil "The headers buffer connected to this view.") (defvar mu4e~contacts nil "Hash of that maps contacts (ie. 'name <e-mail>') to an integer with their sort order. We need to keep this information around to quickly re-sort subsets of the contacts in the completions function in mu4e-compose.") (defvar mu4e~server-props nil "Properties we receive from the mu4e server process. \(in the 'pong-handler').") (defvar mu4e~headers-last-query nil "The present (most recent) query.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; our handlers funcs ;; these handler funcs define what happens when we receive a certain message ;; from the server (defun mu4e~default-handler (&rest args) "*internal* Dummy handler function." (error "Not handled: %S" args)) (defvar mu4e-error-func 'mu4e~default-handler "A function called for each error returned from the server process; the function is passed an error plist as argument. See `mu4e~proc-filter' for the format.") (defvar mu4e-update-func 'mu4e~default-handler "A function called for each :update sexp returned from the server process; the function is passed a msg sexp as argument. See `mu4e~proc-filter' for the format.") (defvar mu4e-remove-func 'mu4e~default-handler "A function called for each :remove sexp returned from the server process, when some message has been deleted. The function is passed the docid of the removed message.") (defvar mu4e-sent-func 'mu4e~default-handler "A function called for each :sent sexp returned from the server process, when some message has been sent. The function is passed the docid and the draft-path of the sent message.") (defvar mu4e-view-func 'mu4e~default-handler "A function called for each single message sexp returned from the server process. The function is passed a message sexp as argument. See `mu4e~proc-filter' for the format.") (defvar mu4e-header-func 'mu4e~default-handler "A function called for each message returned from the server process; the function is passed a msg plist as argument. See `mu4e~proc-filter' for the format.") (defvar mu4e-found-func 'mu4e~default-handler "A function called for when we received a :found sexp after the headers have returns, to report on the number of matches. See `mu4e~proc-filter' for the format.") (defvar mu4e-erase-func 'mu4e~default-handler "A function called for when we received an :erase sexp after the headers have returns, to clear the current headers buffer. See `mu4e~proc-filter' for the format.") (defvar mu4e-compose-func 'mu4e~default-handler "A function called for each message returned from the server process that is used as basis for composing a new message (ie., either a reply or a forward); the function is passed msg and a symbol (either reply or forward). See `mu4e~proc-filter' for the format of <msg-plist>.") (defvar mu4e-info-func 'mu4e~default-handler "A function called for each (:info type ....) sexp received from the server process.") (defvar mu4e-pong-func 'mu4e~default-handler "A function called for each (:pong type ....) sexp received from the server process.") (defvar mu4e-contacts-func 'mu4e~default-handler "A function called for each (:contacts (<list-of-contacts>) sexp received from the server process.") (defvar mu4e-temp-func 'mu4e~default-handler "A function called for each (:temp <file> <cookie>) sexp received from the server process.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (provide 'mu4e-vars) ;;; End of mu4e-vars.el �����������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e.el������������������������������������������������������������������������������0000644�0001750�0001750�00000006746�13020504332�011226� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e.el --- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Keywords: email ;; Version: 0.0 ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;;; Code: (eval-when-compile (require 'cl)) (require 'mu4e-meta) ;; autogenerated file with metadata (version etc.) (require 'mu4e-headers) ;; headers view (require 'mu4e-view) ;; message view (require 'mu4e-main) ;; main screen (require 'mu4e-compose) ;; message composition / sending (require 'mu4e-proc) ;; communication with backend (require 'mu4e-utils) ;; utility functions (require 'mu4e-context) ;; support for contexts (require 'mu4e-speedbar) ;; support for speedbar ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; we can't properly use compose buffers that are revived using ;; desktop-save-mode; so let's turn that off (require 'desktop) (add-to-list 'desktop-modes-not-to-save 'mu4e-compose-mode) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; register our handler functions; these connect server messages to functions ;; to handle them. ;; ;; ;; these are all defined in mu4e-headers (setq mu4e-update-func 'mu4e~headers-update-handler) (setq mu4e-header-func 'mu4e~headers-header-handler) (setq mu4e-found-func 'mu4e~headers-found-handler) (setq mu4e-view-func 'mu4e~headers-view-handler) (setq mu4e-remove-func 'mu4e~headers-remove-handler) (setq mu4e-erase-func 'mu4e~headers-clear) ;; these ones are defined in mu4e-utils (setq mu4e-info-func 'mu4e-info-handler) (setq mu4e-error-func 'mu4e-error-handler) ;; note: mu4e-utils also dynamically (temporarily) ;; registers mu4e-pong func ;; this one is defined in mu4e-compose (setq mu4e-compose-func 'mu4e~compose-handler) ;; note: mu4e-compose.el dynamically registers mu4e-sent-func ;; we don't do that here, because it's only a local (temporary) ;; handler ;; this one is defined in mu4e-view (setq mu4e-temp-func 'mu4e~view-temp-handler) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;###autoload (defun mu4e (&optional background) "If mu4e is not running yet, start it. Then, show the main window, unless BACKGROUND (prefix-argument) is non-nil." (interactive "P") ;; start mu4e, then show the main view (mu4e~start (unless background 'mu4e~main-view))) (defun mu4e-quit() "Quit the mu4e session." (interactive) (if mu4e-confirm-quit (when (y-or-n-p (mu4e-format "Are you sure you want to quit?")) (mu4e~stop)) (mu4e~stop))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (provide 'mu4e) ;;; mu4e.el ends here ��������������������������mu-0.9.18/mu4e/mu4e.info����������������������������������������������������������������������������0000644�0001750�0001750�00000640545�13021037656�011575� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������This is mu4e.info, produced by makeinfo version 6.1 from mu4e.texi. Copyright © 2012-2016 Dirk-Jan C. Binnema Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled “GNU Free Documentation License.” INFO-DIR-SECTION Emacs START-INFO-DIR-ENTRY * mu4e: (Mu4e). An email client for GNU/Emacs. END-INFO-DIR-ENTRY  File: mu4e.info, Node: Top, Next: Introduction, Up: (dir) mu4e manual *********** Welcome to mu4e 0.9.17. mu4e (mu-for-emacs) is an e-mail client for GNU-Emacs version 24.4 or higher, built on top of the mu(1) e-mail search engine. mu4e is optimized for fast handling of large amounts of e-mail. Some of its highlights: • Fully search-based: there are no folders(2), only queries. • Fully documented, with example configurations • User-interface optimized for speed, with quick key strokes for common actions • Support for non-English languages (so “angstrom” matches “Ångström”) • Asynchronous: heavy actions don’t block emacs(3) • Support for cryptography - signing, encrypting and decrypting • Address auto-completion based on the contacts in your messages • Extendable with your own snippets of elisp In this manual, we go through the installation of mu4e, do some basic configuration and explain its daily use. We also show you how you can customize mu4e for your special needs. At the end of the manual, there are some example configurations, to get you up to speed quickly: *note Example configurations::. There’s also a section with answers to frequenly asked questions, *note FAQ::. * Menu: * Introduction:: Where be begin * Getting started:: Setting things up * Main view:: The mu4e overview * Headers view:: Lists of message headers * Message view:: Viewing specific messages * Editor view:: Creating / editing messages * Searching:: Some more background on searching/queries * Marking:: Marking messages and performing actions * Contexts:: Defining contexts and switching between them * Dynamic folders:: Folders that change based on circumstances * Actions:: Defining and using custom actions * Extending mu4e:: Writing code for mu4e Appendices * Interaction with other tools:: mu4e and the rest of the world * Example configurations:: Some examples to set you up quickly * FAQ:: Common questions and answers * Tips and Tricks:: Useful tips * How it works:: Some notes about the implementation of mu4e * Logging and debugging:: How to debug problems in mu4e * GNU Free Documentation License:: The license of this manual ---------- Footnotes ---------- (1) <http://www.djcbsoftware.nl/code/mu> (2) that is, instead of folders, you use queries that match messages in a particular folder (3) currently, the only exception to this is _sending mail_; there are solutions for that though - see the *note FAQ::  File: mu4e.info, Node: Introduction, Next: Getting started, Prev: Top, Up: Top 1 Introduction ************** Let’s get started! * Menu: * Why another e-mail client::Aren’t there enough already * Other mail clients::Where mu4e takes its inspiration * What mu4e does not do::Focus on the core-business, delegate the rest * Becoming a mu4e user::Joining the club  File: mu4e.info, Node: Why another e-mail client, Next: Other mail clients, Up: Introduction 1.1 Why another e-mail client? ============================== I (the author) spend a _lot_ of time dealing with e-mail, both professionally and privately. Having an efficient e-mail client is essential. Since none of the existing ones worked the way I wanted, I thought about creating my own. ‘emacs’ is an integral part of my workflow, so it made a lot of sense to use it for e-mail as well. And as I had already written an e-mail search engine (mu), it seemed only logical to use that as a basis.  File: mu4e.info, Node: Other mail clients, Next: What mu4e does not do, Prev: Why another e-mail client, Up: Introduction 1.2 Other mail clients ====================== Under the hood, mu4e is fully search-based, similar to programs like notmuch(1) and sup(2). However, mu4e’s user-interface is quite different. mu4e’s mail handling (deleting, moving etc.) is inspired by Wanderlustu(3) (another emacs-based e-mail client), mutt(4) and the dired file-manager for emacs. mu4e tries to keep all the ’state’ in your maildirs, so you can easily switch between clients, synchronize over IMAP, backup with rsync and so on. If you delete the database, you won’t lose any information. ---------- Footnotes ---------- (1) <http://notmuchmail.org> (2) <http://sup.rubyforge.org/> (3) <http://www.gohome.org/wl/> (4) <http://www.mutt.org/>  File: mu4e.info, Node: What mu4e does not do, Next: Becoming a mu4e user, Prev: Other mail clients, Up: Introduction 1.3 What mu4e does not do ========================= There are a number of things that mu4e does not do, by design: • mu/mu4e do _not_ get your e-mail messages from a mail server. That task is delegated to other tools, such as offlineimap(1), isync/mbsync(2) or fetchmail(3). As long as the messages end up in a maildir, mu4e and mu are happy to deal with them. • mu4e also does _not_ implement sending of messages; instead, it depends on smtpmail (*note (smtpmail)Top::), which is part of ‘emacs’. In addition, mu4e piggybacks on Gnus’ message editor; *note (message)Top::. Thus, many of the things an e-mail client traditionally needs to do, are delegated to other tools. This leaves mu4e to concentrate on what it does best: quickly finding the mails you are looking for, and handle them as efficiently as possible. ---------- Footnotes ---------- (1) <http://offlineimap.org/> (2) <http://isync.sourceforge.net/> (3) <http://www.fetchmail.info/>  File: mu4e.info, Node: Becoming a mu4e user, Prev: What mu4e does not do, Up: Introduction 1.4 Becoming a mu4e user ======================== If mu4e sounds like something for you, give it a shot! We’re trying hard to make it as easy as possible to set up and use; and while you can use elisp in various places to augment mu4e, a lot of knowledge about programming or elisp shouldn’t be required. The idea is to provide sensible defaults, and allow for customization. When you take mu4e into use, it’s a good idea to subscribe to the mu/mu4e-mailing list(1). Sometimes, you might encounter some unexpected behavior while using mu4e. It could be a bug in mu4e, it could be an issue in other software. Or it could just be a misunderstanding. In any case, if you want to report this (either to the mailing list or to <https://github.com/djcb/mu/issues>, the latter is preferred), please always include the following information: • what did you expect that should happen? what actually happened? • can you provide some exact steps to reproduce? • what version of mu4e and emacs were you using? What operating system? • can you reproduce it with emacs -q and only loading mu4e? • if the problem is related to some specific message, please include the raw message file (appropriately anonimized, of course) ---------- Footnotes ---------- (1) <http://groups.google.com/group/mu-discuss>  File: mu4e.info, Node: Getting started, Next: Main view, Prev: Introduction, Up: Top 2 Getting started ***************** In this chapter, we go through the installation of mu4e and its basic setup. After we have succeeded in *note Getting mail::, and *note Indexing your messages::, we discuss the *note Basic configuration::. After these steps, mu4e should be ready to go! * Menu: * Requirements:: What is needed * Installation:: How to install mu and mu4e * Getting mail:: Getting mail from a server * Indexing your messages:: Creating and maintaining the index * Basic configuration:: Settings for mu4e * Folders:: Setting up standard folders * Retrieval and indexing:: Doing it from mu4e * Sending mail:: How to send mail * Running mu4e:: Overview of the mu4e views  File: mu4e.info, Node: Requirements, Next: Installation, Up: Getting started 2.1 Requirements ================ mu/mu4e are known to work on a wide variety of Unix- and Unix-like systems, including many Linux distributions, OS X and FreeBSD, and even on MS-Windows (with Cygwin). ‘emacs’ 23 or 24 (recommended) is required, as well as Xapian(1) and GMime(2). mu has optional support for the Guile 2.x (Scheme) programming language. There are also some GUI-tools, which require GTK+ 3.x and Webkit. If you intend to compile mu yourself, you need to have the typical development tools, such as C and C++ compilers (both ‘gcc’ and ‘clang’ should work), GNU Autotools and ‘make’, and the development packages for GMime, GLib and Xapian. Optionally (if you use them), you also need the development packages for GTK+, Webkit and Guile. ---------- Footnotes ---------- (1) <http://xapian.org/> (2) <http://spruce.sourceforge.net/gmime/>  File: mu4e.info, Node: Installation, Next: Getting mail, Prev: Requirements, Up: Getting started 2.2 Installation ================ mu4e is part of mu - by installing the latter, the former is installed as well. Some Linux distributions provide packaged versions of mu/mu4e; if you can use those, there is no need to compile anything yourself. However, if there are no packages for your distribution, if they are outdated, or if you want to use the latest development versions, you can follow the steps below. First, you need make sure you have the necessary dependencies; the details depend on your distribution. If you’re using another distribution (or another OS), the below at least be helpful in identifying the packages to install. We provide some instructions for Debian, Ubuntu and Fedora; if those do not apply to you, you can follow either *note Building from a release tarball:: or *note Building from git::. 2.2.1 Dependencies for Debian/Ubuntu ------------------------------------ $ sudo apt-get install libgmime-2.6-dev libxapian-dev # if libgmime-2.6-dev is not available, try libgmime-2.4-dev # get emacs 23 or 24 if you don't have it yet $ sudo apt-get install emacs24 # optional $ sudo apt-get install guile-2.0-dev html2text xdg-utils # optional: only needed for msg2pdf and mug (toy gtk+ frontend) $ sudo apt-get install libwebkit-dev 2.2.2 Dependencies for Fedora ----------------------------- $ sudo yum install gmime-devel xapian-core-devel # get emacs 23 or 24 if you don't have it yet $ sudo yum install emacs # optional $ sudo yum install html2text xdg-utils # optional: only needed for msg2pdf and mug (toy gtk+ frontend) $ sudo yum install webkitgtk3-devel 2.2.3 Building from a release tarball ------------------------------------- Using a release-tarball (as available from GoogleCode(1), installation follows the typical steps: $ tar xvfz mu-<version>.tar.gz # use the specific version $ cd mu-<version> # On the BSDs: use gmake instead of make $ ./configure && make $ sudo make install Xapian, GMime and their dependencies must be installed. 2.2.4 Building from git ----------------------- Alternatively, if you build from the git repository or use a tarball like the ones that github produces, the instructions are slightly different, and require you to have autotools (Autoconf, Automake, Libtool, and friends) installed: # get from git (alternatively, use a github tarball) $ git clone git://github.com/djcb/mu.git $ cd mu $ autoreconf -i && ./configure && make # On the BSDs: use gmake instead of make $ sudo make install (Xapian, GMime and their dependencies must be installed). After this, mu and mu4e should be installed (2) on your system, and be available from the command line in ‘emacs’. You may need to restart ‘emacs’, so it can find mu4e in its ‘load-path’. If, even after restarting, ‘emacs’ cannot find mu4e, you may need to add it to your ‘load-path’ explicitly; check where mu4e is installed, and add something like the following to your configuration before trying again: ;; the exact path may differ -- check it (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e") 2.2.5 mu4e and emacs customization ---------------------------------- There is some support for using the ‘emacs’ customization system in mu4e, but for now, we recommend setting the values manually. Please refer to *note Example configurations:: for a couple of examples of this; here we go through things step-by-step. ---------- Footnotes ---------- (1) <http://code.google.com/p/mu0/downloads/list> (2) there’s a hard dependency between versions of mu4e and mu - you cannot combine different versions  File: mu4e.info, Node: Getting mail, Next: Indexing your messages, Prev: Installation, Up: Getting started 2.3 Getting mail ================ In order for mu (and, by extension, mu4e) to work, you need to have your e-mail messages stored in a _maildir_(1) - a specific directory structure with one-file-per-message. If you are already using a maildir, you are lucky. If not, some setup is required: • _Using an external IMAP or POP server_ - if you are using an IMAP or POP server, you can use tools like getmail, fetchmail, offlineimap or isync to download your messages into a maildir (‘~/Maildir’, often). Because it is such a common case, there is a full example of setting mu4e up with offlineimap and Gmail; *note Gmail configuration::. • _Using a local mail server_ - if you are using a local mail-server (such as postfix or qmail), you can teach them to deliver into a maildir as well, maybe in combination with procmail. A bit of googling should be able to provide you with the details. ---------- Footnotes ---------- (1) <http://en.wikipedia.org/wiki/Maildir>; in this manual we use the term ’maildir’ for both the standard and the hierarchy of maildirs that store your messages  File: mu4e.info, Node: Indexing your messages, Next: Basic configuration, Prev: Getting mail, Up: Getting started 2.4 Indexing your messages ========================== After you have succeeded in *note Getting mail::, we need to _index_ the messages. That is - we need to scan the messages in the maildir and store the information about them in a special database. We can do that from mu4e – *note Main view::, but the first time, it is a good idea to run it from the command line, which makes it easier to verify that everything works correctly. Assuming that your maildir is at ‘~/Maildir’, we issue the following command: $ mu index --maildir=~/Maildir This should scan your ‘~/Maildir’(1) and fill the database, and give progress information while doing so. The indexing process may take a few minutes the first time you do it (for thousands of e-mails); afterwards it is much faster, since mu only scans messages that are new or have changed. Indexing is discussed in full detail in the mu-index man-page. After the indexing process has finished, you can quickly test if everything worked, by trying some command-line searches, for example $ mu find hello which lists all messages that match hello. For more examples of searches, see *note Queries::, or check the mu-find and mu-easy man pages. If all of this worked well, we are well on our way setting things up; the next step is to do some basic configuration for mu4e. ---------- Footnotes ---------- (1) In most cases, you do not even need to provide the --maildir=~/Maildir since it is the default; see the mu-index man-page for details  File: mu4e.info, Node: Basic configuration, Next: Folders, Prev: Indexing your messages, Up: Getting started 2.5 Basic configuration ======================= Before we can start using mu4e, we need to tell ‘emacs’ to load it. So, add to your ‘~/.emacs’ (or its moral equivalent, such as ‘~/.emacs.d/init.el’) something like: (require 'mu4e) If ‘emacs’ complains that it cannot find mu4e, check your ‘load-path’ and make sure that mu4e’s installation directory is part of it. If not, you can add it: (add-to-list 'load-path MU4E-PATH) with MU4E-PATH replaced with the actual path.  File: mu4e.info, Node: Folders, Next: Retrieval and indexing, Prev: Basic configuration, Up: Getting started 2.6 Folders =========== The next step is to tell mu4e where it can find your Maildir, and some special folders. So, for example(1): ;; these are actually the defaults (setq mu4e-maildir "~/Maildir" ;; top-level Maildir mu4e-sent-folder "/sent" ;; folder for sent messages mu4e-drafts-folder "/drafts" ;; unfinished messages mu4e-trash-folder "/trash" ;; trashed messages mu4e-refile-folder "/archive") ;; saved messages Note, ‘mu4e-maildir’ takes an actual filesystem-path, the other folder names are all relative to ‘mu4e-maildir’. Also note that this must _not_ be a symbolic link. If you use mu4e-context, see *note Contexts and special folders:: for what that means for these special folders. ---------- Footnotes ---------- (1) Note that the folders (mu4e-sent-folder, mu4e-drafts-folder, mu4e-trash-folder and mu4e-refile-folder) can also be _functions_ that are evaluated at runtime. This allows for dynamically changing them depending on the situation. See *note Dynamic folders:: for details.  File: mu4e.info, Node: Retrieval and indexing, Next: Sending mail, Prev: Folders, Up: Getting started 2.7 Retrieval and indexing with mu4e ==================================== As we have seen, we can do all of the mail retrieval _outside_ of ‘emacs’/mu4e. However, you can also do it from within mu4e. 2.7.1 Basics ------------ To set up mail-retrieval from withing mu4e, set the variable ‘mu4e-get-mail-command’ to the program or shell command you want to use for retrieving mail. You can then get your e-mail using ‘M-x mu4e-update-mail-and-index’, or ‘C-S-u’ in all mu4e-views; alternatively, you can use ‘C-c C-u’, which may be more convenient if you use emacs in a terminal. You can interrupt the (foreground) update process with ‘q’. It is possible to update your mail and index periodically in the background or foreground, by setting the variable ‘mu4e-update-interval’ to the number of seconds between these updates. If set to ‘nil’, it won’t update at all. After you make changes to ‘mu4e-update-interval’, mu4e must be restarted before the changes take effect. By default, this will run in background and to change it to run in foreground, set ‘mu4e-index-update-in-background’ to ‘nil’ 2.7.2 Handling errors during mail retrieval ------------------------------------------- If the mail-retrieval process returns with a non-zero exit code, mu4e shows a warning (unless ‘mu4e-index-update-error-warning’ is set to ‘nil’), but then try to index your maildirs anyway (unless ‘mu4e-index-update-error-continue’ is set to ‘nil’). Reason for these defaults is that some of the mail-retrieval programs may return non-zero, even when the updating process succeeded; however, it is hard to tell such pseudo-errors from real ones like ’login failed’. If you need more refinement, it may be useful to wrap the mail-retrieval program in a shell-script, for example fetchmail returns 1 to indicate ’no mail’; we can handle that with: (setq mu4e-get-mail-command "fetchmail -v || [ $? -eq 1 ]") A similar approach can be used with other mail retrieval programs, although not all of them have their exit codes documented. 2.7.3 Implicit mail retrieval ----------------------------- If you don’t have a specific command for getting mail, for example because you are running your own mail-server, you can leave ‘mu4e-get-mail-command’ at "true" (the default), in which case mu4e won’t try to get new mail, but still re-index your messages. 2.7.4 Speeding up indexing -------------------------- If you have a large number of e-mail messages in your store, (re)indexing might take a while. The defaults for indexing are to ensure the we always have correct, up-to-date information about your messages, even if other programs have modified the Maildir. The downside of this thoroughness (which is the default) is that it is relatively slow, something that can be noticeable with large e-mail corpa. For a faster approach, you can use the following: (setq mu4e-index-cleanup nil ;; don't do a full cleanup check mu4e-index-lazy-check t) ;; don't consider up-to-date dirs In many cases, the mentioned thoroughness might not be needed, and these settings give a very significant speed-up. Note that you can of course occasionally run a thorough indexing round. For further details, please refer to the mu-index manpage. 2.7.5 Example setup ------------------- A simple setup could look something like: (setq mu4e-get-mail-command "offlineimap" ;; or fetchmail, or ... mu4e-update-interval 300) ;; update every 5 minutes A hook ‘mu4e-update-pre-hook’ is available which is run right before starting the process. That can be useful, for example, to influence, ‘mu4e-get-mail-command’ based on the the current situation (location, time of day, ...). It is possible to get notifications when the indexing process does any updates - for example when receiving new mail. See ‘mu4e-index-updated-hook’ and some tips on its usage in the *note FAQ::.  File: mu4e.info, Node: Sending mail, Next: Running mu4e, Prev: Retrieval and indexing, Up: Getting started 2.8 Sending mail ================ mu4e re-uses Gnu’s ‘message-mode’ (*note (message)Top::) for writing mail and inherits the setup for sending mail as well. For sending mail using SMTP, mu4e uses smtpmail (*note (smtpmail)Top::). This package supports many different ways to send mail; please refer to its documentation for the details. Here, we only provide some simple examples - for more, see *note Example configurations::. A very minimal setup: ;; tell message-mode how to send mail (setq message-send-mail-function 'smtpmail-send-it) ;; if our mail server lives at smtp.example.org; if you have a local ;; mail-server, simply use 'localhost' here. (setq smtpmail-smtp-server "smtp.example.org") Since mu4e (re)uses the same message mode and smtpmail that Gnus uses, many settings for those also apply to mu4e. 2.8.1 Dealing with sent messages -------------------------------- By default, mu4e puts a copy of messages you sent in the folder determined by ‘mu4e-sent-folder’. In some cases, this may not be what you want - for example, when using Gmail-over-IMAP, this interferes with Gmail’s handling of the sent messages folder, and you may end up with duplicate messages. You can use the variable ‘mu4e-sent-messages-behavior’ to customize what happens with sent messages. The default is the symbol ‘sent’ which, as mentioned, causes the message to be copied to your sent-messages folder. Other possible values are the symbols ‘trash’ (the sent message is moved to the trash-folder (‘mu4e-trash-folder’), and ‘delete’ to simply discard the sent message altogether (so Gmail can deal with it). For Gmail-over-IMAP, you could add the following to your settings: ;; don't save messages to Sent Messages, Gmail/IMAP takes care of this (setq mu4e-sent-messages-behavior 'delete) And that’s it! We should now be ready to go. For more complex needs, ‘mu4e-sent-messages-behavior’ can also be a a parameter-less function that returns one of the mentioned symbols; see the built-in documentation for the variable.  File: mu4e.info, Node: Running mu4e, Prev: Sending mail, Up: Getting started 2.9 Running mu4e ================ After following the steps in this chapter, we now (hopefully!) have a working mu4e setup. Great! In the next chapters, we walk you through the various views in mu4e. For your orientation, the diagram below shows how the views relate to each other, and the default key-bindings to navigate between them. [C] +--------+ [RFCE] --------> | editor | <-------- / +--------+ \ / [RFCE]^ \ / | \ +-------+ [sjbB]+---------+ [RET] +---------+ | main | <---> | headers | <----> | message | +-------+ [q] +---------+ [qbBjs] +---------+ [sjbB] ^ [.] | [q] V +-----+ | raw | +-----+ Default bindings ---------------- R: Reply s: search .: raw view (toggle) F: Forward j: jump-to-maildir q: quit C: Compose b: bookmark-search E: Edit B: edit bookmark-search  File: mu4e.info, Node: Main view, Next: Headers view, Prev: Getting started, Up: Top 3 The main view *************** After you have installed mu4e (*note Getting started::), you can start it with ‘M-x mu4e’. mu4e does some checks to ensure everything is set up correctly, and then shows you the mu4e main view. Its major mode is ‘mu4e-main-mode’. * Menu: * Overview: MV Overview. What is the main view * Basic actions::What can we do * Bookmarks: MV Bookmarks. Jumping to other places * Miscellaneous::Notes  File: mu4e.info, Node: MV Overview, Next: Basic actions, Up: Main view 3.1 Overview ============ The main view looks something like the following: * mu4e - mu for emacs version 0.X.X CG Basics * [j]ump to some maildir * enter a [s]earch query * [C]ompose a new message Bookmarks * [bu] Unread messages * [bt] Today's messages * [bw] Last 7 days * [bp] Messages with images * [bs] Sent mail * [bf] Flagged messages * [b]] Flow * [b/] Test Misc * [;]Switch focus * [U]pdate email & database * [N]ews * [A]bout mu4e * [H]elp * [q]uit In the example above, you can see the letters “CG”, which indicate: • C: support for decryption of encrypted messages, and verifying signatures. See *note MSGV Crypto:: in the *note Message view:: for details. • G: support for the Guile 2.0 programming language Whether you see both, one or none of these letters depends on the way mu is built. Let’s walk through the menu.  File: mu4e.info, Node: Basic actions, Next: MV Bookmarks, Prev: MV Overview, Up: Main view 3.2 Basic actions ================= First, the _Basics_: • [j]ump to some maildir: after pressing <j> (“jump”), mu4e asks you for a maildir to visit. These are the maildirs you set in *note Basic configuration:: and any of your own. If you choose <o> (“other”) or </>, you can choose from all maildirs under ‘mu4e-maildir’. After choosing a maildir, the messages in that maildir are listed, in the *note Headers view::. • enter a [s]earch query: after pressing <s>, mu4e asks you for a search query, and after entering one, shows the results in the *note Headers view::. • [C]ompose a new message: after pressing <C>, you are dropped in the *note Editor view:: to write a new message.  File: mu4e.info, Node: MV Bookmarks, Next: Miscellaneous, Prev: Basic actions, Up: Main view 3.3 Bookmarks ============= The next item in the Main view is _Bookmarks_. Bookmarks are predefined queries with a descriptive name and a shortcut - in the example above, we see the default bookmarks. You can view the list of messages matching a certain bookmark by pressing <b> followed by the bookmark’s shortcut. If you’d like to edit the bookmarked query first before invoking it, use <B>. Bookmarks are stored in the variable ‘mu4e-bookmarks’; you can add your own and/or replace the default ones; *Note Bookmarks::.  File: mu4e.info, Node: Miscellaneous, Prev: MV Bookmarks, Up: Main view 3.4 Miscellaneous ================= Finally, there are some _Misc_ (miscellaneous) actions: • [U]pdate email & database executes the shell-command in the variable ‘mu4e-get-mail-command’, and afterwards updates the mu database; see *note Indexing your messages:: and *note Getting mail:: for details. • toggle [m]ail sending mode (direct) toggles between sending mail directly, and queuing it first (for example, when you are offline), and [f]lush queued mail flushes any queued mail. This item is visible only if you have actually set up mail-queuing. *note Queuing mail:: • [A]bout mu4e provides general information about the program • [H]elp shows help information for this view • Finally, [q]uit mu4e quits your mu4e-session  File: mu4e.info, Node: Headers view, Next: Message view, Prev: Main view, Up: Top 4 The headers view ****************** The headers view shows the results of a query. The header-line shows the names of the fields. Below that, there is a line with those fields, for each matching message, followed by a footer line. The major-mode for the headers view is ‘mu4e-headers-mode’. * Menu: * Overview: HV Overview. What is the Header View * Keybindings::Do things with your keyboard * Marking: HV Marking. Selecting messages for doing things * Sorting and threading::Influencing the display * Custom headers: HV Custom headers. Adding your own headers * Actions: HV Actions. Defining and using actions * Split view::Seeing both headers and messages  File: mu4e.info, Node: HV Overview, Next: Keybindings, Up: Headers view 4.1 Overview ============ An example headers view: Date V Flgs From/To List Subject 06:32 Nu To Edmund Dantès GstDev + Re: Gstreamer-V4L... 15:08 Nu Abbé Busoni GstDev + Re: Gstreamer-V... 18:20 Nu Pierre Morrel GstDev \ Re: Gstreamer... 2013-03-18 S Jacopo EmacsUsr + emacs server on win... 2013-03-18 S Mercédès EmacsUsr \ RE: emacs server ... 2013-03-18 S Beachamp EmacsUsr + Re: Copying a whole... 22:07 Nu Albert de Moncerf EmacsUsr \ Re: Copying a who... 2013-03-18 S Gaspard Caderousse GstDev | Issue with GESSimpl... 2013-03-18 Ss Baron Danglars GuileUsr | Guile-SDL 0.4.2 ava... End of search results Some notes to explain what you see in the example: • The fields shown in the headers view can be influenced by customizing the variable ‘mu4e-headers-fields’; see ‘mu4e-header-info’ for the list of built-in fields. Apart from the built-in fields, you can also create custom fields using ‘mu4e-header-info-custom’; see *note HV Custom headers:: for details. • By default, the date is shown with the :human-date field, which shows the _time_ for today’s messages, and the _date_ for older messages. If you want to distinguish between ’today’ and ’older’, you can use the :date field instead. • You can customize the date and time formats with the variable ‘mu4e-headers-date-format’ and ‘mu4e-headers-time-format’, respectively. In the example, we use ‘:human-date’, which shows when the time when the message was sent today, and the date otherwise. • By default, the subject is shown using the :subject field; however, it is also possible to use :thread-subject, which shows the subject of a thread only once, similar to the display of the mutt e-mail client. • The header field used for sorting is indicated by “V” or “^”(1), corresponding to the sort order (descending or ascending, respectively). You can influence this by a mouse click, or <O>. Not all fields allow sorting. • Instead of showing the From: and To: fields separately, you can use From/To (:from-or-to in ‘mu4e-headers-fields’ as a more compact way to convey the most important information: it shows From: _except_ when the e-mail was sent by the user (i.e., you) - in that case it shows To: (prefixed by To(2), as in the example above). To determine whether a message was sent by you, mu4e uses the variable ‘mu4e-user-mail-address-list’, a list of your e-mail addresses. • The ’List’ field shows the mailing-list a message is sent to; ‘mu4e’ tries to create a convenient shortcut for the mailing-list name; the variable ‘mu4e-user-mailing-lists’ can be used to add your your own shortcuts. You can use ‘mu4e-mailing-list-patterns’ to to specify generic shortcuts, e.g. to shorten list names which contain dots (mu4e defaults to shortening up to the first dot): (setq mu4e-mailing-list-patterns '(``\\([-_a-z0-9.]+\\)\.lists\.company\.com''))) • The letters in the ’Flags’ field correspond to the following: D=_draft_, F=_flagged_ (i.e., ’starred’), N=_new_, P=_passed_ (i.e., forwarded), R=_replied_, S=_seen_, T=_trashed_, a=_has-attachment_, x=_encrypted_, s=_signed_, u=_unread_. The tooltip for this field also contains this information. • The subject field also indicates the discussion threads (3). • The headers view is _automatically updated_ if any changes are found during the indexing process, and if there is no current user-interaction. If you do not want such automatic updates, set ‘mu4e-headers-auto-update’ to ‘nil’. • Just before executing a search, a hook-function ‘mu4e-headers-search-hook’ is invoked, which receives the search expression as its parameter. • Also, there is a hook-function ‘mu4e-headers-found-hook’ available which is invoked just after mu4e has completed showing the messages in the headers-view. ---------- Footnotes ---------- (1) or you can use little graphical triangles; see variable ‘mu4e-use-fancy-chars’ (2) You can customize this by changing the variable ‘mu4e-headers-from-or-to-prefix’ (a cons cell) (3) using Jamie Zawinski’s mail threading algorithm, <http://www.jwz.org/doc/threading.html>  File: mu4e.info, Node: Keybindings, Next: HV Marking, Prev: HV Overview, Up: Headers view 4.2 Keybindings =============== Using the below key bindings, you can do various things with these messages; these actions are also listed in the Headers menu in the ‘emacs’ menu bar. key description =========================================================== n,p view the next, previous message ],[ move to the next, previous unread message y select the message view (if it's visible) RET open the message at point in the message view searching --------- s search S edit last query / narrow the search b search bookmark B edit bookmark before search j jump to maildir M-left,\ previous query M-right next query O change sort order P toggle threading Q toggle full-search V toggle skip-duplicates W toggle include-related marking ------- d mark for moving to the trash folder = mark for removing trash flag ('untrash') DEL,D mark for complete deletion m mark for moving to another maildir folder r mark for refiling +,- mark for flagging/unflagging ?,! mark message as unread, read u unmark message at point U unmark *all* messages % mark based on a regular expression T,t mark whole thread, subthread <insert>,* mark for 'something' (decide later) # resolve deferred 'something' marks x execute actions for the marked messages composition ----------- R,F,C reply/forward/compose E edit (only allowed for draft messages) misc ---- ; switch focus a execute some custom action on a header | pipe message through shell command C-+,C-- increase / decrease the number of headers shown H get help C-S-u update mail & reindex q leave the headers buffer  File: mu4e.info, Node: HV Marking, Next: Sorting and threading, Prev: Keybindings, Up: Headers view 4.3 Marking =========== You can _mark_ messages for a certain action, such as deletion or move. After one or more messages are marked, you can then execute (‘mu4e-mark-execute-all’, <x>) these actions. This two-step mark-execute sequence is similar to what e.g. dired does. It is how mu4e tries to be as quick as possible, while avoiding accidents. The mark/unmark commands support the _region_ (i.e., “selection”) – so, for example, if you select some messages and press <DEL>, all messages in the region are marked for deletion. You can mark all messages that match a certain pattern with <%>. In addition, you can mark all messages in the current thread (<T>) or sub-thread (<t>). When you do a new search or refresh the headers buffer while you still have marked messages, you are asked what to do with those marks – whether to _apply_ them before leaving, or _ignore_ them. This behavior can be influenced with the variable ‘mu4e-headers-leave-behavior’. For more information about marking, see *note Marking::.  File: mu4e.info, Node: Sorting and threading, Next: HV Custom headers, Prev: HV Marking, Up: Headers view 4.4 Sorting and threading ========================= By default, mu4e sorts messages by date, in descending order: the most recent messages are shown at the top. In addition, the messages are _threaded_, i.e., shown in the context of a discussion thread; this also affects the sort order. The header field used for sorting is indicated by “V” or “^”(1), indicating the sort order (descending or ascending, respectively). You can change the sort order by clicking the corresponding field with the mouse, or with ‘M-x mu4e-headers-change-sorting’ (<O>); note that not all fields can be used for sorting. You can toggle threading on/off using ‘M-x mu4e-headers-toggle-threading’ or <P>. For both of these functions, unless you provide a prefix argument (<C-u>), the current search is updated immediately using the new parameters. You can toggle full-search (*note Searching::) using ‘M-x mu4e-headers-toggle-full-search’ or <Q>. If you want to change the defaults for these settings, you can use the variables ‘mu4e-headers-sortfield’ and ‘mu4e-headers-show-threads’. ---------- Footnotes ---------- (1) or you can use little graphical triangles; see variable ‘mu4e-use-fancy-chars’  File: mu4e.info, Node: HV Custom headers, Next: HV Actions, Prev: Sorting and threading, Up: Headers view 4.5 Custom headers ================== Sometimes the normal headers that mu4e offers (Date, From, To, Subject etc.) may not be enough. For these cases, mu4e offers _custom headers_ in both the headers-view and the message-view. You can do so by adding a description of your custom header to ‘mu4e-header-info-custom’, which is a list of custom headers. Let’s look at an example – suppose we want to add a custom header that shows the number of recipients for a message, i.e., the sum of the number of recipients in the To: and Cc: fields. Let’s further suppose that our function takes a message-plist as its argument (*note Message functions::). (add-to-list 'mu4e-header-info-custom '(:recipnum . ( :name "Number of recipients" ;; long name, as seen in the message-view :shortname "Recip#" ;; short name, as seen in the headers view :help "Number of recipients for this message" ;; tooltip :function (lambda (msg) (format "%d" (+ (length (mu4e-message-field msg :to)) (length (mu4e-message-field msg :cc)))))))) Or, let’s get the full mailing-list name: (add-to-list 'mu4e-header-info-custom '(:full-mailing-list . ( :name "Mailing-list" ;; long name, as seen in the message-view :shortname "ML" ;; short name, as seen in the headers view :help "Full name for mailing list" ;; tooltip :function (lambda (msg) (or (mu4e-message-field msg :mailing-list) ""))))) You can then add the custom header to your ‘mu4e-headers-fields’, just like the built-in headers. After evaluation, you headers-view should include a new header Recip# with the number of recipients, and/or ML with the full mailing-list name. This function can be used in both the headers-view and the message-view; if you need something specific for one of these, you can check for the mode in your function, or create separate functions.  File: mu4e.info, Node: HV Actions, Next: Split view, Prev: HV Custom headers, Up: Headers view 4.6 Actions =========== ‘mu4e-headers-action’ (<a>) lets you pick custom actions to perform on the message at point. You can specify these actions using the variable ‘mu4e-headers-actions’. See *note Actions:: for the details. mu4e defines some default actions. One of those is for _capturing_ a message: <a c> ’captures’ the current message. Next, when you’re editing some message, you can include the previously captured message as an attachment, using ‘mu4e-compose-attach-captured-message’. See ‘mu4e-actions.el’ in the mu4e source distribution for more example actions.  File: mu4e.info, Node: Split view, Prev: HV Actions, Up: Headers view 4.7 Split view ============== Using the _Split view_, we can see the *note Headers view:: and the *note Message view:: next to each other, with the message selected in the former, visible in the latter. You can influence the way the splitting is done by customizing the variable ‘mu4e-split-view’. Possible values are: • horizontal (this is the default): display the message view below the header view. Use ‘mu4e-headers-visible-lines’ the set the number of lines shown (default: 8). • vertical: display the message view on the right side of the header view. Use ‘mu4e-headers-visible-columns’ to set the number of visible columns (default: 30). • anything else: don’t do any splitting Some useful key bindings in the split view: • <C-+> and <C-->: interactively change the number of columns or headers shown • You can change the selected window from the headers-view to the message-view and vice-versa with ‘mu4e-select-other-view’, bound to <y>  File: mu4e.info, Node: Message view, Next: Editor view, Prev: Headers view, Up: Top 5 The message view ****************** After selecting a message in the *note Headers view::, it appears in a message view window, which shows the message headers, followed by the message body. Its major mode is ‘mu4e-view-mode’. * Menu: * Overview: MSGV Overview. What is the Message View * Keybindings: MSGV Keybindings. Do things with your keyboard * Attachments:: Opening and saving them * Viewing images inline::Images display inside emacs * Displaying rich-text messages::Dealing with HTML mail * Verifying signatures and decryption: MSGV Crypto. Support for cryptography * Custom headers: MSGV Custom headers. Your own headers * Actions: MSGV Actions. Defining and using actions.  File: mu4e.info, Node: MSGV Overview, Next: MSGV Keybindings, Up: Message view 5.1 Overview ============ An example message view: From: randy@epiphyte.com To: julia@eruditorum.org Subject: Re: some pics Flags: (seen attach) Date: Mon 19 Jan 2004 09:39:42 AM EET Maildir: /inbox Attachments(2): [1]DSCN4961.JPG(1.3M), [2]DSCN4962.JPG(1.4M) Hi Julia, Some pics from our trip to Cerin Amroth. Enjoy! All the best, Randy. On Sun 21 Dec 2003 09:06:34 PM EET, Julia wrote: [....] Some notes: • The variable ‘mu4e-view-fields’ determines the header fields to be shown; see ‘mu4e-header-info’ for a list of built-in fields. Apart from the built-in fields, you can also create custom fields using ‘mu4e-header-info-custom’; see *note MSGV Custom headers::. • You can set the date format with the variable ‘mu4e-date-format-long’. • By default, only the names of contacts in address fields are visible (see ‘mu4e-view-show-addresses’ to change this). You can view the e-mail addresses by clicking on the name, or pressing <M-RET>. • You can compose a message for the contact at point by either clicking <[mouse-2]> or pressing <C>. • The body text can be line-wrapped using visual-line-mode. mu4e defines <w> to toggle between the wrapped and unwrapped state. If you want to do this automatically when viewing a message, invoke ‘visual-line-mode’ in your ‘mu4e-view-mode-hook’. • For messages that support it, you can toggle between html and text versions using ‘mu4e-view-toggle-html’, bound to <h>; • You can hide cited parts in messages (the parts starting with “>”) using ‘mu4e-view-hide-cited’, bound to <#>. If you want to do this automatically for every message, invoke the function in your ‘mu4e-view-mode-hook’. • For search-related operations, see *note Searching::. • You can scroll down the message using <SPC>; if you do this at the end of a message,it automatically takes you to the next one. If you want to prevent this behavior, set ‘mu4e-view-scroll-to-next’ to ‘nil’.  File: mu4e.info, Node: MSGV Keybindings, Next: Attachments, Prev: MSGV Overview, Up: Message view 5.2 Keybindings =============== You can find most things you can do with this message in the _View_ menu, or by using the keyboard; the default bindings are: key description ============================================================== n,p view the next, previous message ],[ move to the next, previous unread message y select the headers view (if it's visible) RET scroll down M-RET open URL at point / attachment at point SPC scroll down, if at end, move to next message S-SPC scroll up searching --------- s search e edit last query / narrow the search b search bookmark B edit bookmark before search j jump to maildir M-left previous query M-right next query marking ------- d mark for moving to the trash folder = mark for removing trash flag ('untrash') DEL,D mark for complete deletion m mark for moving to another maildir folder r mark for refiling +,- mark for flagging/unflagging u unmark message at point U unmark *all* messages % mark based on a regular expression T,t mark whole thread, subthread <insert>,* mark for 'something' (decide later) # resolve deferred 'something' marks x execute actions for the marked messages composition ----------- R,F,C reply/forward/compose E edit (only allowed for draft messages) actions ------- g go to (visit) numbered URL (using `browse-url') (or: <mouse-1> or M-RET with point on url) C-u g visits multiple URLs f fetch (download )the numbered URL. C-u f fetches multiple URLs k save the numbered URL in the kill-ring. C-u k saves multiple URLs e extract (save) attachment (asks for number) (or: <mouse-2> or S-RET with point on attachment) C-u e extracts multiple attachments o open attachment (asks for number) (or: <mouse-1> or M-RET with point on attachment) a execute some custom action on the message A execute some custom action on an attachment misc ---- ; switch focus c copy address at point (with C-u copy long version) h toggle between html/text (if available) w toggle line wrapping # toggle show/hide cited parts v show details about the cryptographic signature . show the raw message view. 'q' takes you back. C-+,C-- increase / decrease the number of headers shown H get help C-S-u update mail & reindex q leave the message view For the marking commands, please refer to *note Marking messages::.  File: mu4e.info, Node: Attachments, Next: Viewing images inline, Prev: MSGV Keybindings, Up: Message view 5.3 Attachments =============== By default, mu4e uses the xdg-open-program (1) or (on OS X) the open program for opening attachments. If you want to use another program, you do so by setting the MU_PLAY_PROGRAM environment variable to the program to be used. The default directory for extracting (saving) attachments is your home directory (‘~/’); you can change this using the variable ‘mu4e-attachment-dir’, for example: (setq mu4e-attachment-dir "~/Downloads") For more flexibility, ‘mu4e-attachment-dir’ can also be a user-provided function. This function receives two parameters: the file-name and the mime-type as found in the e-mail message(2) of the attachment, either or both of which can be nil. For example: (setq mu4e-attachment-dir (lambda (fname mtype) (cond ;; docfiles go to ~/Desktop ((and fname (string-match "\\.doc$" fname)) "~/Desktop") ;; ... other cases ... (t "~/Downloads")))) ;; everything else You can extract multiple attachments at once by prefixing the extracting command by <C-u>; so ‘C-u e’ asks you for a range of attachments to extract (for example, ‘1 3-6 8’). The range "‘a’" is a shortcut for _all_ attachments. ---------- Footnotes ---------- (1) <http://portland.freedesktop.org/wiki/> (2) sadly, often application/octet-stream is used for the mime-type, even if a better type is available  File: mu4e.info, Node: Viewing images inline, Next: Displaying rich-text messages, Prev: Attachments, Up: Message view 5.4 Viewing images inline ========================= It is possible to show images inline in the message view buffer if you run ‘emacs’ in GUI-mode. You can enable this by setting the variable ‘mu4e-view-show-images’ to t. Since ‘emacs’ does not always handle images correctly, this is not enabled by default. If you are using ‘emacs’ 24 with _ImageMagick_(1) support, make sure you call ‘imagemagick-register-types’ in your configuration, so it is used for images. ;; enable inline images (setq mu4e-view-show-images t) ;; use imagemagick, if available (when (fboundp 'imagemagick-register-types) (imagemagick-register-types)) ---------- Footnotes ---------- (1) <http://www.imagemagick.org>  File: mu4e.info, Node: Displaying rich-text messages, Next: MSGV Crypto, Prev: Viewing images inline, Up: Message view 5.5 Displaying rich-text messages ================================= mu4e normally prefers the plain-text version for messages that consist of both a plain-text and html (rich-text) versions of the body-text. You can change this by setting ‘mu4e-view-prefer-html’ to t. And you can toggle this value (globally) using ‘h’ in the message view; this also refreshes the message with the new setting. Note, when using html-based rendering, you don’t get the hyperlink shortcuts the text-version provides. If there is only an html-version, or if the plain-text version is too short in comparison with the html part(1), mu4e tries to convert the html into plain-text for display. With emacs 24.4 or newer, this defaults to ‘mu4e-shr2text’, which uses the built-in shr renderer. For older emacs versions, this defaults to the built-in ‘html2text’ function. In practice, the latter gives much better results. If you use ‘mu4e-shr2text’, it might be useful to emulate some of the shr key bindings, with something like: (add-hook 'mu4e-view-mode-hook (lambda() ;; try to emulate some of the eww key-bindings (local-set-key (kbd "<tab>") 'shr-next-link) (local-set-key (kbd "<backtab>") 'shr-previous-link))) If you’re using a dark theme, and the messages are hard to read, it can help to change the luminosity, e.g.: (setq shr-color-visible-luminance-min 80) If your emacs does not have shr yet, it can be useful to use a custom method. For that, you can set the variable ‘mu4e-html2text-command’ to either a shell command or a function instead. 5.5.1 Html2text commands ------------------------ If ‘mu4e-html2text-command’ is a shell command, it is expected to take html from standard input and write plain text in UTF-8 encoding on standard output. An example of such a program is the program that is actually _called_ html2text(2). After installation, you can set it up with something like the following: (setq mu4e-html2text-command "html2text -utf8 -width 72") An alternative to this is the Python python-html2text package; after installing that, you can tell mu4e to use it with something like: (setq mu4e-html2text-command "html2markdown | grep -v ' _place_holder;'") On OS X, there is a program called textutil as yet another alternative: (setq mu4e-html2text-command "textutil -stdin -format html -convert txt -stdout") 5.5.2 Html2text functions ------------------------- If ‘mu4e-html2text-command’ refers to an elisp function, it is expected to take the current buffer in html as input, and transform it into text (just like the ‘html2text’ function). 5.5.3 Privacy aspects --------------------- When opening your messages in a graphical browser, it may expose you doing so to the sender, due to the presence of specially crafted image URLs, or Javascript. If that is an issue, it is recommended to use a browser (or browser profile) that does not load images. The same applies to Javascript. ---------- Footnotes ---------- (1) this is e.g. for the case where the text-part is only a short blurb telling you to use the html-version; see ‘mu4e-view-html-plaintext-ratio-heuristic’ (2) <http://www.mbayer.de/html2text/>  File: mu4e.info, Node: MSGV Crypto, Next: MSGV Custom headers, Prev: Displaying rich-text messages, Up: Message view 5.6 Crypto ========== The mu4e message view supports(1) decryption of encrypted messages, as well as verification of signatures. For signing/encrypting messages your outgoing messages, see *note Signing and encrypting::. Currently, only PGP/MIME is supported; PGP-inline and S/MIME are not. For all of this to work, ‘gpg-agent’ must be running, and it must set the environment variable GPG_AGENT_INFO. You can check from ‘emacs’ with <M-x getenv GPG_AGENT_INFO>. In many mainstream Linux/Unix desktop environments, everything works out-of-the-box, but if your environment does not automatically start ‘gpg-agent’, you can do so by hand: $ eval $(gpg-agent --daemon) This starts the daemon, and sets the environment variable. 5.6.1 Decryption ---------------- If you receive messages that are encrypted (using PGP/MIME), mu4e can try to decrypt them, base on the setting of ‘mu4e-decryption-policy’. If you set it to t, mu4e attempts to decrypt messages automatically; this is the default. If you set it to nil, mu4e _won’t_ attempt to decrypt anything. Finally, if you set it to 'ask, it asks you what to do, each time an encrypted message is encountered. When opening an encrypted message, mu consults gpg-agent to see if it already has unlocked the key needed to decrypt the message; if not, it prompts you for a password (typically with a separate top-level window). This is only needed once per session. 5.6.2 Verifying signatures -------------------------- Some e-mail messages are cryptographically signed, and mu4e can check the validity of these signatures. If a message has one or more signatures, the message view shows an extra header Signature: (assuming it is part of your ‘mu4e-view-fields’), and one or more ’verdicts’ of the signatures found; either verified, unverified or error. For instance: Signature: unverified (Details) You can see the details of the signature verification by activating the Details or pressing <v>. This pops up a little window with the details of the signatures found and whether they could be verified or not. For more information, see the ‘mu-verify’ manual page. ---------- Footnotes ---------- (1) Crypto-support in mu4e requires mu to have been build with crypto-support; see the *note FAQ::  File: mu4e.info, Node: MSGV Custom headers, Next: MSGV Actions, Prev: MSGV Crypto, Up: Message view 5.7 Custom headers ================== Sometimes the normal headers that mu4e offers (Date, From, To, Subject etc.) may not be enough. For these cases, mu4e offers _custom headers_ in both the headers-view and the message-view. See *note HV Custom headers:: for an example of this; the difference for the message-view is that you should add your custom header to ‘mu4e-view-fields’ rather than ‘mu4e-headers-fields’.  File: mu4e.info, Node: MSGV Actions, Prev: MSGV Custom headers, Up: Message view 5.8 Actions =========== You can perform custom functions (“actions”) on messages and their attachments. For a general discussion on how to define your own, see see *note Actions::. 5.8.1 Message actions --------------------- ‘mu4e-view-action’ (<a>) lets you pick some custom action to perform on the current message. You can specify these actions using the variable ‘mu4e-view-actions’; mu4e defines a number of example actions. 5.8.2 Attachment actions ------------------------ Similarly, there is ‘mu4e-view-attachment-action’ (<A>) for actions on attachments, which you can specify with ‘mu4e-view-attachment-actions’. mu4e predefines a number of attachment-actions: • open-with (<w>): open the attachment with some arbitrary program. For example, suppose you have received a message with a picture attachment; then, ‘A w 1 RET gimp RET’ opens that attachment in _The Gimp_ • pipe (<|>: process the attachment with some Unix shell-pipe and see the results. Suppose you receive a patch file, and would like to get an overview of the changes, using the diffstat program. You can use something like: ‘A | 1 RET diffstat -b RET’. • ‘emacs’ (<e>): open the attachment in your running ‘emacs’. For example, if you receive some text file you’d like to open in ‘emacs’: ‘A e 1 RET’. These actions all work on a _temporary copy_ of the attachment.  File: mu4e.info, Node: Editor view, Next: Searching, Prev: Message view, Up: Top 6 The editor view ***************** Writing e-mail messages takes place in the Editor View. mu4e’s editor view builds on top of Gnu’s message-mode. Most of the message-mode functionality is available, as well some mu4e-specifics. Its major mode is ‘mu4e-compose-mode’. * Menu: * Overview: EV Overview. What is the Editor view * Keybindings: EV Keybindings. Doing things with your keyboard * Address autocompletion:: Quickly entering known addresses * Compose hooks::Calling functions when composing * Signing and encrypting:: Support for cryptography * Queuing mail:: Sending mail when the time is ripe * Message signatures:: Adding your personal footer to messages * Other settings::Miscellanea  File: mu4e.info, Node: EV Overview, Next: EV Keybindings, Up: Editor view 6.1 Overview ============ From: Rupert the Monkey <rupert@example.com> To: Wally the Walrus <wally@example.com> Subject: Re: Eau-qui d'eau qui? --text follows this line-- On Mon 16 Jan 2012 10:18:47 AM EET, Wally the Walrus wrote: > Hi Rupert, > > Dude - how are things? > > Later -- wally.  File: mu4e.info, Node: EV Keybindings, Next: Address autocompletion, Prev: EV Overview, Up: Editor view 6.2 Keybindings =============== mu4e’s editor view derives from Gnu’s message editor and shares most of its keybindings. Here are some of the more useful ones (you can use the menu to find more): key description --- ----------- C-c C-c send message C-c C-d save to drafts and leave C-c C-k kill the message buffer (the message remains in the draft folder) C-c C-a attach a file (pro-tip: drag & drop works as well) (mu4e-specific) C-S-u update mail & reindex  File: mu4e.info, Node: Address autocompletion, Next: Compose hooks, Prev: EV Keybindings, Up: Editor view 6.3 Address autocompletion ========================== mu4e supports(1) autocompleting addresses when composing e-mail messages. mu4e uses the e-mail addresses from the messages you sent or received as the source for this. Address auto-completion is enabled by default; if you want to disable it for some reason, set mu4e-compose-complete-addresses to nil. Emacs 24 also supports cycling through the alternatives. When there are more than _5_ matching addresses, they are shown in a *Completions* buffer. Once the number of matches gets below this number, one is inserted in the address field and you can cycle through the alternatives using <TAB>. 6.3.1 Limiting the number of addresses -------------------------------------- If you have a lot of mail, especially from mailing lists and the like, there can be a _lot_ of e-mail addresses, many of which may not be very useful when auto-completing. For this reason, mu4e attempts to limit the number of e-mail addresses in the completion pool by filtering out the ones that are not likely to be relevant. The following variables are available for tuning this: • ‘mu4e-compose-complete-only-personal’ - when set to t, only consider addresses that were seen in _personal_ messages – that is, messages in which one of my e-mail addresses was seen in one of the address fields. This is to exclude mailing list posts. You can define what is considered ’my e-mail address’ using ‘mu4e-user-mail-address-list’, a list of e-mail address (defaults to ‘user-mail-address’, and when indexing from the command line, the --my-address parameter for mu index. • ‘mu4e-compose-complete-only-after’ - only consider e-mail addresses last seen after some date. Parameter is a string, parseable by ‘org-parse-time-string’. This excludes old e-mail addresses. The default is "2010-01-01", i.e., only consider e-mail addresses seen since the start of 2010. • ‘mu4e-compose-complete-ignore-address-regexp’ - a regular expression to filter out other ’junk’ e-mail addresses; defaults to “no-?reply”. ---------- Footnotes ---------- (1) ‘emacs’ 23.2 or higher is required  File: mu4e.info, Node: Compose hooks, Next: Signing and encrypting, Prev: Address autocompletion, Up: Editor view 6.4 Compose hooks ================= If you want to change some setting, or execute some custom action before message composition starts, you can define a _hook function_. mu4e offers two hooks: • ‘mu4e-compose-pre-hook’: this hook is run _before_ composition starts; if you are composing a _reply_, _forward_ a message, or _edit_ an existing message, the variable ‘mu4e-compose-parent-message’ points to the message being replied to, forwarded or edited, and you can use ‘mu4e-message-field’ to get the value of various properties (and see *note Message functions::). • ‘mu4e-compose-mode-hook’: this hook is run just before composition starts, when the whole buffer has already been set up. This is a good place for editing-related settings. ‘mu4e-compose-parent-message’ (see above) is also at your disposal. Let’s look at some examples. First, suppose we want to set the From:-address for a reply message based on the receiver of the original: ;; 1) messages to me@foo.example.com should be replied with From:me@foo.example.com ;; 2) messages to me@bar.example.com should be replied with From:me@bar.example.com ;; 3) all other mail should use From:me@cuux.example.com (add-hook 'mu4e-compose-pre-hook (defun my-set-from-address () "Set the From address based on the To address of the original." (let ((msg mu4e-compose-parent-message)) ;; msg is shorter... (when msg (setq user-mail-address (cond ((mu4e-message-contact-field-matches msg :to "me@foo.example.com") "me@foo.example.com") ((mu4e-message-contact-field-matches msg :to "me@bar.example.com") "me@bar.example.com") (t "me@cuux.example.com"))))))) Second, as mentioned, ‘mu4e-compose-mode-hook’ is especially useful for editing-related settings. For example: (add-hook 'mu4e-compose-mode-hook (defun my-do-compose-stuff () "My settings for message composition." (set-fill-column 72) (flyspell-mode))) This hook is also useful for adding headers or changing headers, since the message is fully formed when this hook runs. For example, to add a Bcc:-header, you could add something like the following, using ‘message-add-header’ from ‘message-mode’. (add-hook 'mu4e-compose-mode-hook (defun my-add-bcc () "Add a Bcc: header." (save-excursion (message-add-header "Bcc: me@example.com\n")))) For a more general discussion about extending mu4e, see *note Extending mu4e::.  File: mu4e.info, Node: Signing and encrypting, Next: Queuing mail, Prev: Compose hooks, Up: Editor view 6.5 Signing and encrypting ========================== Signing and encrypting of messages is possible using emacs-mime (*note (emacs-mime)Composing::), most easily accessed through the Attachments-menu while composing a message, or with ‘M-x mml-secure-message-encrypt-pgp’, ‘M-x mml-secure-message-sign-pgp’. The support for encryption and signing is _independent_ of the support for their counterparts, decrypting and signature verification (as discussed in *note MSGV Crypto::). Even if your mu4e does not have support for the latter two, you can still sign/encrypt messages. Currently, decryption and signature verification only works for PGP/MIME; inline-PGP and S/MIME are not supported. Important note: the messages are encrypted when they are _sent_: this means that draft messages are _not_ encrypted. So if you are using e.g. offlineimap or mbsync to synchronize with some remote IMAP-service, make sure the drafts folder is _not_ in the set of synchronized folders, for obvious reasons.  File: mu4e.info, Node: Queuing mail, Next: Message signatures, Prev: Signing and encrypting, Up: Editor view 6.6 Queuing mail ================ If you cannot send mail right now, for example because you are currently offline, you can _queue_ the mail, and send it when you have restored your internet connection. You can control this from the *note Main view::. To allow for queuing, you need to tell smtpmail where you want to store the queued messages. For example: (setq smtpmail-queue-mail t ;; start in queuing mode smtpmail-queue-dir "~/Maildir/queue/cur") For convenience, we put the queue directory somewhere in our normal maildir. If you want to use queued mail, you should create this directory before starting mu4e. The ‘mu mkdir’ command may be useful here, so for example: $ mu mkdir ~/Maildir/queue $ touch ~/Maildir/queue/.noindex The file created by the ‘touch’ command tells mu to ignore this directory for indexing, which makes sense since it contains smtpmail meta-data rather than normal messages; see the mu-mkdir and mu-index man-pages for details. _Warning_: when you switch on queued-mode, your messages _won’t_ reach their destination until you switch it off again; so, be careful not to do this accidentally!  File: mu4e.info, Node: Message signatures, Next: Other settings, Prev: Queuing mail, Up: Editor view 6.7 Message signatures ====================== Message signatures are the standard footer blobs in e-mail messages where you can put in information you want to include in every message. The text to include is set with ‘mu4e-compose-signature’. If you don’t want to include this automatically with each message, you can set ‘mu4e-compose-signature-auto-include’ to ‘nil’; you can then still include the signature manually, using the function ‘message-insert-signature’, typically bound to ‘C-c C-w’.  File: mu4e.info, Node: Other settings, Prev: Message signatures, Up: Editor view 6.8 Other settings ================== • If you want use mu4e as ‘emacs’’ default program for sending mail, see *note Emacs default::. • Normally, mu4e _buries_ the message buffer after sending; if you want to kill the buffer instead, add something like the following to your configuration: (setq message-kill-buffer-on-exit t) • If you want to exclude your own e-mail address when “replying to all”, set ‘mu4e-compose-dont-reply-to-self’ to ‘t’. In order for this to work properly you need to properly set the ‘user-mail-address’ variable or in the case you use multiple e-mail addresses you need to set the ‘mu4e-user-mail-address-list’ variable accordingly.  File: mu4e.info, Node: Searching, Next: Marking, Prev: Editor view, Up: Top 7 Searching *********** mu4e is fully search-based: even if you ’jump to a folder’, you are executing a query for messages that happen to have the property of being in a certain folder (maildir). Normally, queries return up to ‘mu4e-headers-results-limit’ (default: 500) results. That is usually more than enough, and makes things significantly faster. Sometimes, however, you may want to show _all_ results; you can enable this with ‘M-x mu4e-headers-toggle-full-search’, or by customizing the variable ‘mu4e-headers-full-search’. This applies to all search commands. You can also influence the sort order and whether threads are shown or not; see *note Sorting and threading::. * Menu: * Queries:: Searching for messages. * Bookmarks:: Remembering queries. * Maildir searches:: Queries for maildirs. * Other search functionality:: Some more tricks.  File: mu4e.info, Node: Queries, Next: Bookmarks, Up: Searching 7.1 Queries =========== mu4e queries are the same as the ones that mu find understands(1). Let’s look at some examples here, please refer to the ‘mu-find’ and ‘mu-easy’ man pages for details and more examples. • Get all messages regarding _bananas_: bananas • Get all messages regarding _bananas_ from _John_ with an attachment: from:john flag:attach bananas • Get all messages with subject _wombat_ in June 2009 subject:wombat date:20090601..20090630 • Get all messages with PDF attachments in the /projects folder maildir:/projects mime:application/pdf • Get all messages about _Rupert_ in the /Sent Items folder. Note that maildirs with spaces must be quoted. maildir:"/Sent Items" rupert • Get all important messages which are signed: flag:signed prio:high • Get all messages from _Jim_ without an attachment: from:jim AND NOT flag:attach • Get all messages with Alice in one of the contacts-fields (to, from, cc, bcc): contact:alice • Get all unread messages where the subject mentions Ångström: (search is case-insensitive and accent-insensitive, so this matches Ångström, angstrom, aNGstrøM, ...) subject:Ångström flag:unread • Get all unread messages between Mar-2002 and Aug-2003 about some bird: date:20020301..20030831 nightingale flag:unread • Get today’s messages: date:today..now or, unless you have a really old Xapian date:today • Get all messages we got in the last two weeks regarding _emacs_: date:2w..now emacs or, unless you have a really old Xapian date:2w.. emacs • Get messages from the _Mu_ mailing list: list:mu-discuss.googlegroups.com Note - in the *note Headers view:: you may see the ’friendly name’ for a list; however, when searching you need the real name. You can see the real name for a mailing list from the friendly name’s tool-tip. • Get messages with a subject soccer, Socrates, society, ...; note that the ’*’-wildcard can only appear as a term’s rightmost character: subject:soc* • Get all messages _not_ sent to a mailing-list: NOT flag:list • Get all mails with attachments with filenames starting with _pic_; note that the ’*’ wildcard can only appear as the term’s rightmost character: file:pic* • Get all messages with PDF-attachments: mime:application/pdf Get all messages with image attachments, and note that the ’*’ wildcard can only appear as the term’s rightmost character: mime:image/* ---------- Footnotes ---------- (1) with the caveat that command-line queries are subject to the shell’s interpretation before mu sees them  File: mu4e.info, Node: Bookmarks, Next: Maildir searches, Prev: Queries, Up: Searching 7.2 Bookmarks ============= If you have queries that you use often, you may want to store them as _bookmarks_. Bookmark searches are available in the main view (*note Main view::), header view (*note Headers view::), and message view (*note Message view::), using (by default) the key <b> (‘M-x mu4e-search-bookmark’), or <B> (‘M-x mu4e-search-bookmark-edit’) which lets you edit the bookmark first. 7.2.1 Setting up bookmarks -------------------------- mu4e provides a number of default bookmarks. Their definition is instructive: (defvar mu4e-bookmarks `( ,(make-mu4e-bookmark :name "Unread messages" :query "flag:unread AND NOT flag:trashed" :key ?u) ,(make-mu4e-bookmark :name "Today's messages" :query "date:today..now" :key ?t) ,(make-mu4e-bookmark :name "Last 7 days" :query "date:7d..now" :key ?w) ,(make-mu4e-bookmark :name "Messages with images" :query "mime:image/*" :key ?p)) "A list of pre-defined queries. Each query is represented by a mu4e-bookmark structure with parameters :name with the name of the bookmark, :query with the query expression (a query string or an s-expression that evaluates to query string) and a :key, which is the shortcut-key for the query. An older form of bookmark, a 3-item list with (QUERY DESCRIPTION KEY) is still recognized as well, for backward-compatibility.") You can replace these or add your own items, by putting in your configuration (‘~/.emacs’) something like: (add-to-list 'mu4e-bookmarks (make-mu4e-bookmark :name "Big messages" :query "size:5M..500M" :key ?b)) This prepends your bookmark to the list, and assigns the key <b> to it. If you want to _append_ your bookmark, you can use ‘t’ as the third argument to ‘add-to-list’. In the various mu4e views, pressing <b> lists all the bookmarks defined in the echo area, with the shortcut key highlighted. So, to invoke the bookmark we just defined (to get the list of "Big Messages"), all you need to type is ‘bb’. 7.2.2 Lisp expressions as bookmarks ----------------------------------- Instead of using strings, it is also possible to use Lisp expressions as bookmarks. The only requirement is that they evaluate to a query string. For example, to get all the messages that are at most a week old in your inbox: (add-to-list 'mu4e-bookmarks (make-mu4e-bookmark :name "Inbox messages in the last 7 days" :query (concat "maildir:/inbox AND date:" (format-time-string "%Y%m%d" (subtract-time (current-time) (days-to-time 7)))) :key ?w) t) 7.2.3 Editing bookmarks before searching ---------------------------------------- There is also ‘M-x mu4e-headers-search-bookmark-edit’ (key <B>), which lets you edit the bookmarked query before invoking it. This can be useful if you have many similar queries, but need to change some parameter. For example, you could have a bookmark ‘"date:today..now AND "’(1), which limits any result to today’s messages. ---------- Footnotes ---------- (1) Not a valid search query by itself  File: mu4e.info, Node: Maildir searches, Next: Other search functionality, Prev: Bookmarks, Up: Searching 7.3 Maildir searches ==================== Maildir searches are quite similar to bookmark searches (see *note Bookmarks::), with the difference being that the target is always a maildir – maildir queries provide a ’traditional’ folder-like interface to a search-based e-mail client. By default, maildir searches are available in the *note Main view::, *note Headers view::, and *note Message view::, with the key <j> (‘mu4e-jump-to-maildir’). 7.3.1 Setting up maildir shortcuts ---------------------------------- You can search for maildirs like can for any other message property (e.g. with a query like maildir:/myfolder), but since it is so common, mu4e offers a shortcut for this. For this to work, you need to set the variable ‘mu4e-maildir-shortcuts’ to the list of maildirs you want to have quick access to, for example: (setq mu4e-maildir-shortcuts '( ("/inbox" . ?i) ("/archive" . ?a) ("/lists" . ?l) ("/work" . ?w) ("/sent" . ?s))) This sets <i> as a shortcut for the /inbox folder – effectively a query maildir:/inbox. There is a special shortcut <o> or </> for _other_ (so don’t use those for your own shortcuts!), which allows you to choose from _all_ maildirs that you have. There is support for autocompletion; note that the list of maildirs is determined when mu4e starts; if there are changes in the maildirs while mu4e is running, you need to restart mu4e. Each of the folder names is relative to your top-level maildir directory; so if you keep your mail in ‘~/Maildir’, ‘/inbox’ would refer to ‘~/Maildir/inbox’. With these shortcuts, you can jump around your maildirs (folders) very quickly - for example, getting to the /lists folder only requires you to type ‘jl’, then change to /work with ‘jw’. While in queries you need to quote folder names (maildirs) with spaces in them, you should _not_ quote them when used in ‘mu4e-maildir-shortcuts’, since mu4e does that automatically for you. The very same shortcuts are used by ‘M-x mu4e-mark-for-move’ (default shortcut <m>); so, for example, if you want to move a message to the /archive folder, you can do so by typing ‘ma’.  File: mu4e.info, Node: Other search functionality, Prev: Maildir searches, Up: Searching 7.4 Other search functionality ============================== 7.4.1 Navigating through search queries --------------------------------------- You can navigate through previous/next queries using ‘mu4e-headers-query-prev’ and ‘mu4e-headers-query-next’, which are bound to <M-left> and <M-right>, similar to what some web browsers do. mu4e tries to be smart and not record duplicate queries. Also, the number of queries remembered has a fixed limit, so mu4e won’t use too much memory, even if used for a long time. However, if you want to forget previous/next queries, you can use ‘M-x mu4e-headers-forget-queries’. 7.4.2 Narrowing search results ------------------------------ It can be useful to narrow existing search results, that is, to add some clauses to the current query to match fewer messages. For example, suppose you’re looking at some mailing list, perhaps by jumping to a maildir (‘M-x mu4e-headers-jump-to-maildir’, <j>) or because you followed some bookmark (‘M-x mu4e-headers-search-bookmark’, <b>). Now, you want to narrow things down to only those messages that have attachments. This is when ‘M-x mu4e-headers-search-narrow’ (</>) comes in handy. It asks for an additional search pattern, which is appended to the current search query, in effect getting you the subset of the currently shown headers that also match this extra search pattern. <\> takes you back to the previous query, so, effectively ’widens’ the search. Technically, narrowing the results of query x with expression y implies doing a search (x) AND (y). Note that messages that were not in your original search results because of ‘mu4e-headers-results-limit’ may show up in the narrowed query. 7.4.3 Including related messages -------------------------------- It can be useful to not only show the messages that directly match a certain query, but also include messages that are related to these messages. That is, messages that belong to the same discussion threads are included in the results, just like e.g. Gmail does it. You can enable this behavior by setting ‘mu4e-headers-include-related’ to ‘t’, and you can toggle between including/not-including with <W>. Be careful though when e.g. deleting ranges of messages from a certain folder – the list may now also include messages from _other_ folders. 7.4.4 Skipping duplicates ------------------------- Another useful feature is skipping of _duplicate messages_. When you have copies of messages, there’s usually little value in including more than one in search results. A common reason for having multiple copies of messages is the combination of Gmail and offlineimap, since that is the way the labels / virtual folders in Gmail are represented. You can enable skipping duplicates by setting ‘mu4e-headers-skip-duplicates’ to ‘t’, and you can toggle between the skipping/not skipping with <V>. Note, messages are considered duplicates when they have the same Message-Id.  File: mu4e.info, Node: Marking, Next: Contexts, Prev: Searching, Up: Top 8 Marking ********* In mu4e, the common way to do things with messages is a two-step process - first you _mark_ them for a certain action, then you _execute_ (<x>) those marks. This is similar to the way dired operates. Marking can happen in both the *note Headers view:: and the *note Message view::. * Menu: * Marking messages::Selecting message do something with them * What to mark for::What can we do with them * Executing the marks::Do it * Leaving the headers buffer::Handling marks automatically when leaving * Built-in marking functions::Helper functions for dealing with them * Custom mark functions::Define your own mark function * Adding a new kind of mark::Adding your own marks  File: mu4e.info, Node: Marking messages, Next: What to mark for, Up: Marking 8.1 Marking messages ==================== There are multiple ways to mark messages: • _message at point_: you can put a mark on the message-at-point in either the *note Headers view:: or *note Message view:: • _region_: you can put a mark on all messages in the current region (selection) in the *note Headers view:: • _pattern_: you can put a mark on all messages in the *note Headers view:: matching a certain pattern with ‘M-x mu4e-headers-mark-pattern’ (<%>) • _thread/subthread_: You can put a mark on all the messages in the thread/subthread at point with ‘M-x mu4e-headers-mark-thread’ and ‘M-x mu4e-headers-mark-subthread’, respectively  File: mu4e.info, Node: What to mark for, Next: Executing the marks, Prev: Marking messages, Up: Marking 8.2 What to mark for ==================== mu4e supports a number of marks: mark for/as | keybinding | description -------------+-------------+------------------------------ 'something' | *, <insert> | mark now, decide later delete | D, <delete> | delete flag | + | mark as 'flagged' ('starred') move | m | move to some maildir read | ! | mark as read refile | r | mark for refiling trash | d | move to the trash folder untrash | = | remove 'trash' flag unflag | - | remove 'flagged' mark unmark | u | remove mark at point unmark all | U | remove all marks unread | ? | marks as unread action | a | apply some action After marking a message, the left-most columns in the headers view indicate the kind of mark. This is informative, but if you mark many (say, thousands) messages, this slows things down significantly(1). For this reason, you can disable this by setting ‘mu4e-headers-show-target’ to ‘nil’. something is a special kind of mark; you can use it to mark messages for ’something’, and then decide later what the ’something’ should be(2) Later, you can set the actual mark using ‘M-x mu4e-mark-resolve-deferred-marks’ (<#>). Alternatively, mu4e will ask you when you try to execute the marks (<x>). ---------- Footnotes ---------- (1) this uses an ‘emacs’ feature called _overlays_, which are slow when used a lot in a buffer (2) This kind of ’deferred marking’ is similar to the facility in dired, midnight commander (<http://www.midnight-commander.org/>) and the like, and uses the same key binding (<insert>).  File: mu4e.info, Node: Executing the marks, Next: Leaving the headers buffer, Prev: What to mark for, Up: Marking 8.3 Executing the marks ======================= After you have marked some messages, you can execute them with <x> (‘M-x mu4e-mark-execute-all’). A hook, ‘mu4e-mark-execute-pre-hook’, is available which is run right before execution of each mark. The hook is called with two arguments, the mark and the message itself.  File: mu4e.info, Node: Leaving the headers buffer, Next: Built-in marking functions, Prev: Executing the marks, Up: Marking 8.4 Leaving the headers buffer ============================== When you quit or update a headers buffer that has marked messages (for example, by doing a new search), mu4e asks you what to do with them, depending on the value of the variable ‘mu4e-headers-leave-behavior’ – see its documentation.  File: mu4e.info, Node: Built-in marking functions, Next: Custom mark functions, Prev: Leaving the headers buffer, Up: Marking 8.5 Built-in marking functions ============================== Some examples of mu4e’s built-in marking functions. • _Mark the message at point for trashing_: press <d> • _Mark all messages in the buffer as unread_: press ‘C-x h o’ • _Delete the messages in the current thread_: press ‘T D’ • _Mark messages with a subject matching “hello” for flagging_: press ‘% s hello RET’.  File: mu4e.info, Node: Custom mark functions, Next: Adding a new kind of mark, Prev: Built-in marking functions, Up: Marking 8.6 Custom mark functions ========================= Sometimes, the built-in functions to mark messages may not be sufficient for your needs. For this, mu4e offers an easy way to define your own custom mark functions. You can choose one of the custom marker functions by pressing <&> in the *note Headers view:: and *note Message view::. Custom mark functions are to be appended to the list ‘mu4e-headers-custom-markers’. Each of the elements of this list (’markers’) is a list with two or three elements: 1. The name of the marker - a short string describing this marker. The first character of this string determines its shortcut, so these should be unique. If necessary, simply prefix the name with a unique character. 2. a predicate function, taking two arguments MSG and PARAM. MSG is the message plist (see *note Message functions::) and PARAM is a parameter provided by the third of the marker elements (see the next item). The predicate function should return non-nil if the message matches. 3. (optionally) a function that is evaluated once, and the result is passed as a parameter to the predicate function. This is useful when user-input is needed. Let’s look at an example: suppose we want to match all messages that have more than _n_ recipients – we could do this with the following recipe: (add-to-list 'mu4e-headers-custom-markers '("More than n recipients" (lambda (msg n) (> (+ (length (mu4e-message-field msg :to)) (length (mu4e-message-field msg :cc))) n)) (lambda () (read-number "Match messages with more recipients than: "))) t) After evaluating this expression, you can use it by pressing <&> in the headers buffer to select a custom marker function, and then <M> to choose this particular one (M because it is the first character of the description). As you can see, it’s not very hard to define simple functions to match messages. There are more examples in the defaults for ‘mu4e-headers-custom-markers’; see ‘mu4e-headers.el’ and see *note Extending mu4e:: for general information about writing your own functions.  File: mu4e.info, Node: Adding a new kind of mark, Prev: Custom mark functions, Up: Marking 8.7 Adding a new kind of mark ============================= It is possible to configure new marks. To do so one can add entries in the list ‘mu4e-marks’. Such an element must have the following form: (SYMBOL :char STRING :prompt STRING :ask-target (lambda () TARGET) :dyn-target (lambda (TARGET MSG) DYN-TARGET) :show-target (lambda (DYN-TARGET) STRING) :action (lambda (DOCID MSG DYN-TARGET) nil)) The symbol can be any symbol, except for ’unmark and ’something, which are reserved. The rest is a plist with the following elements: • ‘:char’ – the character to display in the headers view. • ‘:prompt’ – the prompt to use when asking for marks (used for example when marking a whole thread). • ‘:ask-target’ – a function run once per bulk-operation, and thus suitable for querying the user about a target for move-like marks. If nil, the TARGET passed to ‘:dyn-target’ is nil. • ‘:dyn-target’ – a function run once per message (The message is passed as MSG to the function). This function allows to compute a per-message target, for refile-like marks. If nil, the DYN-TARGET passed to the ‘:action’ is the TARGET obtained as above. • ‘:show-target’ – how to display the target in the headers view. If ‘:show-target’ is nil the DYN-TARGET is shown (and DYN-TARGET must be a string). • ‘:action’ – the action to apply on the message when the mark is executed. As an example, suppose we would like to add a mark for tagging messages (gmail-style), then we can run the following code (after loading mu4e): (add-to-list 'mu4e-marks '(tag :char "g" :prompt "gtag" :ask-target (lambda () (read-string "What tag do you want to add?")) :action (lambda (docid msg target) (mu4e-action-retag-message msg (concat "+" target))))) As another example, suppose we would like to “archive and mark read” a message (gmail-style), then we can run the following code (after loading mu4e): (add-to-list 'mu4e-marks '(archive :char "A" :prompt "Archive" :show-target (lambda (target) "archive") :action (lambda (docid msg target) ;; must come before proc-move since retag runs ;; 'sed' on the file (mu4e-action-retag-message msg "-\\Inbox") (mu4e~proc-move docid nil "+S-u-N")))) Adding to ‘mu4e-marks’ list allows to use the mark in bulk operations (for example when tagging a whole thread), but does not bind the mark to a key to use at the top-level. This must be done separately. In our example: (mu4e~headers-defun-mark-for tag) (mu4e~headers-defun-mark-for archive) (define-key mu4e-headers-mode-map (kbd "g") 'mu4e-headers-mark-for-tag) (define-key mu4e-headers-mode-map (kbd "A") 'mu4e-headers-mark-for-archive)  File: mu4e.info, Node: Contexts, Next: Dynamic folders, Prev: Marking, Up: Top 9 Contexts ********** * Menu: * What are contexts::Defining the concept * Context policies::How to determine the current context * Contexts and special folders::Using context variables to determine them * Contexts example::How to define contexts * Some context tricks::Other thing to do with contexts It can be useful to switch between different sets of settings in mu4e; a typical example is the case where you have different e-mail accounts for private and work email, each with their own values for folders, e-mail addresses, mailservers and so on. The ‘mu4e-context’ system is a mu4e-specific mechanism to allow for that; users can be define different contexts corresponding with groups of setting and either manually switch between them, or let mu4e determine the right context when composing a message based on some user-provided function. Note that there are a number of existing ways to switch accounts in mu4e, for example using the method described in the *note Tips and Tricks:: section of this manual. Those still work - but the new mechanism has the benefit of being a core part of ‘mu4e’, thus allowing for deeper integration.  File: mu4e.info, Node: What are contexts, Next: Context policies, Up: Contexts 9.1 What are contexts ===================== Let’s see what’s contained in a context. Most of it is optional. A ‘mu4e-context’ is Lisp object with the following members: • name: the name of the context, e.g. work or private • vars: an association-list (alist) of variable settings for this account. • enter-func: an (optional) function that takes no parameter and is invoked when entering the context. You can use this for extra setup etc. • leave-func: an (optional) function that takes no parameter and is invoked when leaving the context. You can use this for clearing things up. • match-func: an (optional) function that takes an MSG message plist as argument, and returns non-nil if this context matches the situation. mu4e uses the first context that matches, in a couple of situations: • when starting mu4e to determine the starting context; in this case, MSG is nil. You can use e.g. the host you’re running or the time of day to determine which context matches. • before replying to or forwarding a message with the given message plist as parameter, or nil when composing a brand new message. The function should return t when this context is the right one for this message, or nil otherwise. • when determining the target folders for deleting, refiling etc; see *note Contexts and special folders::. mu4e uses a variable ‘mu4e-contexts’, which is a list of such objects.  File: mu4e.info, Node: Context policies, Next: Contexts and special folders, Prev: What are contexts, Up: Contexts 9.2 Context policies ==================== When you have defined contexts and you start mu4e it decides which context to use based on the variable ‘mu4e-context-policy’; similarly, when you compose a new message, the context is determined using ‘mu4e-compose-context-policy’. For both of these, you can choose one of the following policies: • a symbol ‘always-ask’: unconditionally ask the user what context to pick. The other choices only apply if none of the contexts match (i.e., none of the contexts’ match-functions returns ‘t’). We have the following options: • a symbol ‘ask’: ask the user if mu4e can’t figure things out the context by itself (through the match-function). This is a good policy if there are no match functions, or if the match functions don’t cover all cases. • a symbol ‘ask-if-none’: if there’s already a context, don’t change it; otherwise, ask the user. • a symbol ‘pick-first’: pick the first (default) context. This is a good choice if you want to specify context for special case, and fall back to the first one if none match. • ‘nil’: don’t change the context; this is useful if you don’t change contexts very often, and e.g. manually changes contexts with ‘M-x mu4e-context-switch’.  File: mu4e.info, Node: Contexts and special folders, Next: Contexts example, Prev: Context policies, Up: Contexts 9.3 Contexts and special folders ================================ As we discussed in *note Folders:: and *note Dynamic folders::, mu4e recognizes a number of special folders: ‘mu4e-sent-folder’, ‘mu4e-drafts-folder’, ‘mu4e-trash-folder’ and ‘mu4e-refile-folder’. When you have a headers-buffer with messages that belong to different contexts (say, a few different accounts), it is desirable for each of them to use the specific folders for their own context - so, for instance, if you trash a message, it needs to go to the trash-folder for the account it belongs to, which is not necessarily the current context. To make this easy to do, whenever mu4e needs to know the value for such a special folder for a given message, it tries to determine the appropriate context using ‘mu4e-context-determine’ (and policy nil; see *note Context policies::). If it finds a matching context, it let-binds the ‘vars’ for that account, and then determines the value for the folder. It does not, however, call the ‘enter-func’ or ‘leave-func’, since we are not really switching context. In practice, this means that as long as each of the accounts has a good match-func, all message operations automatically find the appropriate folders.  File: mu4e.info, Node: Contexts example, Next: Some context tricks, Prev: Contexts and special folders, Up: Contexts 9.4 Example =========== Let’s explain how contexts work by looking at an example. We define two contexts, ’Private’ and ’Work’ for a fictional user _Alice Derleth_. Note that in this case, we automatically switch to the first context when starting; see the discussion in the previous section. (setq mu4e-contexts `( ,(make-mu4e-context :name "Private" :enter-func (lambda () (mu4e-message "Entering Private context")) :leave-func (lambda () (mu4e-message "Leaving Private context")) ;; we match based on the contact-fields of the message :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg :to "aliced@home.example.com"))) :vars '( ( user-mail-address . "aliced@home.example.com" ) ( user-full-name . "Alice Derleth" ) ( mu4e-compose-signature . (concat "Alice Derleth\n" "Lauttasaari, Finland\n")))) ,(make-mu4e-context :name "Work" :enter-func (lambda () (mu4e-message "Switch to the Work context")) ;; no leave-func ;; we match based on the contact-fields of the message :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg :to "aderleth@miskatonic.example.com"))) :vars '( ( user-mail-address . "aderleth@miskatonic.example.com" ) ( user-full-name . "Alice Derleth" ) ( mu4e-compose-signature . (concat "Prof. Alice Derleth\n" "Miskatonic University, Dept. of Occult Sciences\n")))) ,(make-mu4e-context :name "Cycling" :enter-func (lambda () (mu4e-message "Switch to the Cycling context")) ;; no leave-func ;; we match based on the maildir of the message; assume all ;; cycling-related messages go into the /cycling maildir :match-func (lambda (msg) (when msg (string= (mu4e-message-field msg :maildir) "/cycling"))) :vars '( ( user-mail-address . "aderleth@example.com" ) ( user-full-name . "AliceD" ) ( mu4e-compose-signature . nil))))) ;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should ;; guess or ask the correct context, e.g. ;; start with the first (default) context; ;; default is to ask-if-none (ask when there's no context yet, and none match) ;; (setq mu4e-context-policy 'pick-first) ;; compose with the current context is no context matches; ;; default is to ask ;; (setq mu4e-compose-context-policy nil) A couple of notes about this example: • You can manually switch the focus use ‘M-x mu4e-context-switch’, by default bound to ‘;’ in headers, view and main mode. The current focus appears in the mode-line. • Normally, ‘M-x mu4e-context-switch’ does not call the enter or leave functions if the ’new’ context is the same as the old one. However, with a prefix-argument (‘C-u’), you can force mu4e to invoke those function even in that case. • The function ‘mu4e-context-current’ returns the current-context; the current context is also visible in the mode-line when in headers, view or main mode. • You can set any kind of variable; including settings for mail servers etc. However, settings such as ‘mu4e-maildir’ and ‘mu4e-mu-home’ are not changeable after they have been set without quitting mu4e first. • ‘leave-func’ (if defined) for the context we are leaving, is invoked before the ‘enter-func’ (if defined) of the context we are entering. • ‘enter-func’ (if defined) is invoked before setting the variables. • ‘match-func’ (if defined) is invoked just before ‘mu4e-compose-pre-hook’. • See the variables ‘mu4e-context-policy’ and ‘mu4e-compose-context-policy’ to tweak what mu4e should do when no context matches (or if you always want to be asked). • Finally, be careful to get the quotations right – backticks, single quotes and commas and note the ’.’ between variable name and its value.  File: mu4e.info, Node: Some context tricks, Prev: Contexts example, Up: Contexts 9.5 Some context tricks ======================= It is possible to automatically fill ‘mu4e-user-address-list’ by concatenating the ‘user-mail-address’ fields of all contexts: ;; This sets `mu4e-user-mail-address-list' to the concatenation of all ;; `user-mail-address' values for all contexts. If you have other mail ;; addresses as well, you'll need to add those manually. (setq mu4e-user-mail-address-list (delq nil (mapcar (lambda (context) (when (mu4e-context-vars context) (cdr (assq 'user-mail-address (mu4e-context-vars context))))) mu4e-contexts)))  File: mu4e.info, Node: Dynamic folders, Next: Actions, Prev: Contexts, Up: Top 10 Dynamic folders ****************** In *note Folders::, we explained how you can set up mu4e’s special folders: (setq mu4e-sent-folder "/sent" ;; sent messages mu4e-drafts-folder "/drafts" ;; unfinished messages mu4e-trash-folder "/trash" ;; trashed messages mu4e-refile-folder "/archive") ;; saved messages01 In some cases, having such static folders may not suffice - perhaps you want to change the folders depending on the context. For example, the folder for refiling could vary, based on the sender of the message. To make this possible, instead of setting the standard folders to a string, you can set them to be a _function_ that takes a message as its parameter, and returns the desired folder name. This chapter shows you how to do that. For a more general discussion of how to extend mu4e and writing your own functions, see *note Extending mu4e::. If you use mu4e-context, see *note Contexts and special folders:: for what that means for these special folders. * Menu: * Smart refiling:: Automatically choose the target folder * Other dynamic folders:: Flexible folders for sent, trash, drafts  File: mu4e.info, Node: Smart refiling, Next: Other dynamic folders, Up: Dynamic folders 10.1 Smart refiling =================== When refiling messages, perhaps to archive them, it can be useful to have different target folders for different messages, based on some property of those message – smart refiling. To accomplish this, we can set the refiling folder (‘mu4e-refile-folder’) to a function that returns the actual refiling folder for the particular message. An example should clarify this: (setq mu4e-refile-folder (lambda (msg) (cond ;; messages to the mu mailing list go to the /mu folder ((mu4e-message-contact-field-matches msg :to "mu-discuss@googlegroups.com") "/mu") ;; messages sent directly to me go to /archive ;; also `mu4e-user-mail-address-p' can be used ((mu4e-message-contact-field-matches msg :to "me@example.com") "/private") ;; messages with football or soccer in the subject go to /football ((string-match "football\\|soccer" (mu4e-message-field msg :subject)) "/football") ;; messages sent by me go to the sent folder ((find-if (lambda (addr) (mu4e-message-contact-field-matches msg :from addr)) mu4e-user-mail-address-list) mu4e-sent-folder) ;; everything else goes to /archive ;; important to have a catch-all at the end! (t "/archive")))) This can be very powerful; you can select some messages in the headers view, then press <r>, and have them all marked for refiling to their particular folders. Some notes: • We set ‘mu4e-refile-folder’ to an anonymous (lambda) function. This function takes one argument, a message plist(1). The plist corresponds to the message at point. See *note Message functions:: for a discussion on how to deal with them. • In our function, we use a cond control structure; the function returns the first of the clauses that matches. It’s important to make the last clause a catch-all, so we always return _some_ folder. • We use the convenience function ‘mu4e-message-contact-field-matches’, which evaluates to ‘t’ if any of the names or e-mail addresses in a contact field (in this case, the To:-field) matches the regular expression. With mu4e version 0.9.16 or newer, the contact field can in fact be a list instead of a single value, such as ‘'(:to :cc)'’ ---------- Footnotes ---------- (1) a property list describing a message  File: mu4e.info, Node: Other dynamic folders, Prev: Smart refiling, Up: Dynamic folders 10.2 Other dynamic folders ========================== Using the same mechanism, you can create dynamic sent-, trash-, and drafts-folders. The message-parameter you receive for the sent and drafts folder is the _original_ message, that is, the message you reply to, or forward, or edit. If there is no such message (for example when composing a brand new message) the message parameter is nil. Let’s look at an example. Suppose you want a different trash folder for work-email. You can achieve this with something like: (setq mu4e-trash-folder (lambda (msg) ;; the 'and msg' is to handle the case where msg is nil (if (and msg (mu4e-message-contact-field-matches msg :to "me@work.example.com")) "/trash-work" "/trash"))) Good to remember: • The MSG parameter you receive in the function refers to the _original message_, that is, the message being replied to or forwarded. When re-editing a message, it refers to the message being edited. When you compose a totally new message, the MSG parameter is ‘nil’. • When re-editing messages, the value of ‘mu4e-drafts-folder’ is ignored.  File: mu4e.info, Node: Actions, Next: Extending mu4e, Prev: Dynamic folders, Up: Top 11 Actions ********** mu4e lets you define custom actions for messages in the *note Headers view:: and for both messages and attachments in the *note Message view::. Custom actions allow you to easily extend mu4e for specific needs – for example, marking messages as spam in a spam filter or applying an attachment with a source code patch. You can invoke the actions with key <a> for actions on messages, and key <A> for actions on attachments. For general information extending mu4e and writing your own functions, see *note Extending mu4e::. * Menu: * Defining actions::How to create an action * Headers view actions::Doing things with message headers * Message view actions::Doing things with messages * Attachment actions::Doing things with attachments * Example actions::Some more examples  File: mu4e.info, Node: Defining actions, Next: Headers view actions, Up: Actions 11.1 Defining actions ===================== Defining a new custom action comes down to writing an elisp-function to do the work. Functions that operate on messages receive a MSG parameter, which corresponds to the message at point. Something like: (defun my-action-func (msg) "Describe my message function." ;; do stuff ) Messages that operate on attachments receive a MSG parameter, which corresponds to the message at point, and an ATTACHMENT-NUM, which is the number of the attachment as seen in the message view. An attachment function looks like: (defun my-attachment-action-func (msg attachment-num) "Describe my attachment function." ;; do stuff ) After you have defined your function, you can add it to the list of actions(1), either ‘mu4e-headers-actions’, ‘mu4e-view-actions’ or ‘mu4e-view-attachment-actions’. The format(2) of each action is a cons-cell, ‘(DESCRIPTION . VALUE)’; see below for some examples. If your shortcut is not also the first character of the description, simply prefix the description with that character. Let’s look at some examples. ---------- Footnotes ---------- (1) Instead of defining the functions separately, you can obviously also add a ‘lambda’-function directly to the list; however, separate functions are easier to change (2) Note, the format of the actions has changed since version 0.9.8.4, and you must change your configuration to use the new format; mu4e warns you when you are using the old format.  File: mu4e.info, Node: Headers view actions, Next: Message view actions, Prev: Defining actions, Up: Actions 11.2 Headers view actions ========================= Suppose we want to inspect the number of recipients for a message in the *note Headers view::. We add the following to our configuration: (defun show-number-of-recipients (msg) "Display the number of recipients for the message at point." (message "Number of recipients: %d" (+ (length (mu4e-message-field msg :to)) (length (mu4e-message-field msg :cc))))) ;; define 'N' (the first letter of the description) as the shortcut ;; the 't' argument to add-to-list puts it at the end of the list (add-to-list 'mu4e-headers-actions '("Number of recipients" . show-number-of-recipients) t) After evaluating this, ‘a N’ in the headers view shows the number of recipients for the message at point.  File: mu4e.info, Node: Message view actions, Next: Attachment actions, Prev: Headers view actions, Up: Actions 11.3 Message view actions ========================= As another example, suppose we would like to search for messages by the sender of the message at point: (defun search-for-sender (msg) "Search for messages sent by the sender of the message at point." (mu4e-headers-search (concat "from:" (cdar (mu4e-message-field msg :from))))) ;; define 'x' as the shortcut (add-to-list 'mu4e-view-actions '("xsearch for sender" . search-for-sender) t) If you wonder why we use ‘cdar’, remember that the From:-field is a list of ‘(NAME . EMAIL)’ cells; thus, ‘cdar’ gets us the e-mail address of the first in the list. From:-fields rarely contain multiple cells.  File: mu4e.info, Node: Attachment actions, Next: Example actions, Prev: Message view actions, Up: Actions 11.4 Attachment actions ======================= Finally, let’s define an attachment action. As mentioned, attachment-action functions receive _2_ arguments, the message and the attachment number to use. The following example action counts the number of lines in an attachment, and defines <n> as its shortcut key (the <n> is prefixed to the description). (defun count-lines-in-attachment (msg attachnum) "Count the number of lines in an attachment." (mu4e-view-pipe-attachment msg attachnum "wc -l")) ;; defining 'n' as the shortcut (add-to-list 'mu4e-view-attachment-actions '("ncount lines" . count-lines-in-attachment) t)  File: mu4e.info, Node: Example actions, Prev: Attachment actions, Up: Actions 11.5 Example actions ==================== mu4e includes a number of example actions in the file ‘mu4e-actions.el’ in the source distribution (see ‘C-h f mu4e-action-TAB’). For example, for viewing messages in an external web browser, or listening to a message’s body-text using text-to-speech.  File: mu4e.info, Node: Extending mu4e, Next: Interaction with other tools, Prev: Actions, Up: Top 12 Extending mu4e ***************** mu4e is designed to be easily extendible - that is, write your own emacs-lisp to make mu4e behave exactly as you want. Here, we provide some guidelines for doing so. * Menu: * Extension points::Where to hook into mu4e * Available functions::General helper functions * Message functions::Working with messages * Contact functions::Working with contacts * Utility functions::Miscellaneous helpers  File: mu4e.info, Node: Extension points, Next: Available functions, Up: Extending mu4e 12.1 Extension points ===================== There are a number of places where mu4e lets you plug in your own functions: • Custom functions for message headers in the message-view and headers-view - see *note HV Custom headers::, *note MSGV Custom headers:: • Using message-specific folders for drafts, trash, sent messages and refiling, based on a function - see *note Dynamic folders:: • Using an attachment-specific download-directory - see the variable ‘mu4e-attachment-dir’. • Apply a function to a message in the headers view - see *note Headers view actions:: • Apply a function to a message in the message view - see *note Message view actions:: • Add a new kind of mark for use in the headers view - see *note Adding a new kind of mark:: • Apply a function to an attachment - see *note Attachment actions:: • Custom function to mark certain messages - see *note Custom mark functions:: • Using various _mode_-hooks, ‘mu4e-compose-pre-hook’ (see *note Compose hooks::), ‘mu4e-index-updated-hook’ (see *note FAQ::) You can also write your own functions without using the above. If you want to do so, key useful functions are ‘mu4e-message-at-point’ (see below), ‘mu4e-headers-for-each’ (to iterate over all headers, see its docstring) and ‘mu4e-view-for-each-part’ (to iterate over all parts/attachments, see its docstring). There is also ‘mu4e-view-for-each-uri’ to iterate of all the URIs in the current message. Another useful function is ‘mu4e-headers-find-if’ which searches for a message matching a certain pattern; again, see its docstring.  File: mu4e.info, Node: Available functions, Next: Message functions, Prev: Extension points, Up: Extending mu4e 12.2 Available functions ======================== The whole of mu4e consists of hundreds of elisp functions. However, the majority of those are for _internal_ use only; you can recognize them easily, because they all start with ‘mu4e~’. These function make all kinds of assumptions, and they are subject to change, and should therefore _not_ be used. The same is true for _variables_ that start with ‘mu4e~’; don’t touch them. Let me repeat that: Do not use mu4e~... functions or variables! In addition, you should use functions in the right context; functions that start with mu4e-view- are only applicable to the message view, while functions starting with mu4e-headers- are only applicable to the headers view. Functions without such prefixes are applicable everywhere.  File: mu4e.info, Node: Message functions, Next: Contact functions, Prev: Available functions, Up: Extending mu4e 12.3 Message functions ====================== Many functions in mu4e deal with message plists (property lists). They contain information about messages, such as sender and recipient, subject, date and so on. To deal with these plists, there are a number of ‘mu4e-message-’ functions (in ‘mu4e-message.el’), such as ‘mu4e-message-field’ and ‘mu4e-message-at-point’, and a shortcut to combine the two, ‘mu4e-message-field-at-point’. For example, to get the subject of the message at point, in either the headers view or the message view, you could write: (mu4e-message-field (mu4e-message-at-point) :subject) Note that: • The contact fields (To, From, Cc, Bcc) are lists of cons-pairs ‘(name . email)’; ‘name’ may be ‘nil’. So, for example: (mu4e-message-field some-msg :to) ;; => (("Jack" . "jack@example.com") (nil . "foo@example.com")) If you are only looking for a match in this list (e.g., “Is Jack one of the recipients of the message?”), there is a convenience function ‘mu4e-message-contact-field-matches’ to make this easy. • The message body is only available in the message view, not in the headers view. Note that in headers-mode, you only have access to a reduced message plist, without the information about the message-body or mime-parts; mu4e does this for performance reasons. And even in view-mode, you do not have access to arbitrary message-headers. However, it is possible to get the information indirectly, using the raw-message and some third-party tool like procmail’s formail: (defun my-mu4e-any-message-field-at-point (hdr) "Quick & dirty way to get an arbitrary header HDR at point. Requires the 'formail' tool from procmail." (replace-regexp-in-string "\n$" "" (shell-command-to-string (concat "formail -x " hdr " -c < " (shell-quote-argument (mu4e-message-field-at-point :path))))))  File: mu4e.info, Node: Contact functions, Next: Utility functions, Prev: Message functions, Up: Extending mu4e 12.4 Contact functions ====================== It can sometimes be useful to rewrite the contact information that mu4e provides, for example to convert them to some standardized format, or to fix spelling errors. And sometimes, you may want to remove certain contacts altogether. For this, mu4e provides ‘mu4e-contact-rewrite-function’, which passes each contact to a user-provided function, which is expected to return either the possibly rewritten contact or ‘nil’ to remove the contact from the list - note that the latter can also be achieved using ‘mu4e-compose-complete-ignore-address-regexp’. Each of the contacts are property-lists (’plists’), with properties ‘:name’ (which may be ‘nil’), and ‘:mail’, and a number of other properties which you should return unchanged. Let’s look at an example: (defun my-rewrite-function (contact) (let ((name (or (plist-get contact :name) "")) (mail (plist-get contact :mail))) (cond ;; jonh smiht --> John Smith ((string= "jonh smiht" name) (plist-put contact :name "John C. Smith") contact) ;; remove evilspammer from the contacts list ((string= "evilspammer@example.com" mail) nil) ;; others stay as the are (t contact)))) (setq mu4e-contact-rewrite-function 'my-rewrite-function) This function is called for each of your contacts.  File: mu4e.info, Node: Utility functions, Prev: Contact functions, Up: Extending mu4e 12.5 Utility functions ====================== ‘mu4e-utils’ contains a number of utility functions; we list a few here; see their docstrings for the details: • ‘mu4e-read-option’: read one option from a list. For example: (mu4e-read-option "Choose an animal: " '(("Monkey" . monkey) ("Gnu" . gnu) ("xMoose" . moose))) The user is presented with: Choose an animal: [M]onkey, [G]nu, [x]Moose • ‘mu4e-ask-maildir’: ask for a maildir; try one of the shortcuts (‘mu4e-maildir-shortcuts’), or the full set of available maildirs. • ‘mu4e-running-p’: return ‘t’ if the mu4e process is running, ‘nil’ otherwise. • ‘(mu4e-user-mail-address-p addr)’: return ‘t’ if ADDR is one of the user’s e-mail addresses (as per ‘mu4e-user-mail-address-list’). • ‘mu4e-log’ logs to the mu4e debugging log if it is enabled; see ‘mu4e-toggle-logging’. • ‘mu4e-message’, ‘mu4e-warning’, ‘mu4e-error’ are the mu4e equivalents of the normal elisp ‘message’, ‘user-error’(1) and ‘error’ functions. ---------- Footnotes ---------- (1) ‘user-error’ only appears in ‘emacs’ 24.2 and later; in older versions it falls back to ‘error’  File: mu4e.info, Node: Interaction with other tools, Next: Example configurations, Prev: Extending mu4e, Up: Top Appendix A Interaction with other tools *************************************** In this chapter, we discuss some ways in ways in which mu4e can cooperate with other tools. * Menu: * Emacs default::Making mu4e the default emacs e-mail program * Org-mode links::Adding mu4e to your organized life * Org-contacts::Hooking up with org-contacts * BBDB::Hooking up with the Insidious Big Brother Database * Sauron::Getting new mail notifications with Sauron * Speedbar::A special frame with your folders * Mu-cite:: Fancy citation engine * Dired:: Attaching files using dired  File: mu4e.info, Node: Emacs default, Next: Org-mode links, Up: Interaction with other tools A.1 Emacs default ================= ‘emacs’ allows you to select an e-mail program as the default program it uses when you press <C-x m> (‘compose-mail’), call ‘report-emacs-bug’ and so on. If you want to use mu4e for this, you can do so by adding the following to your configuration: (setq mail-user-agent 'mu4e-user-agent)  File: mu4e.info, Node: Org-mode links, Next: Org-contacts, Prev: Emacs default, Up: Interaction with other tools A.2 Org-mode links ================== It can be useful to include links to e-mail messages or even search queries in your org-mode files. mu4e supports this with the org-mu4e module; you can set it up by adding it to your configuration, which expects org-mode 8.x. or higher(1) (require 'org-mu4e) After this, you can use the normal org-mode mechanisms to store links: ‘M-x org-store-link’ stores a link to a particular message when you are in *note Message view::. When you are in *note Headers view::, ‘M-x org-store-link’ links to the _query_ if ‘org-mu4e-link-query-in-headers-mode’ is non-‘nil’, and to the particular message otherwise (which is the default). You can insert this link later with ‘M-x org-insert-link’. From org-mode, you can go to the query or message the link points to with either ‘M-x org-agenda-open-link’ in agenda buffers, or ‘M-x org-open-at-point’ elsewhere - both typically bound to ‘C-c C-o’. You can also directly _capture_ such links - for example, to add e-mail messages to your todo-list. For that, org-mu4e has a function ‘org-mu4e-store-and-capture’. This captures the message-at-point (or header - see the discussion on ‘org-mu4e-link-query-in-headers-mode’ above), then calls org-mode’s capture functionality. You can add some specific capture-template for this, for example, to add a message to your todo-list, and set a deadline for processing it within two days, you could add this to ‘org-capture-templates’: ("P" "process-soon" entry (file+headline "todo.org" "Todo") "* TODO %a %?\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))") If you use the functionality a lot, you may want to define key-bindings for that in headers and view mode: (define-key mu4e-headers-mode-map (kbd "C-c c") 'org-mu4e-store-and-capture) (define-key mu4e-view-mode-map (kbd "C-c c") 'org-mu4e-store-and-capture) ---------- Footnotes ---------- (1) If you have an older version, you can try using org-old-mu4e instead  File: mu4e.info, Node: Org-contacts, Next: BBDB, Prev: Org-mode links, Up: Interaction with other tools A.3 Org-contacts ================ Note, mu4e supports built-in address autocompletion; *note Address autocompletion::, and that is the recommended way to do this. However, it is also possible to manage your addresses with org-mode, using org-contacts(1). mu4e-actions defines a useful action (*note Actions::) for adding a contact based on the From:-address in the message at point. To enable this, add to your configuration something like: (setq mu4e-org-contacts-file <full-path-to-your-org-contacts-file>) (add-to-list 'mu4e-headers-actions '("org-contact-add" . mu4e-action-add-org-contact) t) (add-to-list 'mu4e-view-actions '("org-contact-add" . mu4e-action-add-org-contact) t) After this, you should be able to add contacts using <a o> in the headers view and the message view, using the org-capture mechanism. Note, the shortcut character <o> is due to the first character of org-contact-add. ---------- Footnotes ---------- (1) <http://julien.danjou.info/software/org-contacts.el>  File: mu4e.info, Node: BBDB, Next: Sauron, Prev: Org-contacts, Up: Interaction with other tools A.4 BBDB ======== Note, mu4e supports built-in address autocompletion; *note Address autocompletion::, and that is the recommended way to do this. However, it is also possible to manage your addresses with the current (2015-06-23) development release of BBDB, or releases of BBDB after 3.1.2.(1). To enable BBDB, add to your ‘~/.emacs’ (or its moral equivalent, such as ‘~/.emacs.d/init.el’) the following _after_ the ‘(require 'mu4e)’ line: ;; Load BBDB (Method 1) (require 'bbdb-loaddefs) ;; OR (Method 2) ;; (require 'bbdb-loaddefs "/path/to/bbdb/lisp/bbdb-loaddefs.el") ;; OR (Method 3) ;; (autoload 'bbdb-insinuate-mu4e "bbdb-mu4e") ;; (bbdb-initialize 'message 'mu4e) (setq bbdb-mail-user-agent 'mu4e-user-agent) (setq mu4e-view-mode-hook 'bbdb-mua-auto-update visual-line-mode) (setq mu4e-compose-complete-addresses nil) (setq bbdb-mua-pop-up t) (setq bbdb-mua-pop-up-window-size 5) (setq mu4e-view-show-addresses t) After this, you should be able to: • In mu4e-view mode, add the sender of the email to BBDB with <C-u :> • Tab-complete addresses from BBDB when composing emails • View the BBDB contact while viewing a message ---------- Footnotes ---------- (1) <http://savannah.nongnu.org/projects/bbdb/>  File: mu4e.info, Node: Sauron, Next: Speedbar, Prev: BBDB, Up: Interaction with other tools A.5 Sauron ========== The ‘emacs’-package sauron(1) (by the same author) can be used to get notifications about new mails. If you run something like the below script from your crontab (or have some other way of having it execute every _n_ minutes), you receive notifications in the sauron-buffer when new messages arrive. #!/bin/sh # the mu binary MU=mu # put the path to your Inbox folder here CHECKDIR="/home/$LOGNAME/Maildir/Inbox" sauron_msg () { DBUS_COOKIE="/home/$LOGNAME/.sauron-dbus" if test "x$DBUS_SESSION_BUS_ADDRESS" = "x"; then if test -e $DBUS_COOKIE; then export DBUS_SESSION_BUS_ADDRESS="`cat $DBUS_COOKIE`" fi fi if test -n "x$DBUS_SESSION_BUS_ADDRESS"; then dbus-send --session \ --dest="org.gnu.Emacs" \ --type=method_call \ "/org/gnu/Emacs/Sauron" \ "org.gnu.Emacs.Sauron.AddMsgEvent" \ string:shell uint32:3 string:"$1" fi } # # -mmin -5: consider only messages that were created / changed in the # the last 5 minutes # for f in `find $CHECKDIR -mmin -5 -a -type f`; do subject=`$MU view $f | grep '^Subject:' | sed 's/^Subject://'` sauron_msg "mail: $subject" done You might want to put: (setq sauron-dbus-cookie t) in your setup, to allow the script to find the D-Bus session bus, even when running outside its session. ---------- Footnotes ---------- (1) Sauron can be found at <https://github.com/djcb/sauron>, or in the Marmalade package-repository at <http://http://marmalade-repo.org/>  File: mu4e.info, Node: Speedbar, Next: Mu-cite, Prev: Sauron, Up: Interaction with other tools A.6 Speedbar ============ ‘speedbar’ is an ‘emacs’-extension that shows navigational information for an ‘emacs’ buffer in a separate frame. Using ‘mu4e-speedbar’, mu4e lists your bookmarks and maildir folders and allows for one-click access to them. mu4e loads mu4e-speedbar automatically; all you need to do to activate it is ‘M-x speedbar’. Then, when then switching to the *note Main view::, the speedbar-frame is updated with your bookmarks and maildirs. For speed reasons, the list of maildirs is determined when mu4e starts; if the list of maildirs changes while mu4e is running, you need to restart mu4e to have those changes reflected in the speedbar and in other places that use this list, such as auto-completion when jumping to a maildir. ‘mu4e-speedbar’ was contributed by _Antono Vasiljev_.  File: mu4e.info, Node: Mu-cite, Next: Dired, Prev: Speedbar, Up: Interaction with other tools A.7 Mu-cite =========== mu-cite(1) is a package to control the way message citations look like (i.e., the message you responded to when you reply to them or forward them), with its latest version available at <http://www.jpl.org/elips/mu/>. After installing mu-cite, you can use something like the following to make it work with mu4e: (require 'mu-cite) (setq mu4e-compose-cite-function 'mu-cite-original) (setq mu-cite-top-format '("On " date ", " from " wrote:\n\n")) (setq mu-cite-prefix-format '(" > ")) ---------- Footnotes ---------- (1) Note, despite its name, mu-cite is a project unconnected to mu/mu4e  File: mu4e.info, Node: Dired, Prev: Mu-cite, Up: Interaction with other tools A.8 Dired ========= It is possible to attach files to mu4e messages using dired (*note (emacs)Dired::), using the following steps (based on a post on the mu-discuss mailing list by _Stephen Eglen_). To prepare for this, you need a special version of the ‘gnus-dired-mail-buffers’ function so it understands mu4e buffers as well; so put in your configuration: (require 'gnus-dired) ;; make the `gnus-dired-mail-buffers' function also work on ;; message-mode derived modes, such as mu4e-compose-mode (defun gnus-dired-mail-buffers () "Return a list of active message buffers." (let (buffers) (save-current-buffer (dolist (buffer (buffer-list t)) (set-buffer buffer) (when (and (derived-mode-p 'message-mode) (null message-sent-message-via)) (push (buffer-name buffer) buffers)))) (nreverse buffers))) (setq gnus-dired-mail-mode 'mu4e-user-agent) (add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode) Then, mark the file(s) in dired you would like to attach and press C-c RET C-a, and you’ll be asked whether to attach them to an existing message, or create a new one.  File: mu4e.info, Node: Example configurations, Next: FAQ, Prev: Interaction with other tools, Up: Top Appendix B Example configurations ********************************* In this chapter, we show some example configurations. While it is very useful to see some working settings, we’d like to warn against blindly copying such things. * Menu: * Minimal configuration::Simplest configuration to get you going * Longer configuration::A more extensive setup * Gmail configuration::GMail-specific setup * Other settings:CONF Other settings. Some other useful configuration  File: mu4e.info, Node: Minimal configuration, Next: Longer configuration, Up: Example configurations B.1 Minimal configuration ========================= An (almost) minimal configuration for mu4e might look like this - as you see most is commented-out. ;; example configuration for mu4e ;; make sure mu4e is in your load-path (require 'mu4e) ;; Only needed if your maildir is _not_ ~/Maildir ;; Must be a real dir, not a symlink ;;(setq mu4e-maildir "/home/user/Maildir") ;; these must start with a "/", and must exist ;; (i.e.. /home/user/Maildir/sent must exist) ;; you use e.g. 'mu mkdir' to make the Maildirs if they don't ;; already exist ;; below are the defaults; if they do not exist yet, mu4e offers to ;; create them. they can also functions; see their docstrings. ;; (setq mu4e-sent-folder "/sent") ;; (setq mu4e-drafts-folder "/drafts") ;; (setq mu4e-trash-folder "/trash") ;; smtp mail setting; these are the same that `gnus' uses. (setq message-send-mail-function 'smtpmail-send-it smtpmail-default-smtp-server "smtp.example.com" smtpmail-smtp-server "smtp.example.com" smtpmail-local-domain "example.com")  File: mu4e.info, Node: Longer configuration, Next: Gmail configuration, Prev: Minimal configuration, Up: Example configurations B.2 Longer configuration ======================== A somewhat longer configuration, showing some more things that you can customize. ;; example configuration for mu4e (require 'mu4e) ;; path to our Maildir directory (setq mu4e-maildir "/home/user/Maildir") ;; the next are relative to `mu4e-maildir' ;; instead of strings, they can be functions too, see ;; their docstring or the chapter 'Dynamic folders' (setq mu4e-sent-folder "/sent" mu4e-drafts-folder "/drafts" mu4e-trash-folder "/trash") ;; the maildirs you use frequently; access them with 'j' ('jump') (setq mu4e-maildir-shortcuts '(("/archive" . ?a) ("/inbox" . ?i) ("/work" . ?w) ("/sent" . ?s))) ;; a list of user's e-mail addresses (setq mu4e-user-mail-address-list '("foo@bar.example.com" "cuux@example.com") ;; the headers to show in the headers list -- a pair of a field ;; and its width, with `nil' meaning 'unlimited' ;; (better only use that for the last field. ;; These are the defaults: (setq mu4e-headers-fields '( (:date . 25) ;; alternatively, use :human-date (:flags . 6) (:from . 22) (:subject . nil))) ;; alternatively, use :thread-subject ;; program to get mail; alternatives are 'fetchmail', 'getmail' ;; isync or your own shellscript. called when 'U' is pressed in ;; main view. ;; If you get your mail without an explicit command, ;; use "true" for the command (this is the default) (setq mu4e-get-mail-command "offlineimap") ;; general emacs mail settings; used when composing e-mail ;; the non-mu4e-* stuff is inherited from emacs/message-mode (setq mu4e-reply-to-address "foo@bar.example.com" user-mail-address "foo@bar.example.com" user-full-name "Foo X. Bar") (setq mu4e-compose-signature "Foo X. Bar\nhttp://www.example.com\n") ;; smtp mail setting (setq message-send-mail-function 'smtpmail-send-it smtpmail-default-smtp-server "smtp.example.com" smtpmail-smtp-server "smtp.example.com" smtpmail-local-domain "example.com" ;; if you need offline mode, set these -- and create the queue dir ;; with 'mu mkdir', i.e.. mu mkdir /home/user/Maildir/queue smtpmail-queue-mail nil smtpmail-queue-dir "/home/user/Maildir/queue/cur") ;; don't keep message buffers around (setq message-kill-buffer-on-exit t)  File: mu4e.info, Node: Gmail configuration, Next: CONF Other settings, Prev: Longer configuration, Up: Example configurations B.3 Gmail configuration ======================= _Gmail_ is a popular e-mail provider; let’s see how we can make it work with mu4e. Since we are using IMAP, you must enable that in the Gmail web interface (in the settings, under the “Forwarding and POP/IMAP”-tab). Gmail users may also be interested in *note skipping duplicates: Including related messages. B.3.1 Setting up offlineimap ---------------------------- First of all, we need a program to get the e-mail from Gmail to our local machine; for this we use offlineimap; on Debian (and derivatives like Ubuntu), this is as easy as: $ sudo apt-get install offlineimap while on Fedora (and similar) you need: $ sudo yum install offlineimap Then, we can configure offlineimap by editing ‘~/.offlineimaprc’: [general] accounts = Gmail maxsyncaccounts = 3 [Account Gmail] localrepository = Local remoterepository = Remote [Repository Local] type = Maildir localfolders = ~/Maildir [Repository Remote] type = IMAP remotehost = imap.gmail.com remoteuser = USERNAME@gmail.com remotepass = PASSWORD ssl = yes maxconnections = 1 realdelete = no Obviously, you need to replace USERNAME and PASSWORD with your actual Gmail username and password. After this, you should be able to download your mail: $ offlineimap OfflineIMAP 6.3.4 Copyright 2002-2011 John Goerzen & contributors. Licensed under the GNU GPL v2+ (v2 or any later version). Account sync Gmail: ***** Processing account Gmail Copying folder structure from IMAP to Maildir Establishing connection to imap.gmail.com:993. Folder sync [Gmail]: Syncing INBOX: IMAP -> Maildir Syncing [Gmail]/All Mail: IMAP -> Maildir Syncing [Gmail]/Drafts: IMAP -> Maildir Syncing [Gmail]/Sent Mail: IMAP -> Maildir Syncing [Gmail]/Spam: IMAP -> Maildir Syncing [Gmail]/Starred: IMAP -> Maildir Syncing [Gmail]/Trash: IMAP -> Maildir Account sync Gmail: ***** Finished processing account Gmail We can now run ‘mu’ to make sure things work: $ mu index mu: indexing messages under /home/foo/Maildir [/home/foo/.mu/xapian] | processing mail; processed: 520; updated/new: 520, cleaned-up: 0 mu: elapsed: 3 second(s), ~ 173 msg/s mu: cleaning up messages [/home/foo/.mu/xapian] / processing mail; processed: 520; updated/new: 0, cleaned-up: 0 mu: elapsed: 0 second(s) We can run both the offlineimap and the mu index from within mu4e, but running it from the command line makes it a bit easier to troubleshoot as we are setting things up. Note: when using encryption, you probably do _not_ want to synchronize your Drafts-folder, since it contains the unencrypted messages. You can use OfflineIMAP’s folderfilter for that. B.3.2 Settings -------------- Next step: let’s make a mu4e configuration for this: (require 'mu4e) ;; default ;; (setq mu4e-maildir "~/Maildir") (setq mu4e-drafts-folder "/[Gmail].Drafts") (setq mu4e-sent-folder "/[Gmail].Sent Mail") (setq mu4e-trash-folder "/[Gmail].Trash") ;; don't save message to Sent Messages, Gmail/IMAP takes care of this (setq mu4e-sent-messages-behavior 'delete) ;; (See the documentation for `mu4e-sent-messages-behavior' if you have ;; additional non-Gmail addresses and want assign them different ;; behavior.) ;; setup some handy shortcuts ;; you can quickly switch to your Inbox -- press ``ji'' ;; then, when you want archive some messages, move them to ;; the 'All Mail' folder by pressing ``ma''. (setq mu4e-maildir-shortcuts '( ("/INBOX" . ?i) ("/[Gmail].Sent Mail" . ?s) ("/[Gmail].Trash" . ?t) ("/[Gmail].All Mail" . ?a))) ;; allow for updating mail using 'U' in the main view: (setq mu4e-get-mail-command "offlineimap") ;; something about ourselves (setq user-mail-address "USERNAME@gmail.com" user-full-name "Foo X. Bar" mu4e-compose-signature (concat "Foo X. Bar\n" "http://www.example.com\n")) ;; sending mail -- replace USERNAME with your gmail username ;; also, make sure the gnutls command line utils are installed ;; package 'gnutls-bin' in Debian/Ubuntu (require 'smtpmail) (setq message-send-mail-function 'smtpmail-send-it starttls-use-gnutls t smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil)) smtpmail-auth-credentials '(("smtp.gmail.com" 587 "USERNAME@gmail.com" nil)) smtpmail-default-smtp-server "smtp.gmail.com" smtpmail-smtp-server "smtp.gmail.com" smtpmail-smtp-service 587) ;; alternatively, for emacs-24 you can use: ;;(setq message-send-mail-function 'smtpmail-send-it ;; smtpmail-stream-type 'starttls ;; smtpmail-default-smtp-server "smtp.gmail.com" ;; smtpmail-smtp-server "smtp.gmail.com" ;; smtpmail-smtp-service 587) ;; don't keep message buffers around (setq message-kill-buffer-on-exit t) And that’s it – put the above in your ‘~/.emacs’, change USERNAME etc. to your own, and restart ‘emacs’, and run ‘M-x mu4e’.  File: mu4e.info, Node: CONF Other settings, Prev: Gmail configuration, Up: Example configurations B.4 Other settings ================== Finally, here are some more settings that are useful, but not enabled by default for various reasons. ;; use 'fancy' non-ascii characters in various places in mu4e (setq mu4e-use-fancy-chars t) ;; save attachment to my desktop (this can also be a function) (setq mu4e-attachment-dir "~/Desktop") ;; attempt to show images when viewing messages (setq mu4e-view-show-images t)  File: mu4e.info, Node: FAQ, Next: Tips and Tricks, Prev: Example configurations, Up: Top Appendix C FAQ - Frequently Asked Questions ******************************************* In this chapter we list a number of actual and anticipated questions and their answers. * Menu: * General::General questions and answers about mu4e * Retrieving mail::Getting mail and indexing * Reading messages::Dealing with incoming messages * Writing messages::Dealing with outgoing messages * Known issues::Limitations we know about  File: mu4e.info, Node: General, Next: Retrieving mail, Up: FAQ C.1 General =========== 1. _Does mu4e provide context-sensitive help information?_ Yes - pressing <H> should take you to the right section in this manual. 2. _How can I quickly delete/move/trash a lot of messages?_ You can select (’mark’ in ‘emacs’-speak) the messages like you would select text in a buffer; the actions you then take (e.g., <DEL> for delete, <m> for move and <t> for trash) apply to all selected messages. You can also use functions like ‘mu4e-headers-mark-thread’ (<T>), ‘mu4e-headers-mark-subthread’ (<t>) to mark whole threads at the same time, and ‘mu4e-headers-mark-pattern’ (<%>) to mark all messages matching a certain regular expression. 3. _mu4e seems to return a subset of all matches - how can I get all?_ For speed reasons, mu4e returns only up to the value of the variable ‘mu4e-search-result-limit’ (default: 500) matches. To show _all_, use ‘M-x mu4e-headers-toggle-full-search’ (<Q>), or customize the variable ‘mu4e-headers-full-search’. This applies to all search commands. 4. _Can I automatically apply the marks on messages when leaving the headers buffer?_ Yes you can – see the documentation for the variable mu4e-headers-leave-behavior. 5. _How can I set mu4e as the default e-mail client in ‘emacs’?_ See *note Emacs default::. 6. _Can mu4e use some fancy Unicode characters instead of these boring plain-ASCII ones?_ Glad you asked! Yes, if you set ‘mu4e-use-fancy-chars’ to t, mu4e uses such fancy characters in a number of places. Since not all fonts include all characters, you may want to install the unifont and/or symbola fonts on your system. 7. _Can I start mu4e in the background?_ Yes - if you provide a prefix-argument (<C-u>), mu4e starts, but does not show the main-window. 8. _Does mu4e support searching for CJK (Chinese-Japanese-Korean) characters?_ Yes, if you have Xapian 1.2.8 or newer, and set the environment variable XAPIAN_CJK_NGRAM to non-empty before indexing, both when using mu from the command-line and from mu4e. 9. _How can I customize the function to select a folder?_ The mu4e-completing-read-function variable can be customized to select a folder in any way. The variable can be set to a function that receives five arguments, following completing-read. The default value is ‘ido-completing-read’; to use emacs’s default behaviour, set the variable to ‘completing-read’. Helm users can use the same value, and by enabling ‘helm-mode’ use helm-style completion. 10. _I have a lot of Maildir folders, so regenerating them each time makes things slow. What can I do?_ Set ‘mu4e-cache-maildir-list’ to ‘t’ (but make sure to read its docstring).  File: mu4e.info, Node: Retrieving mail, Next: Reading messages, Prev: General, Up: FAQ C.2 Retrieving mail =================== 1. _How can I get notifications when receiving mail?_ There is ‘mu4e-index-updated-hook’, which gets triggered when the indexing process triggered sees an update (not just new mail though). To use this hook, put something like the following in your setup (assuming you have aplay and some soundfile, change as needed): (add-hook 'mu4e-index-updated-hook (defun new-mail-sound () (shell-command "aplay ~/Sounds/boing.wav&"))) 2. _It seems my headers-buffer is automatically updated when new messages are found during the indexing process – can I disable this somehow?_ Yes - set ‘mu4e-headers-auto-update’ to ‘nil’. 3. _I don’t use offlineimap, fetchmail etc., I get my mail through my own mailserver. What should I use for ‘mu4e-get-mail-command’_? Use the literal string "true" (or don’t do anything, it’s the default) which then uses /bin/true (a command that does nothing and always succeeds). This makes getting mail a no-op, but the messages are still re-indexed. 4. _How can I re-index my messages without getting new mail?_ Use ‘M-x mu4e-update-index’ 5. _When I try to run mu index while mu4e is running I get errors like:_ mu: mu_store_new_writable: xapian error 'Unable to get write lock on ~/.mu/xapian: already locked _What to do about this?_ You get this error because the underlying Xapian database is locked by some other process; it can be opened only once in read-write mode. There is not much mu4e can do about this, but if is another ‘mu’ instance that is holding the lock, you can ask it to (gracefully) terminate: pkill -2 -u $UID mu # send SIGINT sleep 1 mu index mu4e automatically restarts mu when it needs it. In practice, this seems to work quite well. 6. _I don’t like the Indexing... messages that the indexing process gives me. Can I turn them off?_. Yes: set the variable ‘mu4e-hide-index-messages’ to non-nil. 7. _Some IMAP-synchronization programs such as mbsync (but not offlineimap) don’t like it when message files do not change their names when they are moved to different folders. Can mu4e somehow accommodate this?_ Yes - you can set the variable ‘mu4e-change-filenames-when-moving’ to non-nil. 8. _‘offlineimap’ uses IMAP’s UTF-7 for encoding non-ascii folder names, while mu expects UTF-8 (so, e.g. /まりも えお(1) becomes /&MH4wijCCMEgwSg-). How can I make mu4e display such folders correctly?_ This is best solved by telling ‘offlineimap’ to use UTF-8 instead – see <https://github.com/djcb/mu/issues/68#issuecomment-8598652>. ---------- Footnotes ---------- (1) some Japanese characters  File: mu4e.info, Node: Reading messages, Next: Writing messages, Prev: Retrieving mail, Up: FAQ C.3 Reading messages ==================== 1. _How can I view attached images in my message view buffers?_ See *note Viewing images inline::. 2. _How can I word-wrap long lines in when viewing a message?_ You can toggle between wrapped and non-wrapped states using <w>. If you want to do this automatically, invoke ‘visual-line-mode’ in your ‘mu4e-view-mode-hook’. 3. _How can I perform custom actions on messages and attachments?_ See *note Actions::. 4. _Does mu4e support crypto (i.e., decrypting messages and verifying signatures)?_ Yes – if mu was built with GMime 2.6 or later, it is possible to do both (note, only PGP/MIME is supported). In the *note Main view:: the support is indicated by a big letter C on the right hand side of the mu4e version. See *note Decryption:: and *note Verifying signatures::. For encryption and signing messages, see *note Writing messages::. 5. _How can I prevent mu4e from automatically marking messages as ’read’ when i read them?_ Set ‘mu4e-view-auto-mark-as-read’ to ‘nil’. 6. _Does mu4e support including all related messages in a thread, like Gmail does?_ Yes – see *note Including related messages::. 7. _There seem to be a lot of duplicate messages – how can I get rid of them?_ See *note Skipping duplicates::. 8. _How can I use the eww browser to view rich-text messages?_ With a new enough emacs, this happens automatically. See *note Html2text functions:: for some details. 9. _Some messages are almost unreadable in emacs - can I view them in an external web browser?_ Indeed, airlines often send messages that heavily depend on html and are hard to digest inside emacs. Fortunately, there’s an _action_ (*note Message view actions::) defined for this. Simply add to your configuration: (add-to-list 'mu4e-view-actions '("ViewInBrowser" . mu4e-action-view-in-browser) t) Now, when viewing such a difficult message, type ‘aV’, and the message opens inside a webbrowser. You can influence the browser with ‘browse-url-generic-program’; and see *note Privacy aspects::. 10. _How can read encrypted messages that I sent?_. Since you do not own the recipient’s key you typically cannot read those mails - so the trick is to encrypt outgoing mails with your key, too. This can be automated by adding the following snippet to your configuration (courtesy of user kpachnis): (require 'epg-config) (setq mml2015-use 'epg epg-user-id "gpg_key_id" mml2015-encrypt-to-self t mml2015-sign-with-sender t) 11. _view-as-pdf seems to hang for some e-mails - what can I do about that?_ Short answer: install nspluginwrapper. Longer answer: mu comes with msg2pdf, which is a program used to convert the messages to pdf, and which depends on WebKit, which in some cases needs nspluginwrapper and it waits for a long time if it’s not there. So, installing nspluginwrapper prevents that. 12. _Can I ’bounce’ or ’resend’ messages?_ Yes - it is possible to edit a (copy of) an existing message and then send it, using ‘M-x mu4e-compose-resend’. This gives you a raw copy of the message, including all headers, encoded parts and so on. Reason for this is that for resending, it is important not to change anything (except perhaps for the ’To:’ address when bouncing); since we cannot losslessly decode an existing message, you get the raw version.  File: mu4e.info, Node: Writing messages, Next: Known issues, Prev: Reading messages, Up: FAQ C.4 Writing messages ==================== 1. _What’s the deal with replies to messages I wrote myself?_ Like many other mail-clients, mu4e treats replies to messages you wrote yourself as special – these message keep the same To: and Cc: as the original message. This is to ease the common case of following up to a message you wrote earlier. 2. _How can I automatically set the From:-address for a reply-message, based on some field in the original?_ See *note Compose hooks::. 3. _And what about customizable folders for draft messages, sent messages, trashed messages, based on e.g. the From: header?_ See *note Dynamic folders::. 4. _Can I define aliases for (groups of) e-mail addresses?_ Sure - see *note (emacs) Mail Aliases::. 5. _How can I automatically add some header to an outgoing message?_ Once more, see *note Compose hooks::. 6. _How can I influence the way the original message looks when replying or forwarding?_ Since ‘mu4e-compose-mode’ derives from ‘message-mode’, you can re-use many of its facilities. *note (message)Insertion Variables::. 7. _How can I easily include attachments in the messages I write?_ You can drag-and-drop from your desktop; alternatively, you can use dired – see *note Dired::. 8. _mu4e seems to remove myself from the Cc:-list; how can I prevent that?_ Set ‘mu4e-compose-keep-self-cc’ to t in your configuration. 9. _How can I start a new message-thread from a reply?_ Remove the In-Reply-To header, and mu4e automatically removes the (hidden) References header as well when sending it. This makes the message show up as a top-level message rather than as a response. 10. _How can I attach an existing message?_ Use ‘mu4e-action-capture-message’ (i.e., ‘a c’ in the headers view) to ’capture’ the to-be-attached message, then when editing the message, use ‘M-x mu4e-compose-attach-captured-message’. 11. _How can I sign or encrypt messages?_ You can do so using ‘emacs’’ MIME-support – check the Attachments-menu while composing a message. Also see *note Signing and encrypting::. 12. _Can I use BBDB with mu4e?_ Yes, with the current (2015-06-23) development release of BBDB <http://savannah.nongnu.org/projects/bbdb/>, or releases of BBDB after 3.1.2. *note BBDB::. 13. _After sending some messages, it seems the buffer for these messages stay around. How can I get rid of those?_ (setq message-kill-buffer-on-exit t) 14. _Sending big messages is slow and blocks emacs - what can I do about it?_ For this, there’s <https://github.com/jwiegley/emacs-async> (also available from the Emacs package repository); add the following snippet to your configuration: (require 'smtpmail-async) (setq send-mail-function 'async-smtpmail-send-it message-send-mail-function 'async-smtpmail-send-it) With this, messages are sent using background emacs-instance. A word of warning though, this tends to not be as reliable as sending the message in the normal, synchronous fashion, and people have reported silent failures, where mail sending fails for some reason without any indication of that. You can check the progress of the background by checking the *Messages*-buffer, which should show something like: Delivering message to "William Shakespeare" <will@example.com>... Mark set Saving file /home/djcb/Maildir/sent/cur/20130706-044350-darklady:2,S... Wrote /home/djcb/Maildir/sent/cur/20130706-044350-darklady:2,S Sending...done The first and final messages are the most important, and there may be considerable time between them, depending on the size of the message. 15. _Is it possible to compose messages in a separate frame?_ Yes - set the variable ‘mu4e-compose-in-new-frame’ to ‘t’. 16. _How can I apply format=flowed to my outgoing messages, enabling receiving clients that support this feature to reflow my paragraphs?_ Plain text emails with Content-Type: text/plain; format=flowed can be reflowed (i.e. line endings removed, paragraphs refilled) by receiving clients that support this standard. Clients that don’t support this, show them as is, which means this feature is truly non-invasive. Here’s an explanatory blog post which also shows why this is a desirable feature: <https://mathiasbynens.be/notes/gmail-plain-text> (if you don’t have it, your mails mostly look quite bad especially on mobile devices) and here’s the RFC with all the details: <http://www.ietf.org/rfc/rfc2646.txt>. Since version 0.9.17, mu4e send emails with format=flowed by setting (setq mu4e-compose-format-flowed t) in your Emacs init file (‘~/.emacs’ or ‘~/.emacs.d/init.el’). The transformation of your message into the proper format is done at the time of sending. In order to happen properly, you should write each paragraph of your message of as a long line (i.e. without carriage return). If you introduce unwanted newlines in your paragraph, use ‘M-q’ to reformat it as a single line. If you want to send the message with paragraphs on single lines but without format=flowed (because, say, the receiver does not understand the latter as it is the case for Google or Github), use ‘M-x use-hard-newlines’ (to turn ‘use-hard-newlines’ off) or uncheck the box format=flowed in the Text menu when composing a message.  File: mu4e.info, Node: Known issues, Prev: Writing messages, Up: FAQ C.5 Known issues ================ Although they are not really _questions_, we end this chapter with a list of known issues and/or missing features in mu4e. Thus, users won’t have to search in vain for things that are not there (yet), and the author can use it as a todo-list. • _mu4e does not work well if the ‘emacs’ language environment is not UTF-8_; so, if you encounter problems with encodings, be sure to have ‘(set-language-environment "UTF-8")’ in your ‘~/.emacs’. • _Thread handling is incomplete._ While threads are calculated and are visible in the headers buffer, you cannot collapse/open them. • _The key-bindings are _somewhat_ hard-coded._ That is, the main menu assumes the default key-bindings, as do the clicks-on-bookmarks. • _The emacs front-end of the notmuch e-mail indexer conflicts with mu4e_. notmuch running in parallel with mu4e leads to error in process filter: mu4e-error-handler: Error 70: cannot read ~/Maildir/... when sending a reply to some e-mail. This seems to be caused by notmuch changing the name of the original message file while mu4e is working on it. To prevent this, deactivate notmuch in your Emacs setup. • _The PDF-version of the manual does not show any of the non-ASCII characters_ - this is because the texi2pdf documentation system does not support those. There is not much we can do about that. For a more complete list, please refer to the issues-list in the github-repository.  File: mu4e.info, Node: Tips and Tricks, Next: How it works, Prev: FAQ, Up: Top Appendix D Tips and Tricks ************************** * Menu: * Fancy characters:: Non-ascii characters in the UI * Multiple accounts:: (Obsolete) the old way to deal with multiple accounts * Refiling messages:: Moving message to some archive folder * Saving outgoing messages:: Automatically save sent messages * Confirmation before sending:: Check messages before sending  File: mu4e.info, Node: Fancy characters, Next: Multiple accounts, Up: Tips and Tricks D.1 Fancy characters ==================== When using ’fancy characters’ (‘mu4e-use-fancy-chars’) with the _Inconsolata_-font (and likely others as well), the display may be slightly off; the reason for this issue is that Inconsolata does not contain the glyphs for the ’fancy’ arrows and the glyphs that are used as replacements are too high. To fix this, you can use something like the following workaround (in your .emacs-file): (if (equal window-system 'x) (progn (set-fontset-font "fontset-default" 'unicode "Dejavu Sans Mono") (set-face-font 'default "Inconsolata-10"))) Other fonts with good support for Unicode are unifont and symbola. For a more complete solution, but with greater overhead, you can also try the _unicode-fonts_ package: (require 'unicode-fonts) (require 'persistent-soft) ; To cache the fonts and reduce load time (unicode-fonts-setup)  File: mu4e.info, Node: Multiple accounts, Next: Refiling messages, Prev: Fancy characters, Up: Tips and Tricks D.2 Multiple accounts ===================== Note: - for mu4e version 0.9.16 and higher, the recommended way to deal with multiple accounts is through mu4e’s built-in *note Contexts:: system. For older versions, the below still works. Using mu4e with multiple email accounts is fairly easy. Although variables such as ‘user-mail-address’, ‘mu4e-sent-folder’, ‘message-*’, ‘smtpmail-*’, etc. typically only take one value, it is easy to change their values using ‘mu4e-compose-pre-hook’. The setup described here is one way of doing this (though certainly not the only way). This setup assumes that you have multiple mail accounts under ‘mu4e-maildir’. As an example, we’ll use ~/Maildir/Account1 and ~/Maildir/Account2, but the setup works just as well if ‘mu4e-maildir’ points to something else. First, you need to make sure that all variables that you wish to change based on user account are set to some initial value. So set up your environment with e.g., your main account: (setq mu4e-sent-folder "/Account1/Saved Items" mu4e-drafts-folder "/Account1/Drafts" user-mail-address "my.address@account1.example.com" smtpmail-default-smtp-server "smtp.account1.example.com" smtpmail-local-domain "account1.example.com" smtpmail-smtp-server "smtp.account1.example.com" smtpmail-stream-type 'starttls smtpmail-smtp-service 25) Then create a variable ‘my-mu4e-account-alist’, which should contain a list for each of your accounts. Each list should start with the account name, (which _must_ be identical to the account’s directory name under ~/Maildir), followed by ‘(variable value)’ pairs: (defvar my-mu4e-account-alist '(("Account1" (mu4e-sent-folder "/Account1/Saved Items") (mu4e-drafts-folder "/Account1/Drafts") (user-mail-address "my.address@account1.example.com") (smtpmail-default-smtp-server "smtp.account1.example.com") (smtpmail-local-domain "account1.example.com") (smtpmail-smtp-user "username1") (smtpmail-smtp-server "smtp.account1.example.com") (smtpmail-stream-type starttls) (smtpmail-smtp-service 25)) ("Account2" (mu4e-sent-folder "/Account2/Saved Items") (mu4e-drafts-folder "/Account2/Drafts") (user-mail-address "my.address@account2.example.com") (smtpmail-default-smtp-server "smtp.account2.example.com") (smtpmail-local-domain "account2.example.com") (smtpmail-smtp-user "username2") (smtpmail-smtp-server "smtp.account2.example.com") (smtpmail-stream-type starttls) (smtpmail-smtp-service 587)))) You can put any variable you want in the account lists, just make sure that you put in _all_ the variables that differ for each account. Variables that do not differ need not be included. For example, if you use the same smtp server for both accounts, you don’t need to include the smtp-related variables in ‘my-mu4e-account-alist’. Note that some SMTP servers (such as Gmail) require the SMTP username to match the user mail address. In this case, your mail is appear to originate from whichever SMTP account you use. Thus unless you are certain your SMTP server does not have this requirement, you should generally use different SMTP account credentials for each mail account. Now, the following function can be used to select an account and set the variables in ‘my-mu4e-account-alist’ to the correct values: (defun my-mu4e-set-account () "Set the account for composing a message." (let* ((account (if mu4e-compose-parent-message (let ((maildir (mu4e-message-field mu4e-compose-parent-message :maildir))) (string-match "/\\(.*?\\)/" maildir) (match-string 1 maildir)) (completing-read (format "Compose with account: (%s) " (mapconcat #'(lambda (var) (car var)) my-mu4e-account-alist "/")) (mapcar #'(lambda (var) (car var)) my-mu4e-account-alist) nil t nil nil (caar my-mu4e-account-alist)))) (account-vars (cdr (assoc account my-mu4e-account-alist)))) (if account-vars (mapc #'(lambda (var) (set (car var) (cadr var))) account-vars) (error "No email account found")))) This function then needs to be added to ‘mu4e-compose-pre-hook’: (add-hook 'mu4e-compose-pre-hook 'my-mu4e-set-account) This way, ‘my-mu4e-set-account’ is called every time you edit a message. If you compose a new message, it simply asks you for the account you wish to send the message from (TAB completion works). If you’re replying or forwarding a message, or editing an existing draft, the account is chosen automatically, based on the first component of the maildir of the message being replied to, forwarded or edited (i.e., the directory under ~/Maildir).  File: mu4e.info, Node: Refiling messages, Next: Saving outgoing messages, Prev: Multiple accounts, Up: Tips and Tricks D.3 Refiling messages ===================== By setting ‘mu4e-refile-folder’ to a function, you can dynamically determine where messages are to be refiled. If you want to do this based on the subject of a message, you can use a function that matches the subject against a list of regexes in the following way. First, set up a variable ‘my-mu4e-subject-alist’ containing regexes plus associated mail folders: (defvar my-mu4e-subject-alist '(("kolloqui\\(um\\|a\\)" . "/Kolloquium") ("Calls" . "/Calls") ("Lehr" . "/Lehre") ("webseite\\|homepage\\|website" . "/Webseite")) "List of subjects and their respective refile folders.") Now you can use the following function to automatically refile messages based on their subject line: (defun my-mu4e-refile-folder-function (msg) "Set the refile folder for MSG." (let ((subject (mu4e-message-field msg :subject)) (folder (or (cdar (member* subject my-mu4e-subject-alist :test #'(lambda (x y) (string-match (car y) x)))) "/General"))) folder)) Note the "/General" folder: it is the default folder in case the subject does not match any of the regexes in ‘my-mu4e-subject-alist’. In order to make this work, you’ll of course need to set ‘mu4e-refile-folder’ to this function: (setq mu4e-refile-folder 'my-mu4e-refile-folder-function) If you have multiple accounts, you can accommodate them as well: (defun my-mu4e-refile-folder-function (msg) "Set the refile folder for MSG." (let ((maildir (mu4e-message-field msg :maildir)) (subject (mu4e-message-field msg :subject)) folder) (cond ((string-match "Account1" maildir) (setq folder (or (catch 'found (dolist (mailing-list my-mu4e-mailing-lists) (if (mu4e-message-contact-field-matches msg :to (car mailing-list)) (throw 'found (cdr mailing-list))))) "/Account1/General"))) ((string-match "Gmail" maildir) (setq folder "/Gmail/All Mail")) ((string-match "Account2" maildir) (setq folder (or (cdar (member* subject my-mu4e-subject-alist :test #'(lambda (x y) (string-match (car y) x)))) "/Account2/General")))) folder)) This function actually uses different methods to determine the refile folder, depending on the account: For _Account2_, it uses ‘my-mu4e-subject-alist’, for the _Gmail_ account it simply uses the folder "All Mail". For Account1, it uses another method: it files the message based on the mailing list to which it was sent. This requires another variable: (defvar my-mu4e-mailing-lists '(("mu-discuss@googlegroups.com" . "/Account1/mu4e") ("pandoc-discuss@googlegroups.com" . "/Account1/Pandoc") ("auctex@gnu.org" . "/Account1/AUCTeX")) "List of mailing list addresses and folders where their messages are saved.")  File: mu4e.info, Node: Saving outgoing messages, Next: Confirmation before sending, Prev: Refiling messages, Up: Tips and Tricks D.4 Saving outgoing messages ============================ Like ‘mu4e-refile-folder’, the variable ‘mu4e-sent-folder’ can also be set to a function, in order to dynamically determine the save folder. One might, for example, wish to automatically put messages going to mailing lists into the trash (because you’ll receive them back from the list anyway). If you have set up the variable ‘my-mu4e-mailing-lists’ as mentioned, you can use the following function to determine a save folder: (defun my-mu4e-sent-folder-function (msg) "Set the sent folder for the current message." (let ((from-address (message-field-value "From")) (to-address (message-field-value "To"))) (cond ((string-match "my.address@account1.example.com" from-address) (if (member* to-address my-mu4e-mailing-lists :test #'(lambda (x y) (string-match (car y) x))) "/Trash" "/Account1/Sent")) ((string-match "my.address@gmail.com" from-address) "/Gmail/Sent Mail") (t (mu4e-ask-maildir-check-exists "Save message to maildir: "))))) Note that this function doesn’t use ‘(mu4e-message-field msg :maildir)’ to determine which account the message is being sent from. The reason is that that the function in ‘mu4e-sent-folder’ is called when you send the message, but before mu4e has created the message struct from the compose buffer, so that ‘mu4e-message-field’ cannot be used. Instead, the function uses ‘message-field-value’, which extracts the values of the headers in the compose buffer. This means that it is not possible to extract the account name from the message’s maildir, so instead the from address is used to determine the account. Again, the function shows three different possibilities: for the first account (my.address@account1.example.com) it uses ‘my-mu4e-mailing-lists’ again to determine if the message goes to a mailing list. If so, the message is put in the trash folder, if not, it is saved in /Account1/Sent. For the second (Gmail) account, sent mail is simply saved in the Sent Mail folder. If the from address is not associated with Account1 or with the Gmail account, the function uses ‘mu4e-ask-maildir-check-exists’ to ask the user for a maildir to save the message in.  File: mu4e.info, Node: Confirmation before sending, Prev: Saving outgoing messages, Up: Tips and Tricks D.5 Confirmation before sending =============================== To protect yourself from sending messages too hastily, you can add a final confirmation, which you can of course make as elaborate as you wish. (add-hook 'message-send-hook (lambda () (unless (yes-or-no-p "Sure you want to send this?") (signal 'quit nil))))  File: mu4e.info, Node: How it works, Next: Logging and debugging, Prev: Tips and Tricks, Up: Top Appendix E How it works *********************** While perhaps not interesting for all users of mu4e, some curious souls may want to know how mu4e does its job. * Menu: * High-level overview::How the pieces go together * mu server::The mu process running in the background * Reading from the server::Processing responses from the server * The message s-expression::What messages look like from the inside  File: mu4e.info, Node: High-level overview, Next: mu server, Up: How it works E.1 High-level overview ======================= At a high level, we can summarize the structure of the mu4e system using some ascii-art: +---------+ | emacs | | +------+ +----| mu4e | --> send mail (smtpmail) +------+ | A V | ---/ search, view, move mail +---------+ \ | mu | +---------+ | A V | +---------+ | Maildir | <--- receive mail (fetchmail, +---------+ offlineimap, ...) In words: • Your e-mail messages are stored in a Maildir-directory (typically, ‘~/Maildir’ and its subdirectories), and new mail comes in using tools like fetchmail, offlineimap, or through a local mail server. • mu indexes these messages periodically, so you can quickly search for them. mu can run in a special server-mode, where it provides services to client software. • mu4e, which runs inside ‘emacs’ is such a client; it communicates with ‘mu’ (in its server-mode) to search for messages, and manipulate them. • mu4e uses the facilities offered by ‘emacs’ (the Gnus message editor and smtpmail) to send messages.  File: mu4e.info, Node: mu server, Next: Reading from the server, Prev: High-level overview, Up: How it works E.2 mu server ============= mu4e is based on the mu e-mail searching/indexer. The latter is a C-program; there are different ways to communicate with a client that is emacs-based. One way to implement this, would be to call the mu command-line tool with some parameters and then parse the output. In fact, that was the first approach – mu4e would invoke e.g., mu find and process the output in ‘emacs’. However, with this approach, we need to load the entire e-mail _Xapian_ database (in which the message is stored) for each invocation. Wouldn’t it be nicer to keep a running mu instance around? Indeed, it would - and thus, the mu server sub-command was born. Running mu server starts a simple shell, in which you can give commands to ‘mu’, which then spits out the results/errors. ‘mu server’ is not meant for humans, but it can be used manually, which is great for debugging.  File: mu4e.info, Node: Reading from the server, Next: The message s-expression, Prev: mu server, Up: How it works E.3 Reading from the server =========================== In the design, the next question was what format mu should use for its output for mu4e (‘emacs’) to process. Some other programs use JSON here, but it seemed easier (and possibly, more efficient) just to talk to ‘emacs’ in its native language: _s-expressions_, and interpret those using the ‘emacs’-function ‘read-from-string’. See *note The message s-expression:: for details on the format. So, now let’s look how we process the data from mu server in ‘emacs’. We’ll leave out a lot of detail, mu4e-specifics, and look at a bit more generic approach. The first thing to do is to create a process (for example, with ‘start-process’), and then register a filter function for it, which is invoked whenever the process has some data for us. Something like: (let ((proc (start-process <arguments>))) (set-process-filter proc 'my-process-filter) (set-process-sentinel proc 'my-process-sentinel)) Note, the process sentinel is invoked when the process is terminated – so there you can clean things up. The function ‘my-process-filter’ is a user-defined function that takes the process and the chunk of output as arguments; in mu4e it looks something like (pseudo-lisp): (defun my-process-filter (proc str) ;; mu4e-buf: a global string variable to which data gets appended ;; as we receive it (setq mu4e-buf (concat mu4e-buf str)) (when <we-have-received-a-full-expression> <eat-expression-from mu4e-buf> <evaluate-expression>)) ‘<evaluate-expression>’ de-multiplexes the s-expression we got. For example, if the s-expression looks like an e-mail message header, it is processed by the header-handling function, which appends it to the header list. If the s-expression looks like an error message, it is reported to the user. And so on. The language between frontend and backend is documented in the mu-server man-page. mu4e can log these communications; you can use ‘M-x mu4e-toggle-logging’ to turn logging on and off, and you can view the log using ‘M-x mu4e-show-log’ (<$>).  File: mu4e.info, Node: The message s-expression, Prev: Reading from the server, Up: How it works E.4 The message s-expression ============================ A typical message s-expression looks something like the following: (:docid 32461 :from (("Nikola Tesla" . "niko@example.com")) :to (("Thomas Edison" . "tom@example.com")) :cc (("Rupert The Monkey" . "rupert@example.com")) :subject "RE: what about the 50K?" :date (20369 17624 0) :size 4337 :message-id "C8233AB82D81EE81AF0114E4E74@123213.mail.example.com" :path "/home/tom/Maildir/INBOX/cur/133443243973_1.10027.atlas:2,S" :maildir "/INBOX" :priority normal :flags (seen) :parts ( (:index 1 :mime-type "text/plain" :size 12345 :attachment nil) (:index 2 :name "photo.jpg" :mime-type "image/jpeg" :size 147331 :attachment t) (:index 3 :name "book.pdf" :mime-type "application/pdf" :size 192220 :attachment t)) :references ("C8384574032D81EE81AF0114E4E74@123213.mail.example.com" "38203498230942D81EE81AF0114E4E74@123213.mail.example.com") :in-reply-to "38203498230942D81EE81AF0114E4E74@123213.mail.example.com" :body-txt "Hi Tom, .... ") This s-expression forms a property list (plist), and we can get values from it using plist-get; for example ‘(plist-get msg :subject)’ would get you the message subject. However, it’s better to use the function ‘mu4e-message-field’ to shield you from some of the implementation details that are subject to change; and see the other convenience functions in ‘mu4e-message.el’. Some notes on the format: • The address fields are _lists_ of pairs ‘(name . email)’, where name can be nil. • The date is in format ‘emacs’ uses (for example in ‘current-time’).(1) • Attachments are a list of elements with fields :index (the number of the MIME-part), :name (the file name, if any), :mime-type (the MIME-type, if any) and :size (the size in bytes, if any). • Messages in the *note Headers view:: come from the database and do not have :attachments. :body-txt or :body-html fields. Message in the *note Message view:: use the actual message file, and do include these fields. E.4.1 Example: ping-pong ------------------------ As an example of the communication between mu4e and ‘mu’, let’s look at the ping-pong-sequence. When mu4e starts, it sends a command ping to the mu server backend, to learn about its version. mu server then responds with a pong s-expression to provide this information (this is implemented in ‘mu-cmd-server.c’). We start this sequence when mu4e is invoked (when the program is started). It calls mu4e-proc-ping, and registers a (lambda) function for mu4e-proc-pong-func, to handle the response. -> cmd:ping <- (pong "mu" :version "x.x.x" :doccount 10000) When we receive such a pong (in ‘mu4e-proc.el’), the lambda function we registered is called, and it compares the version we got from the pong with the version we expected, and raises an error, if they differ. ---------- Footnotes ---------- (1) Emacs 32-bit integers have only 29 bits available for the actual number; the other bits are use by ‘emacs’ for internal purposes. Therefore, we need to split time_t in two numbers.  File: mu4e.info, Node: Logging and debugging, Next: GNU Free Documentation License, Prev: How it works, Up: Top Appendix F Logging and debugging ******************************** As explained in *note How it works::, mu4e communicates with its backend (mu server) by sending commands and receiving responses (s-expressions). For debugging purposes, it can be very useful to see this data. For this reason, mu4e can log all these messages. Note that the ’protocol’ is documented to some extent in the mu-server manpage. You can enable (and disable) logging with ‘M-x mu4e-toggle-logging’. The log-buffer is called *mu4e-log*, and in the *note Main view::, *note Headers view:: and *note Message view::, there’s a keybinding <$> that takes you there. You can quit it by pressing <q>. Logging can be a bit resource-intensive, so you may not want to leave it on all the time. By default, the log only maintains the most recent 1200 lines. mu itself keeps a log as well, you can find this it in <MUHOME>/log/mu.log, typically ~/.mu/log/mu.log.  File: mu4e.info, Node: GNU Free Documentation License, Prev: Logging and debugging, Up: Top Appendix G GNU Free Documentation License ***************************************** Version 1.2, November 2002 Copyright © 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, 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. 0. PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document “free” in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of “copyleft”, which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. 1. APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The “Document”, below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as “you”. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A “Modified Version” of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A “Secondary Section” is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document’s overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The “Invariant Sections” are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The “Cover Texts” are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A “Transparent” copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not “Transparent” is called “Opaque”. Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only. The “Title Page” means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, “Title Page” means the text near the most prominent appearance of the work’s title, preceding the beginning of the body of the text. A section “Entitled XYZ” means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as “Acknowledgements”, “Dedications”, “Endorsements”, or “History”.) To “Preserve the Title” of such a section when you modify the Document means that it remains a section “Entitled XYZ” according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. 2. VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. 3. COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document’s license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. 4. MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. C. State on the Title page the name of the publisher of the Modified Version, as the publisher. D. Preserve all the copyright notices of the Document. E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document’s license notice. H. Include an unaltered copy of this License. I. Preserve the section Entitled “History”, Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled “History” in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the “History” section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. K. For any section Entitled “Acknowledgements” or “Dedications”, Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. M. Delete any section Entitled “Endorsements”. Such a section may not be included in the Modified Version. N. Do not retitle any existing section to be Entitled “Endorsements” or to conflict in title with any Invariant Section. O. Preserve any Warranty Disclaimers. If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version’s license notice. These titles must be distinct from any other section titles. You may add a section Entitled “Endorsements”, provided it contains nothing but endorsements of your Modified Version by various parties—for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. 5. COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled “History” in the various original documents, forming one section Entitled “History”; likewise combine any sections Entitled “Acknowledgements”, and any sections Entitled “Dedications”. You must delete all sections Entitled “Endorsements.” 6. COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. 7. AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an “aggregate” if the copyright resulting from the compilation is not used to limit the legal rights of the compilation’s users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document’s Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. 8. TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled “Acknowledgements”, “Dedications”, or “History”, the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. 9. TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document 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. 10. FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See <http://www.gnu.org/copyleft/>. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License “or any later version” applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. ADDENDUM: How to use this License for your documents ==================================================== To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: Copyright (C) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the “with...Texts.” line with this: with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.  Tag Table: Node: Top628 Ref: Top-Footnote-12892 Ref: Top-Footnote-22937 Ref: Top-Footnote-33033 Node: Introduction3156 Node: Why another e-mail client3531 Node: Other mail clients4142 Ref: Other mail clients-Footnote-14884 Ref: Other mail clients-Footnote-24917 Ref: Other mail clients-Footnote-34953 Ref: Other mail clients-Footnote-44989 Node: What mu4e does not do5020 Ref: What mu4e does not do-Footnote-16054 Ref: What mu4e does not do-Footnote-26088 Ref: What mu4e does not do-Footnote-36128 Node: Becoming a mu4e user6165 Ref: Becoming a mu4e user-Footnote-17566 Node: Getting started7618 Node: Requirements8405 Ref: Requirements-Footnote-19305 Ref: Requirements-Footnote-29334 Node: Installation9381 Ref: Building from a release tarball11248 Ref: Building from git11642 Ref: Installation-Footnote-113088 Ref: Installation-Footnote-213142 Node: Getting mail13250 Ref: Getting mail-Footnote-114349 Node: Indexing your messages14519 Ref: Indexing your messages-Footnote-116041 Node: Basic configuration16183 Node: Folders16815 Ref: Folders-Footnote-117760 Node: Retrieval and indexing18039 Node: Sending mail22191 Node: Running mu4e24421 Node: Main view25466 Node: MV Overview25995 Node: Basic actions26978 Node: MV Bookmarks27833 Node: Miscellaneous28472 Node: Headers view29347 Node: HV Overview30107 Ref: HV Overview-Footnote-134512 Ref: HV Overview-Footnote-234603 Ref: HV Overview-Footnote-334710 Node: Keybindings34809 Node: HV Marking36866 Node: Sorting and threading38029 Ref: Sorting and threading-Footnote-139288 Node: HV Custom headers39379 Node: HV Actions41561 Node: Split view42271 Node: Message view43379 Node: MSGV Overview44165 Node: MSGV Keybindings46404 Node: Attachments49279 Ref: Attachments-Footnote-150700 Ref: Attachments-Footnote-250748 Node: Viewing images inline50856 Ref: Viewing images inline-Footnote-151698 Node: Displaying rich-text messages51735 Ref: Html2text functions54388 Ref: Privacy aspects54624 Ref: Displaying rich-text messages-Footnote-154963 Ref: Displaying rich-text messages-Footnote-255128 Node: MSGV Crypto55170 Ref: Decryption56083 Ref: Verifying signatures56800 Ref: MSGV Crypto-Footnote-157507 Node: MSGV Custom headers57610 Node: MSGV Actions58149 Node: Editor view59699 Node: EV Overview60498 Node: EV Keybindings60913 Node: Address autocompletion61537 Ref: Address autocompletion-Footnote-163849 Node: Compose hooks63896 Node: Signing and encrypting66648 Node: Queuing mail67780 Node: Message signatures69074 Node: Other settings69708 Node: Searching70549 Node: Queries71514 Ref: Queries-Footnote-174298 Node: Bookmarks74412 Ref: Bookmarks-Footnote-177733 Node: Maildir searches77776 Node: Other search functionality80137 Ref: Including related messages82041 Ref: Skipping duplicates82660 Node: Marking83249 Node: Marking messages84027 Node: What to mark for84819 Ref: What to mark for-Footnote-186396 Ref: What to mark for-Footnote-286499 Node: Executing the marks86694 Node: Leaving the headers buffer87149 Node: Built-in marking functions87584 Node: Custom mark functions88140 Node: Adding a new kind of mark90480 Node: Contexts93601 Node: What are contexts94849 Node: Context policies96501 Node: Contexts and special folders97974 Node: Contexts example99367 Node: Some context tricks103816 Node: Dynamic folders104541 Node: Smart refiling105802 Ref: Smart refiling-Footnote-1108389 Node: Other dynamic folders108434 Node: Actions109702 Node: Defining actions110605 Ref: Defining actions-Footnote-1111884 Ref: Defining actions-Footnote-2112060 Node: Headers view actions112247 Node: Message view actions113177 Node: Attachment actions114011 Node: Example actions114796 Node: Extending mu4e115187 Node: Extension points115728 Node: Available functions117507 Node: Message functions118417 Node: Contact functions120522 Node: Utility functions122081 Ref: Utility functions-Footnote-1123348 Node: Interaction with other tools123464 Node: Emacs default124158 Node: Org-mode links124603 Ref: Org-mode links-Footnote-1126722 Node: Org-contacts126799 Ref: Org-contacts-Footnote-1127897 Node: BBDB127958 Ref: BBDB-Footnote-1129359 Node: Sauron129411 Ref: Sauron-Footnote-1130940 Node: Speedbar131083 Node: Mu-cite132026 Ref: Mu-cite-Footnote-1132698 Node: Dired132774 Node: Example configurations134041 Node: Minimal configuration134622 Node: Longer configuration135894 Node: Gmail configuration138660 Node: CONF Other settings143970 Node: FAQ144522 Node: General145047 Node: Retrieving mail148012 Ref: Retrieving mail-Footnote-1150973 Node: Reading messages151006 Node: Writing messages154773 Node: Known issues160599 Node: Tips and Tricks162229 Node: Fancy characters162692 Node: Multiple accounts163723 Node: Refiling messages169065 Node: Saving outgoing messages172660 Node: Confirmation before sending175200 Node: How it works175667 Node: High-level overview176179 Node: mu server177691 Node: Reading from the server178718 Node: The message s-expression181025 Ref: The message s-expression-Footnote-1184242 Node: Logging and debugging184435 Node: GNU Free Documentation License185509  End Tag Table  Local Variables: coding: utf-8 End: �����������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-draft.el������������������������������������������������������������������������0000644�0001750�0001750�00000044003�13020504332�012310� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; mu4e-draft.el -- part of mu4e, the mu mail user agent for emacs ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; In this file, various functions to create draft messages ;; Code (eval-when-compile (byte-compile-disable-warning 'cl-functions)) (require 'cl) (require 'mu4e-vars) (require 'mu4e-utils) (require 'mu4e-message) (require 'message) ;; mail-header-separator ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defcustom mu4e-compose-dont-reply-to-self nil "If non-nil, don't include self (that is, any member of `mu4e-user-mail-address-list') in replies." :type 'boolean :group 'mu4e-compose) (defcustom mu4e-compose-cite-function (or message-cite-function 'message-cite-original-without-signature) "The function to use to cite message in replies and forwarded messages. This is the mu4e-specific version of `message-cite-function'." :type 'function :group 'mu4e-compose) (defcustom mu4e-compose-signature (or message-signature "Sent with my mu4e") "The message signature (i.e. the blob at the bottom of messages). This is the mu4e-specific version of `message-signature'." :group 'mu4e-compose) (defcustom mu4e-compose-signature-auto-include t "Whether to automatically include a message-signature in new messages (if it is set)." :type 'boolean :group 'mu4e-compose) (defcustom mu4e-compose-auto-include-date nil "Whether to include a date header when starting to draft a message; if nil, only do so when sending the message." :type 'boolean :group 'mu4e-compose) (defcustom mu4e-compose-in-new-frame nil "Whether to compose messages in a new frame instead of the current window." :type 'boolean :group 'mu4e-compose) (defvar mu4e-user-agent-string (format "mu4e %s; emacs %s" mu4e-mu-version emacs-version) "The User-Agent string for mu4e.") (defun mu4e~draft-cite-original (msg) "Return a cited version of the original message MSG as a plist. This function uses `mu4e-compose-cite-function', and as such all its settings apply." (with-temp-buffer (when (fboundp 'mu4e-view-message-text) ;; keep bytecompiler happy (let ((mu4e-view-date-format "%Y-%m-%dT%T%z")) (insert (mu4e-view-message-text msg))) (message-yank-original) (goto-char (point-min)) (push-mark (point-max)) ;; set the the signature separator to 'loose', since in the real world, ;; many message don't follow the standard... (let ((message-signature-separator "^-- *$") (message-signature-insert-empty-line t)) (funcall mu4e-compose-cite-function)) (pop-mark) (goto-char (point-min)) (buffer-string)))) (defun mu4e~draft-header (hdr val) "Return a header line of the form \"HDR: VAL\". If VAL is nil, return nil." ;; note: the propertize here is currently useless, since gnus sets its own ;; later. (when val (format "%s: %s\n" (propertize hdr 'face 'mu4e-header-key-face) (propertize val 'face 'mu4e-header-value-face)))) (defconst mu4e~max-reference-num 21 "Maximum number of References:, as suggested by `message-shorten-references'.") (defun mu4e~shorten-1 (list cut surplus) "Cut SURPLUS elements out of LIST, beginning with CUTth one. Code borrowed from `message-shorten-1'." (setcdr (nthcdr (- cut 2) list) (nthcdr (+ (- cut 2) surplus 1) list))) (defun mu4e~draft-references-construct (msg) "Construct the value of the References: header based on MSG as a comma-separated string. Normally, this the concatenation of the existing References + In-Reply-To (which may be empty, an note that :references includes the old in-reply-to as well) and the message-id. If the message-id is empty, returns the old References. If both are empty, return nil." (let* ( ;; these are the ones from the message being replied to / forwarded (refs (mu4e-message-field msg :references)) (msgid (mu4e-message-field msg :message-id)) ;; now, append in (refs (if (and msgid (not (string= msgid ""))) (append refs (list msgid)) refs)) ;; no doubles (refs (delete-duplicates refs :test #'equal)) (refnum (length refs)) (cut 2)) ;; remove some refs when there are too many (when (> refnum mu4e~max-reference-num) (let ((surplus (- refnum mu4e~max-reference-num))) (mu4e~shorten-1 refs cut surplus))) (mapconcat (lambda (id) (format "<%s>" id)) refs " "))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; determine the recipient fields for new messages (defun mu4e~draft-recipients-list-to-string (lst) "Convert a lst LST of address cells into a string with a list of e-mail addresses. If LST is nil, returns nil." (when lst (mapconcat (lambda (addrcell) (let ((name (car addrcell)) (email (cdr addrcell))) (if name (format "%s <%s>" (mu4e~rfc822-quoteit name) email) (format "%s" email)))) lst ", "))) (defun mu4e~draft-address-cell-equal (cell1 cell2) "Return t if CELL1 and CELL2 have the same e-mail address. The comparison is done case-insensitively. If the cells done match return nil. CELL1 and CELL2 are cons cells of the form (NAME . EMAIL)." (string= (downcase (or (cdr cell1) "")) (downcase (or (cdr cell2) "")))) (defun mu4e~draft-create-to-lst (origmsg) "Create a list of address for the To: in a new message, based on the original message ORIGMSG. If the Reply-To address is set, use that, otherwise use the From address. Note, whatever was in the To: field before, goes to the Cc:-list (if we're doing a reply-to-all). Special case: if we were the sender of the original, we simple copy the list form the original." (let ((reply-to (or (plist-get origmsg :reply-to) (plist-get origmsg :from)))) (delete-duplicates reply-to :test #'mu4e~draft-address-cell-equal) (if mu4e-compose-dont-reply-to-self (delete-if (lambda (to-cell) (member-if (lambda (addr) (string= (downcase addr) (downcase (cdr to-cell)))) mu4e-user-mail-address-list)) reply-to) reply-to))) (defun mu4e~draft-create-cc-lst (origmsg reply-all) "Create a list of address for the Cc: in a new message, based on the original message ORIGMSG, and whether it's a reply-all." (when reply-all (let* ((cc-lst ;; get the cc-field from the original, remove dups (delete-duplicates (append (plist-get origmsg :to) (plist-get origmsg :cc)) :test #'mu4e~draft-address-cell-equal)) ;; now we have the basic list, but we must remove ;; addresses also in the to list (cc-lst (delete-if (lambda (cc-cell) (find-if (lambda (to-cell) (mu4e~draft-address-cell-equal cc-cell to-cell)) (mu4e~draft-create-to-lst origmsg))) cc-lst)) ;; finally, we need to remove ourselves from the cc-list ;; unless mu4e-compose-keep-self-cc is non-nil (cc-lst (if (or mu4e-compose-keep-self-cc (null user-mail-address)) cc-lst (delete-if (lambda (cc-cell) (member-if (lambda (addr) (string= (downcase addr) (downcase (cdr cc-cell)))) mu4e-user-mail-address-list)) cc-lst)))) cc-lst))) (defun mu4e~draft-recipients-construct (field origmsg &optional reply-all) "Create value (a string) for the recipient field FIELD (a symbol, :to or :cc), based on the original message ORIGMSG, and (optionally) REPLY-ALL which indicates this is a reply-to-all message. Return nil if there are no recipients for the particular field." (mu4e~draft-recipients-list-to-string (case field (:to (mu4e~draft-create-to-lst origmsg)) (:cc (mu4e~draft-create-cc-lst origmsg reply-all)) (otherwise (mu4e-error "Unsupported field"))))) (defun mu4e~draft-from-construct () "Construct a value for the From:-field of the reply to MSG, based on `user-full-name' and `user-mail-address'; if the latter is nil, function returns nil." (when user-mail-address (if user-full-name (format "%s <%s>" (mu4e~rfc822-quoteit user-full-name) user-mail-address) (format "%s" user-mail-address)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~draft-insert-mail-header-separator () "Insert `mail-header-separator' in the first empty line of the message. `message-mode' needs this line to know where the headers end and the body starts. Note, in `mu4e-compose-mode', we use `before-save-hook' and `after-save-hook' to ensure that this separator is never written to the message file. Also see `mu4e-remove-mail-header-separator'." ;; we set this here explicitly, since (as it has happened) a wrong ;; value for this (such as "") breaks address completion and other things (set (make-local-variable 'mail-header-separator) "--text follows this line--") (put 'mail-header-separator 'permanent-local t) (save-excursion ;; make sure there's not one already (mu4e~draft-remove-mail-header-separator) (let ((sepa (propertize mail-header-separator 'intangible t ;; don't make this read-only, message-mode ;; seems to require it being writable in some cases ;;'read-only "Can't touch this" 'rear-nonsticky t 'font-lock-face 'mu4e-compose-separator-face))) (widen) ;; search for the first empty line (goto-char (point-min)) (if (search-forward-regexp "^$" nil t) (replace-match sepa) (progn ;; no empty line? then prepend one (goto-char (point-max)) (insert "\n" sepa)))))) (defun mu4e~draft-remove-mail-header-separator () "Remove `mail-header-separator; we do this before saving a file (and restore it afterwards), to ensure that the separator never hits the disk. Also see `mu4e~draft-insert-mail-header-separator." (save-excursion (widen) (goto-char (point-min)) ;; remove the --text follows this line-- separator (when (search-forward-regexp (concat "^" mail-header-separator) nil t) (let ((inhibit-read-only t)) (replace-match ""))))) (defun mu4e~draft-reply-all-p (origmsg) "Ask user whether she wants to reply to *all* recipients. If there is just one recipient of ORIGMSG do nothing." (let* ((recipnum (+ (length (mu4e~draft-create-to-lst origmsg)) (length (mu4e~draft-create-cc-lst origmsg t)))) (response (if (= recipnum 1) 'all ;; with one recipient, we can reply to 'all'.... (mu4e-read-option "Reply to " `( (,(format "all %d recipients" recipnum) . all) ("sender only" . sender-only)))))) (eq response 'all))) (defun mu4e~draft-message-filename-construct (&optional flagstr) "Construct a randomized name for a message file with flags FLAGSTR. It looks something like <time>-<random>.<hostname>:2, You can append flags." (let* ((sysname (if (fboundp 'system-name) (system-name) (with-no-warnings system-name))) (hostname (downcase (save-match-data (substring sysname (string-match "^[^.]+" sysname) (match-end 0)))))) (format "%s.%04x%04x%04x%04x.%s:2,%s" (format-time-string "%s" (current-time)) (random 65535) (random 65535) (random 65535) (random 65535) hostname (or flagstr "")))) (defun mu4e~draft-common-construct () "Construct the common headers for each message." (concat (mu4e~draft-header "User-agent" mu4e-user-agent-string) (when mu4e-compose-auto-include-date (mu4e~draft-header "Date" (message-make-date))))) (defconst mu4e~draft-reply-prefix "Re: " "String to prefix replies with.") (defun mu4e~draft-reply-construct (origmsg) "Create a draft message as a reply to original message ORIGMSG. Replying-to-self is a special; in that case, the To and Cc fields will be the same as in the original." (let* ((reply-to-self (mu4e-message-contact-field-matches-me origmsg :from)) (recipnum (+ (length (mu4e~draft-create-to-lst origmsg)) (length (mu4e~draft-create-cc-lst origmsg t)))) ;; reply-to-self implies reply-all (reply-all (or reply-to-self (mu4e~draft-reply-all-p origmsg))) (old-msgid (plist-get origmsg :message-id)) (subject (concat mu4e~draft-reply-prefix (message-strip-subject-re (or (plist-get origmsg :subject) ""))))) (concat (mu4e~draft-header "From" (or (mu4e~draft-from-construct) "")) (mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address) (if reply-to-self ;; When we're replying to ourselves, simply keep the same headers. (concat (mu4e~draft-header "To" (mu4e~draft-recipients-list-to-string (mu4e-message-field origmsg :to))) (mu4e~draft-header "Cc" (mu4e~draft-recipients-list-to-string (mu4e-message-field origmsg :cc)))) ;; if there's no-one in To, copy the CC-list (if (zerop (length (mu4e~draft-create-to-lst origmsg))) (mu4e~draft-header "To" (mu4e~draft-recipients-construct :cc origmsg reply-all)) ;; otherwise... (concat (mu4e~draft-header "To" (mu4e~draft-recipients-construct :to origmsg)) (mu4e~draft-header "Cc" (mu4e~draft-recipients-construct :cc origmsg reply-all))))) (mu4e~draft-header "Subject" subject) (mu4e~draft-header "References" (mu4e~draft-references-construct origmsg)) (mu4e~draft-common-construct) (when old-msgid (mu4e~draft-header "In-reply-to" (format "<%s>" old-msgid))) "\n\n" (mu4e~draft-cite-original origmsg)))) (defconst mu4e~draft-forward-prefix "Fwd: " "String to prefix replies with.") (defun mu4e~draft-forward-construct (origmsg) "Create a draft forward message for original message ORIGMSG." (let ((subject (or (plist-get origmsg :subject) ""))) (concat (mu4e~draft-header "From" (or (mu4e~draft-from-construct) "")) (mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address) (mu4e~draft-header "To" "") (mu4e~draft-common-construct) (mu4e~draft-header "References" (mu4e~draft-references-construct origmsg)) (mu4e~draft-header "Subject" (concat ;; if there's no Fwd: yet, prepend it (if (string-match "^Fwd:" subject) "" mu4e~draft-forward-prefix) subject)) "\n\n" (mu4e~draft-cite-original origmsg)))) (defun mu4e~draft-newmsg-construct () "Create a new message." (concat (mu4e~draft-header "From" (or (mu4e~draft-from-construct) "")) (mu4e~draft-header "Reply-To" mu4e-compose-reply-to-address) (mu4e~draft-header "To" "") (mu4e~draft-header "Subject" "") (mu4e~draft-common-construct))) (defvar mu4e~draft-drafts-folder nil "The drafts-folder for this compose buffer, based on `mu4e-drafts-folder', which is evaluated once.") (defun mu4e~draft-open-file (path) "Open the the draft file at PATH." (if mu4e-compose-in-new-frame (find-file-other-frame path) (find-file path))) (defun mu4e~draft-determine-path (draft-dir) "Determine the path for a new draft file." (format "%s/%s/cur/%s" mu4e-maildir draft-dir (mu4e~draft-message-filename-construct "DS"))) (defun mu4e-draft-open (compose-type &optional msg) "Open a draft file for a new message (when COMPOSE-TYPE is `reply', `forward' or `new'), open an existing draft (when COMPOSE-TYPE is `edit'), or re-send an existing message (when COMPOSE-TYPE is `resend'). The name of the draft folder is constructed from the concatenation of `mu4e-maildir' and `mu4e-drafts-folder' (the latter will be evaluated). The message file name is a unique name determined by `mu4e-send-draft-file-name'. The initial contents will be created from either `mu4e~draft-reply-construct', or `mu4e~draft-forward-construct' or `mu4e~draft-newmsg-construct'." (unless mu4e-maildir (mu4e-error "mu4e-maildir not set")) (let ((draft-dir nil)) (case compose-type (edit ;; case-1: re-editing a draft messages. in this case, we do know the ;; full path, but we cannot really know 'drafts folder'... we make a ;; guess (setq draft-dir (mu4e~guess-maildir (mu4e-message-field msg :path))) (mu4e~draft-open-file (mu4e-message-field msg :path))) (resend ;; case-2: copy some exisisting message to a draft message, then edit ;; that. (setq draft-dir (mu4e~guess-maildir (mu4e-message-field msg :path))) (let ((draft-path (mu4e~draft-determine-path draft-dir))) (copy-file (mu4e-message-field msg :path) draft-path) (mu4e~draft-open-file draft-path))) ((reply forward new) ;; case-3: creating a new message; in this case, we can determine ;; mu4e-get-drafts-folder (setq draft-dir (mu4e-get-drafts-folder msg)) (let ((draft-path (mu4e~draft-determine-path draft-dir)) (initial-contents (case compose-type (reply (mu4e~draft-reply-construct msg)) (forward (mu4e~draft-forward-construct msg)) (new (mu4e~draft-newmsg-construct))))) (mu4e~draft-open-file draft-path) (insert initial-contents) (newline) ;; include the message signature (if it's set) (if (and mu4e-compose-signature-auto-include mu4e-compose-signature) (let ((message-signature mu4e-compose-signature)) (save-excursion (message-insert-signature) (mu4e~fontify-signature)))))) (t (mu4e-error "unsupported compose-type %S" compose-type))) ;; if we didn't find a draft folder yet, try some default (unless draft-dir (setq draft-dir (mu4e-get-drafts-folder msg))) ;; evaluate mu4e~drafts-drafts-folder once, here, and use that value ;; throughout. (set (make-local-variable 'mu4e~draft-drafts-folder) draft-dir) (put 'mu4e~draft-drafts-folder 'permanent-local t) (unless mu4e~draft-drafts-folder (mu4e-error "failed to determine drafts folder")))) (provide 'mu4e-draft) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-message.el����������������������������������������������������������������������0000644�0001750�0001750�00000026462�13020504332�012645� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e-message.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2012-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; Functions to get data from mu4e-message plist structure ;;; Code: (eval-when-compile (byte-compile-disable-warning 'cl-functions)) (require 'mu4e-vars) (require 'mu4e-utils) (require 'cl) (require 'html2text) (defcustom mu4e-html2text-command (if (fboundp 'shr-insert-document) 'mu4e-shr2text 'html2text) "Either a shell command or a function that converts from html to plain text. If it is a shell-command, the command reads html from standard input and outputs plain text on standard output. If you use the htmltext program, it's recommended you use \"html2text -utf8 -width 72\". Alternatives are the python-based html2markdown, w3m and on MacOS you may want to use textutil. It can also be a function, which takes the current buffer in html as input, and transforms it into html (like the `html2text' function). In both cases, the output is expected to be in UTF-8 encoding. Newer emacs has the shr renderer, and when it's available conversion defaults to `mu4e-shr2text'; otherwise, the default is emacs' built-in `html2text' function." :type '(choice string function) :group 'mu4e-view) (defcustom mu4e-view-prefer-html nil "Whether to base the body display on the html-version. If the e-mail message has no html-version the plain-text version is always used." :type 'boolean :group 'mu4e-view) (defcustom mu4e-view-html-plaintext-ratio-heuristic 5 "Ratio between the length of the html and the plain text part below which mu4e will consider the plain text part to be 'This messages requires html' text bodies. You can neutralize it (always show the text version) by using `most-positive-fixnum'." :type 'integer :group 'mu4e-view) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defsubst mu4e-message-field-raw (msg field) "Retrieve FIELD from message plist MSG. FIELD is one of :from, :to, :cc, :bcc, :subject, :data, :message-id, :path, :maildir, :priority, :attachments, :references, :in-reply-to, :body-txt, :body-html Returns `nil' if the field does not exist. A message plist looks something like: \(:docid 32461 :from ((\"Nikola Tesla\" . \"niko@example.com\")) :to ((\"Thomas Edison\" . \"tom@example.com\")) :cc ((\"Rupert The Monkey\" . \"rupert@example.com\")) :subject \"RE: what about the 50K?\" :date (20369 17624 0) :size 4337 :message-id \"6BDC23465F79238C8233AB82D81EE81AF0114E4E74@123213.mail.example.com\" :path \"/home/tom/Maildir/INBOX/cur/133443243973_1.10027.atlas:2,S\" :maildir \"/INBOX\" :priority normal :flags (seen) :attachments ((:index 2 :name \"photo.jpg\" :mime-type \"image/jpeg\" :size 147331) (:index 3 :name \"book.pdf\" :mime-type \"application/pdf\" :size 192220)) :references (\"6BDC23465F79238C8384574032D81EE81AF0114E4E74@123213.mail.example.com\" \"6BDC23465F79238203498230942D81EE81AF0114E4E74@123213.mail.example.com\") :in-reply-to \"6BDC23465F79238203498230942D81EE81AF0114E4E74@123213.mail.example.com\" :body-txt \"Hi Tom, ...\" \)). Some notes on the format: - The address fields are lists of pairs (NAME . EMAIL), where NAME can be nil. - The date is in format emacs uses in `current-time' - Attachments are a list of elements with fields :index (the number of the MIME-part), :name (the file name, if any), :mime-type (the MIME-type, if any) and :size (the size in bytes, if any). - Messages in the Headers view come from the database and do not have :attachments, :body-txt or :body-html fields. Message in the Message view use the actual message file, and do include these fields." ;; after all this documentation, the spectacular implementation (if msg (plist-get msg field) (mu4e-error "message must be non-nil"))) (defsubst mu4e-message-field (msg field) "Retrieve FIELD from message plist MSG. Like `mu4e-message-field-nil', but will sanitize `nil' values: - all string field except body-txt/body-html: nil -> \"\" - numeric fields + dates : nil -> 0 - all others : return the value Thus, function will return nil for empty lists, non-existing body-txt or body-html." (let ((val (mu4e-message-field-raw msg field))) (cond (val val) ;; non-nil -> just return it ((member field '(:subject :message-id :path :maildir :in-reply-to)) "") ;; string fields except body-txt, body-html: nil -> "" ((member field '(:body-html :body-txt)) val) ((member field '(:docid :size)) 0) ;; numeric type: nil -> 0 (t val)))) ;; otherwise, just return nil (defsubst mu4e-message-has-field (msg field) "Return t if MSG contains FIELD, nil otherwise." (plist-member msg field)) (defsubst mu4e-message-at-point (&optional noerror) "Get the message s-expression for the message at point in either the headers buffer or the view buffer, or nil if there is no such message. If optional NOERROR is non-nil, do not raise an error when there is no message at point." (let ((msg (or (get-text-property (point) 'msg) mu4e~view-msg))) (if msg msg (unless noerror (mu4e-warn "No message at point"))))) (defsubst mu4e-message-field-at-point (field) "Get the field FIELD from the message at point. This is equivalent to: (mu4e-message-field (mu4e-message-at-point) FIELD)." (mu4e-message-field (mu4e-message-at-point) field)) (defvar mu4e~message-body-html nil "Whether the body text uses HTML.") (defun mu4e~message-use-html-p (msg prefer-html) "Determine whether we want to use html or text; this is based on PREFER-HTML and whether the message supports the given representation." (let* ((txt (mu4e-message-field msg :body-txt)) (html (mu4e-message-field msg :body-html)) (txt-len (length txt)) (html-len (length html)) (txt-limit (* mu4e-view-html-plaintext-ratio-heuristic txt-len)) (txt-limit (if (>= txt-limit 0) txt-limit most-positive-fixnum))) (cond ; user prefers html --> use html if there is (prefer-html (> html-len 0)) ;; otherwise (user prefers text) still use html if there is not enough ;; text ((< txt-limit html-len) t) ;; otherwise, use text (t nil)))) (defun mu4e-message-body-text (msg &optional prefer-html) "Get the body in text form for this message. This is either :body-txt, or if not available, :body-html converted to text, using `mu4e-html2text-command' is non-nil, it will use that. Normally, this function prefers the text part, unless PREFER-HTML is non-nil." (setq mu4e~message-body-html (mu4e~message-use-html-p msg prefer-html)) (let ((body (if mu4e~message-body-html ;; use an HTML body (with-temp-buffer (insert (mu4e-message-field msg :body-html)) (cond ((stringp mu4e-html2text-command) (let* ((tmp-file (mu4e-make-temp-file "html"))) (write-region (point-min) (point-max) tmp-file) (erase-buffer) (call-process-shell-command mu4e-html2text-command tmp-file t t) (delete-file tmp-file))) ((functionp mu4e-html2text-command) (funcall mu4e-html2text-command)) (t (mu4e-error "Invalid `mu4e-html2text-command'"))) (setq mu4e~message-body-html t) (buffer-string)) ;; use a text body (or (mu4e-message-field msg :body-txt) "")))) ;; and finally, remove some crap from the remaining string; it seems ;; esp. outlook lies about its encoding (ie., it says 'iso-8859-1' but ;; really it's 'windows-1252'), thus giving us these funky chars. here, we ;; either remove them, or replace with 'what-was-meant' (heuristically) (with-temp-buffer (insert body) (goto-char (point-min)) (while (re-search-forward "[ ]" nil t) (replace-match (cond ((string= (match-string 0) "") "'") (t "")))) (buffer-string)))) (defun mu4e-message-contact-field-matches (msg cfield rx) "Checks whether any of the of the contacts in field CFIELD (either :to, :from, :cc or :bcc, or a list of those) of msg MSG matches (with their name or e-mail address) regular expressions RX. If there is a match, return non-nil; otherwise return nil. RX can also be a list of regular expressions, in which case any of those are tried for a match." (if (and cfield (listp cfield)) (or (mu4e-message-contact-field-matches msg (car cfield) rx) (mu4e-message-contact-field-matches msg (cdr cfield) rx)) (when cfield (if (listp rx) ;; if rx is a list, try each one of them for a match (find-if (lambda (a-rx) (mu4e-message-contact-field-matches msg cfield a-rx)) rx) ;; not a list, check the rx (find-if (lambda (ct) (let ((name (car ct)) (email (cdr ct))) (or (and name (string-match rx name)) (and email (string-match rx email))))) (mu4e-message-field msg cfield)))))) (defun mu4e-message-contact-field-matches-me (msg cfield) "Checks whether any of the of the contacts in field CFIELD (either :to, :from, :cc or :bcc) of msg MSG matches *me*, that is, any of the e-mail address in `mu4e-user-mail-address-list'. Returns the contact cell that matched, or nil." (find-if (lambda (cc-cell) (member-if (lambda (addr) (string= (downcase addr) (downcase (cdr cc-cell)))) mu4e-user-mail-address-list)) (mu4e-message-field msg cfield))) (defsubst mu4e-message-part-field (msgpart field) "Get some field in a message part; a part would look something like: (:index 2 :name \"photo.jpg\" :mime-type \"image/jpeg\" :size 147331)." (plist-get msgpart field)) ;; backward compatibility ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defalias 'mu4e-msg-field 'mu4e-message-field) (defalias 'mu4e-body-text 'mu4e-message-body-text) ;; backward compatibility (defun mu4e-field-at-point (field) "Get FIELD (a symbol, see `mu4e-header-info') for the message at point in eiter the headers buffer or the view buffer." (plist-get (mu4e-message-at-point) field)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-shr2text () "Html to text using the shr engine; this can be used in `mu4e-html2text-command' in a new enough emacs. Based on code by Titus von der Malsburg." (interactive) (let ( ;; When HTML emails contain references to remote images, ;; retrieving these images leaks information. For example, ;; the sender can see when I openend the email and from which ;; computer (IP address). For this reason, it is preferrable ;; to not retrieve images. ;; See this discussion on mu-discuss: ;; https://groups.google.com/forum/#!topic/mu-discuss/gr1cwNNZnXo (shr-inhibit-images t)) (shr-render-region (point-min) (point-max)) (goto-char (point-min)))) (provide 'mu4e-message) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/org-old-mu4e.el����������������������������������������������������������������������0000644�0001750�0001750�00000024453�13020504332�012562� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; org-mu4e -- Support for links to mu4e messages/queries from within org-mode, ;;; and for writing message in org-mode, sending them as rich-text ;; ;; Copyright (C) 2012-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Keywords: outlines, hypermedia, calendar, mail ;; Version: 0.0 ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;;; Code: ;; This should support org-mode versions 6.x. 7.x ;; the 'noerror is just to make sure bytecompilations does not break... ;; FIXME: find a better solution (require 'org nil 'noerror) (require 'org-exp nil 'noerror) (eval-when-compile (require 'cl)) (require 'mu4e) (defgroup org-mu4e nil "Settings for the org-mode related functionality in mu4e." :group 'mu4e :group 'org) (defcustom org-mu4e-link-desc-func (lambda (msg) (or (plist-get msg :subject) "No subject")) "Function that takes a msg and returns a string for the description part of an org-mode link. Example usage: (defun my-link-descr (msg) (let ((subject (or (plist-get msg :subject) \"No subject\")) (date (or (format-time-string mu4e-headers-date-format (mu4e-msg-field msg :date)) \"No date\"))) (concat subject \" \" date))) (setq org-mu4e-link-desc-func 'my-link-descr)" :type 'function :group 'org-mu4e) (defun org-mu4e-store-link () "Store a link to a mu4e query or message." (cond ;; storing links to queries ((eq major-mode 'mu4e-headers-mode) (let* ((query (mu4e-last-query)) desc link) (org-store-link-props :type "mu4e" :query query) (setq desc (concat "mu4e:query:" query) link desc) (org-add-link-props :link link :description desc) link)) ;; storing links to messages ((eq major-mode 'mu4e-view-mode) (let* ((msg (mu4e-message-at-point)) (msgid (or (plist-get msg :message-id) "<none>")) link) (org-store-link-props :type "mu4e" :link link :message-id msgid) (setq link (concat "mu4e:msgid:" msgid)) (org-add-link-props :link link :description (funcall org-mu4e-link-desc-func msg)) link)))) (org-add-link-type "mu4e" 'org-mu4e-open) (add-hook 'org-store-link-functions 'org-mu4e-store-link) (defun org-mu4e-open (path) "Open the mu4e message (for paths starting with 'msgid:') or run the query (for paths starting with 'query:')." (require 'mu4e) (cond ((string-match "^msgid:\\(.+\\)" path) (mu4e-view-message-with-message-id (match-string 1 path))) ((string-match "^query:\\(.+\\)" path) (mu4e-headers-search (match-string 1 path) current-prefix-arg)) (t (mu4e-error "mu4e: unrecognized link type '%s'" path)))) ;;; editing with org-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; below, some functions for the org->html conversion ;; based on / inspired by Eric Schulte's org-mime.el ;; Homepage: http://orgmode.org/worg/org-contrib/org-mime.php ;; ;; EXPERIMENTAL (defun org~mu4e-mime-file (ext path id) "Create a file for an attachment." (format (concat "<#part type=\"%s\" filename=\"%s\" " "disposition=inline id=\"<%s>\">\n<#/part>\n") ext path id)) (defun org~mu4e-mime-multipart (plain html &optional images) "Create a multipart/alternative with text/plain and text/html alternatives. If the html portion of the message includes images, wrap the html and images in a multipart/related part." (concat "<#multipart type=alternative><#part type=text/plain>" plain (when images "<#multipart type=related>") "<#part type=text/html>" html images (when images "<#/multipart>\n") "<#/multipart>\n")) (defun org~mu4e-mime-replace-images (str current-file) "Replace images in html files with cid links." (let (html-images) (cons (replace-regexp-in-string ;; replace images in html "src=\"\\([^\"]+\\)\"" (lambda (text) (format "src=\"cid:%s\"" (let* ((url (and (string-match "src=\"\\([^\"]+\\)\"" text) (match-string 1 text))) (path (expand-file-name url (file-name-directory current-file))) (ext (file-name-extension path)) (id (replace-regexp-in-string "[\/\\\\]" "_" path))) (add-to-list 'html-images (org~mu4e-mime-file (concat "image/" ext) path id)) id))) str) html-images))) (defun org~mu4e-mime-convert-to-html () "Convert the current body to html." (unless (fboundp 'org-export-string) (mu4e-error "require function 'org-export-string not found.")) (unless (executable-find "dvipng") (mu4e-error "Required program dvipng not found")) (let* ((begin (save-excursion (goto-char (point-min)) (search-forward mail-header-separator))) (end (point-max)) (raw-body (buffer-substring begin end)) (tmp-file (make-temp-name (expand-file-name "mail" temporary-file-directory))) (body (org-export-string raw-body 'org (file-name-directory tmp-file))) ;; because we probably don't want to skip part of our mail (org-export-skip-text-before-1st-heading nil) ;; because we probably don't want to export a huge style file (org-export-htmlize-output-type 'inline-css) ;; makes the replies with ">"s look nicer (org-export-preserve-breaks t) ;; dvipng for inline latex because MathJax doesn't work in mail (org-export-with-LaTeX-fragments 'dvipng) ;; to hold attachments for inline html images (html-and-images (org~mu4e-mime-replace-images (org-export-string raw-body 'html (file-name-directory tmp-file)) tmp-file)) (html-images (cdr html-and-images)) (html (car html-and-images))) (delete-region begin end) (save-excursion (goto-char begin) (newline) (insert (org~mu4e-mime-multipart body html (mapconcat 'identity html-images "\n")))))) ;; next some functions to make the org/mu4e-compose-mode switch as smooth as ;; possible. (defun org~mu4e-mime-decorate-headers () "Make the headers visually distinctive (org-mode)." (save-excursion (goto-char (point-min)) (let* ((eoh (when (search-forward mail-header-separator) (match-end 0))) (olay (make-overlay (point-min) eoh))) (when olay (overlay-put olay 'face 'font-lock-comment-face))))) (defun org~mu4e-mime-undecorate-headers () "Don't make the headers visually distinctive. \(well, mu4e-compose-mode will take care of that)." (save-excursion (goto-char (point-min)) (let* ((eoh (when (search-forward mail-header-separator) (match-end 0)))) (remove-overlays (point-min) eoh)))) (defvar org-mu4e-convert-to-html nil "Whether to do automatic org-mode => html conversion when sending messages.") (defun org~mu4e-mime-convert-to-html-maybe () "Convert to html if `org-mu4e-convert-to-html' is non-nil. This function is called when sending a message (from `message-send-hook') and, if non-nil, will send the message as the rich-text version of the what is assumed to be an org-mode body." (when org-mu4e-convert-to-html (mu4e-message "Converting to html") (org~mu4e-mime-convert-to-html))) (defun org~mu4e-mime-switch-headers-or-body () "Switch the buffer to either mu4e-compose-mode (when in headers) or org-mode (when in the body)." (interactive) (let* ((sepapoint (save-excursion (goto-char (point-min)) (search-forward-regexp mail-header-separator nil t)))) ;; only do stuff when the sepapoint exist; note that after sending the ;; message, this function maybe called on a message with the sepapoint ;; stripped. This is why we don't use `message-point-in-header'. (when sepapoint (cond ;; we're in the body, but in mu4e-compose-mode? ;; if so, switch to org-mode ((and (> (point) sepapoint) (eq major-mode 'mu4e-compose-mode)) (org-mode) (add-hook 'before-save-hook (lambda () (mu4e-error "Switch to mu4e-compose-mode (M-m) before saving.")) nil t) (org~mu4e-mime-decorate-headers) (local-set-key (kbd "M-m") (lambda (keyseq) (interactive "kEnter mu4e-compose-mode key sequence: ") (let ((func (lookup-key mu4e-compose-mode-map keyseq))) (if func (funcall func) (insert keyseq)))))) ;; we're in the headers, but in org-mode? ;; if so, switch to mu4e-compose-mode ((and (<= (point) sepapoint) (eq major-mode 'org-mode)) (org~mu4e-mime-undecorate-headers) (mu4e-compose-mode) (add-hook 'message-send-hook 'org~mu4e-mime-convert-to-html-maybe nil t))) ;; and add the hook (add-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t t)))) (defun org-mu4e-compose-org-mode () "Pseudo-Minor mode for mu4e-compose-mode, to edit the message body using org-mode." (interactive) (unless (member major-mode '(org-mode mu4e-compose-mode)) (mu4e-error "Need org-mode or mu4e-compose-mode")) ;; we can check if we're already in org-mu4e-compose-mode by checking if the ;; post-command-hook is set; hackish...but a buffer-local variable does not ;; seem to survive buffer switching (if (not (member 'org~mu4e-mime-switch-headers-or-body post-command-hook)) (progn (org~mu4e-mime-switch-headers-or-body) (mu4e-message (concat "org-mu4e-compose-org-mode enabled; " "press M-m before issuing message-mode commands"))) (progn ;; otherwise, remove crap (remove-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t) (org~mu4e-mime-undecorate-headers) ;; shut off org-mode stuff (mu4e-compose-mode) (message "org-mu4e-compose-org-mode disabled")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (provide 'org-mu4e) ;;; org-mu4e.el ends here ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-lists.el������������������������������������������������������������������������0000644�0001750�0001750�00000012421�13020504332�012345� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e-lists.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; In this file, we create a table of list-id -> shortname for mailing lists. ;; The shortname (friendly) should a at most 8 characters, camel-case (defvar mu4e~mailing-lists '( ("bbdb-info.lists.sourceforge.net" . "BBDB") ("boost-announce.lists.boost.org" . "BoostA") ("boost-interest.lists.boost.org" . "BoostI") ("conkeror.mozdev.org" . "Conkeror") ("curl-library.cool.haxx.se" . "LibCurl") ("crypto-gram-list.schneier.com " . "CryptoGr") ("dbus.lists.freedesktop.org" . "DBus") ("desktop-devel-list.gnome.org" . "GnomeDT") ("emacs-devel.gnu.org" . "EmacsDev") ("emacs-orgmode.gnu.org" . "Orgmode") ("emms-help.gnu.org" . "Emms") ("enlightenment-devel.lists.sourceforge.net" . "E-Dev") ("erlang-questions.erlang.org" . "Erlang") ("evolution-hackers.lists.ximian.com" . "EvoDev") ("farsight-devel.lists.sourceforge.net" . "Farsight") ("mailman.lists.freedesktop.org" . "FDeskTop") ("gcc-help.gcc.gnu.org" . "Gcc") ("gmime-devel-list.gnome.org" . "GMimeDev") ("gnome-shell-list.gnome.org" . "GnomeSh") ("gnu-emacs-sources.gnu.org" . "EmacsSrc") ("gnupg-users.gnupg.org" . "GnupgU") ("gpe.handhelds.org" . "GPE") ("gstreamer-devel.lists.freedesktop.org" . "GstDev") ("gstreamer-devel.lists.sourceforge.net" . "GstDev") ("gstreamer-openmax.lists.sourceforge.net" . "GstOmx") ("gtk-devel-list.gnome.org" . "GtkDev") ("gtkmm-list.gnome.org" . "GtkmmDev") ("guile-devel.gnu.org" . "GuileDev") ("guile-user.gnu.org" . "GuileUsr") ("help-gnu-emacs.gnu.org" . "EmacsUsr") ("lggdh-algemeen.vvtp.tudelft.nl" . "LGGDH") ("linux-bluetooth.vger.kernel.org" . "Bluez") ("maemo-developers.maemo.org" . "MaemoDev") ("maemo-users.maemo.org" . "MaemoUsr") ("monit-general.nongnu.org" . "Monit") ("mu-discuss.googlegroups.com" . "Mu") ("nautilus-list.gnome.org" . "Nautilus") ("notmuch.notmuchmail.org" . "Notmuch") ("orbit-list.gnome.org" . "ORBit") ("pulseaudio-discuss.lists.freedesktop.org" . "PulseA") ("sqlite-announce.sqlite.org" . "SQliteAnn") ("sqlite-dev.sqlite.org" . "SQLiteDev") ("sup-talk.rubyforge.org" . "Sup") ("sylpheed-claws-users.lists.sourceforge.net" . "Sylpheed") ("tinymail-devel-list.gnome.org" . "Tinymail") ("unicode.sarasvati.unicode.org" . "Unicode") ("xapian-discuss.lists.xapian.org" . "Xapian") ("xdg.lists.freedesktop.org" . "XDG") ("wl-en.lists.airs.net" . "Wdrlust") ("wl-en.ml.gentei.org" . "WdrLust") ("xapian-devel.lists.xapian.org" . "Xapian") ("zsh-users.zsh.org" . "ZshUsr")) "AList of cells (MAILING-LIST-ID . SHORTNAME)") (defvar mu4e-user-mailing-lists nil "An alist with cells (MAILING-LIST-ID . SHORTNAME); these are used in addition to the built-in list `mu4e~mailing-lists'.") (defvar mu4e-mailing-list-patterns nil "A list of regex patterns to capture a shortname out of a list ID. For the first regex that matches, its first matchgroup will be used as the shortname.") (provide 'mu4e-lists) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-meta.el�������������������������������������������������������������������������0000644�0001750�0001750�00000000452�13021065715�012146� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; auto-generated (defconst mu4e-mu-version "0.9.18" "Required mu binary version; mu4e's version must agree with this.") (defconst mu4e-builddir "/home/djcb/Sources/mu" "Top-level build directory.") (defconst mu4e-doc-dir "/usr/local/share/doc/mu" "Mu4e's data-dir.") (provide 'mu4e-meta) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-about.org�����������������������������������������������������������������������0000644�0001750�0001750�00000001440�13020504332�012507� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#+STARTUP:showall * About mu4e *mu4e* is an emacs e-mail client based on the [[http://djcbsoftware.nl/code/mu][mu]] email search engine. It was written & designed by /Dirk-Jan C. Binnema/, with contributions from others. *mu4e* and *mu* are free software, licensed under the terms of the [[http://www.gnu.org/licenses/gpl-3.0.html][GNU GPLv3]]. You can get the code from [[https://github.com/djcb/mu][the git repository]]; there, you can also [[https://github.com/djcb/mu/issues][file bugs and feature requests]]. *mu4e* has its own [[info:mu4e][manual]], which includes an [[info:mu4e#FAQ%20-%20Frequently%20Anticipated%20Questions][FAQ]]. If that is not enough, there's also the [[http://groups.google.com/group/mu-discuss][mu mailing list]]. [Press *q* to quit this buffer] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-speedbar.el���������������������������������������������������������������������0000644�0001750�0001750�00000010146�13020504332�012776� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; mu4e-speedbar --- Speedbar support for mu4e ;; Copyright (C) 2012-2016 Antono Vasiljev, Dirk-Jan C. Binnema ;; ;; Author: Antono Vasiljev <self@antono.info> ;; Version: 0.1 ;; Keywords: file, tags, tools ;; ;; This file is not part of GNU Emacs. ;; ;; 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, or (at your option) ;; any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; ;; Speedbar provides a frame in which files, and locations in files ;; are displayed. These functions provide mu4e specific support, ;; showing maildir list in the side-bar. ;; ;; This file requires speedbar. ;;; Code: (require 'speedbar) (require 'mu4e-vars) (require 'mu4e-headers) (require 'mu4e-utils) (defvar mu4e-main-speedbar-key-map nil "Keymap used when in mu4e display mode.") (defvar mu4e-headers-speedbar-key-map nil "Keymap used when in mu4e display mode.") (defvar mu4e-view-speedbar-key-map nil "Keymap used when in mu4e display mode.") (defvar mu4e-main-speedbar-menu-items nil "Additional menu-items to add to speedbar frame.") (defvar mu4e-headers-speedbar-menu-items nil "Additional menu-items to add to speedbar frame.") (defvar mu4e-view-speedbar-menu-items nil "Additional menu-items to add to speedbar frame.") (defun mu4e-speedbar-install-variables () "Install those variables used by speedbar to enhance mu4e." (dolist (keymap '( mu4e-main-speedbar-key-map mu4e-headers-speedbar-key-map mu4e-view-speedbar-key-map)) (unless keymap (setq keymap (speedbar-make-specialized-keymap)) (define-key keymap "RET" 'speedbar-edit-line) (define-key keymap "e" 'speedbar-edit-line)))) ;; Make sure our special speedbar major mode is loaded (if (featurep 'speedbar) (mu4e-speedbar-install-variables) (add-hook 'speedbar-load-hook 'mu4e-speedbar-install-variables)) (defun mu4e~speedbar-render-maildir-list () "Insert the list of maildirs in the speedbar." (interactive) (mapcar (lambda (maildir-name) (speedbar-insert-button (concat " " maildir-name) 'mu4e-highlight-face 'highlight 'mu4e~speedbar-maildir maildir-name)) (mu4e-get-maildirs))) (defun mu4e~speedbar-maildir (&optional text token ident) "Jump to maildir TOKEN. TEXT and INDENT are not used." (speedbar-with-attached-buffer (mu4e-headers-search (concat "\"maildir:" token "\"") current-prefix-arg))) (defun mu4e~speedbar-render-bookmark-list () "Insert the list of bookmarks in the speedbar" (interactive) (mapcar (lambda (bookmark) (speedbar-insert-button (concat " " (mu4e-bookmark-name bookmark)) 'mu4e-highlight-face 'highlight 'mu4e~speedbar-bookmark (mu4e-bookmark-query bookmark))) (mu4e-bookmarks))) (defun mu4e~speedbar-bookmark (&optional text token ident) "Run bookmarked query TOKEN. TEXT and INDENT are not used." (speedbar-with-attached-buffer (mu4e-headers-search token current-prefix-arg))) ;;;###autoload (defun mu4e-speedbar-buttons (buffer) "Create buttons for any mu4e BUFFER." (interactive) (erase-buffer) (insert (propertize "* mu4e\n\n" 'face 'mu4e-title-face)) (insert (propertize " Bookmarks\n" 'face 'mu4e-title-face)) (mu4e~speedbar-render-bookmark-list) (insert "\n") (insert (propertize " Maildirs\n" 'face 'mu4e-title-face)) (mu4e~speedbar-render-maildir-list)) (defun mu4e-main-speedbar-buttons (buffer) (mu4e-speedbar-buttons buffer)) (defun mu4e-headers-speedbar-buttons (buffer) (mu4e-speedbar-buttons buffer)) (defun mu4e-view-speedbar-buttons (buffer) (mu4e-speedbar-buttons buffer)) (provide 'mu4e-speedbar) ;;; mu4e-speedbar.el ends here ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/mu4e/mu4e-context.el����������������������������������������������������������������������0000644�0001750�0001750�00000013774�13021037573�012720� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; mu4e-context.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2015-2016 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; A mu4e 'context' is a a set of variable-settings and functions, which can be ;; used e.g. to switch between accounts. (eval-when-compile (byte-compile-disable-warning 'cl-functions)) (require 'cl) (require 'mu4e-utils) (defvar mu4e-contexts nil "The list of `mu4e-context' objects describing mu4e's contexts.") (defvar mu4e~context-current nil "The current context; for internal use. Use `mu4e-context-switch' to change it.") (defun mu4e-context-current () "Get the currently active context, or nil if there is none." mu4e~context-current) (defun mu4e-context-label () "Propertized string with the current context name, or \"\" if there is none." (if (mu4e-context-current) (concat "[" (propertize (mu4e~quote-for-modeline (mu4e-context-name (mu4e-context-current))) 'face 'mu4e-context-face) "]") "")) (defstruct mu4e-context "A mu4e context object with the following members: - `name': the name of the context, eg. \"Work\" or \"Private\".' - `enter-func': a parameterless function invoked when entering this context, or nil - `leave-func':a parameterless fuction invoked when leaving this context, or nil - `match-func': a function called when comnposing a new messages, and takes a message plist for the message replied to or forwarded, and nil otherwise. Before composing a new message, `mu4e' switches to the first context for which `match-func' return t." name ;; name of the context, e.g. "work" (enter-func nil) ;; function invoked when entering the context (leave-func nil) ;; function invoked when leaving the context (match-func nil) ;; function that takes a msg-proplist, and return t ;; if it matches, nil otherwise vars) ;; alist of variables. (defun mu4e~context-ask-user (prompt) "Let user choose some context based on its name." (when mu4e-contexts (let* ((names (map 'list (lambda (context) (cons (mu4e-context-name context) context)) mu4e-contexts)) (context (mu4e-read-option prompt names))) (or context (mu4e-error "No such context"))))) (defun mu4e-context-switch (&optional force name) "Switch context to a context with NAME which is part of `mu4e-contexts'; if NAME is nil, query user. If the new context is the same and the current context, only switch (run associated functions) when prefix argument FORCE is non-nil." (interactive "P") (unless mu4e-contexts (mu4e-error "No contexts defined")) (let* ((names (map 'list (lambda (context) (cons (mu4e-context-name context) context)) mu4e-contexts)) (context (if name (cdr-safe (assoc name names)) (mu4e~context-ask-user "Switch to context: ")))) (unless context (mu4e-error "No such context")) ;; if new context is same as old one one switch with FORCE is set. (when (or force (not (eq context (mu4e-context-current)))) (when (and (mu4e-context-current) (mu4e-context-leave-func mu4e~context-current)) (funcall (mu4e-context-leave-func mu4e~context-current))) ;; enter the new context (when (mu4e-context-enter-func context) (funcall (mu4e-context-enter-func context))) (when (mu4e-context-vars context) (mapc #'(lambda (cell) (set (car cell) (cdr cell))) (mu4e-context-vars context))) (setq mu4e~context-current context) (mu4e~main-view-real nil nil) (mu4e-message "Switched context to %s" (mu4e-context-name context))) context)) (defun mu4e~context-autoswitch (&optional msg policy) "When contexts are defined but there is no context yet, switch to the first whose :match-func return non-nil. If none of them match, return the first. For MSG and POLICY, see `mu4e-context-determine'." (when mu4e-contexts (let ((context (mu4e-context-determine msg policy))) (when context (mu4e-context-switch nil (mu4e-context-name context)))))) (defun mu4e-context-determine (msg &optional policy) "Return the first context with a match-func that returns t. MSG points to the plist for the message replied to or forwarded, or nil if there is no such MSG; similar to what `mu4e-compose-pre-hook' does. POLICY specifies how to do the determination. If POLICY is 'always-ask, we ask the user unconditionally. In all other cases, if any context matches (using its match function), this context is returned. If none of the contexts match, POLICY determines what to do: - pick-first: pick the first of the contexts available - ask: ask the user - ask-if-none: ask if there is no context yet - otherwise, return nil. Effectively, this leaves the current context as it is." (when mu4e-contexts (if (eq policy 'always-ask) (mu4e~context-ask-user "Select context: ") (or ;; is there a matching one? (find-if (lambda (context) (when (mu4e-context-match-func context) (funcall (mu4e-context-match-func context) msg))) mu4e-contexts) ;; no context found yet; consult policy (case policy (pick-first (car mu4e-contexts)) (ask (mu4e~context-ask-user "Select context: ")) (ask-if-none (or (mu4e-context-current) (mu4e~context-ask-user "Select context: "))) (otherwise nil)))))) (provide 'mu4e-context) ����mu-0.9.18/lib/��������������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065720�007775� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-msg-iter.cc������������������������������������������������������������������������0000644�0001750�0001750�00000024521�13020504332�012372� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <stdlib.h> #include <unistd.h> #include <iostream> #include <string.h> #include <errno.h> #include <algorithm> #include <xapian.h> #include <string> #include <set> #include <map> #include "mu-util.h" #include "mu-msg.h" #include "mu-msg-iter.h" #include "mu-threader.h" struct ltstr { bool operator () (const std::string &s1, const std::string &s2) const { return g_strcmp0 (s1.c_str(), s2.c_str()) < 0; } }; typedef std::map <std::string, unsigned, ltstr> msgid_docid_map; class ThreadKeyMaker: public Xapian::KeyMaker { public: ThreadKeyMaker (GHashTable *threadinfo): _threadinfo(threadinfo) {} virtual std::string operator()(const Xapian::Document &doc) const { MuMsgIterThreadInfo *ti; ti = (MuMsgIterThreadInfo*)g_hash_table_lookup (_threadinfo, GUINT_TO_POINTER(doc.get_docid())); return std::string (ti && ti->threadpath ? ti->threadpath : ""); } private: GHashTable *_threadinfo; }; struct _MuMsgIter { public: _MuMsgIter (Xapian::Enquire &enq, size_t maxnum, MuMsgFieldId sortfield, MuMsgIterFlags flags): _enq(enq), _thread_hash (0), _msg(0), _flags(flags), _skip_unreadable(flags & MU_MSG_ITER_FLAG_SKIP_UNREADABLE), _skip_dups (flags & MU_MSG_ITER_FLAG_SKIP_DUPS) { bool descending = (flags & MU_MSG_ITER_FLAG_DESCENDING); bool threads = (flags & MU_MSG_ITER_FLAG_THREADS); // first, we get _all_ matches (G_MAXINT), based the threads // on that, then return <maxint> of those _matches = _enq.get_mset (0, G_MAXINT); if (_matches.empty()) return; if (threads) { _matches.fetch(); _cursor = _matches.begin(); // NOTE: temporarily turn-off skipping duplicates, since we // need threadinfo for *all* _skip_dups = false; _thread_hash = mu_threader_calculate (this, _matches.size(), sortfield, descending); _skip_dups = (flags & MU_MSG_ITER_FLAG_SKIP_DUPS); ThreadKeyMaker keymaker(_thread_hash); enq.set_sort_by_key (&keymaker, false); _matches = _enq.get_mset (0, maxnum); } else if (sortfield != MU_MSG_FIELD_ID_NONE) { enq.set_sort_by_value ((Xapian::valueno)sortfield, descending); _matches = _enq.get_mset (0, maxnum); _cursor = _matches.begin(); } _cursor = _matches.begin(); } ~_MuMsgIter () { if (_thread_hash) g_hash_table_destroy (_thread_hash); set_msg (NULL); } const Xapian::Enquire& enquire() const { return _enq; } Xapian::MSet& matches() { return _matches; } Xapian::MSet::const_iterator cursor () const { return _cursor; } void set_cursor (Xapian::MSetIterator cur) { _cursor = cur; } void cursor_next () { ++_cursor; } GHashTable *thread_hash () { return _thread_hash; } MuMsg *msg() const { return _msg; } MuMsg *set_msg (MuMsg *msg) { if (_msg) mu_msg_unref (_msg); return _msg = msg; } MuMsgIterFlags flags() const { return _flags; } const std::string msgid () const { const Xapian::Document doc (cursor().get_document()); return doc.get_value(MU_MSG_FIELD_ID_MSGID); } unsigned docid () const { const Xapian::Document doc (cursor().get_document()); return doc.get_docid(); } bool looks_like_dup () const { try { const Xapian::Document doc (cursor().get_document()); // is this message in the preferred map? if // so, it's not a duplicate, otherwise, it // isn't msgid_docid_map::const_iterator pref_iter (_preferred_map.find (msgid())); if (pref_iter != _preferred_map.end()) { //std::cerr << "in the set!" << std::endl; if ((*pref_iter).second == docid()) return false; // in the set: not a dup! else return true; } // otherwise, simply check if we've already seen this message-id, // and, if so, it's considered a dup if (_msg_uid_set.find (msgid()) != _msg_uid_set.end()) { return true; } else { _msg_uid_set.insert (msgid()); return false; } } catch (...) { return true; } } static void each_preferred (const char *msgid, gpointer docidp, msgid_docid_map *preferred_map) { (*preferred_map)[msgid] = GPOINTER_TO_SIZE(docidp); } void set_preferred_map (GHashTable *preferred_hash) { if (!preferred_hash) _preferred_map.clear(); else g_hash_table_foreach (preferred_hash, (GHFunc)each_preferred, &_preferred_map); } bool skip_dups () const { return _skip_dups; } bool skip_unreadable () const { return _skip_unreadable; } private: const Xapian::Enquire _enq; Xapian::MSet _matches; Xapian::MSet::const_iterator _cursor; GHashTable *_thread_hash; MuMsg *_msg; MuMsgIterFlags _flags; mutable std::set <std::string, ltstr> _msg_uid_set; bool _skip_unreadable; // the 'preferred map' (msgid->docid) is used when checking // for duplicates; if a message is in the preferred map, it // will not be excluded (but other messages with the same // msgid will) msgid_docid_map _preferred_map; bool _skip_dups; }; static gboolean is_msg_file_readable (MuMsgIter *iter) { gboolean readable; std::string path (iter->cursor().get_document().get_value(MU_MSG_FIELD_ID_PATH)); if (path.empty()) return FALSE; readable = (access (path.c_str(), R_OK) == 0) ? TRUE : FALSE; return readable; } MuMsgIter * mu_msg_iter_new (XapianEnquire *enq, size_t maxnum, MuMsgFieldId sortfield, MuMsgIterFlags flags, GError **err) { g_return_val_if_fail (enq, NULL); /* sortfield should be set to .._NONE when we're not threading */ g_return_val_if_fail (mu_msg_field_id_is_valid (sortfield) || sortfield == MU_MSG_FIELD_ID_NONE, FALSE); try { MuMsgIter *iter (new MuMsgIter ((Xapian::Enquire&)*enq, maxnum, sortfield, flags)); // note: we check if it's a dup even for the first message, // since we need its uid in the set for checking later messages if ((iter->skip_unreadable() && !is_msg_file_readable (iter)) || (iter->skip_dups() && iter->looks_like_dup ())) mu_msg_iter_next (iter); /* skip! */ return iter; } catch (const Xapian::DatabaseModifiedError &dbmex) { mu_util_g_set_error (err, MU_ERROR_XAPIAN_MODIFIED, "database was modified; please reopen"); return 0; } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, 0); } void mu_msg_iter_destroy (MuMsgIter *iter) { try { delete iter; } MU_XAPIAN_CATCH_BLOCK; } void mu_msg_iter_set_preferred (MuMsgIter *iter, GHashTable *preferred_hash) { g_return_if_fail (iter); iter->set_preferred_map (preferred_hash); } MuMsg* mu_msg_iter_get_msg_floating (MuMsgIter *iter) { g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); try { MuMsg *msg; GError *err; Xapian::Document *docp; docp = new Xapian::Document(iter->cursor().get_document()); err = NULL; msg = iter->set_msg (mu_msg_new_from_doc((XapianDocument*)docp, &err)); if (!msg) MU_HANDLE_G_ERROR(err); return msg; } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } gboolean mu_msg_iter_reset (MuMsgIter *iter) { g_return_val_if_fail (iter, FALSE); iter->set_msg (NULL); try { iter->set_cursor(iter->matches().begin()); } MU_XAPIAN_CATCH_BLOCK_RETURN (FALSE); return TRUE; } gboolean mu_msg_iter_next (MuMsgIter *iter) { g_return_val_if_fail (iter, FALSE); iter->set_msg (NULL); if (mu_msg_iter_is_done(iter)) return FALSE; try { iter->cursor_next(); if (iter->cursor() == iter->matches().end()) return FALSE; if ((iter->skip_unreadable() && !is_msg_file_readable (iter)) || (iter->skip_dups() && iter->looks_like_dup ())) return mu_msg_iter_next (iter); /* skip! */ return TRUE; } MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE); } gboolean mu_msg_iter_is_done (MuMsgIter *iter) { g_return_val_if_fail (iter, TRUE); try { return iter->cursor() == iter->matches().end() ? TRUE : FALSE; } MU_XAPIAN_CATCH_BLOCK_RETURN (TRUE); } /* hmmm.... is it impossible to get a 0 docid, or just very improbable? */ unsigned mu_msg_iter_get_docid (MuMsgIter *iter) { g_return_val_if_fail (iter, (unsigned int)-1); g_return_val_if_fail (!mu_msg_iter_is_done(iter), (unsigned int)-1); try { return iter->docid(); } MU_XAPIAN_CATCH_BLOCK_RETURN ((unsigned int)-1); } char* mu_msg_iter_get_msgid (MuMsgIter *iter) { g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); try { return g_strdup (iter->msgid().c_str()); } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } char** mu_msg_iter_get_refs (MuMsgIter *iter) { g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); try { std::string refs ( iter->cursor().get_document().get_value(MU_MSG_FIELD_ID_REFS)); if (refs.empty()) return NULL; return g_strsplit (refs.c_str(),",", -1); } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } char* mu_msg_iter_get_thread_id (MuMsgIter *iter) { g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); try { const std::string thread_id ( iter->cursor().get_document().get_value(MU_MSG_FIELD_ID_THREAD_ID).c_str()); return thread_id.empty() ? NULL : g_strdup (thread_id.c_str()); } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } const MuMsgIterThreadInfo* mu_msg_iter_get_thread_info (MuMsgIter *iter) { g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); /* maybe we don't have thread info */ if (!iter->thread_hash()) return NULL; try { const MuMsgIterThreadInfo *ti; unsigned int docid; docid = mu_msg_iter_get_docid (iter); ti = (const MuMsgIterThreadInfo*)g_hash_table_lookup (iter->thread_hash(), GUINT_TO_POINTER(docid)); if (!ti) g_warning ("no ti for %u\n", docid); return ti; } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-str.c������������������������������������������������������������������������������0000644�0001750�0001750�00000052176�13020504332�011317� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include <stdio.h> #include "mu-util.h" /* PATH_MAX */ #include "mu-str.h" #include "mu-msg-fields.h" const char* mu_str_size_s (size_t s) { static char buf[32]; #ifdef HAVE_GLIB216 char *tmp; tmp = g_format_size_for_display ((goffset)s); strncpy (buf, tmp, sizeof(buf)); buf[sizeof(buf) -1] = '\0'; /* just in case */ g_free (tmp); #else if (s >= 1000 * 1000) g_snprintf(buf, sizeof(buf), "%.1f MB", (double)s/(1000*1000)); else g_snprintf(buf, sizeof(buf), "%.1f kB", (double)s/(1000)); #endif /*HAVE_GLIB216*/ return buf; } char* mu_str_size (size_t s) { return g_strdup (mu_str_size_s(s)); } const char* mu_str_flags_s (MuFlags flags) { return mu_flags_to_str_s (flags, MU_FLAG_TYPE_ANY); } char* mu_str_flags (MuFlags flags) { return g_strdup (mu_str_flags_s(flags)); } char* mu_str_summarize (const char* str, size_t max_lines) { char *summary; size_t nl_seen; unsigned i,j; gboolean last_was_blank; g_return_val_if_fail (str, NULL); g_return_val_if_fail (max_lines > 0, NULL); /* len for summary <= original len */ summary = g_new (gchar, strlen(str) + 1); /* copy the string up to max_lines lines, replace CR/LF/tab with * single space */ for (i = j = 0, nl_seen = 0, last_was_blank = TRUE; nl_seen < max_lines && str[i] != '\0'; ++i) { if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == ' ' ) { if (str[i] == '\n') ++nl_seen; /* no double-blanks or blank at end of str */ if (!last_was_blank && str[i+1] != '\0') summary[j++] = ' '; last_was_blank = TRUE; } else { summary[j++] = str[i]; last_was_blank = FALSE; } } summary[j] = '\0'; return summary; } static void cleanup_contact (char *contact) { char *c, *c2; /* replace "'<> with space */ for (c2 = contact; *c2; ++c2) if (*c2 == '"' || *c2 == '\'' || *c2 == '<' || *c2 == '>') *c2 = ' '; /* remove everything between '()' if it's after the 5th pos; * good to cleanup corporate contact address spam... */ c = g_strstr_len (contact, -1, "("); if (c && c - contact > 5) *c = '\0'; g_strstrip (contact); } /* this is still somewhat simplistic... */ const char* mu_str_display_contact_s (const char *str) { static gchar contact[255]; gchar *c, *c2; str = str ? str : ""; g_strlcpy (contact, str, sizeof(contact)); /* we check for '<', so we can strip out the address stuff in * e.g. 'Hello World <hello@world.xx>, but only if there is * something alphanumeric before the < */ c = g_strstr_len (contact, -1, "<"); if (c != NULL) { for (c2 = contact; c2 < c && !(isalnum(*c2)); ++c2); if (c2 != c) /* apparently, there was something, * so we can remove the <... part*/ *c = '\0'; } cleanup_contact (contact); return contact; } char* mu_str_display_contact (const char *str) { g_return_val_if_fail (str, NULL); return g_strdup (mu_str_display_contact_s (str)); } gint64 mu_str_size_parse_bkm (const char* str) { gint64 num; g_return_val_if_fail (str, -1); if (!isdigit(str[0])) return -1; num = atoi(str); for (++str; isdigit(*str); ++str); switch (tolower(*str)) { case '\0': case 'b' : return num; /* bytes */ case 'k': return num * 1000; /* kilobyte */ case 'm': return num * 1000 * 1000; /* megabyte */ default: return -1; } } char* mu_str_replace (const char *str, const char *substr, const char *repl) { GString *gstr; const char *cur; g_return_val_if_fail (str, NULL); g_return_val_if_fail (substr, NULL); g_return_val_if_fail (repl, NULL); gstr = g_string_sized_new (2 * strlen (str)); for (cur = str; *cur; ++cur) { if (g_str_has_prefix (cur, substr)) { g_string_append (gstr, repl); cur += strlen (substr) - 1; } else g_string_append_c (gstr, *cur); } return g_string_free (gstr, FALSE); } char* mu_str_from_list (const GSList *lst, char sepa) { const GSList *cur; char *str; g_return_val_if_fail (sepa, NULL); for (cur = lst, str = NULL; cur; cur = g_slist_next(cur)) { char *tmp; /* two extra dummy '\0' so -Wstack-protector won't complain */ char sep[4] = { '\0', '\0', '\0', '\0' }; sep[0] = cur->next ? sepa : '\0'; tmp = g_strdup_printf ("%s%s%s", str ? str : "", (gchar*)cur->data, sep); g_free (str); str = tmp; } return str; } GSList* mu_str_to_list (const char *str, char sepa, gboolean strip) { GSList *lst; gchar **strs, **cur; /* two extra dummy '\0' so -Wstack-protector won't complain */ char sep[4] = { '\0', '\0', '\0', '\0' }; g_return_val_if_fail (sepa, NULL); if (!str) return NULL; sep[0] = sepa; strs = g_strsplit (str, sep, -1); for (cur = strs, lst = NULL; cur && *cur; ++cur) { char *elm; elm = g_strdup(*cur); if (strip) elm = g_strstrip (elm); lst = g_slist_prepend (lst, elm); } lst = g_slist_reverse (lst); g_strfreev (strs); return lst; } GSList* mu_str_esc_to_list (const char *strings) { GSList *lst; GString *part; unsigned u; gboolean quoted, escaped; g_return_val_if_fail (strings, NULL); part = g_string_new (NULL); for (u = 0, lst = NULL, quoted = FALSE, escaped = FALSE; u != strlen (strings); ++u) { char kar; kar = strings[u]; if (kar == '\\') { if (escaped) g_string_append_c (part, '\\'); escaped = !escaped; continue; } if (quoted && kar != '"') { g_string_append_c (part, kar); continue; } switch (kar) { case '"': if (!escaped) quoted = !quoted; else g_string_append_c (part, kar); continue; case ' ': if (part->len > 0) { lst = g_slist_prepend (lst, g_string_free (part, FALSE)); part = g_string_new (NULL); } continue; default: g_string_append_c (part, kar); } } if (part->len) lst = g_slist_prepend (lst, g_string_free (part, FALSE)); return g_slist_reverse (lst); } void mu_str_free_list (GSList *lst) { g_slist_foreach (lst, (GFunc)g_free, NULL); g_slist_free (lst); } /* this function is critical for sorting performance; therefore, no * regexps, but just some good old c pointer magic */ const gchar* mu_str_subject_normalize (const gchar* str) { const char* cur; g_return_val_if_fail (str, NULL); cur = str; while (isspace(*cur)) ++cur; /* skip space */ /* starts with Re:? */ if (tolower(cur[0]) == 'r' && tolower(cur[1]) == 'e') cur += 2; /* starts with Fwd:? */ else if (tolower(cur[0]) == 'f' && tolower(cur[1]) == 'w' && tolower(cur[2]) == 'd') cur += 3; else /* nope, different string */ return str; /* we're now past either 'Re' or 'Fwd'. Maybe there's a [<num>] now? * ie., the Re[3]: foo case */ if (cur[0] == '[') { /* handle the Re[3]: case */ if (isdigit(cur[1])) { do { ++cur; } while (isdigit(*cur)); if ( cur[0] != ']') { return str; /* nope: no ending ']' */ } else /* skip ']' and space */ do { ++cur; } while (isspace(*cur)); } else /* nope: no number after '[' */ return str; } /* now, cur points past either 're' or 'fwd', possibly with * [<num>]; check if it's really a prefix -- after re or fwd * there should either a ':' and possibly some space */ if (cur[0] == ':') { do { ++cur; } while (isspace(*cur)); /* note: there may still be another prefix, such as * Re[2]: Fwd: foo */ return mu_str_subject_normalize (cur); } else return str; /* nope, it was not a prefix */ } struct _CheckPrefix { const char *str; gboolean match; gboolean range_field; }; typedef struct _CheckPrefix CheckPrefix; static void each_check_prefix (MuMsgFieldId mfid, CheckPrefix *cpfx) { const char *pfx; char pfx_short[3] = { 'X', ':', '\0'}; char k; if (!cpfx || cpfx->match) return; k = pfx_short[0] = mu_msg_field_shortcut (mfid); if (k && g_str_has_prefix (cpfx->str, pfx_short)) { cpfx->match = TRUE; cpfx->range_field = mu_msg_field_is_range_field (mfid); } pfx = mu_msg_field_name (mfid); if (pfx && g_str_has_prefix (cpfx->str, pfx) && cpfx->str[strlen(pfx)] == ':') { cpfx->match = TRUE; cpfx->range_field = mu_msg_field_is_range_field (mfid); } } /* check if it looks like either i:<msgid> or msgid:<msgid> */ static gboolean is_msgid_field (const char *str) { const char *name; if (!str || strlen(str) < 3) return FALSE; if (str[0] == mu_msg_field_shortcut (MU_MSG_FIELD_ID_MSGID) && str[1] == ':') return TRUE; name = mu_msg_field_name (MU_MSG_FIELD_ID_MSGID); if (g_str_has_prefix (str, name) && str[strlen(name)] == ':') return TRUE; return FALSE; } /* message-ids need a bit more massaging -- we replace all * non-alphanum with '_'. Note, this function assumes we're looking at * a msg-id field, ie. i:<msgid> or msgid:<msgid> */ char* mu_str_process_msgid (const char *str, gboolean query) { char *s, *c; g_return_val_if_fail (str, NULL); g_return_val_if_fail (!query || strchr(str, ':'), NULL); if (!str) return NULL; s = g_strdup (str); if (query) c = strchr (s, ':') + 1; else c = s; for (; *c; ++c) *c = isalnum (*c) ? tolower (*c) : '_'; return s; } static void check_for_field (const char *str, gboolean *is_field, gboolean *is_range_field) { CheckPrefix pfx; pfx.str = str; /* skip any non-alphanum starts in cpfx->str; this is to * handle the case where we have e.g. "(maildir:/abc)" */ while (pfx.str && *pfx.str && !isalnum(*pfx.str)) ++pfx.str; pfx.match = pfx.range_field = FALSE; mu_msg_field_foreach ((MuMsgFieldForeachFunc)each_check_prefix, &pfx); /* also check special prefixes... */ if (!pfx.match) pfx.match = g_str_has_prefix (str, MU_MSG_FIELD_PSEUDO_CONTACT ":") || g_str_has_prefix (str, MU_MSG_FIELD_PSEUDO_RECIP ":"); *is_field = pfx.match; *is_range_field = pfx.range_field; } static gboolean handle_esc_maybe (GString *gstr, char **cur, gunichar uc, gboolean query_esc, gboolean range_field) { char kar; kar = *cur[0]; if (query_esc) { switch (kar) { case ':': case '(': case ')': case '*': case '&': case '"': g_string_append_c (gstr, kar); return TRUE; case '.': if (!range_field) break; if ((*cur)[1] == '.' && (*cur)[2] != '.') { g_string_append (gstr, ".."); *cur = g_utf8_next_char (*cur); return TRUE; } default: break; } } if (g_unichar_ispunct(uc) || isblank(kar)) { g_string_append_c (gstr, '_'); return TRUE; } return FALSE; } static char* process_str (const char *str, gboolean xapian_esc, gboolean query_esc) { GString *gstr; char *norm, *cur; gboolean is_field, is_range_field; norm = g_utf8_normalize (str, -1, G_NORMALIZE_ALL); if (G_UNLIKELY(!norm)) { /* not valid utf8? */ char *u8; u8 = mu_str_utf8ify (str); norm = g_utf8_normalize (u8, -1, G_NORMALIZE_ALL); g_free (u8); } if (!norm) return NULL; /* msg-id needs some special care in queries */ if (query_esc && is_msgid_field (str)) return mu_str_process_msgid (str, TRUE); check_for_field (str, &is_field, &is_range_field); gstr = g_string_sized_new (strlen (norm)); for (cur = norm; cur && *cur; cur = g_utf8_next_char (cur)) { gunichar uc; uc = g_utf8_get_char (cur); if (xapian_esc) if (handle_esc_maybe (gstr, &cur, uc, query_esc, is_range_field)) continue; if (g_unichar_ismark(uc)) continue; if (!is_range_field) uc = g_unichar_tolower (uc); g_string_append_unichar (gstr, uc); } g_free (norm); return g_string_free (gstr, FALSE); } char* mu_str_process_text (const char *str) { g_return_val_if_fail (str, NULL); return process_str (str, FALSE, FALSE); } char* mu_str_process_term (const char *str) { g_return_val_if_fail (str, NULL); return process_str (str, TRUE, FALSE); } char* mu_str_process_query_term (const char *str) { g_return_val_if_fail (str, NULL); return process_str (str, TRUE, TRUE); } /* * Split simple search term into prefix, expression and suffix. * Meant to handle cases like "(maildir:/abc)", prefix and * suffix are the non-alphanumeric stuff at the beginning * and the end of string. * * Values of *pfx, *cond and *sfx will be allocated from heap * and must be g_free()d. * * Returns TRUE if all went fine and FALSE if some error was * occured. */ static gboolean split_term (const gchar *term, const gchar **pfx, const gchar **cond, const gchar **sfx) { size_t l; const gchar *start, *tail; const gchar *p, *c, *s; g_return_val_if_fail (term, FALSE); g_return_val_if_fail (pfx, FALSE); g_return_val_if_fail (cond, FALSE); g_return_val_if_fail (sfx, FALSE); l = strlen (term); if (l == 0) { p = g_strdup (""); c = g_strdup (""); s = g_strdup (""); goto _done; } /* * Invariants: * - start will point to the first symbol after leading * non-alphanumerics (can be alphanumeric or '\0'); * - tail will point to the beginning of trailing * non-alphanumerics or '\0'. * So: * - len (prefix) = start - term; * - len (cond) = tail - start; * - len (suffix) = term + len (term) - tail. */ for (start = term; *start && !isalnum (*start); start++); for (tail = term + l; tail > start && !isalnum (*(tail-1)); tail--); p = g_strndup (term, start - term); c = g_strndup (start, tail - start); s = g_strndup (tail, term + l - tail); _done: if (!p || !c || !s) { g_free ((gchar *)p); g_free ((gchar *)c); g_free ((gchar *)s); return FALSE; } else { *pfx = p; *cond = c; *sfx = s; return TRUE; } /* NOTREACHED */ } /* * Fixup handlers. * * Every fixup handler will take three string arguments, * prefix, condition and suffix (as split by split_term). * * It will either return NULL that means "no fixup was done" * or the pointer to the newly-allocated string with the * new contents. */ typedef gchar * (*fixup_handler_t)(const gchar *pfx, const gchar *cond, const gchar *sfx); static gchar* fixup_date(const gchar *pfx, const gchar *cond, const gchar *sfx) { const gchar *p; p = cond + sizeof ("date:") - 1; if (strstr (p, "..")) return NULL; return g_strdup_printf ("%s%s..%s%s", pfx, cond, p, sfx); } /* * Looks up fixup handler for the given condition. * * Returns fixup handler if we can and NULL if there is * no fixup for this condition. */ static fixup_handler_t find_fixup (const gchar *cond) { size_t n; /* NULL-terminated list of term names for fixups. */ static struct { const char *name; size_t len; fixup_handler_t handler; } fixups[] = { {"date:", sizeof("date:") - 1, fixup_date}, {NULL, 0, NULL} }; g_return_val_if_fail (cond, NULL); for (n = 0; fixups[n].name; n++) { if (!strncasecmp (cond, fixups[n].name, fixups[n].len)) break; } return fixups[n].handler; } gchar* mu_str_xapian_fixup_terms (const gchar *term) { gboolean is_field, is_range_field; const gchar *cond, *pfx, *sfx; gchar *retval; fixup_handler_t fixup; g_return_val_if_fail (term, NULL); if (strlen (term) == 0) return g_strdup (term); check_for_field (term, &is_field, &is_range_field); if (!is_field || !is_range_field) return g_strdup (term); if (!split_term (term, &pfx, &cond, &sfx)) return g_strdup (term); retval = NULL; fixup = find_fixup (cond); if (fixup) retval = fixup (pfx, cond, sfx); if (!retval) retval = g_strdup (term); /* At this point retval should contain the result */ g_free ((gchar *)pfx); g_free ((gchar *)sfx); g_free ((gchar *)cond); return retval; } /* note: this function is *not* re-entrant, it returns a static buffer */ const char* mu_str_fullpath_s (const char* path, const char* name) { static char buf[PATH_MAX + 1]; g_return_val_if_fail (path, NULL); snprintf (buf, sizeof(buf), "%s%c%s", path, G_DIR_SEPARATOR, name ? name : ""); return buf; } char* mu_str_escape_c_literal (const gchar* str, gboolean in_quotes) { const char* cur; GString *tmp; g_return_val_if_fail (str, NULL); tmp = g_string_sized_new (2 * strlen(str)); if (in_quotes) g_string_append_c (tmp, '"'); for (cur = str; *cur; ++cur) switch (*cur) { case '\\': tmp = g_string_append (tmp, "\\\\"); break; case '"': tmp = g_string_append (tmp, "\\\""); break; default: tmp = g_string_append_c (tmp, *cur); } if (in_quotes) g_string_append_c (tmp, '"'); return g_string_free (tmp, FALSE); } /* turn \0-terminated buf into ascii (which is a utf8 subset); convert * any non-ascii into '.' */ char* mu_str_asciify_in_place (char *buf) { char *c; g_return_val_if_fail (buf, NULL); for (c = buf; c && *c; ++c) { if ((!isprint(*c) && !isspace (*c)) || !isascii(*c)) *c = '.'; } return buf; } char* mu_str_utf8ify (const char *buf) { char *utf8; g_return_val_if_fail (buf, NULL); utf8 = g_strdup (buf); if (!g_utf8_validate (buf, -1, NULL)) mu_str_asciify_in_place (utf8); return utf8; } gchar* mu_str_convert_to_utf8 (const char* buffer, const char *charset) { GError *err; gchar * utf8; g_return_val_if_fail (buffer, NULL); g_return_val_if_fail (charset, NULL ); err = NULL; utf8 = g_convert_with_fallback (buffer, -1, "UTF-8", charset, NULL, NULL, NULL, &err); if (!utf8) /* maybe the charset lied; try 8859-15 */ utf8 = g_convert_with_fallback (buffer, -1, "UTF-8", "ISO8859-15", NULL, NULL, NULL, &err); /* final attempt, maybe it was utf-8 already */ if (!utf8 && g_utf8_validate (buffer, -1, NULL)) utf8 = g_strdup (buffer); if (!utf8) { g_warning ("%s: conversion failed from %s: %s", __func__, charset, err ? err->message : ""); } g_clear_error (&err); return utf8; } gchar* mu_str_quoted_from_strv (const gchar **params) { GString *str; int i; g_return_val_if_fail (params, NULL); if (!params[0]) return g_strdup (""); str = g_string_sized_new (64); /* just a guess */ for (i = 0; params[i]; ++i) { if (i > 0) g_string_append_c (str, ' '); g_string_append_c (str, '"'); g_string_append (str, params[i]); g_string_append_c (str, '"'); } return g_string_free (str, FALSE); } static char* read_key (const char *str, const char **val, GError **err) { const char *cur; GString *gstr; cur = str; gstr = g_string_sized_new (strlen(cur)); while (*cur && *cur != ':') { g_string_append_c (gstr, *cur); ++cur; } if (*cur != ':' || gstr->len == 0) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR, "expected: '<alphanum>+:' (%s)", str); g_string_free (gstr, TRUE); *val = NULL; return NULL; } else { *val = cur + 1; return g_string_free (gstr, FALSE); } } static char* read_val (const char *str, const char **endval, GError **err) { const char *cur; gboolean quoted; GString *gstr; gstr = g_string_sized_new (strlen(str)); for (quoted = FALSE, cur = str; *cur; ++cur) { if (*cur == '\\') { if (cur[1] != '"' && cur[1] != '\\') { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR, "invalid escaping"); goto errexit; } else { ++cur; g_string_append_c (gstr, *cur); continue; } } else if (*cur == '"') { quoted = !quoted; continue; } else if (isblank(*cur) && !quoted) break; else g_string_append_c (gstr, *cur); } if (quoted) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR, "error in quoting"); goto errexit; } *endval = cur; return g_string_free (gstr, FALSE); errexit: g_string_free (gstr, TRUE); return NULL; } GHashTable* mu_str_parse_arglist (const char *args, GError **err) { GHashTable *hash; const char *cur; g_return_val_if_fail (args, NULL); hash = g_hash_table_new_full ( g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)g_free); cur = args; while ((isblank(*cur))) ++cur; do { char *key, *val; const char *valstart, *valend; key = read_key (cur, &valstart, err); if (!key) goto errexit; val = read_val (valstart, &valend, err); if (!val) goto errexit; /* g_print ("%s->%s\n", key, val); */ g_hash_table_insert (hash, key, val); cur = valend; while ((isblank(*cur))) ++cur; } while (*cur); return hash; errexit: g_hash_table_destroy (hash); return NULL; } char * mu_str_remove_ctrl_in_place (char *str) { char *cur; g_return_val_if_fail (str, NULL); for (cur = str; *cur; ++cur) { GString *gstr; if (!iscntrl(*cur)) continue; if (isspace(*cur)) { /* squash special white space into a simple space */ *cur = ' '; } else { /* remove other control characters */ gstr = g_string_sized_new (strlen (str)); for (cur = str; *cur; ++cur) if (!iscntrl (*cur)) g_string_append_c (gstr, *cur); memcpy (str, gstr->str, gstr->len); /* fits */ g_string_free (gstr, TRUE); break; } } return str; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-msg-file.c�������������������������������������������������������������������������0000644�0001750�0001750�00000043242�13020504332�012204� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <stdlib.h> #include <ctype.h> #include <gmime/gmime.h> #include "mu-util.h" #include "mu-str.h" #include "mu-maildir.h" #include "mu-msg-priv.h" static gboolean init_file_metadata (MuMsgFile *self, const char* path, const char *mdir, GError **err); static gboolean init_mime_msg (MuMsgFile *msg, const char *path, GError **err); MuMsgFile* mu_msg_file_new (const char* filepath, const char *mdir, GError **err) { MuMsgFile *self; g_return_val_if_fail (filepath, NULL); self = g_slice_new0 (MuMsgFile); if (!init_file_metadata (self, filepath, mdir, err)) { mu_msg_file_destroy (self); return NULL; } if (!init_mime_msg (self, filepath, err)) { mu_msg_file_destroy (self); return NULL; } return self; } void mu_msg_file_destroy (MuMsgFile *self) { if (!self) return; if (self->_mime_msg) g_object_unref (self->_mime_msg); g_slice_free (MuMsgFile, self); } static gboolean init_file_metadata (MuMsgFile *self, const char* path, const gchar* mdir, GError **err) { struct stat statbuf; if (access (path, R_OK) != 0) { mu_util_g_set_error (err, MU_ERROR_FILE, "cannot read file %s: %s", path, strerror(errno)); return FALSE; } if (stat (path, &statbuf) < 0) { mu_util_g_set_error (err, MU_ERROR_FILE, "cannot stat %s: %s", path, strerror(errno)); return FALSE; } if (!S_ISREG(statbuf.st_mode)) { mu_util_g_set_error (err, MU_ERROR_FILE, "not a regular file: %s", path); return FALSE; } self->_timestamp = statbuf.st_mtime; self->_size = (size_t)statbuf.st_size; /* remove double slashes, relative paths etc. from path & mdir */ if (!realpath (path, self->_path)) { mu_util_g_set_error (err, MU_ERROR_FILE, "could not get realpath for %s: %s", path, strerror(errno)); return FALSE; } strncpy (self->_maildir, mdir ? mdir : "", PATH_MAX); return TRUE; } static GMimeStream* get_mime_stream (MuMsgFile *self, const char *path, GError **err) { FILE *file; GMimeStream *stream; file = fopen (path, "r"); if (!file) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE, "cannot open %s: %s", path, strerror (errno)); return NULL; } stream = g_mime_stream_file_new (file); if (!stream) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "cannot create mime stream for %s", path); fclose (file); return NULL; } return stream; } static gboolean init_mime_msg (MuMsgFile *self, const char* path, GError **err) { GMimeStream *stream; GMimeParser *parser; stream = get_mime_stream (self, path, err); if (!stream) return FALSE; parser = g_mime_parser_new_with_stream (stream); g_object_unref (stream); if (!parser) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "cannot create mime parser for %s", path); return FALSE; } self->_mime_msg = g_mime_parser_construct_message (parser); g_object_unref (parser); if (!self->_mime_msg) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "message seems invalid, ignoring (%s)", path); return FALSE; } return TRUE; } static char* get_recipient (MuMsgFile *self, GMimeRecipientType rtype) { char *recip; InternetAddressList *recips; recips = g_mime_message_get_recipients (self->_mime_msg, rtype); /* FALSE --> don't encode */ recip = (char*)internet_address_list_to_string (recips, FALSE); if (recip && !g_utf8_validate (recip, -1, NULL)) { g_debug ("invalid recipient in %s\n", self->_path); mu_str_asciify_in_place (recip); /* ugly... */ } if (mu_str_is_empty(recip)) { g_free (recip); return NULL; } if (recip) mu_str_remove_ctrl_in_place (recip); return recip; } /* * let's try to guess the mailing list from some other * headers in the mail */ static gchar* get_fake_mailing_list_maybe (MuMsgFile *self) { const char* hdr; hdr = g_mime_object_get_header (GMIME_OBJECT(self->_mime_msg), "X-Feed2Imap-Version"); if (!hdr) return NULL; /* looks like a feed2imap header; guess the source-blog * from the msgid */ { const char *msgid, *e; msgid = g_mime_message_get_message_id (self->_mime_msg); if (msgid && (e = strchr (msgid, '-'))) return g_strndup (msgid, e - msgid); } return NULL; } static gchar* get_mailing_list (MuMsgFile *self) { const char *hdr, *b, *e; hdr = g_mime_object_get_header (GMIME_OBJECT(self->_mime_msg), "List-Id"); if (mu_str_is_empty (hdr)) return get_fake_mailing_list_maybe (self); e = NULL; b = strchr (hdr, '<'); if (b) e = strchr (b, '>'); if (b && e) return g_strndup (b + 1, e - b - 1); else return g_strdup (hdr); } static gboolean looks_like_attachment (GMimeObject *part) { GMimeContentDisposition *disp; GMimeContentType *ctype; const char *dispstr; guint u; const struct { const char *type; const char *sub_type; } att_types[] = { { "image", "*" }, { "audio", "*" }, { "application", "*"}, { "text", "x-diff" }, { "text", "x-patch"}, { "application", "x-patch"} }; disp = g_mime_object_get_content_disposition (part); if (!GMIME_IS_CONTENT_DISPOSITION(disp)) return FALSE; dispstr = g_mime_content_disposition_get_disposition (disp); if (g_ascii_strcasecmp (dispstr, "attachment") == 0) return TRUE; /* we also consider patches, images, audio, and non-pgp-signature * application attachments to be attachments... */ ctype = g_mime_object_get_content_type (part); if (g_mime_content_type_is_type (ctype, "*", "pgp-signature")) return FALSE; /* don't consider as a signature */ for (u = 0; u != G_N_ELEMENTS(att_types); ++u) if (g_mime_content_type_is_type ( ctype, att_types[u].type, att_types[u].sub_type)) return TRUE; return FALSE; } static void msg_cflags_cb (GMimeObject *parent, GMimeObject *part, MuFlags *flags) { if (GMIME_IS_MULTIPART_SIGNED(part)) *flags |= MU_FLAG_SIGNED; /* FIXME: An encrypted part might be signed at the same time. * In that case the signed flag is lost. */ if (GMIME_IS_MULTIPART_ENCRYPTED(part)) *flags |= MU_FLAG_ENCRYPTED; if (*flags & MU_FLAG_HAS_ATTACH) return; if (!GMIME_IS_PART(part)) return; if (*flags & MU_FLAG_HAS_ATTACH) return; if (looks_like_attachment (part)) *flags |= MU_FLAG_HAS_ATTACH; } static MuFlags get_content_flags (MuMsgFile *self) { MuFlags flags; char *ml; flags = MU_FLAG_NONE; if (GMIME_IS_MESSAGE(self->_mime_msg)) mu_mime_message_foreach (self->_mime_msg, FALSE, /* never decrypt for this */ (GMimeObjectForeachFunc)msg_cflags_cb, &flags); ml = get_mailing_list (self); if (ml) { flags |= MU_FLAG_LIST; g_free (ml); } return flags; } static MuFlags get_flags (MuMsgFile *self) { MuFlags flags; g_return_val_if_fail (self, MU_FLAG_INVALID); flags = mu_maildir_get_flags_from_path (self->_path); flags |= get_content_flags (self); /* pseudo-flag --> unread means either NEW or NOT SEEN, just * for searching convenience */ if ((flags & MU_FLAG_NEW) || !(flags & MU_FLAG_SEEN)) flags |= MU_FLAG_UNREAD; return flags; } static size_t get_size (MuMsgFile *self) { g_return_val_if_fail (self, 0); return self->_size; } static MuMsgPrio parse_prio_str (const char* priostr) { int i; struct { const char* _str; MuMsgPrio _prio; } str_prio[] = { { "high", MU_MSG_PRIO_HIGH }, { "1", MU_MSG_PRIO_HIGH }, { "2", MU_MSG_PRIO_HIGH }, { "normal", MU_MSG_PRIO_NORMAL }, { "3", MU_MSG_PRIO_NORMAL }, { "low", MU_MSG_PRIO_LOW }, { "list", MU_MSG_PRIO_LOW }, { "bulk", MU_MSG_PRIO_LOW }, { "4", MU_MSG_PRIO_LOW }, { "5", MU_MSG_PRIO_LOW } }; for (i = 0; i != G_N_ELEMENTS(str_prio); ++i) if (g_ascii_strcasecmp (priostr, str_prio[i]._str) == 0) return str_prio[i]._prio; /* e.g., last-fm uses 'fm-user'... as precedence */ return MU_MSG_PRIO_NORMAL; } static MuMsgPrio get_prio (MuMsgFile *self) { GMimeObject *obj; const char* priostr; g_return_val_if_fail (self, MU_MSG_PRIO_NONE); obj = GMIME_OBJECT(self->_mime_msg); priostr = g_mime_object_get_header (obj, "Precedence"); if (!priostr) priostr = g_mime_object_get_header (obj, "X-Priority"); if (!priostr) priostr = g_mime_object_get_header (obj, "Importance"); return priostr ? parse_prio_str (priostr) : MU_MSG_PRIO_NORMAL; } /* NOTE: buffer will be *freed* or returned unchanged */ static char* convert_to_utf8 (GMimePart *part, char *buffer) { GMimeContentType *ctype; const char* charset; ctype = g_mime_object_get_content_type (GMIME_OBJECT(part)); g_return_val_if_fail (GMIME_IS_CONTENT_TYPE(ctype), NULL); /* of course, the charset specified may be incorrect... */ charset = g_mime_content_type_get_parameter (ctype, "charset"); if (charset) { char *utf8; if ((utf8 = mu_str_convert_to_utf8 (buffer, g_mime_charset_iconv_name (charset)))) { g_free (buffer); buffer = utf8; } } else if (!g_utf8_validate (buffer, -1, NULL)) { /* if it's already utf8, nothing to do otherwise: no charset at all, or conversion failed; ugly * hack: replace all non-ascii chars with '.' */ mu_str_asciify_in_place (buffer); } return buffer; } static gchar* stream_to_string (GMimeStream *stream, size_t buflen) { char *buffer; ssize_t bytes; buffer = g_new(char, buflen + 1); g_mime_stream_reset (stream); /* we read everything in one go */ bytes = g_mime_stream_read (stream, buffer, buflen); if (bytes < 0) { g_warning ("%s: failed to read from stream", __func__); g_free (buffer); return NULL; } buffer[bytes]='\0'; return buffer; } gchar* mu_msg_mime_part_to_string (GMimePart *part, gboolean *err) { GMimeDataWrapper *wrapper; GMimeStream *stream; ssize_t buflen; char *buffer; buffer = NULL; stream = NULL; g_return_val_if_fail (err, NULL); *err = TRUE; /* guilty until proven innocent */ g_return_val_if_fail (GMIME_IS_PART(part), NULL); wrapper = g_mime_part_get_content_object (part); if (!wrapper) { /* this happens with invalid mails */ g_debug ("failed to create data wrapper"); goto cleanup; } stream = g_mime_stream_mem_new (); if (!stream) { g_warning ("failed to create mem stream"); goto cleanup; } buflen = g_mime_data_wrapper_write_to_stream (wrapper, stream); if (buflen <= 0) {/* empty buffer, not an error */ *err = FALSE; goto cleanup; } buffer = stream_to_string (stream, (size_t)buflen); /* convert_to_utf8 will free the old 'buffer' if needed */ buffer = convert_to_utf8 (part, buffer); *err = FALSE; cleanup: if (G_IS_OBJECT(stream)) g_object_unref (stream); return buffer; } static gboolean contains (GSList *lst, const char *str) { for (; lst; lst = g_slist_next(lst)) if (g_strcmp0 ((char*)lst->data, str) == 0) return TRUE; return FALSE; } /* * NOTE: this will get the list of references with the oldest parent * at the beginning */ static GSList* get_references (MuMsgFile *self) { GSList *msgids; unsigned u; const char *headers[] = { "References", "In-reply-to", NULL }; for (msgids = NULL, u = 0; headers[u]; ++u) { char *str; const GMimeReferences *cur; GMimeReferences *mime_refs; str = mu_msg_file_get_header (self, headers[u]); if (!str) continue; mime_refs = g_mime_references_decode (str); g_free (str); for (cur = mime_refs; cur; cur = g_mime_references_get_next(cur)) { const char* msgid; msgid = g_mime_references_get_message_id (cur); /* don't include duplicates */ if (msgid && !contains (msgids, msgid)) /* explicitly ensure it's utf8-safe, * as GMime does not ensure that */ msgids = g_slist_prepend (msgids, g_strdup((msgid))); } g_mime_references_free (mime_refs); } /* reverse, because we used g_slist_prepend for performance * reasons */ return g_slist_reverse (msgids); } /* see: http://does-not-exist.org/mail-archives/mutt-dev/msg08249.html */ static GSList* get_tags (MuMsgFile *self) { GSList *lst; unsigned u; struct { const char *header; char sepa; } tagfields[] = { { "X-Label", ' ' }, { "X-Keywords", ',' }, { "Keywords", ' ' } }; for (lst = NULL, u = 0; u != G_N_ELEMENTS(tagfields); ++u) { gchar *hdr; hdr = mu_msg_file_get_header (self, tagfields[u].header); if (hdr) { GSList *hlst; hlst = mu_str_to_list (hdr, tagfields[u].sepa, TRUE); if (lst) (g_slist_last (lst))->next = hlst; else lst = hlst; g_free (hdr); } } return lst; } static char* cleanup_maybe (const char *str, gboolean *do_free) { char *s; if (!str) return NULL; if (!g_utf8_validate(str, -1, NULL)) { if (*do_free) s = mu_str_asciify_in_place ((char*)str); else { *do_free = TRUE; s = mu_str_asciify_in_place(g_strdup (str)); } } else s = (char*)str; mu_str_remove_ctrl_in_place (s); return s; } G_GNUC_CONST static GMimeRecipientType recipient_type (MuMsgFieldId mfid) { switch (mfid) { case MU_MSG_FIELD_ID_BCC: return GMIME_RECIPIENT_TYPE_BCC; case MU_MSG_FIELD_ID_CC : return GMIME_RECIPIENT_TYPE_CC; case MU_MSG_FIELD_ID_TO : return GMIME_RECIPIENT_TYPE_TO; default: g_return_val_if_reached (-1); } } static gchar* get_msgid (MuMsgFile *self, gboolean *do_free) { const char *msgid; msgid = g_mime_message_get_message_id (self->_mime_msg); if (msgid) return (char*)msgid; else { /* if there is none, fake it */ *do_free = TRUE; return g_strdup_printf ( "%s@fake-msgid", mu_util_get_hash (self->_path)); } } char* mu_msg_file_get_str_field (MuMsgFile *self, MuMsgFieldId mfid, gboolean *do_free) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (mu_msg_field_is_string(mfid), NULL); *do_free = FALSE; /* default */ switch (mfid) { case MU_MSG_FIELD_ID_BCC: case MU_MSG_FIELD_ID_CC: case MU_MSG_FIELD_ID_TO: *do_free = TRUE; return get_recipient (self, recipient_type(mfid)); case MU_MSG_FIELD_ID_FROM: return (char*)cleanup_maybe (g_mime_message_get_sender (self->_mime_msg), do_free); case MU_MSG_FIELD_ID_PATH: return self->_path; case MU_MSG_FIELD_ID_MAILING_LIST: *do_free = TRUE; return (char*)get_mailing_list (self); case MU_MSG_FIELD_ID_SUBJECT: return (char*)cleanup_maybe (g_mime_message_get_subject (self->_mime_msg), do_free); case MU_MSG_FIELD_ID_MSGID: return get_msgid (self, do_free); case MU_MSG_FIELD_ID_MAILDIR: return self->_maildir; case MU_MSG_FIELD_ID_BODY_TEXT: /* use mu_msg_get_body_text */ case MU_MSG_FIELD_ID_BODY_HTML: /* use mu_msg_get_body_html */ case MU_MSG_FIELD_ID_EMBEDDED_TEXT: g_warning ("%s is not retrievable through: %s", mu_msg_field_name (mfid), __func__); return NULL; default: g_return_val_if_reached (NULL); } } GSList* mu_msg_file_get_str_list_field (MuMsgFile *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (mu_msg_field_is_string_list(mfid), NULL); switch (mfid) { case MU_MSG_FIELD_ID_REFS: return get_references (self); case MU_MSG_FIELD_ID_TAGS: return get_tags (self); default: g_return_val_if_reached (NULL); } } gint64 mu_msg_file_get_num_field (MuMsgFile *self, const MuMsgFieldId mfid) { g_return_val_if_fail (self, -1); g_return_val_if_fail (mu_msg_field_is_numeric(mfid), -1); switch (mfid) { case MU_MSG_FIELD_ID_DATE: { time_t t; g_mime_message_get_date (self->_mime_msg, &t, NULL); return (time_t)t; } case MU_MSG_FIELD_ID_FLAGS: return (gint64)get_flags(self); case MU_MSG_FIELD_ID_PRIO: return (gint64)get_prio(self); case MU_MSG_FIELD_ID_SIZE: return (gint64)get_size(self); default: g_return_val_if_reached (-1); } } char* mu_msg_file_get_header (MuMsgFile *self, const char *header) { const gchar *hdr; g_return_val_if_fail (self, NULL); g_return_val_if_fail (header, NULL); /* sadly, g_mime_object_get_header may return non-ascii; * so, we need to ensure that */ hdr = g_mime_object_get_header (GMIME_OBJECT(self->_mime_msg), header); return hdr ? mu_str_utf8ify(hdr) : NULL; } struct _ForeachData { GMimeObjectForeachFunc user_func; gpointer user_data; gboolean decrypt; }; typedef struct _ForeachData ForeachData; static void foreach_cb (GMimeObject *parent, GMimeObject *part, ForeachData *fdata) { /* invoke the callback function */ fdata->user_func (parent, part, fdata->user_data); /* maybe iterate over decrypted parts */ if (fdata->decrypt && GMIME_IS_MULTIPART_ENCRYPTED (part)) { GMimeObject *dec; dec = mu_msg_crypto_decrypt_part (GMIME_MULTIPART_ENCRYPTED(part), MU_MSG_OPTION_NONE, NULL, NULL, NULL); if (!dec) return; if (GMIME_IS_MULTIPART (dec)) g_mime_multipart_foreach ( (GMIME_MULTIPART(dec)), (GMimeObjectForeachFunc)foreach_cb, fdata); else foreach_cb (parent, dec, fdata); g_object_unref (dec); } } void mu_mime_message_foreach (GMimeMessage *msg, gboolean decrypt, GMimeObjectForeachFunc func, gpointer user_data) { ForeachData fdata; g_return_if_fail (GMIME_IS_MESSAGE (msg)); g_return_if_fail (func); fdata.user_func = func; fdata.user_data = user_data; fdata.decrypt = decrypt; g_mime_message_foreach (msg, (GMimeObjectForeachFunc)foreach_cb, &fdata); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-runtime.c��������������������������������������������������������������������������0000644�0001750�0001750�00000011145�13020504332�012161� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2010-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #include "mu-runtime.h" #include <glib-object.h> #include <locale.h> /* for setlocale() */ #include <stdio.h> /* for fileno() */ #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include "mu-msg.h" #include "mu-log.h" #include "mu-util.h" #define MU_XAPIAN_DIRNAME "xapian" #define MU_BOOKMARKS_FILENAME "bookmarks" #define MU_CACHE_DIRNAME "cache" #define MU_CONTACTS_FILENAME "contacts" #define MU_LOG_DIRNAME "log" struct _MuRuntimeData { gchar *_str[MU_RUNTIME_PATH_NUM]; gchar *_name; /* e.g., 'mu', 'mug' */ }; typedef struct _MuRuntimeData MuRuntimeData; /* static, global data for this singleton */ static gboolean _initialized = FALSE; static MuRuntimeData *_data = NULL; static void runtime_free (void); static gboolean init_paths (const char* muhome, MuRuntimeData *data); static const char* runtime_path (MuRuntimePath path); static gboolean init_log (const char *muhome, const char *name, MuLogOptions opts) { gboolean rv; char *logpath; logpath = g_strdup_printf ("%s%c%s%c%s.log", muhome, G_DIR_SEPARATOR, MU_LOG_DIRNAME, G_DIR_SEPARATOR, name); rv = mu_log_init (logpath, opts); g_free (logpath); return rv; } gboolean mu_runtime_init (const char* muhome_arg, const char *name) { gchar *muhome; g_return_val_if_fail (!_initialized, FALSE); g_return_val_if_fail (name, FALSE); setlocale (LC_ALL, ""); #ifndef GLIB_VERSION_2_36 g_type_init (); #endif /*GLIB_VERSION_2_36*/ if (muhome_arg) muhome = g_strdup (muhome_arg); else muhome = mu_util_guess_mu_homedir (); if (!mu_util_create_dir_maybe (muhome, 0700, TRUE)) { g_printerr ("mu: invalid mu homedir specified;" " use --muhome=<dir>\n"); runtime_free (); return FALSE; } _data = g_new0 (MuRuntimeData, 1); _data->_str[MU_RUNTIME_PATH_MUHOME] = muhome; init_paths (muhome, _data); _data->_name = g_strdup (name); if (!init_log (muhome, name, MU_LOG_OPTIONS_BACKUP)) { runtime_free (); g_free (muhome); return FALSE; } return _initialized = TRUE; } static void runtime_free (void) { int i; mu_log_uninit(); if (!_data) return; for (i = 0; i != MU_RUNTIME_PATH_NUM; ++i) g_free (_data->_str[i]); g_free (_data->_name); g_free (_data); } void mu_runtime_uninit (void) { if (!_initialized) return; runtime_free (); _initialized = FALSE; } static gboolean create_dirs_maybe (MuRuntimeData *data) { if (!mu_util_create_dir_maybe (data->_str[MU_RUNTIME_PATH_CACHE], 0700, TRUE)) { g_warning ("failed to create cache dir"); return FALSE; } if (!mu_util_create_dir_maybe (data->_str[MU_RUNTIME_PATH_LOG], 0700, TRUE)) { g_warning ("failed to create log dir"); return FALSE; } return TRUE; } static gboolean init_paths (const char* muhome, MuRuntimeData *data) { data->_str [MU_RUNTIME_PATH_XAPIANDB] = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, MU_XAPIAN_DIRNAME); data->_str [MU_RUNTIME_PATH_BOOKMARKS] = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, MU_BOOKMARKS_FILENAME); data->_str [MU_RUNTIME_PATH_CACHE] = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, MU_CACHE_DIRNAME); data->_str [MU_RUNTIME_PATH_CONTACTS] = g_strdup_printf ("%s%c%s", data->_str[MU_RUNTIME_PATH_CACHE], G_DIR_SEPARATOR, MU_CONTACTS_FILENAME); data->_str [MU_RUNTIME_PATH_LOG] = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, MU_LOG_DIRNAME); if (!create_dirs_maybe (data)) return FALSE; return TRUE; } /* so we can called when _initialized is FALSE still */ static const char* runtime_path (MuRuntimePath path) { return _data->_str[path]; } const char* mu_runtime_path (MuRuntimePath path) { g_return_val_if_fail (_initialized, NULL); g_return_val_if_fail (path < MU_RUNTIME_PATH_NUM, NULL); return runtime_path (path); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-query.cc���������������������������������������������������������������������������0000644�0001750�0001750�00000036422�13021037573�012024� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include <stdexcept> #include <string> #include <cctype> #include <cstring> #include <stdlib.h> #include <xapian.h> #include <glib/gstdio.h> #include "mu-query.h" #include "mu-msg-fields.h" #include "mu-msg-iter.h" #include "mu-util.h" #include "mu-str.h" #include "mu-date.h" /* * custom parser for date ranges */ class MuDateRangeProcessor : public Xapian::StringValueRangeProcessor { public: MuDateRangeProcessor(): Xapian::StringValueRangeProcessor( (Xapian::valueno)MU_MSG_FIELD_ID_DATE) {} Xapian::valueno operator()(std::string &begin, std::string &end) { if (!clear_prefix (begin)) return Xapian::BAD_VALUENO; begin = to_sortable (begin, true); end = to_sortable (end, false); if (begin > end) throw Xapian::QueryParserError ("end time is before begin"); return (Xapian::valueno)MU_MSG_FIELD_ID_DATE; } private: std::string to_sortable (std::string& s, bool is_begin) { const char* str; time_t t; // note: if s is empty and not is_begin, xapian seems // to repeat it. if (s.empty() || g_str_has_suffix (s.c_str(), "..")) { str = mu_date_complete_s ("", is_begin); } else { str = mu_date_interpret_s (s.c_str(), is_begin ? TRUE: FALSE); str = mu_date_complete_s (str, is_begin ? TRUE: FALSE); t = mu_date_str_to_time_t (str, TRUE /*local*/); str = mu_date_time_t_to_str_s (t, FALSE /*UTC*/); } return s = std::string(str); } bool clear_prefix (std::string& begin) { const std::string colon (":"); const std::string name (mu_msg_field_name (MU_MSG_FIELD_ID_DATE) + colon); const std::string shortcut ( std::string(1, mu_msg_field_shortcut (MU_MSG_FIELD_ID_DATE)) + colon); if (begin.find (name) == 0) { begin.erase (0, name.length()); return true; } else if (begin.find (shortcut) == 0) { begin.erase (0, shortcut.length()); return true; } else return false; } }; class MuSizeRangeProcessor : public Xapian::NumberValueRangeProcessor { public: MuSizeRangeProcessor(): Xapian::NumberValueRangeProcessor(MU_MSG_FIELD_ID_SIZE) { } Xapian::valueno operator()(std::string &begin, std::string &end) { if (!clear_prefix (begin)) return Xapian::BAD_VALUENO; if (!substitute_size (begin) || !substitute_size (end)) return Xapian::BAD_VALUENO; begin = Xapian::sortable_serialise (atol(begin.c_str())); end = Xapian::sortable_serialise (atol(end.c_str())); /* swap if b > e */ if (begin > end) std::swap (begin, end); return (Xapian::valueno)MU_MSG_FIELD_ID_SIZE; } private: bool clear_prefix (std::string& begin) { const std::string colon (":"); const std::string name (mu_msg_field_name (MU_MSG_FIELD_ID_SIZE) + colon); const std::string shortcut ( std::string(1, mu_msg_field_shortcut (MU_MSG_FIELD_ID_SIZE)) + colon); if (begin.find (name) == 0) { begin.erase (0, name.length()); return true; } else if (begin.find (shortcut) == 0) { begin.erase (0, shortcut.length()); return true; } else return false; } bool substitute_size (std::string& size) { gchar str[16]; gint64 num = mu_str_size_parse_bkm(size.c_str()); if (num < 0) throw Xapian::QueryParserError ("invalid size"); snprintf (str, sizeof(str), "%" G_GUINT64_FORMAT, num); size = str; return true; } }; static void add_prefix (MuMsgFieldId field, Xapian::QueryParser* qparser); struct _MuQuery { public: _MuQuery (MuStore *store): _store(mu_store_ref(store)) { _qparser.set_database (db()); _qparser.set_default_op (Xapian::Query::OP_AND); _qparser.add_valuerangeprocessor (&_date_range_processor); _qparser.add_valuerangeprocessor (&_size_range_processor); mu_msg_field_foreach ((MuMsgFieldForeachFunc)add_prefix, &_qparser); /* add some convenient special prefixes */ add_special_prefixes (); } ~_MuQuery () { mu_store_unref (_store); } Xapian::Database& db() const { Xapian::Database* db; db = reinterpret_cast<Xapian::Database*> (mu_store_get_read_only_database (_store)); if (!db) throw std::runtime_error ("no database"); return *db; } Xapian::QueryParser& query_parser () { return _qparser; } private: void add_special_prefixes () { char pfx[] = { '\0', '\0' }; /* add 'contact' as a shortcut for From/Cc/Bcc/To: */ pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_FROM); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_CONTACT, pfx); pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_TO); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_CONTACT, pfx); pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_CC); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_CONTACT, pfx); pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_BCC); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_CONTACT, pfx); /* add 'recip' as a shortcut for Cc/Bcc/To: */ pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_TO); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_RECIP, pfx); pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_CC); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_RECIP, pfx); pfx[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_BCC); _qparser.add_prefix (MU_MSG_FIELD_PSEUDO_RECIP, pfx); } Xapian::QueryParser _qparser; MuDateRangeProcessor _date_range_processor; MuSizeRangeProcessor _size_range_processor; MuStore *_store; }; static const Xapian::Query get_query (MuQuery *mqx, const char* searchexpr, GError **err) { Xapian::Query query; char *preprocessed; preprocessed = mu_query_preprocess (searchexpr, err); if (!preprocessed) throw std::runtime_error ("parse error while preprocessing query"); try { query = mqx->query_parser().parse_query (preprocessed, Xapian::QueryParser::FLAG_BOOLEAN | Xapian::QueryParser::FLAG_PURE_NOT | Xapian::QueryParser::FLAG_AUTO_SYNONYMS | Xapian::QueryParser::FLAG_WILDCARD | Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE ); g_free (preprocessed); return query; } catch (...) { mu_util_g_set_error (err,MU_ERROR_XAPIAN_QUERY, "parse error in query"); g_free (preprocessed); throw; } } static void add_prefix (MuMsgFieldId mfid, Xapian::QueryParser* qparser) { if (!mu_msg_field_xapian_index(mfid) && !mu_msg_field_xapian_term(mfid) && !mu_msg_field_xapian_contact(mfid)) return; try { const std::string pfx (1, mu_msg_field_xapian_prefix (mfid)); const std::string shortcut (1, mu_msg_field_shortcut (mfid)); if (mu_msg_field_uses_boolean_prefix (mfid)) { qparser->add_boolean_prefix (mu_msg_field_name(mfid), pfx); qparser->add_boolean_prefix (shortcut, pfx); } else { qparser->add_prefix (mu_msg_field_name(mfid), pfx); qparser->add_prefix (shortcut, pfx); } // all fields are also matched implicitly, without // any prefix qparser->add_prefix ("", pfx); } MU_XAPIAN_CATCH_BLOCK; } MuQuery* mu_query_new (MuStore *store, GError **err) { g_return_val_if_fail (store, NULL); if (mu_store_count (store, err) == 0) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_XAPIAN_IS_EMPTY, "database is empty"); return 0; } try { return new MuQuery (store); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, 0); return 0; } void mu_query_destroy (MuQuery *self) { try { delete self; } MU_XAPIAN_CATCH_BLOCK; } /* preprocess a query to make them a bit more promiscuous */ char* mu_query_preprocess (const char *query, GError **err) { GSList *parts, *cur; gchar *myquery; g_return_val_if_fail (query, NULL); /* convert the query to a list of query terms, and escape them * separately */ parts = mu_str_esc_to_list (query); if (!parts) return NULL; for (cur = parts; cur; cur = g_slist_next(cur)) { char *data; data = (gchar*)cur->data; cur->data = mu_str_process_query_term (data); g_free (data); /* run term fixups */ data = (gchar*)cur->data; cur->data = mu_str_xapian_fixup_terms (data); g_free (data); } myquery = mu_str_from_list (parts, ' '); mu_str_free_list (parts); return myquery ? myquery : g_strdup (""); } /* this function is for handling the case where a DatabaseModified * exception is raised. We try to reopen the database, and run the * query again. */ static MuMsgIter * try_requery (MuQuery *self, const char* searchexpr, MuMsgFieldId sortfieldid, int maxnum, MuQueryFlags flags, GError **err) { try { /* let's assume that infinite regression is * impossible */ self->db().reopen(); MU_WRITE_LOG ("reopening db after modification"); return mu_query_run (self, searchexpr, sortfieldid, maxnum, flags, err); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, 0); } static MuMsgIterFlags msg_iter_flags (MuQueryFlags flags) { MuMsgIterFlags iflags; iflags = MU_MSG_ITER_FLAG_NONE; if (flags & MU_QUERY_FLAG_DESCENDING) iflags |= MU_MSG_ITER_FLAG_DESCENDING; if (flags & MU_QUERY_FLAG_SKIP_UNREADABLE) iflags |= MU_MSG_ITER_FLAG_SKIP_UNREADABLE; if (flags & MU_QUERY_FLAG_SKIP_DUPS) iflags |= MU_MSG_ITER_FLAG_SKIP_DUPS; if (flags & MU_QUERY_FLAG_THREADS) iflags |= MU_MSG_ITER_FLAG_THREADS; return iflags; } static Xapian::Enquire get_enquire (MuQuery *self, const char *searchexpr, MuMsgFieldId sortfieldid, bool descending, GError **err) { Xapian::Enquire enq (self->db()); try { /* empty or "" means "matchall" */ if (!mu_str_is_empty(searchexpr) && g_strcmp0 (searchexpr, "\"\"") != 0) /* NULL or "" or """" */ enq.set_query(get_query (self, searchexpr, err)); else enq.set_query(Xapian::Query::MatchAll); } catch (...) { mu_util_g_set_error (err, MU_ERROR_XAPIAN_QUERY, "parse error in query"); throw; } enq.set_cutoff(0,0); return enq; } /* * record all threadids for the messages; also 'orig_set' receives all * original matches (a map msgid-->docid), so we can make sure the * originals are not seen as 'duplicates' later (when skipping * duplicates). We want to favor the originals over the related * messages, when skipping duplicates. */ static GHashTable* get_thread_ids (MuMsgIter *iter, GHashTable **orig_set) { GHashTable *ids; ids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL); *orig_set = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL); while (!mu_msg_iter_is_done (iter)) { char *thread_id, *msgid; unsigned docid; /* record the thread id for the message */ if ((thread_id = mu_msg_iter_get_thread_id (iter))) g_hash_table_insert (ids, thread_id, GSIZE_TO_POINTER(TRUE)); /* record the original set */ docid = mu_msg_iter_get_docid(iter); if (docid != 0 && (msgid = mu_msg_iter_get_msgid (iter))) g_hash_table_insert (*orig_set, msgid, GSIZE_TO_POINTER(docid)); if (!mu_msg_iter_next (iter)) break; } return ids; } static Xapian::Query get_related_query (MuMsgIter *iter, GHashTable **orig_set) { GHashTable *hash; GList *id_list, *cur; std::vector<Xapian::Query> qvec; static std::string pfx (1, mu_msg_field_xapian_prefix (MU_MSG_FIELD_ID_THREAD_ID)); /* orig_set receives the hash msgid->docid of the set of * original matches */ hash = get_thread_ids (iter, orig_set); /* id_list now gets a list of all thread-ids seen in the query * results; either in the Message-Id field or in * References. */ id_list = g_hash_table_get_keys (hash); // now, we create a vector with queries for each of the // thread-ids, which we combine below. This is /much/ faster // than creating the query as 'query = Query (OR, query)'... for (cur = id_list; cur; cur = g_list_next(cur)) qvec.push_back (Xapian::Query((std::string (pfx + (char*)cur->data)))); g_hash_table_destroy (hash); g_list_free (id_list); return Xapian::Query (Xapian::Query::OP_OR, qvec.begin(), qvec.end()); } static void include_related (MuQuery *self, MuMsgIter **iter, int maxnum, MuMsgFieldId sortfieldid, MuQueryFlags flags) { GHashTable *orig_set; Xapian::Enquire enq (self->db()); MuMsgIter *rel_iter; orig_set = NULL; enq.set_query(get_related_query (*iter, &orig_set)); enq.set_cutoff(0,0); rel_iter= mu_msg_iter_new ( reinterpret_cast<XapianEnquire*>(&enq), maxnum, sortfieldid, msg_iter_flags (flags), NULL); mu_msg_iter_destroy (*iter); // set the preferred set for the iterator (ie., the set of // messages not considered to be duplicates) to be the // original matches -- the matches without considering // 'related' mu_msg_iter_set_preferred (rel_iter, orig_set); g_hash_table_destroy (orig_set); *iter = rel_iter; } MuMsgIter* mu_query_run (MuQuery *self, const char *searchexpr, MuMsgFieldId sortfieldid, int maxnum, MuQueryFlags flags, GError **err) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (searchexpr, NULL); g_return_val_if_fail (mu_msg_field_id_is_valid (sortfieldid) || sortfieldid == MU_MSG_FIELD_ID_NONE, NULL); try { MuMsgIter *iter; MuQueryFlags first_flags; bool inc_related = flags & MU_QUERY_FLAG_INCLUDE_RELATED; bool descending = flags & MU_QUERY_FLAG_DESCENDING; Xapian::Enquire enq (get_enquire(self, searchexpr, sortfieldid, descending, err)); /* when we're doing a 'include-related query', we're * actually doing /two/ queries; one to get the * initial matches, and based on that one to get all * messages in threads in those matches. */ /* get the 'real' maxnum if it was specified as < 0 */ maxnum = maxnum < 0 ? self->db().get_doccount() : maxnum; /* if we do a include-related query, it's wasted * effort to calculate threads already in the first * query since we can do it in the second one */ first_flags = inc_related ? (flags & ~MU_QUERY_FLAG_THREADS) : flags; iter = mu_msg_iter_new ( reinterpret_cast<XapianEnquire*>(&enq), maxnum, /* with inc_related, we do the sorting in the * second query */ inc_related ? MU_MSG_FIELD_ID_NONE : sortfieldid, msg_iter_flags (first_flags), err); /* * if we want related messages, do a second query, * based on the message ids / refs of the first one * */ if (inc_related) include_related (self, &iter, maxnum, sortfieldid, flags); if (err && *err && (*err)->code == MU_ERROR_XAPIAN_MODIFIED) { g_clear_error (err); return try_requery (self, searchexpr, sortfieldid, maxnum, flags, err); } else return iter; } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, 0); } char* mu_query_as_string (MuQuery *self, const char *searchexpr, GError **err) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (searchexpr, NULL); try { Xapian::Query query (get_query(self, searchexpr, err)); return g_strdup(query.get_description().c_str()); } MU_XAPIAN_CATCH_BLOCK_RETURN(NULL); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-store-read.cc����������������������������������������������������������������������0000644�0001750�0001750�00000013465�13020504332�012715� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8-*- */ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <cstdio> #include <xapian.h> #include <cstring> #include <stdexcept> #include <limits.h> #include <stdlib.h> #include <errno.h> #include "mu-store.h" #include "mu-store-priv.hh" /* _MuStore */ #include "mu-msg.h" #include "mu-msg-part.h" #include "mu-store.h" #include "mu-util.h" #include "mu-str.h" #include "mu-date.h" #include "mu-flags.h" #include "mu-contacts.h" // note: not re-entrant const char* _MuStore::get_uid_term (const char* path) const { static char uid_term[64] = { '\0' }; if (G_UNLIKELY(uid_term[0] == '\0')) uid_term[0] = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_UID); strncpy (uid_term + 1, mu_util_get_hash (path), sizeof(uid_term) - 1); return uid_term; } MuStore* mu_store_new_read_only (const char* xpath, GError **err) { g_return_val_if_fail (xpath, NULL); try { return new _MuStore (xpath); } catch (const MuStoreError& merr) { mu_util_g_set_error (err, merr.mu_error(), "%s", merr.what().c_str()); } MU_XAPIAN_CATCH_BLOCK_G_ERROR(err, MU_ERROR_XAPIAN); return NULL; } gboolean mu_store_is_read_only (const MuStore *store) { g_return_val_if_fail (store, FALSE); try { return store->is_read_only() ? TRUE : FALSE; } MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE); } unsigned mu_store_count (const MuStore *store, GError **err) { g_return_val_if_fail (store, (unsigned)-1); try { return store->db_read_only()->get_doccount(); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, (unsigned)-1); } const char* mu_store_version (const MuStore *store) { g_return_val_if_fail (store, NULL); return store->version(); } gboolean mu_store_versions_match (const MuStore *store) { g_return_val_if_fail (store, TRUE); return g_strcmp0 (mu_store_version (store), MU_STORE_SCHEMA_VERSION) == 0; } char* mu_store_get_metadata (const MuStore *store, const char *key, GError **err) { g_return_val_if_fail (store, NULL); g_return_val_if_fail (store->db_read_only(), NULL); g_return_val_if_fail (key, NULL); try { std::string val; val = store->db_read_only()->get_metadata (key); if (!val.empty()) return g_strdup (val.c_str()); else return NULL; } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, NULL); } XapianDatabase* mu_store_get_read_only_database (MuStore *store) { g_return_val_if_fail (store, NULL); return (XapianWritableDatabase*)store->db_read_only(); } gboolean mu_store_contains_message (const MuStore *store, const char* path, GError **err) { g_return_val_if_fail (store, FALSE); g_return_val_if_fail (path, FALSE); try { const std::string term (store->get_uid_term(path)); return store->db_read_only()->term_exists (term) ? TRUE: FALSE; } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, FALSE); } unsigned mu_store_get_docid_for_path (const MuStore *store, const char* path, GError **err) { g_return_val_if_fail (store, FALSE); g_return_val_if_fail (path, FALSE); try { const std::string term (store->get_uid_term(path)); Xapian::Query query (term); Xapian::Enquire enq (*store->db_read_only()); enq.set_query (query); Xapian::MSet mset (enq.get_mset (0,1)); if (mset.empty()) throw MuStoreError (MU_ERROR_NO_MATCHES, "message not found"); return *mset.begin(); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, MU_STORE_INVALID_DOCID); } time_t mu_store_get_timestamp (const MuStore *store, const char *msgpath, GError **err) { char *stampstr; time_t rv; g_return_val_if_fail (store, 0); g_return_val_if_fail (msgpath, 0); stampstr = mu_store_get_metadata (store, msgpath, err); if (!stampstr) return (time_t)0; rv = (time_t) g_ascii_strtoull (stampstr, NULL, 10); g_free (stampstr); return rv; } MuError mu_store_foreach (MuStore *self, MuStoreForeachFunc func, void *user_data, GError **err) { g_return_val_if_fail (self, MU_ERROR); g_return_val_if_fail (func, MU_ERROR); try { Xapian::Enquire enq (*self->db_read_only()); enq.set_query (Xapian::Query::MatchAll); enq.set_cutoff (0,0); Xapian::MSet matches (enq.get_mset (0, self->db_read_only()->get_doccount())); if (matches.empty()) return MU_OK; /* database is empty */ for (Xapian::MSet::iterator iter = matches.begin(); iter != matches.end(); ++iter) { Xapian::Document doc (iter.get_document()); const std::string path(doc.get_value(MU_MSG_FIELD_ID_PATH)); MuError res = func (path.c_str(), user_data); if (res != MU_OK) return res; } } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, MU_ERROR_XAPIAN); return MU_OK; } MuMsg* mu_store_get_msg (const MuStore *self, unsigned docid, GError **err) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (docid != 0, NULL); try { Xapian::Document *doc = new Xapian::Document (self->db_read_only()->get_document (docid)); return mu_msg_new_from_doc ((XapianDocument*)doc, err); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, 0); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/Makefile.in���������������������������������������������������������������������������0000644�0001750�0001750�00000071261�13021065705�011774� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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 = : build_triplet = @build@ host_triplet = @host@ subdir = lib ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/perlmod.m4 $(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 = doxyfile CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) am__DEPENDENCIES_1 = libmu_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am_libmu_la_OBJECTS = mu-bookmarks.lo mu-contacts.lo mu-container.lo \ mu-date.lo mu-flags.lo mu-index.lo mu-log.lo mu-maildir.lo \ mu-msg-crypto.lo mu-msg-doc.lo mu-msg-fields.lo mu-msg-file.lo \ mu-msg-iter.lo mu-msg-part.lo mu-msg-prio.lo mu-msg-sexp.lo \ mu-msg.lo mu-query.lo mu-runtime.lo mu-script.lo mu-store.lo \ mu-store-read.lo mu-store-write.lo mu-str.lo mu-threader.lo \ mu-util.lo libmu_la_OBJECTS = $(am_libmu_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = 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__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=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 = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=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 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libmu_la_SOURCES) DIST_SOURCES = $(libmu_la_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 \ distdir 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 DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/doxyfile.in \ $(top_srcdir)/depcomp $(top_srcdir)/gtest.mk DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) 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" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMACS = @EMACS@ EMACSLOADPATH = @EMACSLOADPATH@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GMIME_CFLAGS = @GMIME_CFLAGS@ GMIME_LIBS = @GMIME_LIBS@ GREP = @GREP@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ GUILE_BINARY = @GUILE_BINARY@ GUILE_CFLAGS = @GUILE_CFLAGS@ GUILE_LIBS = @GUILE_LIBS@ GUILE_SITEDIR = @GUILE_SITEDIR@ GUILE_SNARF = @GUILE_SNARF@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MU_DOC_DIR = @MU_DOC_DIR@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PMCCABE = @PMCCABE@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SORT = @SORT@ STRIP = @STRIP@ VERSION = @VERSION@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ XAPIAN_CONFIG = @XAPIAN_CONFIG@ XAPIAN_CXXFLAGS = @XAPIAN_CXXFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ 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 = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_makeinfo = @have_makeinfo@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ lispdir = @lispdir@ 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@ TEST_PROGS = # enforce compiling guile (optionally) first,then this dir first # before decending into tests/ SUBDIRS = . tests AM_CPPFLAGS = $(XAPIAN_CXXFLAGS) $(GMIME_CFLAGS) $(GLIB_CFLAGS) $(GUILE_CFLAGS) # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS = -Wall -Wextra -Wno-unused-parameter \ -Wdeclaration-after-statement -Wno-variadic-macros AM_CXXFLAGS = -Wall -Wextra -Wno-unused-parameter noinst_LTLIBRARIES = \ libmu.la libmu_la_SOURCES = \ mu-bookmarks.c \ mu-bookmarks.h \ mu-contacts.c \ mu-contacts.h \ mu-container.c \ mu-container.h \ mu-date.c \ mu-date.h \ mu-flags.h \ mu-flags.c \ mu-index.c \ mu-index.h \ mu-log.c \ mu-log.h \ mu-maildir.c \ mu-maildir.h \ mu-msg-crypto.c \ mu-msg-doc.cc \ mu-msg-doc.h \ mu-msg-fields.c \ mu-msg-fields.h \ mu-msg-file.c \ mu-msg-file.h \ mu-msg-iter.cc \ mu-msg-iter.h \ mu-msg-part.c \ mu-msg-part.h \ mu-msg-prio.c \ mu-msg-prio.h \ mu-msg-priv.h \ mu-msg-sexp.c \ mu-msg.c \ mu-msg.h \ mu-msg.h \ mu-query.cc \ mu-query.h \ mu-runtime.c \ mu-runtime.h \ mu-script.c \ mu-script.h \ mu-store.cc \ mu-store.h \ mu-store-read.cc \ mu-store-write.cc \ mu-store-priv.hh \ mu-str.c \ mu-str.h \ mu-threader.c \ mu-threader.h \ mu-util.c \ mu-util.h libmu_la_LIBADD = \ $(XAPIAN_LIBS) \ $(GMIME_LIBS) \ $(GLIB_LIBS) \ $(GUILE_LIBS) EXTRA_DIST = \ mu-msg-crypto.c \ doxyfile.in all: all-recursive .SUFFIXES: .SUFFIXES: .c .cc .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtest.mk $(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) --gnu lib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu lib/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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtest.mk $(am__empty): $(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): doxyfile: $(top_builddir)/config.status $(srcdir)/doxyfile.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libmu.la: $(libmu_la_OBJECTS) $(libmu_la_DEPENDENCIES) $(EXTRA_libmu_la_DEPENDENCIES) $(AM_V_CXXLD)$(CXXLINK) $(libmu_la_OBJECTS) $(libmu_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-bookmarks.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-contacts.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-container.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-date.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-flags.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-index.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-maildir.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-msg-crypto.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-msg-doc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-msg-fields.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-msg-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-msg-iter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-msg-part.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-msg-prio.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-msg-sexp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-msg.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-query.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-runtime.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-script.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-store-read.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-store-write.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-store.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-str.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-threader.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-util.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< .cc.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cc.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cc.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # 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" 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 distdir: $(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 @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 check-am: all-am check: check-recursive all-am: Makefile $(LTLIBRARIES) 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 clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ 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 -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool \ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir 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-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am .PRECIOUS: Makefile # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof # 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: �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-date.h�����������������������������������������������������������������������������0000644�0001750�0001750�00000010547�12605152236�011437� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #include <glib.h> #ifndef __MU_DATE_H__ #define __MU_DATE_H__ G_BEGIN_DECLS /** * @addtogroup MuDate * Date-related functions * @{ */ /** * get a string for a given time_t * * mu_date_str_s returns a ptr to a static buffer, * while mu_date_str returns dynamically allocated * memory that must be freed after use. * * @param frm the format of the string (in strftime(3) format) * @param t the time as time_t * * @return a string representation of the time; see above for what to * do with it. Lenght is max. 128 bytes, inc. the ending \0. if the * format is too long, the value will be truncated. in practice this * should not happen. */ const char* mu_date_str_s (const char* frm, time_t t) G_GNUC_CONST; char* mu_date_str (const char* frm, time_t t) G_GNUC_WARN_UNUSED_RESULT; /** * get a display string for a given time_t; if the given is less than * 24h from the current time, we display the time, otherwise the date, * using the preferred date/time for the current locale * * mu_str_display_date_s returns a ptr to a static buffer, * * @param t the time as time_t * * @return a string representation of the time/date */ const char* mu_date_display_s (time_t t); /** * * parse strings like 1h, 3w, 2m to mean '1 hour before now', '3 weeks * before now' and '2 * 30 days before now' * * the format is <n>(h|d|w|m|y), where <n> is an integer > 0, and * h=hour, d=day, w=week, m=30 days, year=365 days. function returns * *now* minus this value as time_t (UTC) * * if the number cannot be parsed, return (time_t)-1 * * @param str a str * * @return the time_t of the point in time indicated by 'now' minus * the value, or (time_t)-1 otherwise */ time_t mu_date_parse_hdwmy (const char* str); /** * complete a date (a string of the form YYYYMMDDHHMMSS with [0..14] * of the rightmost characters missing) to the the full YYYYMMDDHHMMSS. * * if is_begin is TRUE, add to the 'floor' (e.g, * 20110101=>20110101000000), otherwise go to the 'ceiling', * e.g. 2009=>20091231235050) * * @param date a date string (assumed to have the beginning of the * date, this is not checked * @param is_begin if TRUE go to floor (as described), otherwise to * the ceiling * * @return mu_date_complete: return a newly allocated string (free * with g_free) with the full, 14-char date; mu_date_complete_s: * return a statically allocated string. NOT REENTRANT. */ char* mu_date_complete (const char *date, gboolean is_begin); const char* mu_date_complete_s (const char *date, gboolean is_begin); /** * * * @param datespec * @param is_begin * * @return */ const char* mu_date_interpret_s (const char *datespec, gboolean is_begin); char* mu_date_interpret (const char *datespec, gboolean is_begin); /** * convert a date of the form 'YYYYMMDDHHMMSS' into time_t * * @param date a date str of the form 'YYYYMMDDHHMMSS' * @param local if TRUE, source is assumed to bin in local time, UTC otherwise * * @return the corresponding time_t, or (time_t)-1 in case of error */ time_t mu_date_str_to_time_t (const char* date, gboolean local); /** * convert a time_t value into a date string of the form * 'YYYYMMDDHHMMSS'; assume UTC * * @param t a time_t value * @param local if TRUE, convert to local time, otherwise use UTC * * @return mu_date_time_t_to_str_s: a static string (don't modify, * non-reentrant) of the form 'YYYYMMDDHHMMSS'; mu_date_time_t_to_str: * return a newly allocated string with the same. */ const char* mu_date_time_t_to_str_s (time_t t, gboolean local); char* mu_date_time_t_to_str (time_t t, gboolean local); /** @} */ G_END_DECLS #endif /*__MU_DATE_H__*/ ���������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-index.c����������������������������������������������������������������������������0000644�0001750�0001750�00000026275�13020504331�011616� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify 1** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include "mu-index.h" #include <stdlib.h> #include <string.h> #include <unistd.h> #include <glib.h> #include <glib/gstdio.h> #include <errno.h> #include "mu-maildir.h" #include "mu-store.h" #include "mu-util.h" #define MU_LAST_USED_MAILDIR_KEY "last_used_maildir" #define MU_INDEX_MAX_FILE_SIZE (500*1000*1000) /* 500 Mb */ /* apparently, people are getting really big mails, so let us index those (by * default)*/ struct _MuIndex { MuStore *_store; gboolean _needs_reindex; guint _max_filesize; }; MuIndex* mu_index_new (MuStore *store, GError **err) { MuIndex *index; unsigned count; g_return_val_if_fail (store, NULL); g_return_val_if_fail (!mu_store_is_read_only(store), NULL); index = g_new0 (MuIndex, 1); index->_store = mu_store_ref (store); /* set the default max file size */ index->_max_filesize = MU_INDEX_MAX_FILE_SIZE; count = mu_store_count (store, err); if (count == (unsigned)-1) return NULL; else if (count == 0) index->_needs_reindex = FALSE; /* FIXME */ /* else */ /* index->_needs_reindex = */ /* mu_store_database_needs_upgrade (xpath); */ return index; } void mu_index_destroy (MuIndex *index) { if (!index) return; mu_store_unref (index->_store); g_free (index); } struct _MuIndexCallbackData { MuIndexMsgCallback _idx_msg_cb; MuIndexDirCallback _idx_dir_cb; MuStore* _store; void* _user_data; MuIndexStats* _stats; gboolean _reindex; gboolean _lazy_check; time_t _dirstamp; guint _max_filesize; }; typedef struct _MuIndexCallbackData MuIndexCallbackData; /* checks to determine if we need to (re)index this message note: * simply checking timestamps is not good enough because message may * be moved from other dirs (e.g. from 'new' to 'cur') and the time * stamps won't change. */ static inline gboolean needs_index (MuIndexCallbackData *data, const char *fullpath, time_t filestamp) { /* unconditionally reindex */ if (data->_reindex) return TRUE; /* it's not in the database yet (FIXME: GError)*/ if (!mu_store_contains_message (data->_store, fullpath, NULL)) return TRUE; /* it's there, but it's not up to date */ if ((unsigned)filestamp >= (unsigned)data->_dirstamp) return TRUE; return FALSE; /* index not needed */ } static MuError insert_or_update_maybe (const char *fullpath, const char *mdir, time_t filestamp, MuIndexCallbackData *data, gboolean *updated) { MuMsg *msg; GError *err; gboolean rv; *updated = FALSE; if (!needs_index (data, fullpath, filestamp)) return MU_OK; /* nothing to do for this one */ err = NULL; msg = mu_msg_new_from_file (fullpath, mdir, &err); if (!msg) { if (!err) g_warning ("error creating message object: %s", fullpath); else { g_warning ("%s", err->message); g_clear_error (&err); } /* warn, then simply continue */ return MU_OK; } /* we got a valid id; scan the message contents as well */ rv = mu_store_add_msg (data->_store, msg, &err); mu_msg_unref (msg); if (!rv) { g_warning ("error storing message object: %s", err ? err->message : "cause unknown"); g_clear_error (&err); return MU_ERROR; } *updated = TRUE; return MU_OK; } static MuError run_msg_callback_maybe (MuIndexCallbackData *data) { MuError result; if (!data || !data->_idx_msg_cb) return MU_OK; result = data->_idx_msg_cb (data->_stats, data->_user_data); if (G_UNLIKELY(result != MU_OK && result != MU_STOP)) g_warning ("error in callback"); return result; } static MuError on_run_maildir_msg (const char *fullpath, const char *mdir, struct stat *statbuf, MuIndexCallbackData *data) { MuError result; gboolean updated; /* protect against too big messages */ if (G_UNLIKELY(statbuf->st_size > data->_max_filesize)) { g_warning ("ignoring because bigger than %u bytes: %s", data->_max_filesize, fullpath); return MU_OK; /* not an error */ } result = run_msg_callback_maybe (data); if (result != MU_OK) return result; /* see if we need to update/insert anything... * use the ctime, so any status change will be visible (perms, * filename etc.)*/ result = insert_or_update_maybe (fullpath, mdir, statbuf->st_ctime, data, &updated); if (result == MU_OK && data && data->_stats) { /* update statistics */ ++data->_stats->_processed; updated ? ++data->_stats->_updated : ++data->_stats->_uptodate; } return result; } static time_t get_dir_timestamp (const char *path) { struct stat statbuf; if (stat (path, &statbuf) != 0) { g_warning ("failed to stat %s: %s", path, strerror(errno)); return 0; } return statbuf.st_ctime; } static MuError on_run_maildir_dir (const char* fullpath, gboolean enter, MuIndexCallbackData *data) { GError *err; err = NULL; /* xapian stores a per-dir timestamp; we use this timestamp to determine * whether a message is up-to-date */ if (enter) { data->_dirstamp = mu_store_get_timestamp (data->_store, fullpath, &err); /* in 'lazy' mode, we only check the dir timestamp, and if it's * up to date, we don't bother with this dir. This fails to * account for messages below this dir that have merely * _changed_ though */ if (data->_lazy_check && mu_maildir_is_leaf_dir(fullpath)) { time_t dirstamp; dirstamp = get_dir_timestamp (fullpath); if (dirstamp <= data->_dirstamp) { g_debug ("ignore %s (up-to-date)", fullpath); return MU_IGNORE; } } g_debug ("entering %s", fullpath); } else { mu_store_set_timestamp (data->_store, fullpath, time(NULL), &err); g_debug ("leaving %s", fullpath); } if (data->_idx_dir_cb) return data->_idx_dir_cb (fullpath, enter, data->_user_data); if (err) { MU_WRITE_LOG ("%s: %s", __func__, err->message); g_clear_error(&err); } return MU_OK; } static gboolean check_path (const char *path) { g_return_val_if_fail (path, FALSE); if (!g_path_is_absolute (path)) { g_warning ("%s: not an absolute path: %s", __func__, path); return FALSE; } if (access (path, R_OK) != 0) { g_warning ("%s: cannot open '%s': %s", __func__, path, strerror (errno)); return FALSE; } return TRUE; } static void init_cb_data (MuIndexCallbackData *cb_data, MuStore *xapian, gboolean reindex, gboolean lazycheck, guint max_filesize, MuIndexStats *stats, MuIndexMsgCallback msg_cb, MuIndexDirCallback dir_cb, void *user_data) { cb_data->_idx_msg_cb = msg_cb; cb_data->_idx_dir_cb = dir_cb; cb_data->_user_data = user_data; cb_data->_store = xapian; cb_data->_reindex = reindex; cb_data->_lazy_check = lazycheck; cb_data->_dirstamp = 0; cb_data->_max_filesize = max_filesize; cb_data->_stats = stats; if (cb_data->_stats) memset (cb_data->_stats, 0, sizeof(MuIndexStats)); } void mu_index_set_max_msg_size (MuIndex *index, guint max_size) { g_return_if_fail (index); if (max_size == 0) index->_max_filesize = MU_INDEX_MAX_FILE_SIZE; else index->_max_filesize = max_size; } void mu_index_set_xbatch_size (MuIndex *index, guint xbatchsize) { g_return_if_fail (index); mu_store_set_batch_size (index->_store, xbatchsize); } MuError mu_index_run (MuIndex *index, const char *path, gboolean reindex, gboolean lazycheck, MuIndexStats *stats, MuIndexMsgCallback msg_cb, MuIndexDirCallback dir_cb, void *user_data) { MuIndexCallbackData cb_data; MuError rv; g_return_val_if_fail (index && index->_store, MU_ERROR); g_return_val_if_fail (msg_cb, MU_ERROR); if (!check_path (path)) return MU_ERROR; if (!reindex && index->_needs_reindex) { g_warning ("database not up-to-date; needs full reindex"); return MU_ERROR; } init_cb_data (&cb_data, index->_store, reindex, lazycheck, index->_max_filesize, stats, msg_cb, dir_cb, user_data); rv = mu_maildir_walk (path, (MuMaildirWalkMsgCallback)on_run_maildir_msg, (MuMaildirWalkDirCallback)on_run_maildir_dir, reindex, /* re-index, ie. do a full update */ &cb_data); mu_store_flush (index->_store); return rv; } static MuError on_stats_maildir_file (const char *fullpath, const char *mdir, struct stat *statbuf, MuIndexCallbackData *cb_data) { MuError result; if (cb_data && cb_data->_idx_msg_cb) result = cb_data->_idx_msg_cb (cb_data->_stats, cb_data->_user_data); else result = MU_OK; if (result == MU_OK) { if (cb_data->_stats) ++cb_data->_stats->_processed; return MU_OK; } return result; /* MU_STOP or MU_OK */ } MuError mu_index_stats (MuIndex *index, const char *path, MuIndexStats *stats, MuIndexMsgCallback cb_msg, MuIndexDirCallback cb_dir, void *user_data) { MuIndexCallbackData cb_data; g_return_val_if_fail (index, MU_ERROR); g_return_val_if_fail (cb_msg, MU_ERROR); if (!check_path (path)) return MU_ERROR; if (stats) memset (stats, 0, sizeof(MuIndexStats)); cb_data._idx_msg_cb = cb_msg; cb_data._idx_dir_cb = cb_dir; cb_data._stats = stats; cb_data._user_data = user_data; cb_data._dirstamp = 0; return mu_maildir_walk (path, (MuMaildirWalkMsgCallback)on_stats_maildir_file, NULL, FALSE, &cb_data); } struct _CleanupData { MuStore *_store; MuIndexStats *_stats; MuIndexCleanupDeleteCallback _cb; void *_user_data; }; typedef struct _CleanupData CleanupData; static MuError foreach_doc_cb (const char* path, CleanupData *cudata) { if (access (path, R_OK) != 0) { if (errno != EACCES) g_debug ("cannot access %s: %s", path, strerror(errno)); if (!mu_store_remove_path (cudata->_store, path)) return MU_ERROR; /* something went wrong... bail out */ if (cudata->_stats) ++cudata->_stats->_cleaned_up; } if (cudata->_stats) ++cudata->_stats->_processed; if (!cudata->_cb) return MU_OK; return cudata->_cb (cudata->_stats, cudata->_user_data); } MuError mu_index_cleanup (MuIndex *index, MuIndexStats *stats, MuIndexCleanupDeleteCallback cb, void *user_data, GError **err) { MuError rv; CleanupData cudata; g_return_val_if_fail (index, MU_ERROR); cudata._store = index->_store; cudata._stats = stats; cudata._cb = cb; cudata._user_data = user_data; rv = mu_store_foreach (index->_store, (MuStoreForeachFunc)foreach_doc_cb, &cudata, err); mu_store_flush (index->_store); return rv; } gboolean mu_index_stats_clear (MuIndexStats *stats) { if (!stats) return FALSE; memset (stats, 0, sizeof(MuIndexStats)); return TRUE; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-date.c�����������������������������������������������������������������������������0000644�0001750�0001750�00000014034�13020504331�011412� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2012 <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #include <string.h> #include <stdlib.h> #include <ctype.h> #include "mu-util.h" #include "mu-date.h" #include "mu-str.h" const char* mu_date_str_s (const char* frm, time_t t) { struct tm *tmbuf; static char buf[128]; static int is_utf8 = -1; size_t len; if (G_UNLIKELY(is_utf8 == -1)) is_utf8 = mu_util_locale_is_utf8 () ? 1 : 0; g_return_val_if_fail (frm, NULL); tmbuf = localtime(&t); len = strftime (buf, sizeof(buf) - 1, frm, tmbuf); if (len == 0) return ""; /* not necessarily an error... */ if (!is_utf8) { /* charset is _not_ utf8, so we need to convert it, so * the date could contain locale-specific characters*/ gchar *conv; GError *err; err = NULL; conv = g_locale_to_utf8 (buf, -1, NULL, NULL, &err); if (err) { g_warning ("conversion failed: %s", err->message); g_error_free (err); strcpy (buf, "<error>"); } else strncpy (buf, conv, sizeof(buf)); g_free (conv); } return buf; } char* mu_date_str (const char *frm, time_t t) { return g_strdup (mu_date_str_s(frm, t)); } const char* mu_date_display_s (time_t t) { time_t now; static const time_t SECS_IN_DAY = 24 * 60 * 60; now = time (NULL); if (ABS(now - t) > SECS_IN_DAY) return mu_date_str_s ("%x", t); else return mu_date_str_s ("%X", t); } time_t mu_date_parse_hdwmy (const char *nptr) { long int num; char *endptr; time_t now, delta; time_t never = (time_t)-1; g_return_val_if_fail (nptr, never); num = strtol (nptr, &endptr, 10); if (num <= 0 || num > 9999) return never; if (endptr == NULL || *endptr == '\0') return never; switch (endptr[0]) { case 'h': /* hour */ case 'H': delta = num * 60 * 60; break; case 'd': /* day */ case 'D': delta = num * 24 * 60 * 60; break; case 'w': /* week */ case 'W': delta = num * 7 * 24 * 60 * 60; break; case 'm': /* month */ case 'M': delta = num * 30 * 24 * 60 * 60; break; case 'y': /* year */ case 'Y': delta = num * 365 * 24 * 60 * 60; break; default: return never; } now = time(NULL); return delta <= now ? now - delta : never; } /* clear a date of anything non-numberic; static string, non-reentrant */ static char* clear_date_s (const char *date) { static char cleandate [14 + 1]; unsigned u1, u2; for (u1 = u2 = 0; date[u1] != '\0'; ++u1) if (isdigit(date[u1])) cleandate[u2++] = date[u1]; cleandate[u2] = '\0'; return cleandate; } const char* mu_date_complete_s (const char *date, gboolean is_begin) { static char fulldate[14 + 1]; static const char* full_begin = "00000101000000"; static const char* full_end = "99991231235959"; char *cleardate; g_return_val_if_fail (date, NULL); cleardate = clear_date_s (date); strncpy (fulldate, is_begin ? full_begin : full_end, sizeof(fulldate)); memcpy (fulldate, cleardate, strlen(cleardate)); return fulldate; } char* mu_date_complete (const char *date, gboolean is_begin) { const char *s; g_return_val_if_fail (date, NULL); s = mu_date_complete_s (date, is_begin); return s ? g_strdup (s) : NULL; } const char* mu_date_interpret_s (const char *datespec, gboolean is_begin) { static char fulldate[14 + 1]; time_t now, t; g_return_val_if_fail (datespec, NULL); if (mu_str_is_empty (datespec) && is_begin) return "000000000000"; /* beginning of time*/ now = time(NULL); if (strcmp (datespec, "today") == 0) { strftime(fulldate, sizeof(fulldate), is_begin ? "%Y%m%d000000" : "%Y%m%d235959", localtime(&now)); return fulldate; } if (mu_str_is_empty (datespec) || strcmp (datespec, "now") == 0) { strftime(fulldate, sizeof(fulldate), "%Y%m%d%H%M%S", localtime(&now)); return fulldate; } t = mu_date_parse_hdwmy (datespec); if (t != (time_t)-1) { strftime(fulldate, sizeof(fulldate), "%Y%m%d%H%M%S", localtime(&t)); return fulldate; } return datespec; /* nothing changed */ } char* mu_date_interpret (const char *datespec, gboolean is_begin) { char *s; g_return_val_if_fail (datespec, NULL); s = mu_date_interpret (datespec, is_begin); return s ? g_strdup(s) : NULL; } time_t mu_date_str_to_time_t (const char* date, gboolean local) { struct tm tm; char mydate[14 + 1]; /* YYYYMMDDHHMMSS */ time_t t; memset (&tm, 0, sizeof(struct tm)); strncpy (mydate, date, 15); mydate[sizeof(mydate)-1] = '\0'; g_return_val_if_fail (date, (time_t)-1); tm.tm_sec = atoi (mydate + 12); mydate[12] = '\0'; tm.tm_min = atoi (mydate + 10); mydate[10] = '\0'; tm.tm_hour = atoi (mydate + 8); mydate[8] = '\0'; tm.tm_mday = atoi (mydate + 6); mydate[6] = '\0'; tm.tm_mon = atoi (mydate + 4) - 1; mydate[4] = '\0'; tm.tm_year = atoi (mydate) - 1900; tm.tm_isdst = -1; /* let timegm/mktime figure out the dst */ if (local) t = mktime (&tm); else t = timegm (&tm); /* GNU/BSD specific */ return t; } const char* mu_date_time_t_to_str_s (time_t t, gboolean local) { /* static char datestr[14 + 1]; /\* YYYYMMDDHHMMSS *\/ */ static char datestr[14+1]; /* YYYYMMDDHHMMSS */ static const char *frm = "%Y%m%d%H%M%S"; size_t len; len = strftime (datestr, sizeof(datestr), frm, local ? localtime (&t) : gmtime(&t)); if (len == 0) { g_warning ("bug: error converting time"); return "00000000000000"; } return datestr; } char* mu_date_time_t_to_str (time_t t, gboolean local) { const char* str; str = mu_date_time_t_to_str_s (t, local); return str ? g_strdup(str): NULL; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/doxyfile.in���������������������������������������������������������������������������0000644�0001750�0001750�00000015576�12605152236�012114� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Doxyfile 0.1 #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- PROJECT_NAME = mu PROJECT_NUMBER = @VERSION@ OUTPUT_DIRECTORY = apidocs OUTPUT_LANGUAGE = English EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ALWAYS_DETAILED_SEC = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = INTERNAL_DOCS = NO STRIP_CODE_COMMENTS = YES CASE_SENSE_NAMES = YES SHORT_NAMES = NO HIDE_SCOPE_NAMES = NO VERBATIM_HEADERS = YES SHOW_INCLUDE_FILES = YES JAVADOC_AUTOBRIEF = YES INHERIT_DOCS = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES DISTRIBUTE_GROUP_DOC = NO TAB_SIZE = 8 GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES ALIASES = ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 OPTIMIZE_OUTPUT_FOR_C = YES SHOW_USED_FILES = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_FORMAT = WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = @top_srcdir@/lib/ FILE_PATTERNS = *.c *.h RECURSIVE = YES EXCLUDE = tests # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = Makefile.* ChangeLog CHANGES CHANGES.* README \ README.* *.png AUTHORS DESIGN DESIGN.* *.desktop \ DESKTOP* COMMENTS HOWTO magic NOTES TODO THANKS # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NO TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = YES MAN_OUTPUT = man MAN_EXTENSION = .3mu MAN_LINKS = YES #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = YES #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = "G_BEGIN_DECLS=" \ "G_END_DECLS=" # "DOXYGEN_SHOULD_SKIP_THIS" \ # "DBUS_GNUC_DEPRECATED=" \ # "_DBUS_DEFINE_GLOBAL_LOCK(name)=" \ # "_DBUS_GNUC_PRINTF(from,to)=" SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO PERL_PATH = #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES HAVE_DOT = NO CLASS_GRAPH = YES COLLABORATION_GRAPH = YES TEMPLATE_RELATIONS = YES HIDE_UNDOC_RELATIONS = YES INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES GRAPHICAL_HIERARCHY = YES DOT_PATH = DOTFILE_DIRS = MAX_DOT_GRAPH_WIDTH = 640 MAX_DOT_GRAPH_HEIGHT = 1024 GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO ����������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-store-write.cc���������������������������������������������������������������������0000644�0001750�0001750�00000050721�13020504332�013130� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8-*- */ /* ** Copyright (C) 2008-2016 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <cstdio> #include <xapian.h> #include <cstring> #include <stdexcept> #include "mu-store.h" #include "mu-store-priv.hh" /* _MuStore */ #include "mu-msg.h" #include "mu-msg-part.h" #include "mu-store.h" #include "mu-util.h" #include "mu-str.h" #include "mu-date.h" #include "mu-flags.h" #include "mu-contacts.h" void _MuStore::begin_transaction () { try { db_writable()->begin_transaction(); in_transaction (true); } MU_XAPIAN_CATCH_BLOCK; } void _MuStore::commit_transaction () { try { in_transaction (false); db_writable()->commit_transaction(); } MU_XAPIAN_CATCH_BLOCK; } void _MuStore::rollback_transaction () { try { in_transaction (false); db_writable()->cancel_transaction(); } MU_XAPIAN_CATCH_BLOCK; } /* we cache these prefix strings, so we don't have to allocate them all * the time; this should save 10-20 string allocs per message */ G_GNUC_CONST static const std::string& prefix (MuMsgFieldId mfid) { static std::string fields[MU_MSG_FIELD_ID_NUM]; static bool initialized = false; if (G_UNLIKELY(!initialized)) { for (int i = 0; i != MU_MSG_FIELD_ID_NUM; ++i) fields[i] = std::string (1, mu_msg_field_xapian_prefix ((MuMsgFieldId)i)); initialized = true; } return fields[mfid]; } static void add_synonym_for_flag (MuFlags flag, Xapian::WritableDatabase *db) { static const std::string pfx(prefix(MU_MSG_FIELD_ID_FLAGS)); db->clear_synonyms (pfx + mu_flag_name (flag)); db->add_synonym (pfx + mu_flag_name (flag), pfx + (std::string(1, (char)(tolower(mu_flag_char(flag)))))); } static void add_synonym_for_prio (MuMsgPrio prio, Xapian::WritableDatabase *db) { static const std::string pfx (prefix(MU_MSG_FIELD_ID_PRIO)); std::string s1 (pfx + mu_msg_prio_name (prio)); std::string s2 (pfx + (std::string(1, mu_msg_prio_char (prio)))); db->clear_synonyms (s1); db->clear_synonyms (s2); db->add_synonym (s1, s2); } static void add_synonyms (MuStore *store) { mu_flags_foreach ((MuFlagsForeachFunc)add_synonym_for_flag, store->db_writable()); mu_msg_prio_foreach ((MuMsgPrioForeachFunc)add_synonym_for_prio, store->db_writable()); } MuStore* mu_store_new_writable (const char* xpath, const char *contacts_cache, gboolean rebuild, GError **err) { g_return_val_if_fail (xpath, NULL); try { try { MuStore *store; store = new _MuStore (xpath, contacts_cache, rebuild ? true : false); add_synonyms (store); return store; } MU_STORE_CATCH_BLOCK_RETURN(err,NULL); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN (err, MU_ERROR_XAPIAN, NULL); } void mu_store_set_batch_size (MuStore *store, guint batchsize) { g_return_if_fail (store); store->set_batch_size (batchsize); } gboolean mu_store_set_metadata (MuStore *store, const char *key, const char *val, GError **err) { g_return_val_if_fail (store, FALSE); g_return_val_if_fail (key, FALSE); g_return_val_if_fail (val, FALSE); try { try { store->db_writable()->set_metadata (key, val); return TRUE; } MU_STORE_CATCH_BLOCK_RETURN(err, FALSE); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, FALSE); } gboolean mu_store_clear (MuStore *store, GError **err) { g_return_val_if_fail (store, FALSE); try { try { store->clear(); return TRUE; } MU_STORE_CATCH_BLOCK_RETURN(err, FALSE); } MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE); } void mu_store_flush (MuStore *store) { g_return_if_fail (store); try { if (store->in_transaction()) store->commit_transaction (); store->db_writable()->commit (); } MU_XAPIAN_CATCH_BLOCK; if (store->contacts()) mu_contacts_serialize (store->contacts()); } static void add_terms_values_date (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { time_t t; const char *datestr; t = (time_t)mu_msg_get_field_numeric (msg, mfid); datestr = mu_date_time_t_to_str_s (t, FALSE /*UTC*/); doc.add_value ((Xapian::valueno)mfid, datestr); } G_GNUC_CONST static const std::string& flag_val (char flagchar) { static const std::string pfx (prefix(MU_MSG_FIELD_ID_FLAGS)), draftstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_DRAFT))), flaggedstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_FLAGGED))), passedstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_PASSED))), repliedstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_REPLIED))), seenstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_SEEN))), trashedstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_TRASHED))), newstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_NEW))), signedstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_SIGNED))), cryptstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_ENCRYPTED))), attachstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_HAS_ATTACH))), unreadstr (pfx + (char)tolower(mu_flag_char(MU_FLAG_UNREAD))), liststr (pfx + (char)tolower(mu_flag_char(MU_FLAG_LIST))); switch (flagchar) { case 'D': return draftstr; case 'F': return flaggedstr; case 'P': return passedstr; case 'R': return repliedstr; case 'S': return seenstr; case 'T': return trashedstr; case 'N': return newstr; case 'z': return signedstr; case 'x': return cryptstr; case 'a': return attachstr; case 'l': return liststr; case 'u': return unreadstr; default: g_return_val_if_reached (flaggedstr); return flaggedstr; } } /* pre-calculate; optimization */ G_GNUC_CONST static const std::string& prio_val (MuMsgPrio prio) { static const std::string pfx (prefix(MU_MSG_FIELD_ID_PRIO)); static const std::string low (pfx + std::string(1, mu_msg_prio_char(MU_MSG_PRIO_LOW))), norm (pfx + std::string(1, mu_msg_prio_char(MU_MSG_PRIO_NORMAL))), high (pfx + std::string(1, mu_msg_prio_char(MU_MSG_PRIO_HIGH))); switch (prio) { case MU_MSG_PRIO_LOW: return low; case MU_MSG_PRIO_NORMAL: return norm; case MU_MSG_PRIO_HIGH: return high; default: g_return_val_if_reached (norm); return norm; } } static void add_terms_values_number (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { gint64 num = mu_msg_get_field_numeric (msg, mfid); const std::string numstr (Xapian::sortable_serialise((double)num)); doc.add_value ((Xapian::valueno)mfid, numstr); if (mfid == MU_MSG_FIELD_ID_FLAGS) { const char *cur = mu_flags_to_str_s ((MuFlags)num,(MuFlagType)MU_FLAG_TYPE_ANY); g_return_if_fail (cur); while (*cur) { doc.add_term (flag_val(*cur)); ++cur; } } else if (mfid == MU_MSG_FIELD_ID_PRIO) doc.add_term (prio_val((MuMsgPrio)num)); } static void add_terms_values_msgid (Xapian::Document& doc, MuMsg *msg) { char *str; const char *orig; if (!(orig = mu_msg_get_field_string ( msg, MU_MSG_FIELD_ID_MSGID))) return; /* nothing to do */ str = mu_str_process_msgid (orig, FALSE); doc.add_value ((Xapian::valueno)MU_MSG_FIELD_ID_MSGID, orig); doc.add_term (prefix(MU_MSG_FIELD_ID_MSGID) + std::string(str, 0, _MuStore::MAX_TERM_LENGTH)); g_free (str); } /* for string and string-list */ static void add_terms_values_str (Xapian::Document& doc, const char *val, MuMsgFieldId mfid) { char *str; if (mu_msg_field_preprocess (mfid)) str = mu_str_process_term (val); else str = g_strdup (val); if (mu_msg_field_xapian_index (mfid)) { Xapian::TermGenerator termgen; termgen.set_document (doc); termgen.index_text_without_positions (str, 1, prefix(mfid)); if (g_strcmp0 (val, str) != 0) termgen.index_text_without_positions ( val, 1, prefix(mfid)); } if (mu_msg_field_xapian_term(mfid)) doc.add_term (prefix(mfid) + std::string(str, 0, _MuStore::MAX_TERM_LENGTH)); g_free (str); } static void add_terms_values_string (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { const char *orig; if (!(orig = mu_msg_get_field_string (msg, mfid))) return; /* nothing to do */ /* the value is what we display in search results; the * unchanged original */ if (mu_msg_field_xapian_value(mfid)) doc.add_value ((Xapian::valueno)mfid, orig); add_terms_values_str (doc, orig, mfid); } static void add_terms_values_string_list (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { const GSList *lst; lst = mu_msg_get_field_string_list (msg, mfid); if (!lst) return; if (mu_msg_field_xapian_value (mfid)) { gchar *str; str = mu_str_from_list (lst, ','); if (str) doc.add_value ((Xapian::valueno)mfid, str); g_free (str); } if (mu_msg_field_xapian_term (mfid)) { for (; lst; lst = g_slist_next ((GSList*)lst)) add_terms_values_str (doc, (const gchar*)lst->data, mfid); } } struct PartData { PartData (Xapian::Document& doc, MuMsgFieldId mfid): _doc (doc), _mfid(mfid) {} Xapian::Document _doc; MuMsgFieldId _mfid; }; /* index non-body text parts */ static void maybe_index_text_part (MuMsg *msg, MuMsgPart *part, PartData *pdata) { char *txt, *str; Xapian::TermGenerator termgen; /* only deal with attachments/messages; inlines are indexed as * body parts */ if (!(part->part_type & MU_MSG_PART_TYPE_ATTACHMENT) && !(part->part_type & MU_MSG_PART_TYPE_MESSAGE)) return; txt = mu_msg_part_get_text (msg, part, MU_MSG_OPTION_NONE); if (!txt) return; termgen.set_document(pdata->_doc); str = mu_str_process_text (txt); termgen.index_text_without_positions (str, 1, prefix(MU_MSG_FIELD_ID_EMBEDDED_TEXT)); g_free (txt); g_free (str); } static void each_part (MuMsg *msg, MuMsgPart *part, PartData *pdata) { char *fname; static const std::string file (prefix(MU_MSG_FIELD_ID_FILE)), mime (prefix(MU_MSG_FIELD_ID_MIME)); /* save the mime type of any part */ if (part->type) { /* note, we use '_' instead of '/' to separate * type/subtype -- Xapian doesn't treat '/' as * desired, so we use '_' and pre-process queries; see * mu_query_preprocess */ char ctype[MuStore::MAX_TERM_LENGTH + 1]; snprintf (ctype, sizeof(ctype), "%s_%s", part->type, part->subtype); pdata->_doc.add_term (mime + std::string(ctype, 0, MuStore::MAX_TERM_LENGTH)); } if ((fname = mu_msg_part_get_filename (part, FALSE))) { char *str; str = mu_str_process_term (fname); g_free (fname); pdata->_doc.add_term (file + std::string(str, 0, MuStore::MAX_TERM_LENGTH)); g_free (str); } maybe_index_text_part (msg, part, pdata); } static void add_terms_values_attach (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { PartData pdata (doc, mfid); mu_msg_part_foreach (msg, MU_MSG_OPTION_RECURSE_RFC822, (MuMsgPartForeachFunc)each_part, &pdata); } static void add_terms_values_body (Xapian::Document& doc, MuMsg *msg, MuMsgFieldId mfid) { const char *str; char *flat; if (mu_msg_get_flags(msg) & MU_FLAG_ENCRYPTED) return; /* ignore encrypted bodies */ str = mu_msg_get_body_text (msg, MU_MSG_OPTION_NONE); if (!str) /* FIXME: html->txt fallback needed */ str = mu_msg_get_body_html (msg, MU_MSG_OPTION_NONE); if (!str) return; /* no body... */ Xapian::TermGenerator termgen; termgen.set_document(doc); flat = mu_str_process_text (str); // g_print ("\n--\n%s\n--\n", flat); termgen.index_text_without_positions (flat, 1, prefix(mfid)); g_free (flat); } struct _MsgDoc { Xapian::Document *_doc; MuMsg *_msg; MuStore *_store; /* callback data, to determine whether this message is 'personal' */ gboolean _personal; GSList *_my_addresses; }; typedef struct _MsgDoc MsgDoc; static void add_terms_values_default (MuMsgFieldId mfid, MsgDoc *msgdoc) { if (mu_msg_field_is_numeric (mfid)) add_terms_values_number (*msgdoc->_doc, msgdoc->_msg, mfid); else if (mu_msg_field_is_string (mfid)) add_terms_values_string (*msgdoc->_doc, msgdoc->_msg, mfid); else if (mu_msg_field_is_string_list(mfid)) add_terms_values_string_list (*msgdoc->_doc, msgdoc->_msg, mfid); else g_return_if_reached (); } static void add_terms_values (MuMsgFieldId mfid, MsgDoc* msgdoc) { /* note: contact-stuff (To/Cc/From) will handled in * each_contact_info, not here */ if (!mu_msg_field_xapian_index(mfid) && !mu_msg_field_xapian_term(mfid) && !mu_msg_field_xapian_value(mfid)) return; // if (mu_msg_field_xapian_contact (mfid)) // return; /* handled in new_doc_from_message */ switch (mfid) { case MU_MSG_FIELD_ID_DATE: add_terms_values_date (*msgdoc->_doc, msgdoc->_msg, mfid); break; case MU_MSG_FIELD_ID_BODY_TEXT: add_terms_values_body (*msgdoc->_doc, msgdoc->_msg, mfid); break; /* note: add_terms_values_attach handles _FILE, _MIME and * _ATTACH_TEXT msgfields */ case MU_MSG_FIELD_ID_FILE: add_terms_values_attach (*msgdoc->_doc, msgdoc->_msg, mfid); break; case MU_MSG_FIELD_ID_MIME: case MU_MSG_FIELD_ID_EMBEDDED_TEXT: break; case MU_MSG_FIELD_ID_MSGID: add_terms_values_msgid (*msgdoc->_doc, msgdoc->_msg); break; case MU_MSG_FIELD_ID_THREAD_ID: case MU_MSG_FIELD_ID_UID: break; /* already taken care of elsewhere */ default: return add_terms_values_default (mfid, msgdoc); } } static const std::string& xapian_pfx (MuMsgContact *contact) { static const std::string empty; /* use ptr to string to prevent copy... */ switch (contact->type) { case MU_MSG_CONTACT_TYPE_TO: return prefix(MU_MSG_FIELD_ID_TO); case MU_MSG_CONTACT_TYPE_FROM: return prefix(MU_MSG_FIELD_ID_FROM); case MU_MSG_CONTACT_TYPE_CC: return prefix(MU_MSG_FIELD_ID_CC); case MU_MSG_CONTACT_TYPE_BCC: return prefix(MU_MSG_FIELD_ID_BCC); default: g_warning ("unsupported contact type %u", (unsigned)contact->type); return empty; } } static void add_address_subfields (Xapian::Document& doc, const char *addr, const std::string& pfx) { const char *at, *domain_part; char *name_part, *f1, *f2; /* add "foo" and "bar.com" as terms as well for * "foo@bar.com" */ if (G_UNLIKELY(!(at = (g_strstr_len (addr, -1, "@"))))) return; name_part = g_strndup(addr, at - addr); // foo domain_part = at + 1; f1 = mu_str_process_term (name_part); f2 = mu_str_process_term (domain_part); g_free (name_part); doc.add_term (pfx + std::string(f1, 0, _MuStore::MAX_TERM_LENGTH)); doc.add_term (pfx + std::string(f2, 0, _MuStore::MAX_TERM_LENGTH)); g_free (f1); g_free (f2); } static gboolean each_contact_info (MuMsgContact *contact, MsgDoc *msgdoc) { /* for now, don't store reply-to addresses */ if (mu_msg_contact_type (contact) == MU_MSG_CONTACT_TYPE_REPLY_TO) return TRUE; const std::string pfx (xapian_pfx(contact)); if (pfx.empty()) return TRUE; /* unsupported contact type */ if (!mu_str_is_empty(contact->name)) { Xapian::TermGenerator termgen; termgen.set_document (*msgdoc->_doc); char *flat = mu_str_process_text (contact->name); termgen.index_text_without_positions (flat, 1, pfx); g_free (flat); } if (!mu_str_is_empty(contact->address)) { char *flat; flat = mu_str_process_term (contact->address); msgdoc->_doc->add_term (std::string (pfx + flat, 0, MuStore::MAX_TERM_LENGTH)); g_free (flat); add_address_subfields (*msgdoc->_doc, contact->address, pfx); /* store it also in our contacts cache */ if (msgdoc->_store->contacts()) mu_contacts_add (msgdoc->_store->contacts(), contact->address, contact->name, msgdoc->_personal, mu_msg_get_date(msgdoc->_msg)); } return TRUE; } static gboolean each_contact_check_if_personal (MuMsgContact *contact, MsgDoc *msgdoc) { GSList *cur; if (msgdoc->_personal || !contact->address) return TRUE; for (cur = msgdoc->_my_addresses; cur; cur = g_slist_next (cur)) { if (g_ascii_strcasecmp ( contact->address, (const char*)cur->data) == 0) { msgdoc->_personal = TRUE; break; } } return TRUE; } Xapian::Document new_doc_from_message (MuStore *store, MuMsg *msg) { Xapian::Document doc; MsgDoc docinfo = {&doc, msg, store, 0, FALSE}; mu_msg_field_foreach ((MuMsgFieldForeachFunc)add_terms_values, &docinfo); /* determine whether this is 'personal' email, ie. one of my * e-mail addresses is explicitly mentioned -- it's not a * mailing list message. Callback will update docinfo->_personal */ if (store->my_addresses()) { docinfo._my_addresses = store->my_addresses(); mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)each_contact_check_if_personal, &docinfo); } /* also store the contact-info as separate terms, and add it * to the cache */ mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)each_contact_info, &docinfo); // g_printerr ("\n--%s\n--\n", doc.serialise().c_str()); return doc; } static void update_threading_info (Xapian::WritableDatabase* db, MuMsg *msg, Xapian::Document& doc) { const GSList *refs; // refs contains a list of parent messages, with the oldest // one first until the last one, which is the direct parent of // the current message. of course, it may be empty. // // NOTE: there may be cases where the the list is truncated; // we happily ignore that case. refs = mu_msg_get_references (msg); std::string thread_id; if (refs) thread_id = mu_util_get_hash ((const char*)refs->data); else thread_id = mu_util_get_hash (mu_msg_get_msgid (msg)); doc.add_term (prefix(MU_MSG_FIELD_ID_THREAD_ID) + thread_id); doc.add_value((Xapian::valueno)MU_MSG_FIELD_ID_THREAD_ID, thread_id); } unsigned add_or_update_msg (MuStore *store, unsigned docid, MuMsg *msg, GError **err) { g_return_val_if_fail (store, MU_STORE_INVALID_DOCID); g_return_val_if_fail (msg, MU_STORE_INVALID_DOCID); try { Xapian::docid id; Xapian::Document doc (new_doc_from_message(store, msg)); const std::string term (store->get_uid_term (mu_msg_get_path(msg))); if (!store->in_transaction()) store->begin_transaction(); doc.add_term (term); // update the threading info if this message has a message id if (mu_msg_get_msgid (msg)) update_threading_info (store->db_writable(), msg, doc); if (docid == 0) id = store->db_writable()->replace_document (term, doc); else { store->db_writable()->replace_document (docid, doc); id = docid; } if (store->inc_processed() % store->batch_size() == 0) store->commit_transaction(); return id; } MU_XAPIAN_CATCH_BLOCK_G_ERROR (err, MU_ERROR_XAPIAN_STORE_FAILED); if (store->in_transaction()) store->rollback_transaction(); return MU_STORE_INVALID_DOCID; } unsigned mu_store_add_msg (MuStore *store, MuMsg *msg, GError **err) { g_return_val_if_fail (store, MU_STORE_INVALID_DOCID); g_return_val_if_fail (msg, MU_STORE_INVALID_DOCID); return add_or_update_msg (store, 0, msg, err); } unsigned mu_store_update_msg (MuStore *store, unsigned docid, MuMsg *msg, GError **err) { g_return_val_if_fail (store, MU_STORE_INVALID_DOCID); g_return_val_if_fail (msg, MU_STORE_INVALID_DOCID); g_return_val_if_fail (docid != 0, MU_STORE_INVALID_DOCID); return add_or_update_msg (store, docid, msg, err); } unsigned mu_store_add_path (MuStore *store, const char *path, const char *maildir, GError **err) { MuMsg *msg; unsigned docid; g_return_val_if_fail (store, FALSE); g_return_val_if_fail (path, FALSE); msg = mu_msg_new_from_file (path, maildir, err); if (!msg) return MU_STORE_INVALID_DOCID; docid = add_or_update_msg (store, 0, msg, err); mu_msg_unref (msg); return docid; } XapianWritableDatabase* mu_store_get_writable_database (MuStore *store) { g_return_val_if_fail (store, NULL); return (XapianWritableDatabase*)store->db_writable(); } gboolean mu_store_remove_path (MuStore *store, const char *msgpath) { g_return_val_if_fail (store, FALSE); g_return_val_if_fail (msgpath, FALSE); try { const std::string term (store->get_uid_term(msgpath)); store->db_writable()->delete_document (term); store->inc_processed(); return TRUE; } MU_XAPIAN_CATCH_BLOCK_RETURN (FALSE); } gboolean mu_store_set_timestamp (MuStore *store, const char* msgpath, time_t stamp, GError **err) { char buf[21]; g_return_val_if_fail (store, FALSE); g_return_val_if_fail (msgpath, FALSE); sprintf (buf, "%" G_GUINT64_FORMAT, (guint64)stamp); return mu_store_set_metadata (store, msgpath, buf, err); } �����������������������������������������������mu-0.9.18/lib/mu-script.c���������������������������������������������������������������������������0000644�0001750�0001750�00000016604�12605152236�012021� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #ifdef BUILD_GUILE #include <libguile.h> #endif /*BUILD_GUILE*/ #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <dirent.h> #include <errno.h> #include <unistd.h> #include "mu-str.h" #include "mu-script.h" #include "mu-util.h" /** * Structure with information about a certain script. * the values will be *freed* when MuScriptInfo is freed */ struct _MuScriptInfo { char *_name; /* filename-sans-extension */ char *_path; /* full path to script */ char *_oneline; /* one-line description */ char *_descr; /* longer description */ }; /* create a new MuScriptInfo* object*/ static MuScriptInfo* script_info_new (void) { return g_slice_new0 (MuScriptInfo); } /* destroy a MuScriptInfo* object */ static void script_info_destroy (MuScriptInfo *msi) { if (!msi) return; g_free (msi->_name); g_free (msi->_path); g_free (msi->_oneline); g_free (msi->_descr); g_slice_free (MuScriptInfo, msi); } /* compare two MuScripInfo* objects (for sorting) */ static int script_info_cmp (MuScriptInfo *msi1, MuScriptInfo *msi2) { return strcmp (msi1->_name, msi2->_name); } const char* mu_script_info_name (MuScriptInfo *msi) { g_return_val_if_fail (msi, NULL); return msi->_name; } const char* mu_script_info_path (MuScriptInfo *msi) { g_return_val_if_fail (msi, NULL); return msi->_path; } const char* mu_script_info_one_line (MuScriptInfo *msi) { g_return_val_if_fail (msi, NULL); return msi->_oneline; } const char* mu_script_info_description (MuScriptInfo *msi) { g_return_val_if_fail (msi, NULL); return msi->_descr; } gboolean mu_script_info_matches_regex (MuScriptInfo *msi, const char *rxstr, GError **err) { GRegex *rx; gboolean match; g_return_val_if_fail (msi, FALSE); g_return_val_if_fail (rxstr, FALSE); rx = g_regex_new (rxstr, G_REGEX_CASELESS|G_REGEX_OPTIMIZE, 0, err); if (!rx) return FALSE; match = FALSE; if (msi->_name) match = g_regex_match (rx, msi->_name, 0, NULL); if (!match && msi->_oneline) match = g_regex_match (rx, msi->_oneline, 0, NULL); return match; } void mu_script_info_list_destroy (GSList *lst) { g_slist_foreach (lst, (GFunc)script_info_destroy, NULL); g_slist_free (lst); } static GIOChannel * open_channel (const char *path) { GError *err; GIOChannel *io_chan; err = NULL; io_chan = g_io_channel_new_file (path, "r", &err); if (!io_chan) { g_warning ("failed to open '%s': %s", path, err ? err->message : "something went wrong"); g_clear_error (&err); return NULL; } return io_chan; } static void end_channel (GIOChannel *io_chan) { GIOStatus status; GError *err; err = NULL; status = g_io_channel_shutdown (io_chan, FALSE, &err); if (status != G_IO_STATUS_NORMAL) { g_warning ("failed to shutdown io-channel: %s", err ? err->message : "something went wrong"); g_clear_error (&err); } g_io_channel_unref (io_chan); } static gboolean get_descriptions (MuScriptInfo *msi, const char *prefix) { GIOStatus io_status; GIOChannel *script_io; GError *err; char *line, *descr, *oneline; if (!prefix) return TRUE; /* not an error */ if (!(script_io = open_channel (msi->_path))) return FALSE; err = NULL; line = descr = oneline = NULL; do { g_free (line); io_status = g_io_channel_read_line (script_io, &line, NULL, NULL, &err); if (io_status != G_IO_STATUS_NORMAL) break; if (!g_str_has_prefix (line, prefix)) continue; if (!oneline) oneline = g_strdup (line + strlen (prefix)); else { char *tmp; tmp = descr; descr = g_strdup_printf ("%s%s", descr ? descr : "", line + strlen(prefix)); g_free (tmp); } } while (TRUE); if (io_status != G_IO_STATUS_EOF) { g_warning ("error reading %s: %s", msi->_path, err ? err->message : "something went wrong"); g_clear_error (&err); } end_channel (script_io); msi->_oneline = oneline; msi->_descr = descr; return TRUE; } GSList* mu_script_get_script_info_list (const char *path, const char *ext, const char *descprefix, GError **err) { DIR *dir; GSList *lst; struct dirent *dentry; g_return_val_if_fail (path, NULL); dir = opendir (path); if (!dir) { mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_OPEN, "failed to open '%s': %s", path, strerror(errno)); return NULL; } /* create a list of names, paths */ lst = NULL; while ((dentry = readdir (dir))) { MuScriptInfo *msi; /* only consider files with certain extensions, * if ext != NULL */ if (ext && !g_str_has_suffix (dentry->d_name, ext)) continue; msi = script_info_new (); msi->_name = g_strdup (dentry->d_name); if (ext) /* strip the extension */ msi->_name[strlen(msi->_name) - strlen(ext)] = '\0'; msi->_path = g_strdup_printf ("%s%c%s", path, G_DIR_SEPARATOR, dentry->d_name); /* set the one-line and long description */ get_descriptions (msi, descprefix); lst = g_slist_prepend (lst, msi); } closedir (dir); /* ignore error checking... */ return g_slist_sort (lst, (GCompareFunc)script_info_cmp); } MuScriptInfo* mu_script_find_script_with_name (GSList *lst, const char *name) { GSList *cur; g_return_val_if_fail (name, NULL); for (cur = lst; cur; cur = g_slist_next (cur)) { MuScriptInfo *msi; msi = (MuScriptInfo*)cur->data; if (g_strcmp0 (name, mu_script_info_name (msi)) == 0) return msi; } return NULL; } #ifdef BUILD_GUILE static void guile_shell (void *closure, int argc, char **argv) { scm_shell (argc, argv); } gboolean mu_script_guile_run (MuScriptInfo *msi, const char *muhome, const char **args, GError **err) { char *mainargs, *expr; char *argv[] = { "guile", "-l", NULL, "-c", NULL, NULL }; g_return_val_if_fail (msi, FALSE); g_return_val_if_fail (muhome, FALSE); if (access (mu_script_info_path (msi), R_OK) != 0) { mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_READ, "failed to read script: %s", strerror(errno)); return FALSE; } argv[2] = (char*)mu_script_info_path (msi); mainargs = mu_str_quoted_from_strv (args); expr = g_strdup_printf ( "(main '(\"%s\" \"--muhome=%s\" %s))", mu_script_info_name (msi), muhome, mainargs ? mainargs : ""); g_free (mainargs); argv[4] = expr; scm_boot_guile (5, argv, guile_shell, NULL); /* never reached but let's be correct(TM)*/ g_free (expr); return TRUE; } #else /*!BUILD_GUILE*/ gboolean mu_script_guile_run (MuScriptInfo *msi, const char *muhome, const char **args, GError **err) { mu_util_g_set_error (err, MU_ERROR_INTERNAL, "this mu does not have guile support"); return FALSE; } #endif /*!BUILD_GUILE*/ ����������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-container.c������������������������������������������������������������������������0000644�0001750�0001750�00000033056�13020504331�012464� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #include <string.h> /* for memset */ #include <math.h> /* for log, ceil */ #include "mu-container.h" #include "mu-msg.h" #include "mu-msg-iter.h" /* * path data structure, to determine the thread paths mentioned above; * the path is filled as we're traversing the tree of MuContainers * (messages) */ struct _Path { int *_data; guint _len; }; typedef struct _Path Path; static Path* path_new (guint initial); static void path_destroy (Path *p); static void path_inc (Path *p, guint index); static gchar* path_to_string (Path *p, const char* frmt); MuContainer* mu_container_new (MuMsg *msg, guint docid, const char *msgid) { MuContainer *c; g_return_val_if_fail (!msg || docid != 0, NULL); c = g_slice_new0 (MuContainer); if (msg) c->msg = mu_msg_ref (msg); c->leader = c; c->docid = docid; c->msgid = msgid; return c; } void mu_container_destroy (MuContainer *c) { if (!c) return; if (c->msg) mu_msg_unref (c->msg); g_slice_free (MuContainer, c); } static void set_parent (MuContainer *c, MuContainer *parent) { while (c) { c->parent = parent; c = c->next; } } G_GNUC_UNUSED static gboolean check_dup (MuContainer *c, GHashTable *hash) { if (g_hash_table_lookup (hash, c)) { g_warning ("ALREADY!!"); mu_container_dump (c, TRUE); g_assert (0); } else g_hash_table_insert (hash, c, GUINT_TO_POINTER(TRUE)); return TRUE; } G_GNUC_UNUSED static void assert_no_duplicates (MuContainer *c) { GHashTable *hash; hash = g_hash_table_new (g_direct_hash, g_direct_equal); mu_container_foreach (c, (MuContainerForeachFunc)check_dup, hash); g_hash_table_destroy (hash); } MuContainer* mu_container_append_siblings (MuContainer *c, MuContainer *sibling) { g_assert (c); g_return_val_if_fail (c, NULL); g_return_val_if_fail (sibling, NULL); g_return_val_if_fail (c != sibling, NULL); /* assert_no_duplicates (c); */ set_parent (sibling, c->parent); /* find the last sibling and append; first we try our cache * 'last', otherwise we need to walk the chain. We use a * cached last as to avoid walking the chain (which is * O(n*n)) */ if (c->last) c->last->next = sibling; else { /* no 'last' cached, so walk the chain */ MuContainer *c2; for (c2 = c; c2 && c2->next; c2 = c2->next); c2->next = sibling; } /* update the cached last */ c->last = sibling->last ? sibling->last : sibling; /* assert_no_duplicates (c); */ return c; } MuContainer* mu_container_remove_sibling (MuContainer *c, MuContainer *sibling) { MuContainer *cur, *prev; g_return_val_if_fail (c, NULL); g_return_val_if_fail (sibling, NULL); for (prev = NULL, cur = c; cur; cur = cur->next) { if (cur == sibling) { if (!prev) c = cur->next; else prev->next = cur->next; break; } prev = cur; } /* unset the cached last; it's not valid anymore * * TODO: we could actually do a better job updating last * rather than invalidating it. */ if (c) c->last = NULL; return c; } MuContainer* mu_container_append_children (MuContainer *c, MuContainer *child) { g_return_val_if_fail (c, NULL); g_return_val_if_fail (child, NULL); g_return_val_if_fail (c != child, NULL); /* assert_no_duplicates (c); */ set_parent (child, c); if (!c->child) c->child = child; else c->child = mu_container_append_siblings (c->child, child); /* assert_no_duplicates (c->child); */ return c; } MuContainer* mu_container_remove_child (MuContainer *c, MuContainer *child) { g_return_val_if_fail (c, NULL); g_return_val_if_fail (child, NULL); /* g_assert (!child->child); */ /* g_return_val_if_fail (!child->child, NULL); */ g_return_val_if_fail (c != child, NULL); c->child = mu_container_remove_sibling (c->child, child); return c; } typedef void (*MuContainerPathForeachFunc) (MuContainer*, gpointer, Path*); static void mu_container_path_foreach_real (MuContainer *c, guint level, Path *path, MuContainerPathForeachFunc func, gpointer user_data) { if (!c) return; path_inc (path, level); func (c, user_data, path); /* children */ mu_container_path_foreach_real (c->child, level + 1, path, func, user_data); /* siblings */ mu_container_path_foreach_real (c->next, level, path, func, user_data); } static void mu_container_path_foreach (MuContainer *c, MuContainerPathForeachFunc func, gpointer user_data) { Path *path; path = path_new (100); mu_container_path_foreach_real (c, 0, path, func, user_data); path_destroy (path); } gboolean mu_container_foreach (MuContainer *c, MuContainerForeachFunc func, gpointer user_data) { g_return_val_if_fail (func, FALSE); if (!c) return TRUE; if (!mu_container_foreach (c->child, func, user_data)) return FALSE; /* recurse into children */ /* recurse into siblings */ if (!mu_container_foreach (c->next, func, user_data)) return FALSE; return func (c, user_data); } MuContainer* mu_container_splice_children (MuContainer *c, MuContainer *sibling) { MuContainer *children; g_return_val_if_fail (c, NULL); g_return_val_if_fail (sibling, NULL); children = sibling->child; sibling->child = NULL; return mu_container_append_siblings (c, children); } MuContainer* mu_container_splice_grandchildren (MuContainer *parent, MuContainer *child) { MuContainer *newchild; g_return_val_if_fail (parent, NULL); g_return_val_if_fail (child, NULL); g_return_val_if_fail (parent != child, NULL); newchild = child->child; child->child=NULL; return mu_container_append_children (parent, newchild); } static GSList* mu_container_to_list (MuContainer *c) { GSList *lst; for (lst = NULL; c; c = c->next) lst = g_slist_prepend (lst, c); return lst; } static gpointer list_last_data (GSList *lst) { GSList *tail; tail = g_slist_last (lst); return tail->data; } static MuContainer* mu_container_from_list (GSList *lst) { MuContainer *c, *cur, *tail; if (!lst) return NULL; tail = list_last_data (lst); for (c = cur = (MuContainer*)lst->data; cur; lst = g_slist_next(lst)) { cur->next = lst ? (MuContainer*)lst->data : NULL; cur->last = tail; cur=cur->next; } return c; } struct _SortFuncData { MuMsgFieldId mfid; gboolean descending; gpointer user_data; }; typedef struct _SortFuncData SortFuncData; static int container_cmp (MuContainer *a, MuContainer *b, MuMsgFieldId mfid) { if (a == b) return 0; else if (!a->msg) return -1; else if (!b->msg) return 1; return mu_msg_cmp (a->msg, b->msg, mfid); } static gboolean container_is_leaf (const MuContainer *c) { return c->child == NULL; } static MuContainer* container_max (MuContainer *a, MuContainer *b, MuMsgFieldId mfid) { return container_cmp (a, b, mfid) > 0 ? a : b; } static MuContainer* find_sorted_tree_leader (MuContainer *root, SortFuncData *order) { MuContainer *last_child; if (container_is_leaf (root)) return root; if (!order->descending) last_child = root->child->last; else /* reversed order, first is last */ last_child = root->child; return container_max (root, last_child->leader, order->mfid); } static int sort_func_wrapper (MuContainer *a, MuContainer *b, SortFuncData *data) { if (data->descending) return container_cmp (b->leader, a->leader, data->mfid); else return container_cmp (a->leader, b->leader, data->mfid); } static MuContainer* container_sort_real (MuContainer *c, SortFuncData *sfdata) { GSList *lst; MuContainer *cur; if (!c) return NULL; for (cur = c; cur; cur = cur->next) { if (cur->child) cur->child = container_sort_real (cur->child, sfdata); cur->leader = find_sorted_tree_leader (cur, sfdata); } /* sort siblings */ lst = mu_container_to_list (c); lst = g_slist_sort_with_data(lst, (GCompareDataFunc)sort_func_wrapper, sfdata); c = mu_container_from_list (lst); g_slist_free (lst); return c; } MuContainer* mu_container_sort (MuContainer *c, MuMsgFieldId mfid, gboolean descending, gpointer user_data) { SortFuncData sfdata; sfdata.mfid = mfid; sfdata.descending = descending; sfdata.user_data = user_data; g_return_val_if_fail (c, NULL); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), NULL); return container_sort_real (c, &sfdata); } static gboolean unequal (MuContainer *a, MuContainer *b) { return a == b ? FALSE : TRUE; } gboolean mu_container_reachable (MuContainer *haystack, MuContainer *needle) { g_return_val_if_fail (haystack, FALSE); g_return_val_if_fail (needle, FALSE); if (!mu_container_foreach (haystack, (MuContainerForeachFunc)unequal, needle)) return TRUE; return FALSE; } static gboolean dump_container (MuContainer *c) { const gchar* subject; if (!c) { g_print ("<empty>\n"); return TRUE; } subject = (c->msg) ? mu_msg_get_subject (c->msg) : "<none>"; g_print ("[%s][%s m:%p p:%p docid:%u %s]\n",c->msgid, subject, (void*)c, (void*)c->parent, c->docid, c->msg ? mu_msg_get_path (c->msg) : ""); return TRUE; } void mu_container_dump (MuContainer *c, gboolean recursive) { g_return_if_fail (c); if (!recursive) dump_container (c); else mu_container_foreach (c, (MuContainerForeachFunc)dump_container, NULL); } static Path* path_new (guint initial) { Path *p; p = g_slice_new0 (Path); p->_data = g_new0 (int, initial); p->_len = initial; return p; } static void path_destroy (Path *p) { if (!p) return; g_free (p->_data); g_slice_free (Path, p); } static void path_inc (Path *p, guint index) { if (index + 1 >= p->_len) { p->_data = g_renew (int, p->_data, 2 * p->_len); memset (&p->_data[p->_len], 0, p->_len); p->_len *= 2; } ++p->_data[index]; p->_data[index + 1] = 0; } static gchar* path_to_string (Path *p, const char* frmt) { char *str; guint u; if (!p->_data) return NULL; for (u = 0, str = NULL; p->_data[u] != 0; ++u) { char segm[16]; snprintf (segm, sizeof(segm), frmt, p->_data[u] - 1); if (!str) str = g_strdup (segm); else { gchar *tmp; tmp = g_strdup_printf ("%s:%s", str, segm); g_free (str); str = tmp; } } return str; } static unsigned count_colons (const char *str) { unsigned num; num = 0; while (str++ && *str) if (*str == ':') ++num; return num; } static MuMsgIterThreadInfo* thread_info_new (gchar *threadpath, gboolean root, gboolean first_child, gboolean empty_parent, gboolean has_child, gboolean is_dup) { MuMsgIterThreadInfo *ti; ti = g_slice_new (MuMsgIterThreadInfo); ti->threadpath = threadpath; ti->level = count_colons (threadpath); /* hacky... */ ti->prop = MU_MSG_ITER_THREAD_PROP_NONE; ti->prop |= root ? MU_MSG_ITER_THREAD_PROP_ROOT : 0; ti->prop |= first_child ? MU_MSG_ITER_THREAD_PROP_FIRST_CHILD : 0; ti->prop |= empty_parent ? MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT : 0; ti->prop |= is_dup ? MU_MSG_ITER_THREAD_PROP_DUP : 0; ti->prop |= has_child ? MU_MSG_ITER_THREAD_PROP_HAS_CHILD : 0; return ti; } static void thread_info_destroy (MuMsgIterThreadInfo *ti) { if (ti) { g_free (ti->threadpath); g_slice_free (MuMsgIterThreadInfo, ti); } } struct _ThreadInfo { GHashTable *hash; const char *format; }; typedef struct _ThreadInfo ThreadInfo; static void add_to_thread_info_hash (GHashTable *thread_info_hash, MuContainer *c, char *threadpath) { gboolean is_root, first_child, empty_parent, is_dup, has_child; /* 'root' means we're a child of the dummy root-container */ is_root = (c->parent == NULL); first_child = is_root ? FALSE : (c->parent->child == c); empty_parent = is_root ? FALSE : (!c->parent->msg); is_dup = c->flags & MU_CONTAINER_FLAG_DUP; has_child = c->child ? TRUE : FALSE; g_hash_table_insert (thread_info_hash, GUINT_TO_POINTER(c->docid), thread_info_new (threadpath, is_root, first_child, empty_parent, has_child, is_dup)); } /* device a format string that is the minimum size to fit up to * matchnum matches -- returns static memory */ static const char* thread_segment_format_string (size_t matchnum) { unsigned digitnum; static char frmt[16]; /* get the number of digits needed in a hex-representation of * matchnum */ digitnum = (unsigned) (ceil (log(matchnum)/log(16))); snprintf (frmt, sizeof(frmt), "%%0%ux", digitnum); return frmt; } static gboolean add_thread_info (MuContainer *c, ThreadInfo *ti, Path *path) { gchar *pathstr; pathstr = path_to_string (path, ti->format); add_to_thread_info_hash (ti->hash, c, pathstr); return TRUE; } GHashTable* mu_container_thread_info_hash_new (MuContainer *root_set, size_t matchnum) { ThreadInfo ti; g_return_val_if_fail (root_set, NULL); g_return_val_if_fail (matchnum > 0, NULL); /* create hash docid => thread-info */ ti.hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)thread_info_destroy); ti.format = thread_segment_format_string (matchnum); mu_container_path_foreach (root_set, (MuContainerPathForeachFunc)add_thread_info, &ti); return ti.hash; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/mu-contacts.h�������������������������������������������������������������������������0000644�0001750�0001750�00000007761�13020504331�012331� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #ifndef __MU_CONTACTS_H__ #define __MU_CONTACTS_H__ #include <glib.h> #include <time.h> G_BEGIN_DECLS struct _MuContacts; typedef struct _MuContacts MuContacts; /** * create a new MuContacts object; use mu_contacts_destroy when you no longer need it * * @param ccachefile full path to the file with cached list of contacts * * @return a new MuContacts* if succeeded, NULL otherwise */ MuContacts* mu_contacts_new (const gchar *ccachefile) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * add a contacts; if there's a contact with this e-mail address * already, it will not updated unless the timestamp of this one is * higher and has a non-empty name * * @param contacts a contacts object * @param email e-mail address of the contact (not NULL) * @param name name of the contact (or NULL) * @param personal whether the contact is 'personal' (ie., my address * appears in one of the address fields) * @param tstamp timestamp for this address * * @return TRUE if succeeded, FALSE otherwise */ gboolean mu_contacts_add (MuContacts *self, const char *email, const char* name, gboolean personal, time_t tstamp); /** * destroy the Contacts object * * @param contacts a contacts object */ void mu_contacts_destroy (MuContacts *self); /** * clear all contacts from the cache * * @param self a MuContacts instance */ void mu_contacts_clear (MuContacts *self); /** * get the path for the contacts cache file * * @param contacts a contacts object * * @return the path as a constant string (don't free), or NULL in case * of error */ const gchar* mu_contacts_get_path (MuContacts *self); /** * return the number of contacts * * @param self a contacts object * * @return the number of contacts */ size_t mu_contacts_count (MuContacts *self); /** * call called for mu_contacts_foreach; returns the e-mail address, * name (which may be NULL) , whether the message is 'personal', the * timestamp for the address (when it was last seen), and the * frequency (in how many message did this contact participate) * */ typedef void (*MuContactsForeachFunc) (const char *email, const char *name, gboolean personal, time_t tstamp, unsigned freq, gpointer user_data); /** * call a function for either each contact, or each contact satisfying * a regular expression, * * @param self contacts object * @param func callback function to be called for each * @param user_data user data to pass to the callback * @param pattern a regular expression which matches either the e-mail * or name, to filter out contacts, or NULL to not do any filtering. * @param num receives the number of contacts found, or NULL * * @return TRUE if the function succeeded, or FALSE if the provide * regular expression was invalid (and not NULL) */ gboolean mu_contacts_foreach (MuContacts *self, MuContactsForeachFunc func, gpointer user_data, const char* pattern, size_t *num); /** * serialize the contacts to the contacts cache file * * @param self contacts object * * @return TRUE if the function succeeded, FALSE otherwise * */ gboolean mu_contacts_serialize (MuContacts *self); G_END_DECLS #endif /*__MU_CONTACTS_H__*/ ���������������mu-0.9.18/lib/tests/��������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�011140� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/test-mu-msg.c�������������������������������������������������������������������0000644�0001750�0001750�00000032403�13020504332�013403� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <locale.h> #include "test-mu-common.h" #include "mu-msg.h" #include "mu-str.h" static MuMsg* get_msg (const char *path) { GError *err; MuMsg *msg; if (g_test_verbose ()) g_print (">> %s\n", path); err = NULL; msg = mu_msg_new_from_file (path, NULL, &err); if (!msg) { g_printerr ("failed to load %s: %s\n", path, err ? err->message : "something went wrong"); g_clear_error (&err); g_assert (0); } return msg; } static gboolean check_contact_01 (MuMsgContact *contact, int *idx) { switch (*idx) { case 0: g_assert_cmpstr (mu_msg_contact_name (contact), ==, "Mickey Mouse"); g_assert_cmpstr (mu_msg_contact_address (contact), ==, "anon@example.com"); break; case 1: g_assert_cmpstr (mu_msg_contact_name (contact), ==, "Donald Duck"); g_assert_cmpstr (mu_msg_contact_address (contact), ==, "gcc-help@gcc.gnu.org"); break; default: g_assert_not_reached (); } ++(*idx); return TRUE; } static void test_mu_msg_01 (void) { MuMsg *msg; gint i; msg = get_msg (MU_TESTMAILDIR4 "/1220863042.12663_1.mindcrime!2,S"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "Donald Duck <gcc-help@gcc.gnu.org>"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "gcc include search order"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "Mickey Mouse <anon@example.com>"); g_assert_cmpstr (mu_msg_get_msgid(msg), ==, "3BE9E6535E3029448670913581E7A1A20D852173@" "emss35m06.us.lmco.com"); g_assert_cmpstr (mu_msg_get_header(msg, "Mailing-List"), ==, "contact gcc-help-help@gcc.gnu.org; run by ezmlm"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'klub' */ ==, MU_MSG_PRIO_NORMAL); g_assert_cmpuint (mu_msg_get_date(msg), ==, 1217530645); i = 0; mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)check_contact_01, &i); g_assert_cmpint (i,==,2); mu_msg_unref (msg); } static gboolean check_contact_02 (MuMsgContact *contact, int *idx) { switch (*idx) { case 0: g_assert_cmpstr (mu_msg_contact_name (contact), ==, NULL); g_assert_cmpstr (mu_msg_contact_address (contact), ==, "anon@example.com"); break; case 1: g_assert_cmpstr (mu_msg_contact_name (contact), ==, NULL); g_assert_cmpstr (mu_msg_contact_address (contact), ==, "help-gnu-emacs@gnu.org"); break; default: g_assert_not_reached (); } ++(*idx); return TRUE; } static void test_mu_msg_02 (void) { MuMsg *msg; int i; msg = get_msg (MU_TESTMAILDIR4 "/1220863087.12663_19.mindcrime!2,S"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "help-gnu-emacs@gnu.org"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "Re: Learning LISP; Scheme vs elisp."); g_assert_cmpstr (mu_msg_get_from(msg), ==, "anon@example.com"); g_assert_cmpstr (mu_msg_get_msgid(msg), ==, "r6bpm5-6n6.ln1@news.ducksburg.com"); g_assert_cmpstr (mu_msg_get_header(msg, "Errors-To"), ==, "help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_LOW); g_assert_cmpuint (mu_msg_get_date(msg), ==, 1218051515); i = 0; mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)check_contact_02, &i); g_assert_cmpint (i,==,2); g_assert_cmpuint (mu_msg_get_flags(msg), ==, MU_FLAG_SEEN|MU_FLAG_LIST); mu_msg_unref (msg); } static void test_mu_msg_03 (void) { MuMsg *msg; msg = get_msg (MU_TESTMAILDIR4 "/1283599333.1840_11.cthulhu!2,"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "Bilbo Baggins <bilbo@anotherexample.com>"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "Greetings from Lothlórien"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "Frodo Baggins <frodo@example.com>"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_NORMAL); g_assert_cmpuint (mu_msg_get_date(msg), ==, 0); g_assert_cmpstr (mu_msg_get_body_text(msg, MU_MSG_OPTION_NONE), ==, "\nLet's write some fünkÿ text\nusing umlauts.\n\nFoo.\n"); g_assert_cmpuint (mu_msg_get_flags(msg), ==, MU_FLAG_UNREAD); /* not seen => unread */ mu_msg_unref (msg); } static void test_mu_msg_04 (void) { MuMsg *msg; msg = get_msg (MU_TESTMAILDIR4 "/mail5"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "George Custer <gac@example.com>"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "pics for you"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "Sitting Bull <sb@example.com>"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_NORMAL); g_assert_cmpuint (mu_msg_get_date(msg), ==, 0); g_assert_cmpuint (mu_msg_get_flags(msg), ==, MU_FLAG_HAS_ATTACH|MU_FLAG_UNREAD); mu_msg_unref (msg); } static void test_mu_msg_multimime (void) { MuMsg *msg; msg = get_msg (MU_TESTMAILDIR4 "/multimime!2,FS"); /* ie., are text parts properly concatenated? */ g_assert_cmpstr (mu_msg_get_subject(msg), ==, "multimime"); g_assert_cmpstr (mu_msg_get_body_text(msg, MU_MSG_OPTION_NONE), ==, "abcdef"); g_assert_cmpuint (mu_msg_get_flags(msg), ==, MU_FLAG_FLAGGED | MU_FLAG_SEEN | MU_FLAG_HAS_ATTACH); mu_msg_unref (msg); } static void test_mu_msg_flags (void) { unsigned u; struct { const char *path; MuFlags flags; } msgflags [] = { { MU_TESTMAILDIR4 "/multimime!2,FS", MU_FLAG_FLAGGED | MU_FLAG_SEEN | MU_FLAG_HAS_ATTACH }, { MU_TESTMAILDIR4 "/special!2,Sabc", MU_FLAG_SEEN } }; for (u = 0; u != G_N_ELEMENTS(msgflags); ++u) { MuMsg *msg; MuFlags flags; g_assert ((msg = get_msg (msgflags[u].path))); flags = mu_msg_get_flags (msg); if (g_test_verbose()) g_print ("=> %s [ %s, %u] <=> [ %s, %u]\n", msgflags[u].path, mu_flags_to_str_s(msgflags[u].flags, MU_FLAG_TYPE_ANY), (unsigned)msgflags[u].flags, mu_flags_to_str_s(flags, MU_FLAG_TYPE_ANY), (unsigned)flags); g_assert_cmpuint (flags ,==, msgflags[u].flags); mu_msg_unref (msg); } } static void test_mu_msg_umlaut (void) { MuMsg *msg; msg = get_msg (MU_TESTMAILDIR4 "/1305664394.2171_402.cthulhu!2,"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "Helmut Kröger <hk@testmu.xxx>"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "Motörhead"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "Mü <testmu@testmu.xx>"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_NORMAL); g_assert_cmpuint (mu_msg_get_date(msg), ==, 0); mu_msg_unref (msg); } static void test_mu_msg_references (void) { MuMsg *msg; const GSList *refs; msg = get_msg (MU_TESTMAILDIR4 "/1305664394.2171_402.cthulhu!2,"); refs = mu_msg_get_references(msg); g_assert_cmpuint (g_slist_length ((GSList*)refs), ==, 4); g_assert_cmpstr ((char*)refs->data,==, "non-exist-01@msg.id"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "non-exist-02@msg.id"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "non-exist-03@msg.id"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "non-exist-04@msg.id"); refs = g_slist_next (refs); mu_msg_unref (msg); } static void test_mu_msg_references_dups (void) { MuMsg *msg; const GSList *refs; const char *mlist; msg = get_msg (MU_TESTMAILDIR4 "/1252168370_3.14675.cthulhu!2,S"); refs = mu_msg_get_references(msg); /* make sure duplicate msg-ids are filtered out */ g_assert_cmpuint (g_slist_length ((GSList*)refs), ==, 6); g_assert_cmpstr ((char*)refs->data,==, "439C1136.90504@euler.org"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "4399DD94.5070309@euler.org"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "20051209233303.GA13812@gauss.org"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "439B41ED.2080402@euler.org"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "439A1E03.3090604@euler.org"); refs = g_slist_next (refs); g_assert_cmpstr ((char*)refs->data,==, "20051211184308.GB13513@gauss.org"); refs = g_slist_next (refs); mlist = mu_msg_get_mailing_list (msg); g_assert_cmpstr (mlist ,==, "Example of List Id"); mu_msg_unref (msg); } static void test_mu_msg_references_many (void) { MuMsg *msg; unsigned u; const GSList *refs, *cur; const char* expt_refs[] = { "e9065dac-13c1-4103-9e31-6974ca232a89@t15g2000prt.googlegroups.com", "87hbblwelr.fsf@sapphire.mobileactivedefense.com", "pql248-4va.ln1@wilbur.25thandClement.com", "ikns6r$li3$1@Iltempo.Update.UU.SE", "8762s0jreh.fsf@sapphire.mobileactivedefense.com", "ikqqp1$jv0$1@Iltempo.Update.UU.SE", "87hbbjc5jt.fsf@sapphire.mobileactivedefense.com", "ikr0na$lru$1@Iltempo.Update.UU.SE", "tO8cp.1228$GE6.370@news.usenetserver.com", "ikr6ks$nlf$1@Iltempo.Update.UU.SE", "8ioh48-8mu.ln1@leafnode-msgid.gclare.org.uk" }; msg = get_msg (MU_TESTMAILDIR2 "/bar/cur/181736.eml"); refs = mu_msg_get_references(msg); g_assert_cmpuint (G_N_ELEMENTS(expt_refs), ==, g_slist_length((GSList*)refs)); for (cur = refs, u = 0; cur; cur = g_slist_next(cur), ++u) { if (g_test_verbose()) g_print ("%u. '%s' =? '%s'\n", u, (char*)cur->data, expt_refs[u]); g_assert_cmpstr ((char*)cur->data, ==, expt_refs[u]); } mu_msg_unref (msg); } static void test_mu_msg_tags (void) { MuMsg *msg; const GSList *tags; msg = get_msg (MU_TESTMAILDIR4 "/mail1"); g_assert_cmpstr (mu_msg_get_to(msg), ==, "Julius Caesar <jc@example.com>"); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "Fere libenter homines id quod volunt credunt"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "John Milton <jm@example.com>"); g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_HIGH); g_assert_cmpuint (mu_msg_get_date(msg), ==, 1217530645); tags = mu_msg_get_tags (msg); g_assert_cmpstr ((char*)tags->data,==,"Paradise"); g_assert_cmpstr ((char*)tags->next->data,==,"losT"); g_assert_cmpstr ((char*)tags->next->next->data,==,"john"); g_assert_cmpstr ((char*)tags->next->next->next->data,==,"milton"); g_assert (!tags->next->next->next->next); mu_msg_unref (msg); } static void test_mu_msg_comp_unix_programmer (void) { MuMsg *msg; char *refs; msg = get_msg (MU_TESTMAILDIR4 "/181736.eml"); g_assert_cmpstr (mu_msg_get_to(msg), ==, NULL); g_assert_cmpstr (mu_msg_get_subject(msg), ==, "Re: Are writes \"atomic\" to readers of the file?"); g_assert_cmpstr (mu_msg_get_from(msg), ==, "Jimbo Foobarcuux <jimbo@slp53.sl.home>"); g_assert_cmpstr (mu_msg_get_msgid(msg), ==, "oktdp.42997$Te.22361@news.usenetserver.com"); refs = mu_str_from_list (mu_msg_get_references(msg), ','); g_assert_cmpstr (refs, ==, "e9065dac-13c1-4103-9e31-6974ca232a89@t15g2000prt" ".googlegroups.com," "87hbblwelr.fsf@sapphire.mobileactivedefense.com," "pql248-4va.ln1@wilbur.25thandClement.com," "ikns6r$li3$1@Iltempo.Update.UU.SE," "8762s0jreh.fsf@sapphire.mobileactivedefense.com," "ikqqp1$jv0$1@Iltempo.Update.UU.SE," "87hbbjc5jt.fsf@sapphire.mobileactivedefense.com," "ikr0na$lru$1@Iltempo.Update.UU.SE," "tO8cp.1228$GE6.370@news.usenetserver.com," "ikr6ks$nlf$1@Iltempo.Update.UU.SE," "8ioh48-8mu.ln1@leafnode-msgid.gclare.org.uk"); g_free (refs); //"jimbo@slp53.sl.home (Jimbo Foobarcuux)"; g_assert_cmpuint (mu_msg_get_prio(msg), /* 'low' */ ==, MU_MSG_PRIO_NORMAL); g_assert_cmpuint (mu_msg_get_date(msg), ==, 1299603860); mu_msg_unref (msg); } int main (int argc, char *argv[]) { int rv; g_test_init (&argc, &argv, NULL); /* mu_msg_str_date */ g_test_add_func ("/mu-msg/mu-msg-01", test_mu_msg_01); g_test_add_func ("/mu-msg/mu-msg-02", test_mu_msg_02); g_test_add_func ("/mu-msg/mu-msg-03", test_mu_msg_03); g_test_add_func ("/mu-msg/mu-msg-04", test_mu_msg_04); g_test_add_func ("/mu-msg/mu-msg-multimime", test_mu_msg_multimime); g_test_add_func ("/mu-msg/mu-msg-flags", test_mu_msg_flags); g_test_add_func ("/mu-msg/mu-msg-tags", test_mu_msg_tags); g_test_add_func ("/mu-msg/mu-msg-references", test_mu_msg_references); g_test_add_func ("/mu-msg/mu-msg-references_dups", test_mu_msg_references_dups); g_test_add_func ("/mu-msg/mu-msg-references_many", test_mu_msg_references_many); g_test_add_func ("/mu-msg/mu-msg-umlaut", test_mu_msg_umlaut); g_test_add_func ("/mu-msg/mu-msg-comp-unix-programmer", test_mu_msg_comp_unix_programmer); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); rv = g_test_run (); return rv; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/�����������������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�012701� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/������������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�013640� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/new/��������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�014431� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/new/.noindex������������������������������������������������������0000644�0001750�0001750�00000000000�12605152237�016012� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/��������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�014431� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/root2���������������������������������������������������������0000644�0001750�0001750�00000000205�12605152237�015344� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: root2 Message-Id: <root2@msg.id> Date: Tue, 21 Jun 2011 13:00 +0000 abc �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/root0���������������������������������������������������������0000644�0001750�0001750�00000000205�12605152237�015342� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: root0 Message-Id: <root0@msg.id> Date: Tue, 21 Jun 2011 11:00 +0000 abc �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/root1���������������������������������������������������������0000644�0001750�0001750�00000000205�12605152237�015343� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: root1 Message-Id: <root1@msg.id> Date: Tue, 21 Jun 2011 12:00 +0000 abc �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/child0.0������������������������������������������������������0000644�0001750�0001750�00000000310�12605152237�015575� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: Re: child 0.0 Message-Id: <child0.0@msg.id> References: <root0@msg.id> In-reply-to: <root0@msg.id> Date: Tue, 21 Jun 2011 11:10 +0000 abc ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/child4.1������������������������������������������������������0000644�0001750�0001750�00000000333�12605152237�015607� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: Re: child 4.1 Message-Id: <child4.1@msg.id> References: <non-exist-root4@msg.id> In-reply-to: <non-exist-root4@msg.id> Date: Tue, 24 Jun 2011 11:20 +0000 abc �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/child4.0������������������������������������������������������0000644�0001750�0001750�00000000334�12605152237�015607� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: Re: child 4.0 Message-Id: <child4.0@msg.id> References: <non-exist-root4@msg.id> In-reply-to: <non-exist-root4@msg.id> Date: Tue, 24 Jun 2011 11:10 +0000 abc ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/child0.1������������������������������������������������������0000644�0001750�0001750�00000000310�12605152237�015576� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: Re: child 0.1 Message-Id: <child0.1@msg.id> References: <root0@msg.id> In-reply-to: <root0@msg.id> Date: Tue, 21 Jun 2011 11:20 +0000 abc ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/child3.0.0.0.0������������������������������������������������0000644�0001750�0001750�00000000442�12605152237�016240� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: Re: child 3.0.0.0 Message-Id: <child3.0.0.0.0@msg.id> References: <non-exist-01@msg.id> <non-exist-02@msg.id> <non-exist-03@msg.id> <non-exist-04@msg.id> 1n-Reply-To: <non-exist-04@msg.id> Date: Wed, 22 Jun 2011 16:33 +0000 abc ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/child0.1.0����������������������������������������������������0000644�0001750�0001750�00000000340�12605152237�015737� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: Re: child 0.1.0 Message-Id: <child0.1.0@msg.id> References: <root0@msg.id> <child0.1@msg.id> In-Reply-To: <child0.1@msg.id> Date: Tue, 21 Jun 2011 11:22 +0000 abc ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/cur/child2.0.0����������������������������������������������������0000644�0001750�0001750�00000000507�12605152237�015745� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: Re: child 2.0.0 Message-Id: <child2.0.0@msg.id> References: <root2@msg.id> <nonexistant@msg.id> In-Reply-To: <nonexistant@msg.id> Date: Tue, 21 Jun 2011 15:02 +0000 abc note, there's no message for 'nonexistant@msg.id', so this msg should be promoted to level 2.0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/tmp/��������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�014440� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/tree/tmp/.noindex������������������������������������������������������0000644�0001750�0001750�00000000000�12605152237�016021� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/������������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�013670� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/child-does-not-promote-thread/������������������������������������0000755�0001750�0001750�00000000000�13021065721�021431� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/��������������������������������0000755�0001750�0001750�00000000000�13021065721�022222� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/X�������������������������������0000644�0001750�0001750�00000000175�13020504332�022272� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: X Message-Id: <X@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 X ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Y�������������������������������0000644�0001750�0001750�00000000175�13020504332�022273� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: Y Message-Id: <Y@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 Y ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/A�������������������������������0000644�0001750�0001750�00000000254�13020504332�022241� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: A Message-Id: <A@msg.id> References: <Y@msg.id> In-reply-to: <Y@msg.id> Aate: Sat, 17 May 2014 10:00:00 +0000 A ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Z�������������������������������0000644�0001750�0001750�00000000175�13020504332�022274� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: Z Message-Id: <Z@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 Z ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/2nd-child-promotes-thread/����������������������������������������0000755�0001750�0001750�00000000000�13021065721�020547� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/������������������������������������0000755�0001750�0001750�00000000000�13021065721�021340� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/E�����������������������������������0000644�0001750�0001750�00000000254�13020504332�021363� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: E Message-Id: <E@msg.id> References: <B@msg.id> In-reply-to: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 E ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/C�����������������������������������0000644�0001750�0001750�00000000254�13020504332�021361� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: C Message-Id: <C@msg.id> References: <B@msg.id> In-reply-to: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 C ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/D�����������������������������������0000644�0001750�0001750�00000000175�13020504332�021364� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: D Message-Id: <D@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 D ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/B�����������������������������������0000644�0001750�0001750�00000000175�13020504332�021362� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: B Message-Id: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 B ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/A�����������������������������������0000644�0001750�0001750�00000000175�13020504332�021361� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: A Message-Id: <A@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 A ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/�������������������������������0000755�0001750�0001750�00000000000�13021065721�022573� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/���������������������������0000755�0001750�0001750�00000000000�13021065721�023364� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/E��������������������������0000644�0001750�0001750�00000000254�13020504332�023407� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: E Message-Id: <E@msg.id> References: <B@msg.id> In-reply-to: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 E ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/C��������������������������0000644�0001750�0001750�00000000254�13020504332�023405� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: C Message-Id: <C@msg.id> References: <B@msg.id> In-reply-to: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 C ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/D��������������������������0000644�0001750�0001750�00000000254�13020504332�023406� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: D Message-Id: <D@msg.id> References: <B@msg.id> In-reply-to: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 D ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/B��������������������������0000644�0001750�0001750�00000000175�13020504332�023406� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: B Message-Id: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 B ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/F��������������������������0000644�0001750�0001750�00000000267�13020504332�023414� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: F Message-Id: <F@msg.id> References: <B@msg.id> <D@msg.id> In-reply-to: <D@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 F �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/A��������������������������0000644�0001750�0001750�00000000175�13020504332�023405� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: A Message-Id: <A@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 A ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/G��������������������������0000644�0001750�0001750�00000000175�13020504332�023413� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: G Message-Id: <G@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 G ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/1st-child-promotes-thread/����������������������������������������0000755�0001750�0001750�00000000000�13021065721�020573� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/������������������������������������0000755�0001750�0001750�00000000000�13021065721�021364� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/C�����������������������������������0000644�0001750�0001750�00000000175�13020504332�021407� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: C Message-Id: <C@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 C ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/D�����������������������������������0000644�0001750�0001750�00000000254�13020504332�021406� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: D Message-Id: <D@msg.id> References: <B@msg.id> In-reply-to: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 D ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/B�����������������������������������0000644�0001750�0001750�00000000175�13020504332�021406� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: B Message-Id: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 B ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/A�����������������������������������0000644�0001750�0001750�00000000175�13020504332�021405� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: A Message-Id: <A@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 A ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-thread/���������������������������������������0000755�0001750�0001750�00000000000�13021065721�021102� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/�����������������������������������0000755�0001750�0001750�00000000000�13021065721�021673� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/E����������������������������������0000644�0001750�0001750�00000000267�13020504332�021722� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: E Message-Id: <E@msg.id> References: <B@msg.id> <C@msg.id> In-reply-to: <C@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 E �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/C����������������������������������0000644�0001750�0001750�00000000254�13020504332�021714� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: C Message-Id: <C@msg.id> References: <B@msg.id> In-reply-to: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 C ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/D����������������������������������0000644�0001750�0001750�00000000175�13020504332�021717� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: D Message-Id: <D@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 D ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/B����������������������������������0000644�0001750�0001750�00000000175�13020504332�021715� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: B Message-Id: <B@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 B ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/A����������������������������������0000644�0001750�0001750�00000000175�13020504332�021714� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: testfrom@example.com To: testto@example.com Subject: A Message-Id: <A@msg.id> Date: Sat, 17 May 2014 10:00:00 +0000 A ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/cycle/�����������������������������������������������������������������0000755�0001750�0001750�00000000000�12605152237�014006� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/cycle/new/�������������������������������������������������������������0000755�0001750�0001750�00000000000�12605152237�014577� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/cycle/new/.noindex�����������������������������������������������������0000644�0001750�0001750�00000000000�12605152237�016152� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/cycle/cur/�������������������������������������������������������������0000755�0001750�0001750�00000000000�12605152237�014577� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/cycle/cur/cycle0.0.0���������������������������������������������������0000644�0001750�0001750�00000000266�12605152237�016121� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: foo@example.com To: bar@example.com Subject: cycle0.0.0 Message-Id: <cycle0.0.0@msg.id> References: <cycle0@msg.id> <cycle0.0@msg.id> Date: Tue, 21 Jun 2011 13:00 +0000 def ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/cycle/cur/cycle0.0�����������������������������������������������������0000644�0001750�0001750�00000000240�12605152237�015753� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: foo@example.com To: bar@example.com Subject: cycle0.0 Message-Id: <cycle0.0@msg.id> References: <cycle0@msg.id> Date: Tue, 21 Jun 2011 12:00 +0000 def ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/cycle/cur/rogue0�������������������������������������������������������0000644�0001750�0001750�00000000257�12605152237�015647� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: foo@example.com To: bar@example.com Subject: rogue0 Message-Id: <rogue0@msg.id> References: <cycle0.0@msg.id> <cycle0@msg.id> Date: Tue, 21 Jun 2011 15:00 +0000 def �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/cycle/cur/cycle0�������������������������������������������������������0000644�0001750�0001750�00000000200�12605152237�015611� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: foo@example.com To: bar@example.com Subject: cycle0 Message-Id: <cycle0@msg.id> Date: Tue, 21 Jun 2011 11:00 +0000 def ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/cycle/tmp/�������������������������������������������������������������0000755�0001750�0001750�00000000000�12605152237�014606� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir3/cycle/tmp/.noindex�����������������������������������������������������0000644�0001750�0001750�00000000000�12605152237�016161� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/test-mu-date.c������������������������������������������������������������������0000644�0001750�0001750�00000012315�12605152237�013545� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <locale.h> #include "test-mu-common.h" #include "mu-date.h" static void test_mu_date_str (void) { struct tm *tmbuf; char buf[64]; gchar *tmp; time_t some_time; some_time = 1234567890; tmbuf = localtime (&some_time); strftime (buf, 64, "%x", tmbuf); /* $ date -ud@1234567890; Fri Feb 13 23:31:30 UTC 2009 */ g_assert_cmpstr (mu_date_str_s ("%x", some_time), ==, buf); /* date -ud@987654321 Thu Apr 19 04:25:21 UTC 2001 */ some_time = 987654321; tmbuf = localtime (&some_time); strftime (buf, 64, "%c", tmbuf); tmp = mu_date_str ("%c", some_time); g_assert_cmpstr (tmp, ==, buf); g_free (tmp); } static void test_mu_date_parse_hdwmy (void) { time_t diff; diff = time(NULL) - mu_date_parse_hdwmy ("3h"); g_assert (diff > 0); g_assert_cmpuint (3 * 60 * 60 - diff, <=, 1); diff = time(NULL) - mu_date_parse_hdwmy ("5y"); g_assert (diff > 0); g_assert_cmpuint (5 * 365 * 24 * 60 * 60 - diff, <=, 1); diff = time(NULL) - mu_date_parse_hdwmy ("3m"); g_assert (diff > 0); g_assert_cmpuint (3 * 30 * 24 * 60 * 60 - diff, <=, 1); diff = time(NULL) - mu_date_parse_hdwmy ("21d"); g_assert (diff > 0); g_assert_cmpuint (21 * 24 * 60 * 60 - diff, <=, 1); diff = time(NULL) - mu_date_parse_hdwmy ("2w"); g_assert (diff > 0); g_assert_cmpuint (2 * 7 * 24 * 60 * 60 - diff, <=, 1); g_assert_cmpint (mu_date_parse_hdwmy("-1y"),==, (time_t)-1); } static void test_mu_date_complete_begin (void) { g_assert_cmpstr (mu_date_complete_s("2000", TRUE), ==, "20000101000000"); g_assert_cmpstr (mu_date_complete_s("2", TRUE), ==, "20000101000000"); g_assert_cmpstr (mu_date_complete_s ("", TRUE), ==, "00000101000000"); g_assert_cmpstr (mu_date_complete_s ("201007", TRUE), ==, "20100701000000"); g_assert_cmpstr (mu_date_complete_s ("19721214", TRUE), ==, "19721214000000"); g_assert_cmpstr (mu_date_complete_s ("19721214234512", TRUE), ==, "19721214234512"); g_assert_cmpstr (mu_date_complete_s ("2010-07", TRUE), ==, "20100701000000"); g_assert_cmpstr (mu_date_complete_s ("1972/12/14", TRUE), ==, "19721214000000"); g_assert_cmpstr (mu_date_complete_s ("1972-12-14 23:45:12", TRUE), ==, "19721214234512"); } static void test_mu_date_complete_end (void) { g_assert_cmpstr (mu_date_complete_s ("2000", FALSE), ==, "20001231235959"); g_assert_cmpstr (mu_date_complete_s ("2", FALSE), ==, "29991231235959"); g_assert_cmpstr (mu_date_complete_s ("", FALSE), ==, "99991231235959"); g_assert_cmpstr (mu_date_complete_s ("201007", FALSE), ==, "20100731235959"); g_assert_cmpstr (mu_date_complete_s ("19721214", FALSE), ==, "19721214235959"); g_assert_cmpstr (mu_date_complete_s ("19721214234512", FALSE), ==, "19721214234512"); g_assert_cmpstr (mu_date_complete_s ("2010-07", FALSE), ==, "20100731235959"); g_assert_cmpstr (mu_date_complete_s ("1972.12.14", FALSE), ==, "19721214235959"); g_assert_cmpstr (mu_date_complete_s ("1972.12.14 23:45/12", FALSE), ==, "19721214234512"); } static void test_mu_date_interpret_begin (void) { time_t now; now = time (NULL); g_assert_cmpstr (mu_date_interpret_s ("now", TRUE) , ==, mu_date_str_s("%Y%m%d%H%M%S", now)); g_assert_cmpstr (mu_date_interpret_s ("today", TRUE) , ==, mu_date_str_s("%Y%m%d000000", now)); } static void test_mu_date_interpret_end (void) { time_t now; now = time (NULL); g_assert_cmpstr (mu_date_interpret_s ("now", FALSE) , ==, mu_date_str_s("%Y%m%d%H%M%S", now)); g_assert_cmpstr (mu_date_interpret_s ("today", FALSE) , ==, mu_date_str_s("%Y%m%d235959", now)); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_str_date */ g_test_add_func ("/mu-str/mu-date-str", test_mu_date_str); g_test_add_func ("/mu-str/mu_date_parse_hdwmy", test_mu_date_parse_hdwmy); g_test_add_func ("/mu-str/mu_date_complete_begin", test_mu_date_complete_begin); g_test_add_func ("/mu-str/mu_date_complete_end", test_mu_date_complete_end); g_test_add_func ("/mu-str/mu_date_interpret_begin", test_mu_date_interpret_begin); g_test_add_func ("/mu-str/mu_date_interpret_end", test_mu_date_interpret_end); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/Makefile.in���������������������������������������������������������������������0000644�0001750�0001750�00000077750�13021065705�013147� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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 = : build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = $(am__EXEEXT_1) subdir = lib/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/perlmod.m4 $(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 = LTLIBRARIES = $(noinst_LTLIBRARIES) libtestmucommon_la_DEPENDENCIES = ../libmu.la am_libtestmucommon_la_OBJECTS = test-mu-common.lo libtestmucommon_la_OBJECTS = $(am_libtestmucommon_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am__EXEEXT_1 = test-mu-util$(EXEEXT) test-mu-str$(EXEEXT) \ test-mu-maildir$(EXEEXT) test-mu-msg-fields$(EXEEXT) \ test-mu-msg$(EXEEXT) test-mu-store$(EXEEXT) \ test-mu-date$(EXEEXT) test-mu-flags$(EXEEXT) \ test-mu-container$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) am_test_mu_container_OBJECTS = test-mu-container.$(OBJEXT) \ dummy.$(OBJEXT) test_mu_container_OBJECTS = $(am_test_mu_container_OBJECTS) test_mu_container_DEPENDENCIES = libtestmucommon.la am_test_mu_date_OBJECTS = test-mu-date.$(OBJEXT) dummy.$(OBJEXT) test_mu_date_OBJECTS = $(am_test_mu_date_OBJECTS) test_mu_date_DEPENDENCIES = libtestmucommon.la am_test_mu_flags_OBJECTS = test-mu-flags.$(OBJEXT) dummy.$(OBJEXT) test_mu_flags_OBJECTS = $(am_test_mu_flags_OBJECTS) test_mu_flags_DEPENDENCIES = libtestmucommon.la am_test_mu_maildir_OBJECTS = test-mu-maildir.$(OBJEXT) dummy.$(OBJEXT) test_mu_maildir_OBJECTS = $(am_test_mu_maildir_OBJECTS) test_mu_maildir_DEPENDENCIES = libtestmucommon.la am_test_mu_msg_OBJECTS = test-mu-msg.$(OBJEXT) dummy.$(OBJEXT) test_mu_msg_OBJECTS = $(am_test_mu_msg_OBJECTS) test_mu_msg_DEPENDENCIES = libtestmucommon.la am_test_mu_msg_fields_OBJECTS = test-mu-msg-fields.$(OBJEXT) \ dummy.$(OBJEXT) test_mu_msg_fields_OBJECTS = $(am_test_mu_msg_fields_OBJECTS) test_mu_msg_fields_DEPENDENCIES = libtestmucommon.la am_test_mu_store_OBJECTS = test-mu-store.$(OBJEXT) dummy.$(OBJEXT) test_mu_store_OBJECTS = $(am_test_mu_store_OBJECTS) test_mu_store_DEPENDENCIES = libtestmucommon.la am_test_mu_str_OBJECTS = test-mu-str.$(OBJEXT) dummy.$(OBJEXT) test_mu_str_OBJECTS = $(am_test_mu_str_OBJECTS) test_mu_str_DEPENDENCIES = libtestmucommon.la am_test_mu_util_OBJECTS = test-mu-util.$(OBJEXT) dummy.$(OBJEXT) test_mu_util_OBJECTS = $(am_test_mu_util_OBJECTS) test_mu_util_DEPENDENCIES = libtestmucommon.la 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__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=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 = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=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 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libtestmucommon_la_SOURCES) $(test_mu_container_SOURCES) \ $(test_mu_date_SOURCES) $(test_mu_flags_SOURCES) \ $(test_mu_maildir_SOURCES) $(test_mu_msg_SOURCES) \ $(test_mu_msg_fields_SOURCES) $(test_mu_store_SOURCES) \ $(test_mu_str_SOURCES) $(test_mu_util_SOURCES) DIST_SOURCES = $(libtestmucommon_la_SOURCES) \ $(test_mu_container_SOURCES) $(test_mu_date_SOURCES) \ $(test_mu_flags_SOURCES) $(test_mu_maildir_SOURCES) \ $(test_mu_msg_SOURCES) $(test_mu_msg_fields_SOURCES) \ $(test_mu_store_SOURCES) $(test_mu_str_SOURCES) \ $(test_mu_util_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac 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 \ $(top_srcdir)/gtest.mk DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMACS = @EMACS@ EMACSLOADPATH = @EMACSLOADPATH@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GMIME_CFLAGS = @GMIME_CFLAGS@ GMIME_LIBS = @GMIME_LIBS@ GREP = @GREP@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ GUILE_BINARY = @GUILE_BINARY@ GUILE_CFLAGS = @GUILE_CFLAGS@ GUILE_LIBS = @GUILE_LIBS@ GUILE_SITEDIR = @GUILE_SITEDIR@ GUILE_SNARF = @GUILE_SNARF@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MU_DOC_DIR = @MU_DOC_DIR@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PMCCABE = @PMCCABE@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SORT = @SORT@ STRIP = @STRIP@ VERSION = @VERSION@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ XAPIAN_CONFIG = @XAPIAN_CONFIG@ XAPIAN_CXXFLAGS = @XAPIAN_CXXFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ 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 = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_makeinfo = @have_makeinfo@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ lispdir = @lispdir@ 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@ TEST_PROGS = test-mu-util test-mu-str test-mu-maildir \ test-mu-msg-fields test-mu-msg test-mu-store test-mu-date \ test-mu-flags test-mu-container AM_CPPFLAGS = $(XAPIAN_CXXFLAGS) \ $(GMIME_CFLAGS) \ $(GLIB_CFLAGS) \ -I ${top_srcdir} \ -I ${top_srcdir}/lib \ -DMU_TESTMAILDIR=\"${abs_srcdir}/testdir\" \ -DMU_TESTMAILDIR2=\"${abs_srcdir}/testdir2\" \ -DMU_TESTMAILDIR3=\"${abs_srcdir}/testdir3\" \ -DMU_TESTMAILDIR4=\"${abs_srcdir}/testdir4\" \ -DABS_CURDIR=\"${abs_builddir}\" \ -DABS_SRCDIR=\"${abs_srcdir}\" # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS = -Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement AM_CXXFLAGS = -Wall -Wextra -Wno-unused-parameter noinst_LTLIBRARIES = libtestmucommon.la test_mu_util_SOURCES = test-mu-util.c dummy.cc test_mu_util_LDADD = libtestmucommon.la test_mu_str_SOURCES = test-mu-str.c dummy.cc test_mu_str_LDADD = libtestmucommon.la test_mu_maildir_SOURCES = test-mu-maildir.c dummy.cc test_mu_maildir_LDADD = libtestmucommon.la test_mu_msg_fields_SOURCES = test-mu-msg-fields.c dummy.cc test_mu_msg_fields_LDADD = libtestmucommon.la test_mu_msg_SOURCES = test-mu-msg.c dummy.cc test_mu_msg_LDADD = libtestmucommon.la test_mu_store_SOURCES = test-mu-store.c dummy.cc test_mu_store_LDADD = libtestmucommon.la test_mu_date_SOURCES = test-mu-date.c dummy.cc test_mu_date_LDADD = libtestmucommon.la test_mu_flags_SOURCES = test-mu-flags.c dummy.cc test_mu_flags_LDADD = libtestmucommon.la test_mu_container_SOURCES = test-mu-container.c dummy.cc test_mu_container_LDADD = libtestmucommon.la # we need to use dummy.cc to enforce c++ linking... BUILT_SOURCES = \ dummy.cc libtestmucommon_la_SOURCES = \ test-mu-common.c \ test-mu-common.h libtestmucommon_la_LIBADD = ../libmu.la # note the question marks; make does not like files with ':', so we # use the (also supported) version with '!' instead. We could escape # the : with \: but automake does not recognize that.... # test messages, the '.ignore' message should be ignored # when indexing EXTRA_DIST = \ testdir/tmp/1220863087.12663.ignore \ testdir/new/1220863087.12663_9.mindcrime \ testdir/new/1220863087.12663_25.mindcrime \ testdir/new/1220863087.12663_21.mindcrime \ testdir/new/1220863087.12663_23.mindcrime \ testdir/cur/1220863087.12663_5.mindcrime!2,S \ testdir/cur/1220863087.12663_7.mindcrime!2,RS \ testdir/cur/1220863087.12663_15.mindcrime!2,PS \ testdir/cur/1220863087.12663_19.mindcrime!2,S \ testdir/cur/1220863042.12663_1.mindcrime!2,S \ testdir/cur/1220863060.12663_3.mindcrime!2,S \ testdir/cur/1283599333.1840_11.cthulhu!2, \ testdir/cur/1305664394.2171_402.cthulhu!2, \ testdir/cur/1252168370_3.14675.cthulhu!2,S \ testdir/cur/encrypted!2,S \ testdir/cur/multimime!2,FS \ testdir/cur/signed!2,S \ testdir/cur/signed-encrypted!2,S \ testdir/cur/special!2,Sabc \ testdir/cur/multirecip!2,S \ testdir2/bar/cur/mail1 \ testdir2/bar/cur/mail2 \ testdir2/bar/cur/mail3 \ testdir2/bar/cur/mail4 \ testdir2/bar/cur/mail5 \ testdir2/bar/cur/181736.eml \ testdir2/bar/cur/mail6 \ testdir2/bar/tmp/.noindex \ testdir2/bar/new/.noindex \ testdir2/Foo/cur/mail5 \ testdir2/Foo/cur/arto.eml \ testdir2/Foo/cur/fraiche.eml \ testdir2/Foo/tmp/.noindex \ testdir2/Foo/new/.noindex \ testdir2/wom_bat/cur/atomic \ testdir2/wom_bat/cur/rfc822.1 \ testdir2/wom_bat/cur/rfc822.2 \ testdir3/cycle \ testdir3/cycle/new/.noindex \ testdir3/cycle/cur/rogue0 \ testdir3/cycle/cur/cycle0 \ testdir3/cycle/cur/cycle0.0 \ testdir3/cycle/cur/cycle0.0.0 \ testdir3/cycle/tmp/.noindex \ testdir3/tree/new/.noindex \ testdir3/tree/cur/child0.0 \ testdir3/tree/cur/child4.0 \ testdir3/tree/cur/root2 \ testdir3/tree/cur/root1 \ testdir3/tree/cur/child3.0.0.0.0 \ testdir3/tree/cur/root0 \ testdir3/tree/cur/child2.0.0 \ testdir3/tree/cur/child0.1 \ testdir3/tree/cur/child0.1.0 \ testdir3/tree/cur/child4.1 \ testdir3/tree/tmp/.noindex \ testdir3/sort/1st-child-promotes-thread/cur/A \ testdir3/sort/1st-child-promotes-thread/cur/B \ testdir3/sort/1st-child-promotes-thread/cur/C \ testdir3/sort/1st-child-promotes-thread/cur/D \ testdir3/sort/2nd-child-promotes-thread/cur/A \ testdir3/sort/2nd-child-promotes-thread/cur/B \ testdir3/sort/2nd-child-promotes-thread/cur/C \ testdir3/sort/2nd-child-promotes-thread/cur/D \ testdir3/sort/2nd-child-promotes-thread/cur/E \ testdir3/sort/child-does-not-promote-thread/cur/A \ testdir3/sort/child-does-not-promote-thread/cur/X \ testdir3/sort/child-does-not-promote-thread/cur/Y \ testdir3/sort/child-does-not-promote-thread/cur/Z \ testdir3/sort/grandchild-promotes-only-subthread/cur/A \ testdir3/sort/grandchild-promotes-only-subthread/cur/B \ testdir3/sort/grandchild-promotes-only-subthread/cur/C \ testdir3/sort/grandchild-promotes-only-subthread/cur/D \ testdir3/sort/grandchild-promotes-only-subthread/cur/E \ testdir3/sort/grandchild-promotes-only-subthread/cur/F \ testdir3/sort/grandchild-promotes-only-subthread/cur/G \ testdir3/sort/grandchild-promotes-thread/cur/A \ testdir3/sort/grandchild-promotes-thread/cur/B \ testdir3/sort/grandchild-promotes-thread/cur/C \ testdir3/sort/grandchild-promotes-thread/cur/D \ testdir3/sort/grandchild-promotes-thread/cur/E \ testdir4/1220863087.12663_19.mindcrime!2,S \ testdir4/1220863042.12663_1.mindcrime!2,S \ testdir4/1283599333.1840_11.cthulhu!2, \ testdir4/1305664394.2171_402.cthulhu!2, \ testdir4/1252168370_3.14675.cthulhu!2,S \ testdir4/mail1 \ testdir4/mail5 \ testdir4/181736.eml \ testdir4/encrypted!2,S \ testdir4/multimime!2,FS \ testdir4/signed!2,S \ testdir4/signed-bad!2,S \ testdir4/signed-encrypted!2,S \ testdir4/special!2,Sabc all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .cc .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtest.mk $(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) --gnu lib/tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu lib/tests/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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtest.mk $(am__empty): $(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): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libtestmucommon.la: $(libtestmucommon_la_OBJECTS) $(libtestmucommon_la_DEPENDENCIES) $(EXTRA_libtestmucommon_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libtestmucommon_la_OBJECTS) $(libtestmucommon_la_LIBADD) $(LIBS) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list test-mu-container$(EXEEXT): $(test_mu_container_OBJECTS) $(test_mu_container_DEPENDENCIES) $(EXTRA_test_mu_container_DEPENDENCIES) @rm -f test-mu-container$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_mu_container_OBJECTS) $(test_mu_container_LDADD) $(LIBS) test-mu-date$(EXEEXT): $(test_mu_date_OBJECTS) $(test_mu_date_DEPENDENCIES) $(EXTRA_test_mu_date_DEPENDENCIES) @rm -f test-mu-date$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_mu_date_OBJECTS) $(test_mu_date_LDADD) $(LIBS) test-mu-flags$(EXEEXT): $(test_mu_flags_OBJECTS) $(test_mu_flags_DEPENDENCIES) $(EXTRA_test_mu_flags_DEPENDENCIES) @rm -f test-mu-flags$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_mu_flags_OBJECTS) $(test_mu_flags_LDADD) $(LIBS) test-mu-maildir$(EXEEXT): $(test_mu_maildir_OBJECTS) $(test_mu_maildir_DEPENDENCIES) $(EXTRA_test_mu_maildir_DEPENDENCIES) @rm -f test-mu-maildir$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_mu_maildir_OBJECTS) $(test_mu_maildir_LDADD) $(LIBS) test-mu-msg$(EXEEXT): $(test_mu_msg_OBJECTS) $(test_mu_msg_DEPENDENCIES) $(EXTRA_test_mu_msg_DEPENDENCIES) @rm -f test-mu-msg$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_mu_msg_OBJECTS) $(test_mu_msg_LDADD) $(LIBS) test-mu-msg-fields$(EXEEXT): $(test_mu_msg_fields_OBJECTS) $(test_mu_msg_fields_DEPENDENCIES) $(EXTRA_test_mu_msg_fields_DEPENDENCIES) @rm -f test-mu-msg-fields$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_mu_msg_fields_OBJECTS) $(test_mu_msg_fields_LDADD) $(LIBS) test-mu-store$(EXEEXT): $(test_mu_store_OBJECTS) $(test_mu_store_DEPENDENCIES) $(EXTRA_test_mu_store_DEPENDENCIES) @rm -f test-mu-store$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_mu_store_OBJECTS) $(test_mu_store_LDADD) $(LIBS) test-mu-str$(EXEEXT): $(test_mu_str_OBJECTS) $(test_mu_str_DEPENDENCIES) $(EXTRA_test_mu_str_DEPENDENCIES) @rm -f test-mu-str$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_mu_str_OBJECTS) $(test_mu_str_LDADD) $(LIBS) test-mu-util$(EXEEXT): $(test_mu_util_OBJECTS) $(test_mu_util_DEPENDENCIES) $(EXTRA_test_mu_util_DEPENDENCIES) @rm -f test-mu-util$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_mu_util_OBJECTS) $(test_mu_util_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-container.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-date.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-flags.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-maildir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-msg-fields.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-msg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-store.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-str.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mu-util.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< .cc.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cc.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cc.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs 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: $(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 $(LTLIBRARIES) $(PROGRAMS) installdirs: install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: 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: 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." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -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-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: all check install install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir 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 maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof dummy.cc: touch dummy.cc # 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: ������������������������mu-0.9.18/lib/tests/test-mu-common.h����������������������������������������������������������������0000644�0001750�0001750�00000002701�12605152237�014123� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #ifndef __TEST_MU_COMMON_H__ #define __TEST_MU_COMMON_H__ #include <glib.h> G_BEGIN_DECLS /** * get a dir name for a random temporary directory to do tests * * @return a random dir name, g_free when it's no longer needed */ char* test_mu_common_get_random_tmpdir (void); /** * set the output to /dev/null * */ void black_hole (void); /** * set the timezone * * @param tz timezone * * @return the old timezone */ const char* set_tz (const char* tz); /** * switch the locale to en_US.utf8, return TRUE if it succeeds * * @return TRUE if the switch succeeds, FALSE otherwise */ gboolean set_en_us_utf8_locale (void); G_END_DECLS #endif /*__TEST_MU_COMMON_H__*/ ���������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/�����������������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�012702� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/multimime!2,FS���������������������������������������������������������0000644�0001750�0001750�00000001160�12605152237�015203� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-path: <> Envelope-to: djcb@localhost Delivery-date: Sun, 20 May 2012 09:59:51 +0300 From: Steve Jobs <jobs@example.com> To: Bill Gates <bg@example.com> Subject: multimime User-agent: mu4e 0.9.8.4; emacs 23.3.1 Date: Sat, 19 May 2012 20:57:56 +0100 Message-ID: <m2fwaw2baz.fsf@example.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" --=-=-= Content-Type: text/plain abc --=-=-= Content-Type: application/octet-stream Content-Disposition: attachment; filename="test1.C" Content-Transfer-Encoding: base64 aGVyZSBpcyBhIHNpbXBsZSB0ZXN0IGZpbGUuCg== --=-=-= Content-Type: text/plain def --=-=-=-- ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/1283599333.1840_11.cthulhu!2,������������������������������������������0000644�0001750�0001750�00000000706�12605152237�016421� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: Frodo Baggins <frodo@example.com> To: Bilbo Baggins <bilbo@anotherexample.com> Subject: Greetings from =?UTF-8?B?TG90aGzDs3JpZW4=?= User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/24.0 Mule/6.0 (HANACHIRUSATO) Fcc: .sent Organization: The Fellowship of the Ring MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's write some fünkÿ text using umlauts. Foo. ����������������������������������������������������������mu-0.9.18/lib/tests/testdir4/181736.eml�������������������������������������������������������������0000644�0001750�0001750�00000004074�12605152237�014105� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Path: uutiset.elisa.fi!feeder2.news.elisa.fi!feeder.erje.net!newsfeed.kamp.net!newsfeed0.kamp.net!nx02.iad01.newshosting.com!newshosting.com!post01.iad!not-for-mail X-newsreader: xrn 9.03-beta-14-64bit Sender: jimbo@lews (Jimbo Foobarcuux) From: jimbo@slp53.sl.home (Jimbo Foobarcuux) Reply-To: slp53@pacbell.net Subject: Re: Are writes "atomic" to readers of the file? Newsgroups: comp.unix.programmer References: <e9065dac-13c1-4103-9e31-6974ca232a89@t15g2000prt.googlegroups.com> <87hbblwelr.fsf@sapphire.mobileactivedefense.com> <pql248-4va.ln1@wilbur.25thandClement.com> <ikns6r$li3$1@Iltempo.Update.UU.SE> <8762s0jreh.fsf@sapphire.mobileactivedefense.com> <ikqqp1$jv0$1@Iltempo.Update.UU.SE> <87hbbjc5jt.fsf@sapphire.mobileactivedefense.com> <ikr0na$lru$1@Iltempo.Update.UU.SE> <tO8cp.1228$GE6.370@news.usenetserver.com> <ikr6ks$nlf$1@Iltempo.Update.UU.SE> <8ioh48-8mu.ln1@leafnode-msgid.gclare.org.uk> Organization: UseNetServer - www.usenetserver.com X-Complaints-To: abuse@usenetserver.com Message-ID: <oktdp.42997$Te.22361@news.usenetserver.com> Date: 08 Mar 2011 17:04:20 GMT Lines: 27 Xref: uutiset.elisa.fi comp.unix.programmer:181736 John Denver <jd@clare.See-My-Signature.invalid> writes: >Eric the Red wrote: > >>> There _IS_ a requirement that all reads and writes to regular files >>> be atomic. There is also an ordering guarantee. Any implementation >>> that doesn't provide both atomicity and ordering guarantees is broken. >> >> But where is it specified? > >The place where it is stated most explicitly is in XSH7 2.9.7 >Thread Interactions with Regular File Operations: > > All of the following functions shall be atomic with respect to each > other in the effects specified in POSIX.1-2008 when they operate on > regular files or symbolic links: > > [List of functions that includes read() and write()] > > If two threads each call one of these functions, each call shall > either see all of the specified effects of the other call, or none > of them. > And, for the purposes of this paragraph, the two threads need not be part of the same process. jimbo ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/1220863042.12663_1.mindcrime!2,S���������������������������������������0000644�0001750�0001750�00000014325�12605152237�017023� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-4.9 required=3.0 tests=BAYES_00,DATE_IN_PAST_96_XX, RCVD_IN_DNSWL_MED autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 5123469CB3 for <xxxx@localhost>; Thu, 7 Aug 2008 08:10:19 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Thu, 07 Aug 2008 08:10:19 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs39272wfh; Wed, 6 Aug 2008 20:15:17 -0700 (PDT) Received: by 10.65.133.8 with SMTP id k8mr2071878qbn.7.1218078916289; Wed, 06 Aug 2008 20:15:16 -0700 (PDT) Received: from sourceware.org (sourceware.org [209.132.176.174]) by mx.google.com with SMTP id 28si7904461qbw.0.2008.08.06.20.15.15; Wed, 06 Aug 2008 20:15:16 -0700 (PDT) Received-SPF: neutral (google.com: 209.132.176.174 is neither permitted nor denied by domain of gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org) client-ip=209.132.176.174; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.132.176.174 is neither permitted nor denied by domain of gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org) smtp.mail=gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org Received: (qmail 13493 invoked by alias); 7 Aug 2008 03:15:13 -0000 Received: (qmail 13485 invoked by uid 22791); 7 Aug 2008 03:15:12 -0000 Received: from mailgw1a.lmco.com (HELO mailgw1a.lmco.com) (192.31.106.7) by sourceware.org (qpsmtpd/0.31) with ESMTP; Thu, 07 Aug 2008 03:14:27 +0000 Received: from emss07g01.ems.lmco.com (relay5.ems.lmco.com [166.29.2.16])by mailgw1a.lmco.com (LM-6) with ESMTP id m773EPZH014730for <gcc-help@gcc.gnu.org>; Wed, 6 Aug 2008 21:14:25 -0600 (MDT) Received: from CONVERSION2-DAEMON.lmco.com by lmco.com (PMDF V6.3-x14 #31428) id <0K5700601NO18J@lmco.com> for gcc-help@gcc.gnu.org; Wed, 06 Aug 2008 21:14:25 -0600 (MDT) Received: from EMSS04I00.us.lmco.com ([166.17.13.135]) by lmco.com (PMDF V6.3-x14 #31428) with ESMTP id <0K5700H5MNNWGX@lmco.com> for gcc-help@gcc.gnu.org; Wed, 06 Aug 2008 21:14:20 -0600 (MDT) Received: from EMSS35M06.us.lmco.com ([158.187.107.143]) by EMSS04I00.us.lmco.com with Microsoft SMTPSVC(5.0.2195.6713); Wed, 06 Aug 2008 23:14:20 -0400 Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "Mickey Mouse" <anon@example.com> Subject: gcc include search order To: "Donald Duck" <gcc-help@gcc.gnu.org> Message-id: <3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Content-class: urn:content-classes:message Mailing-List: contact gcc-help-help@gcc.gnu.org; run by ezmlm Precedence: klub List-Id: <gcc-help.gcc.gnu.org> List-Unsubscribe: <mailto:gcc-help-unsubscribe-xxxx.klub=gmail.com@gcc.gnu.org> List-Archive: <http://gcc.gnu.org/ml/gcc-help/> List-Post: <mailto:gcc-help@gcc.gnu.org> List-Help: <mailto:gcc-help-help@gcc.gnu.org> Sender: gcc-help-owner@gcc.gnu.org Delivered-To: mailing list gcc-help@gcc.gnu.org Content-Length: 3024 Hi. In my unit testing I need to change some header files (target is vxWorks, which supports some things that the sun does not). So, what I do is fetch the development tree, and then in a new unit test directory I attempt to compile the unit under test. Since this is NOT vxworks, I use sed to change some of the .h files and put them in a ./changed directory. When I try to compile the file, it is still using the .h file from the original location, even though I have listed the include path for ./changed before the include path for the development tree. Here is a partial output from gcc using the -v option GNU CPP version 3.1 (cpplib) (sparc ELF) GNU C++ version 3.1 (sparc-sun-solaris2.8) compiled by GNU C version 3.1. ignoring nonexistent directory "NONE/include" #include "..." search starts here: #include <...> search starts here: . changed /export/home4/xxx/yyyy/builds/int_rel5_latest/src/mp/interface /export/home4/xxx/yyyy/builds/int_rel5_latest/src/ap/app /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/interface /usr/local/include/g++-v3 /usr/local/include/g++-v3/sparc-sun-solaris2.8 /usr/local/include/g++-v3/backward /usr/local/include /usr/local/lib/gcc-lib/sparc-sun-solaris2.8/3.1/include /usr/local/sparc-sun-solaris2.8/include /usr/include End of search list. I know the changed file is correct and that the include is not working as expected, because when I copy the file from ./changed, back into the development tree, the compilation works as expected. One more bit of information. The source that I cam compiling is in /export/home4/xxx/yyyy/builds/int_rel5_latest/src/ap/app And it is including files from /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common These include files should be including the files from ./changed (when they exist) but they are ignoring the .h files in the ./changed directory and are instead using other, unchanged files in the /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common directory. The gcc command line is something like TEST_DIR="." CHANGED_DIR_NAME=changed CHANGED_FILES_DIR=${TEST_DIR}/${CHANGED_DIR_NAME} CICU_HEADER_FILES="-I ${AP_INTERFACE_FILES} -I ${AP_APP_FILES} -I ${SHARED_COMMON_FILES} -I ${SHARED_INTERFACE_FILES}" HEADERS="-I ./ -I ${CHANGED_FILES_DIR} ${CICU_HEADER_FILES}" DEFINES="-DSUNRUN -DA10_DEBUG -DJOETEST" CFLAGS="-v -c -g -O1 -pipe -Wformat -Wunused -Wuninitialized -Wshadow -Wmissing-prototypes -Wmissing-declarations" printf "Compiling the UUT File\n" gcc -fprofile-arcs -ftest-coverage ${CFLAGS} ${HEADERS} ${DEFINES} ${AP_APP_FILES}/unitUnderTest.cpp I hope this explanation is clear. If anyone knows how to fix the command line so that it gets the .h files in the "changed" directory are used instead of files in the other include directories. Thanks Joe ---------------------------------------------------- Time Flies like an Arrow. Fruit Flies like a Banana �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/signed-bad!2,S���������������������������������������������������������0000644�0001750�0001750�00000001661�12605152237�015076� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-path: <> Envelope-to: skipio@localhost Delivery-date: Fri, 11 May 2012 16:21:57 +0300 Received: from localhost.roma.net([127.0.0.1] helo=borealis) by borealis with esmtp (Exim 4.77) id 1SSpnB-00038a-55 for djcb@localhost; Fri, 11 May 2012 16:21:57 +0300 From: Skipio <skipio@roma.net> To: Hannibal <hanni@carthago.net> Subject: signed User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:20:45 +0300 Message-ID: <878vgy97ma.fsf@roma.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha1; protocol="application/pgp-signature" --=-=-= Content-Type: text/plain I am signed! But it's not good because I added this later --=-=-= Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iEYEARECAAYFAk+tEi0ACgkQ6WrHoQF92jxTzACeKd/XxY+P7bpymWL3JBRHaW9p DpwAoKw7PDW4z/lNTkWjndVTjoO9jGhs =blXz -----END PGP SIGNATURE----- --=-=-=-- �������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/mail1������������������������������������������������������������������0000644�0001750�0001750�00000002772�12605152237�013566� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "John Milton" <jm@example.com> Subject: Fere libenter homines id quod volunt credunt To: "Julius Caesar" <jc@example.com> Message-id: <3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com> MIME-version: 1.0 x-label: Paradise losT X-keywords: john, milton Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high OF Mans First Disobedience, and the Fruit Of that Forbidden Tree, whose mortal tast Brought Death into the World, and all our woe, With loss of Eden, till one greater Man Restore us, and regain the blissful Seat, [ 5 ] Sing Heav'nly Muse,that on the secret top Of Oreb, or of Sinai, didst inspire That Shepherd, who first taught the chosen Seed, In the Beginning how the Heav'ns and Earth Rose out of Chaos: Or if Sion Hill [ 10 ] Delight thee more, and Siloa's Brook that flow'd Fast by the Oracle of God; I thence Invoke thy aid to my adventrous Song, That with no middle flight intends to soar Above th' Aonian Mount, while it pursues [ 15 ] Things unattempted yet in Prose or Rhime. And chiefly Thou O Spirit, that dost prefer Before all Temples th' upright heart and pure, Instruct me, for Thou know'st; Thou from the first Wast present, and with mighty wings outspread [ 20 ] Dove-like satst brooding on the vast Abyss And mad'st it pregnant: What in me is dark Illumin, what is low raise and support; That to the highth of this great Argument I may assert Eternal Providence, [ 25 ] And justifie the wayes of God to men. ������mu-0.9.18/lib/tests/testdir4/1220863087.12663_19.mindcrime!2,S��������������������������������������0000644�0001750�0001750�00000007136�12605152237�017127� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id C4D6569CB3 for <xxxx@localhost>; Thu, 7 Aug 2008 08:10:08 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Thu, 07 Aug 2008 08:10:08 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs34794wfh; Wed, 6 Aug 2008 13:40:29 -0700 (PDT) Received: by 10.100.33.13 with SMTP id g13mr1093301ang.79.1218055228418; Wed, 06 Aug 2008 13:40:28 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id d19si15908789and.17.2008.08.06.13.40.27; Wed, 06 Aug 2008 13:40:28 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Received: from localhost ([127.0.0.1]:56316 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQpo3-0007Pc-Qk for xxxx.klub@gmail.com; Wed, 06 Aug 2008 16:40:27 -0400 From: anon@example.com Newsgroups: gnu.emacs.help Date: Wed, 6 Aug 2008 20:38:35 +0100 Message-ID: <r6bpm5-6n6.ln1@news.ducksburg.com> References: <55dbm5-qcl.ln1@news.ducksburg.com> <mailman.15710.1217599959.18990.help-gnu-emacs@gnu.org> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Trace: individual.net bABVU1hcJwWAuRwe/097AAoOXnGGeYR8G1In635iFGIyfDLPUv X-Orig-Path: news.ducksburg.com!news Cancel-Lock: sha1:wK7dsPRpNiVxpL/SfvmNzlvUR94= sha1:oepBoM0tJBLN52DotWmBBvW5wbg= User-Agent: slrn/pre0.9.9-120/mm/ao (Ubuntu Hardy) Path: news.stanford.edu!headwall.stanford.edu!newshub.sdsu.edu!feeder.erje.net!proxad.net!feeder1-2.proxad.net!feed.ac-versailles.fr!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail Xref: news.stanford.edu gnu.emacs.help:160868 To: help-gnu-emacs@gnu.org Subject: Re: Learning LISP; Scheme vs elisp. X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor <help-gnu-emacs.gnu.org> List-Unsubscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=unsubscribe> List-Archive: <http://lists.gnu.org/pipermail/help-gnu-emacs> List-Post: <mailto:help-gnu-emacs@gnu.org> List-Help: <mailto:help-gnu-emacs-request@gnu.org?subject=help> List-Subscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=subscribe> Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 417 Lines: 11 On 2008-08-01, Thien-Thi Nguyen wrote: > warriors attack, felling foe after foe, > few growing old til they realize: to know > what deceit is worth deflection; > such receipt reversed rejection! > then their heavy arms, e'er transformed to shields: > balanced hooked charms, ploughed deep, rich yields. Aha: the exercise for the reader is to place the parens correctly. Might take me a while to solve this puzzle. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/signed!2,S�������������������������������������������������������������0000644�0001750�0001750�00000001604�12605152237�014347� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-path: <> Envelope-to: skipio@localhost Delivery-date: Fri, 11 May 2012 16:21:57 +0300 Received: from localhost.roma.net([127.0.0.1] helo=borealis) by borealis with esmtp (Exim 4.77) id 1SSpnB-00038a-55 for djcb@localhost; Fri, 11 May 2012 16:21:57 +0300 From: Skipio <skipio@roma.net> To: Hannibal <hanni@carthago.net> Subject: signed User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:20:45 +0300 Message-ID: <878vgy97ma.fsf@roma.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha1; protocol="application/pgp-signature" --=-=-= Content-Type: text/plain I am signed! --=-=-= Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iEYEARECAAYFAk+tEi0ACgkQ6WrHoQF92jxTzACeKd/XxY+P7bpymWL3JBRHaW9p DpwAoKw7PDW4z/lNTkWjndVTjoO9jGhs =blXz -----END PGP SIGNATURE----- --=-=-=-- ����������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/signed-encrypted!2,S���������������������������������������������������0000644�0001750�0001750�00000004401�12605152237�016340� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-path: <> Envelope-to: karjala@localhost Delivery-date: Fri, 11 May 2012 16:37:57 +0300 From: karjala@example.com To: lapinkulta@example.com Subject: signed + encrypted User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:36:08 +0300 Message-ID: <874nrm96wn.fsf@example.com> MIME-Version: 1.0 Content-Type: multipart/encrypted; boundary="=-=-="; protocol="application/pgp-encrypted" --=-=-= Content-Type: application/pgp-encrypted Version: 1 --=-=-= Content-Type: application/octet-stream -----BEGIN PGP MESSAGE----- Version: GnuPG v1.4.12 (GNU/Linux) hQQOA1T38TPQrHD6EA/+K4kSpMa7zk+qihUkQnHSq28xYxisNQx6X5DVNjA/Qx16 uZj/40ae+PoSMTVfklP+B2S/IomuTW6dwVqS7aQ3u4MTzi+YOi11k1lEMD7hR0Wb L0i48o3/iCPuCTpnOsaLZvRL06g+oTi0BF2pgz/YdsgsBTGrTb3pkDGSlLIhvh/J P8eE3OuzkXS6d8ymJKx2S2wQJrc1AFf1BgJfgc5T0iAvcV+zIMG+PIYcVd04zVpj cORFEfvGgfxWkeX+Ks3tu/l5PA1EesnoqFdNFZm+RKBg3RFsOm8tBlJ46xJjfeHg zLgifeSLy3tOX7CvWYs9torrx7s7UOI2gV8kzBqz+a7diyCMezceeQ9l0nIRybwW C9Egp8Bpfb02iXTOGdE/vRiNItQH14GKmXf4nCSwdtQUm3yzaqY9yL3xBxAlW53e YOFfPMESt+E7IlPn0c7llWGrcdrhJbUEoGOIPezES7kdeNPzi8G1lLtvT04/SSZJ QxPH5FNzSFaYFAQSdI7TR69P7L7vtLL8ndkjY49HfLFXochQQzsqrzVxzRCruHxA zbZSRptNf9SuXEaX9buO1vlFHheGvrCKzEWa6O7JD/DiyrE/zqy4jdlh9abMCouQ GWGSbn8jk6SMTQQ2Yv/VOyFqifHZp0UJD59tyIdenpxoYu5M0lwHLNVDlRjLEwUQ AIDz1tbLoM7lxs2FOKGr8QqbKIeMfL+NUmbvVIDc4mJrOlRnHh+cZYm4Z49iTl1v bYNMYgR5nY7W6rqh0ae7ZOW0h2NzpkAwTzuf1YrSjNavd9KBwOCFtAoZhRwfwFVx ju+ByHFNnf7g/R6DekHS0pSiatM0cPDJT05atEZb+13CRHHznonmLHi+VahXjrpg cIUA8Lhjdfm6Fsabo7gNZnTTRxNBqUXKK2vJF/XLbNrH5K2BH2dCCmUNtm3yFWiM DOzaw3665Y3S6MvZdyKpatbNrVoJdBpRgPxJ1YCSEituFUqHJBStay+aRb5fVkQR w3+9hWw+Ob0+2EumKbgfQ7iMwTZBCZP4VOxkoqdHvs9aWm4N7wHtXsyCew3icbJx lyUWsDx/FI+HlQRfOqeAMxmp8kKybmHNw8oGiw+uPPUHSD1NFYVm2DtwhYll3Fvs YY7r5s3yP1ZnwxMqWI3OsExVUXs8MS4UTAgO+cggO7YidPcANbBDihBFP8mTXtni Oo5n5v+/eRoLfHmnsGcaK8EkKsfFHpbqn4gxXGcBuHaTTJ/ZhbW6bi1WWZA9ExaJ IeTDtp5Bks1pJvTjCDacvgwl3rEBM6yaeIvB7575Y/GPMTOZhawhfOxV1smMmTKI JOWYb3+PuN2cvWetkjFgH8re4sRXq22DKBZHJEWYU8sH0sACAePnIr+pkrOtGeJB t1zBqZUnrupH6ptk9n/AjbQ+XSMTEKu55gSjYLAYx1EHApx52QLkdh+ej5xCIVeY 6wS1Iipkoc6/r6F7CKctupXurNY2AlD4uQIOfD6kQgkqK4PY3hsRHQA+Zqj6oRfr kxysFJZvhgt26IeBVapFs10WuYt9iHfpbPUBQUIZCLyPAh08UdVW64Uc2DvUPy+I C+3RrmTHQPP/YNKgDQaZ3ySVEDkqjaDPmXr5K0Ibaib2dtPCLcA= =pv03 -----END PGP MESSAGE----- --=-=-=-- ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/mail5������������������������������������������������������������������0000644�0001750�0001750�00000132226�12605152237�013570� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: Sitting Bull <sb@example.com> To: George Custer <gac@example.com> Subject: pics for you Mail-Reply-To: djcb@djcbsoftware.nl User-Agent: Hunkpapa/2.15.9 (Almost Unreal) Fcc: .sent MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: multipart/mixed; boundary="Multipart_Sun_Oct_17_10:37:40_2010-1" --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: text/plain; charset=US-ASCII Dude! Here are some pics! --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: image/jpeg Content-Disposition: inline; filename="sittingbull.jpg" Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAQAAAQABAAD/4QvoRXhpZgAASUkqAAgAAAAIABIBCQABAAAAAQAAABoBCQAB AAAASAAAABsBCQABAAAASAAAACgBCQABAAAAAgAAADEBAgAOAAAAbgAAADIBAgAUAAAAfAAAABMC CQABAAAAAQAAAGmHBAABAAAAkAAAAN4AAABndGh1bWIgMi4xMS4zADIwMTA6MTA6MTcgMTA6MzM6 MzcABgAAkAcABAAAADAyMjEBkQcABAAAAAECAwAAoAcABAAAADAxMDABoAkAAQAAAAEAAAACoAkA AQAAAMgAAAADoAkAAQAAAGsBAAAAAAAABgADAQMAAQAAAAYAAAAaAQkAAQAAAEgAAAAbAQkAAQAA AEgAAAAoAQkAAQAAAAIAAAABAgQAAQAAACwBAAACAgQAAQAAALMKAAAAAAAA/9j/4AAQSkZJRgAB AQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwc KDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIy MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCACAAEcDASIAAhEBAxEB/8QAHwAA AQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIh MUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpT VFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5 usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAA AAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEI FEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVm Z2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK 0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDq77xdrX/CQ6laRXjRxQTF ECovA/EUg8Sa6W/5CUuP9xP8K5yWQnxjrw9Lwj9BWjkgZHFAG6mu6yV51OXP+4n/AMTUq61rBB/4 mU2f9xP/AImsJJTuAJFW0YDnfmgCTUPFGqWFq882p3G1eyqmT/47VfRfGGpawkgGp3CyIeg2cj1+ 7XK+O7zybCGMNjzHyR6gD/69ZvgG8zqU67vvRZH4EUAesJe6m/XVLv8ANf8A4mpf7Qvl/wCX+6b6 uP8ACs+ObKdaeh3Hg9aANTw/4gurjxTLpU7tIv2cTKzHpgkH+n5UVheHGI+KWzJwdNP/AKFRQBzD 7f8AhMfEDEHH24j/AMdWrs0oCkDrVKJs+NfEsZ+79u/9kWrd5GqKTmgCstwwkyT0p5uzu61mOzbj zSFn3DmgDB8ePLPe2MEQZyykhRzk9/5Va8D6Vd2Mz3d3CYxJHiPd16+la0hhMybkUvxhj1HWr5uM uB0wMYoA3YJARjvV+DBPasC2lYsOuK3LVunWgCLQRj4sIPXTGP8A4/RSaF/yV2P30tv/AEOigDmY QD408UE9ftw/9AFXpv3iFT9Kgs4t3jXxV6C+H/oAq5cxkMcCgDFltXVyMVVv7iGwtzNcNsQfiT+F a8jbAxdgFAzmuTZZfEV81vG+xTyX/uIPT3P9aAIBr9vdNHcQI/lxk5DDBrfsLuK+jE0MqupPOOx9 KzNY8L6fbaaYrdGXb3BOcnuT+FcpodzN4c8RRRyylrW4IDE9MdM/UUAes2wbOAK27PhRms6CJlwc VrWowRkUAV9CP/F3YffSm/8AQ6Kfo+P+FuWp9dLf/wBDooAxrH/kd/Ff/X8P/Ra1evUOcgVW01Qf G/izIz/py/8Aota2LqPK4xQBxniWc2mi3MxBGFA/Mgf1rmtEF/Z6HNqMNuzvPnY+7G1V6Hoe+T0r qfGmnT3Xhm8WNWJVQ+B/skH+lUPBt3d3PhuzXyBM6xBY0YfKDnALewxmgDE1BfEDaPaXNzMRJPIQ +TjCgDHb69u9ZGt2Us2lrdNDtMLAgq27Kng84Fd74qnaMwWB8qWRTnzUcfePGSOx4ziuf1kzT6S9 tuRHlVUG5sDJOMA+lAHofh5/tvh3T7k4ZnhXcfcDB/UVuRQEdqzvDelPo/hywsJGDSRRjeR0yeTj 2ya3I8/3aAMXSU2/FmzJ/wCgbJ/6FRUunf8AJV7H/sGy/wDoQooAyNJXf448XYPS+X/0Wtb8ynyj 0rm/DIll8W+KDKQ0pvF3FehPlr0rvINMzbfN8rsc7upH0oA5ie3mktZSI1ICn5W43e1ec6ZrDwax facIj9liUNtUcgE8j0IzXrHiqS20rQJbiadoyBsWQjc2T2HvXnvhbREuzeXTbvMlfILcsF6D6jFA GJr+pWE1ymFkwFzhlwo+i1xevazLd3Fva2+UiQhh7kdPyr0jVfA8t0BeXNybe35UK2EJAJwST/QG uS1Pw7HYalbKHUIxYxyDd8wHUnNAHsnhXVBrGhWkrBlmEYVww6sAATXQInA5rn/AOZtIa3mQHZI+ xwfvAnJ6d8n9a6yazEKhlzgUAc1YAr8WbH302X/0IUU6xBPxYsSe2my/+hUUAV/Bdj5fi3xWJJDJ JHeopY8bj5a5OK9AUArwARXEeFjjxh4xbub5f/RYrsIZgJhGTjcuQMGgDnfHiwnw1KJoVkUuB8yg hfeuZ+HemTLpjx3OCZNzKUbPy54/Sut8Z263OlJE1wYgzkkjvgH86yfBb+XYWuIGiEithWzn9aAN loTcO0ctuGjV9oMg5JGCSOOnp9K8/wDH1qH1iERrukRAqqB3Jzj9BXpsk6F+oyCuRjJ54rhNcg+3 Ge5XiUSL5ZGc87sdPagDQ+HlvJHoAdo9h85mUY7dK7WSRCoB6HiuV8IiW10JYs7yszDJ7fN/k1tG Rpb4xj7qpnj3Iwfx5oAwLMgfF+1UHI/suTH/AH3RTLJNnxltx2Olvj/vuigB3hgf8Vp4vH/T8v8A 6AK6aRWFk2CA2CPSua8M4/4T3xcp/wCftD/45XR6q32e1JjUySCRdqA4J3HH9aAKHiJTceH4mliK r5e5lDfMpx2Iqp4eQR6Zp75Y4jX7xyfTn8q29djjbS/LMqxYGFdugNZWlskOh2pKgYj2AqO4OB/M 0AW7+NLQ3Fwi/O6hsk5yRwOO3WuS1qGJtNuvN3iNJkX5e+EIxn8f1re1e4ubq8jSOMiBArZJ/wBY xOcfQcZ+tVNTsYh4dnjmG9PMJIP8XYUAQ20z2Hg6OeJGTYQzd+N3Le+RzXQ6TGwtjLLkuxAy3XAH f8Saw9Mlt7vwsI4yZI9m07xtyM/y5rqodqxIFAIx1oA5iDj4w2ZHfS3/APQjRSw8/GGzx20x/wD0 I0UAee+I/GV/4S+IXiAWlvFKJ7gM28njC+1Ubn4v6xclC1hbAq6vwzdjn+lXviB4X1O88b6lPBYX EkUkgZXWJiDwPQVzH/CH61zjSbwj1EDf4UAbV78YdZvYPJbT7UA+7HP61HbfFXXLW3SFdOtSqZ67 v8fesg+Ddbzn+yL3P/XBv8Kcvg3Xc5Oj3x/7YP8A4UAaY+KuvIQP7PtM5JXKt/jUF78Udcu7F7WS ytEVv4grZHPB61VPg/Ws/wDIGvs9v3T/AOFMPg7XcHOk32P+uD/4UAWLb4l6vb2zQJZ2m1gP4WGC FAz19q17f4va0sSobS04GB8rf41z3/CIayOuk3g/7d2/wqRfCWr8f8S27/78P/hQB33w78Q3fib4 jR3l3HHG6WTxgR5xjOe/1oq78JvCmo6dq8+qXUBhhETQqJAVYsSDkA8496KAP//ZAP/bAEMABQME BAQDBQQEBAUFBQYHDAgHBwcHDwsLCQwRDxISEQ8RERMWHBcTFBoVEREYIRgaHR0fHx8TFyIkIh4k HB4fHv/bAEMBBQUFBwYHDggIDh4UERQeHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e Hh4eHh4eHh4eHh4eHh4eHv/AABEIAWsAyAMBIgACEQEDEQH/xAAdAAABBAMBAQAAAAAAAAAAAAAH AwQFBgACCAEJ/8QAVhAAAQMCBAIHAwYHCwoGAgMAAQIDEQAEBQYSITFBBwgTIlFhcRSBkRUjMqGx 0RYXQoKSssElJjNDRFJicpOi8CQ0NmNzo7PC0uEnNVNUVYMJRWR08f/EABQBAQAAAAAAAAAAAAAA AAAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwDqjNudMEyw6lvE3HEqUnUS kDSmeEkkCTB241Aq6X8mgSL5J/PT99DPrX3S232GwohPbp5/6v8A70PMsrSuwYKgkgtjiONB0Ovp lykFQLhJ8+0Fefjoyh/6/wDfFBctsJWkpt0wRzSK9ft7cwVMNmf6IoDOOmjJ5/lB/SFejpmyeT/n I/TFBdFqwACGGR6IFOWbRhZ3Ya349wUBgT0y5QP8qT+n/wBq3T0x5OPG8A/OFCy3tbZCRpt2j+YK cotLMjvWjM/1BQEs9MWTR/LUn0VWfjiyb/72hki1sw5q9kaHL6ApZDFspQHsze3DuCgI6umLJqUg +2Eg+AJ/ZSR6aMmj+UOfon7qozjTACUhlsCP5opo402AQWkeumgIK+mrKAA0uuK8e6R+ykj03ZU5 JePx+6h6gaTGlPHmkUwzDjlrhDKVvtIWXVaUIjj4+6gKKOm7KZUQrtU+YCj/AMteHpuyvvpbeV7l f9NAHEc3Ls8ed9iYZLTZIAI4+vpRAy1i7WKYS3dhtIDghQHIjiKC9npwy3O1rcH3K/6a1V034Ef4 OyfPqlf/AE1WG1KKxAAHhFO0LgTCfhQTf47cGPCwuJ/qr/6a9R004as7YXcn0bX/ANNRLbqhsAOF OWnVgEnh4UEm30wWbhhvBL5e3Jpz/ppwOlMLSC3lzElT4suD/kqHN642YT9tIu3T7h2UYHnQWW36 S9YleXcQA8Qhf7U05b6SMPXiFpYGwfZfu19myl9XZ6leEkR9dVBy7XEFXLcUN+mPEn7VnCrplZQ6 zclaFeCgAQfqoOqbO4L6VBxvsnURrRqConcbisqo9FOYE5mw1zGENdkLllpZQSCUkApO/wCbWUAY 63i9N0x3v5SkHy+aqj5QBXhFqRzbFWrrjrWm5a1ER7Unh/saqOSXP3Bst/4pNBbWhLY1nvCt1NyZ mRWtuoKESCOdKjSOB99BiEjYHYU4ZQpKpTwpArTxpVt8g7RQPmlQkpP2UoFE8VkU0buEkK4A+NbN rCzHDagfQCmN69TtsPjTRLoBA1b0oHFaZPwoH6IPODFJPwOG9asklJJ228a8egp9aBspcHhvQf6T b55eZHWVu6m2kgISDsnaT76Kt46hlpx1WyUJKj7q59xzEFXV9dXC1krccK9/M0CybtYUTrMnj50R uiPGLl69OHBTaWG2isgjdRkffQgFyoqO441eOiW4WM1W8D6aVBXpFAfGCVGQmnDZO07jmaYW74G0 yKfNuJUBB50D0ISAIAiNyK3UUkQIpDtgUhAAIApNToAKQACedAotaZgCfOvQrUCAIpsVAma31kEE bbUG60Dwn1oW9O6S3h2HqnYvK291E1SlEkzvQr6f1qRg1gVHc3B4+lAaeq2rXkFhcz3NPwcXWU06 pzqnchtQ4CkBQjT/AKxVZQDXrpOEXLJO0XYj+xFVDI6lHAbEeLSfsq1dd2UPWyxMG7HHhPZCqtkX /wAhw9Svo9gj7KC4Wx2MCKdt7ncTTaxbGmBz3p4hISNzHhQJrQSYnjXiUkQOYpZzupJJpFK5g7n3 0ChQSkbxSluSFDfam5WACokj1NetPAqA29KCXb0HdQ9TSwQlSduVR6XBpJnetkXWgCTQSTQCRpMk Uk8djzpsm9ExNaOP6pOqBQRWcblNtly/eI3SwoTPiIrnbEXAl7j4UaulrEHLbJ9z2ZEuKS2fQneg Fevy62ASQYoHCHkqXsqrl0U3aWs52mpelLgU3v5jah20/C9zO/hVmyc8Bj+HjfvXCB9dB09bq0mD zqQZUkAVDtriOdO7ZzSYO8UEoHBw3r0KB3mm6XUxERWdpO0UDhJBVvSulKh9kU2bUdzvvSzKu9tI mgXShOjfaTQk6xZ/cOw4GLk7/m0XlqlGxEcCDzoP9Y1KhgNhI/lR/VNAWuqGpJyG3pBHdVPn84qs rTqgqJyKyCI+bXHp2qqygH/XoQEW9i5M6r0CJ/1VVnJKP3Aw8QP4BvYf1RVh69K1lm0BMgXyQPL5 moPJ50YDY+TCP1RQW+yRoaC44HhSygNXe99NbW7lCUKA48K3cdgbk8KDa5IAMcKj3XiklIMTSrj0 zCppi8STwNAo5cEo4RXtq53pn1pke8NIUR5UoydWoA+lBMdrCdjSLr5G8yfCmJdWE6ZpNx2NiZoH 4uZjlThKitJ35TUOl4DntUlZOgoiaAe9Nl+tnDbS1QAULdlzxHgftoMLdm4VzAmKKvTsvsrywUqS y8y42seBBBB9xoSWTa7hVwocGmyrh7qDZpUKFXzoptG77NdoFkwiXD7uFUvCMOu7+5SxbNLccO8A TRd6Nsq3eB5mbVezq9l7ThsCTEUBcQvhtuKdWy0qVJ40wS4NqcNOTvA+NBKpMokRSiFKnx8opo07 KAAYNOGwrko0DlB/JO005Y0gTNNW9pkzTm3SDJJoHIQhIlKZ50IesgFDLdgYgC74/mmjC03KaEXW WBTlmykH/Ox+qqgJnVAV+8S3hMbOA78fnVVlZ1Oio5BbnkHI/tVVlAOOvSIbtlSN8QSD/Y0yyayh 3Ldg4BBNs37+6KedfERaWqo2+UE/8E1FdGdwF5Yw0kgzatmPzRQWdhnRClCTWPAEHeKkENodbSpO xpBy3IJJjegjFwjh6U1eWSoxw50+uGQHPE86j7qEqiY9KBstYSe6B61gfLapB5QaaPOQs77V4twR wNA8TcFyPKsdd25UyS6RBAivXXJSDQOm1jyinlq/CoSY22qDDh1AVI22mUqmNqCpdI+GXOZb9Ng1 I9jZL+qNiVSAPiKiMhZQFixcHGGgkvJPaJJ4JBolsKaTdO9zdTcKV4CdqalLSrtevcQEkeVA7ytl /CcOm7sLVKFugb+VSt06k3WgfSSO9HKss30NsagAkAd0GminCXSr8onegfNKSDO/nJp22tCSDUYF xEJpVDkkwDPrQTTCoGxBmnjDp4KG9QVu4rVxjepS3WY1KVPpQSbSiT508tBvBImo9hJICt96f2yS TwIigkWht6UHes8ojK9mJE+2Dj/UVRhaEpn7aDnWhH71LP8A/uj9VVATupmZyCiePzn/ABVVledT OfxfszG6XYj/AGyqygHfX3A+TrPmTiKZ3/1JqvdFX+h+EqPO2TU91+NrK1Mf/sER/YmoLopSpWR8 II/9sPtNARbNSdAKdzHCtnY0Eq4zypnbFaEAqImnRUFt6p93OgjbyEEn6qhrxQUo+FTGIp7m23rU HeGKBi6E6orRcRtWyiSoxWp4ERQeoAI3NeLSnnW7Q2341s5skcOMUCSEpMk08to0gA/Gmu4HCvW1 qoPMQWtOJIDR/I3HiKyycacfU4o86Y4qpxBcutiUphIpLLy1LGtSN1RsTwoLS0SQpx3uoG4HlSHa gq1JMSa9ullVulJAA5+dN0wYiKCQbcJ+lNLoVzmmjKoEmndudSoVAHpQOrbVq3mIqWtYME7Co9oE xHAeFPWVqCQY250EsyuBsSPWn7C1CI4+tRdq4F7gjzFSduoCBHoKCRYUYAG1CHrQJjKFqqB/nqf1 FUWbck96NO3jNCXrPmMm23Ha9R+oqgJPUvM9H6PLtR/vTWVt1MkoT0eMaQAVpdUYPPtSPurKAb9f ZQNkwnf/AMwR/wAA1GdD7QOQMHUeduPtNSfX3H7n2y4mcSSPT5g1G9D69XR9gxCeFvHHzNBczGkR tWqXIBjlW4HdO0U1XqQsGOe9ApcAKTHEGoe7tyJqWWSBv61roQ6nvCgrD7cEyPqpNCYn0qavrMhW w5U1NsYAAoGLAEmaUcAinItSZ8R4VjjJ0Qo+tBHK+NeoI0kVXs3ZptMIS42wk3FwB9EGAPU0KMWz zma6uCtm67BoHZLSQEx6nc0BdzRfBq0UhKu8BwFI4BchttK1EyrlPCqjhmJOXlrbquHUPOrbBJJ5 8+FaXC32NTguLi2MmCYU2fLjPxoCel0vDUFFQPCnSAQaH+Vc625fRhuKpbtX+CHAruL+6iC0QtKV JUFA8CKByyFE8KeI2CTzpG2E8h8acpQSRCRH20DxlRMGPqp2zOkJB2prZtlRIg/Gn7bSUwNW9A+t BAO4B8Kk7UeJmo20SUkmeHlUnbK2G1A+aiZjfxoS9Z8k5Ltwrleo4f1VUV0d6IG/OhR1mkg5HZkz /lqP1VUBI6l5H4vmRJOzvHl86aykOpOf3iEb/Sdj+0rKCgdfUj5NtxzGJI9/zBqP6Gkj8XuDDn2H /Malev6lHybakQD8oNg7f6hVMehVIV0dYMqJhj/mNBcm29Q24U3u2gDtUohvS2dtzTO6H+IoI93d PmBTcq0xBmnDoM7b7U1KZUQCBvQLKcQtuFRSCwAmBFIvBxJ2MGaVAUUhQIngaBNUg7kVV81YoUhV rbuweCiOPpUxmC9Rh9it5Su+dkDmTQ9Nz2q1PrO5JCd5JNAxxyztBaOOXyktsNp1rUefqaH7dpc5 hvlDCLJabVJgOLG5H2D0q7YhhV5nDFrfBLYrTbA9reOAwAkHYep/YKL+X8qWGHYe1aWjCUBtMDQn c+cmgDtvlq7scEQ2grTdtnVqKdhVXx3EscYTpumoIkdogbKHmDXRWJ2NotlxpbjRUOYVJ9N6G+bc Mt3EdghaFx9IAUAXfxB25bIe0haOHd+qiR0QZ1dRdIwXFXdbayEsOqP0T/NNU/MuXH7Qret0KKUn dIE/CoCxUW3UnfcyCOINB2BaiRI8Nop+0gxP21R+iPHF45gCUPq1XVsezd347bGr8wlQERQLWp0r Gw41IoAKp2k0xQkg/Qnwp9bSYJSZFA7YSfjUhbN6uMAimTBIUCakbZW44UD1hkJAoUdZ9pKMgNqH H25H6qqLTR7oPOhZ1ngD0dSOIvGz9SqC4dSZJGQyeRU7H6YrK36lBH4vk7flvQfzxWUFE6/oBwm1 Vz+UWh7+wVTboPSR0cYKkCf8nk/pGpHr/o/e9aLjf5SbH+4XTPoOUk9G2CyCT7PwA/pGgvaR3eAN M71E8qkkJBRsDvTa7QNBFBCuoHGKaqbGuZEU/udIERvTbUmSNO9A0ukjSTG9Nm1BC5PDnUg8pIHC ozEn0tMLcCeCSfqoB9nrElXOLi2bJ0IOkeE86rVzcBDKnAoBLRgCNyfL/HjXmO4koPaoPaOEmf5s 86aYHbPY7mmywdk6mmyHbjwKZkz60Bl6NcGRhWX0XDrWm6vPnnIHeAPAe4VZx2iEFSVrKYgpI0/X UJeYuzhrae0Qt9SANmwdIH7ajh0j4WrW204dYEEE8NqCXW6+7dP2yUMNnTKdZJ24cao2ZGCVO9ol tzcmUnh/j7qXTm3tbC8xdcKQnsWRJ7pIUoq+qKqeMZ3YUp9xxxsJcUCQngOJ2+NAmptDxKHEqSCN vGhnnCxOHY8sISEpe76YECecfb76J2GXDV8wLj2a4DDhlDikEA+h8Kr/AEk4St7Chdtd4sHUI4xz Hw391At0JY38mZntmnHdLV38y4nlP5J/x4102ywVAEbiuLMDfcYeauGjpUlQUDHMGu1MpXPyll+x vkbh5hKifOKBZtuCRoiKWbQoGYpx2KkmYO/lSiGjI2EeYoMaQSSDTplJA2rxtJG1OmUgD/tQKM6h 50MOsv3ujlwgzF02ftopJMAAxt5ULeslKujq5I/9w2frNBb+pM6Dkfsuep8n9NH31lJ9SUfvOBgb F/cf10VlBVuv7q/B61g7fKTW3/0Lpn0IjT0d4IN/82HPzNO+v8Vfg/apkR8osn/crpr0HEno5wUE cLeJnzNARGSCmCD8ab3IBkAcqctjbhwrV1oqTMAUEBdpABnemRAHvqYu7WASQDNMOw7xAKfHjQMn YjvDaq/m64RaYFevcNLSj9VWp+3IEyKo3S8U2+RcSXIkthI9SQKAKXV4u5ue21BLY3JngImrb0S2 TrmCXuYVuKb9pfUkr0nZpA4Ty3maHDSXsQLOHW/8PdOJZbHLvH7AAfjXUOW8rWWGZbssMTZtutMt BGpbQMniVRE7mTQDzMfSJctYXcpwbLq30MoSFP3KinXO3cQBJ9SRQpu/lvE8WS+jCQXXVAyylaUu T68TXQubsNNpZK1YwLVsDV3mSSPSFCoPJeTH3rv5dvO2UiYtu1RpKxH0o4xvzNBHXuX0WnRpcGzQ supWlSyqZMjccfGN6FGG2+Iv4u5cW+ALxNLSoaZUfmwoc1Abq9Jrq3GsPtrbI12gcFNHaI3oJW9q 7Z9qbEpSpapg+NA2ezHnJ4rZxLCrZLVshHYWzdutBXPECCoAjzqRtlKxNpxp22U0tSTLS4kCPXen +XLW5xR2EN3iXE/SCmwUj3wD9tTSsHLPdUtK1cYiaDnq1bXa4jcWLuy23FAe7b7q616v1yb/ACFa JMyySg78INc19IViLHNAuG0hPa94jzTxo79Vq/DtjiWHDcJWHk+iuXxFAXrls7xO1etJkCCaevNy NXOvGGxG6QDQaIbM8AactohMRWyWyD605aSInwoGwTIgCD40MOsa3/4cXh8Hm/1qLSkhMqNDHrHN T0Y38Dg43+sKCd6k4H4EHxDj36yKyvOpTIyZpJPF79dFZQVLr/T+Dtt4DEmf+Cuk+g1uejfAzEk2 wn4ml+v8EjK9sYknE2Y8vmXKT6BQ4OjTAyT/ACfh+caAiBoDYCaxxOlAGnjSralQJg7V4+sdmBtQ Rd2gaZM7nwqLdShCzE1NXS2wmJmot5CSdSQregY3KthE+kUNOnjtF5Hf7PVHatlfoFCim+hOkwDI 86p3SRhyr/Kt/apaKipklIHGRuKDmjKV81ZZuwi6cWSlNzpE8BtE/EiuqLbMPtFi2WVDVA2rjXEy 5bX1skApW2AQPA6ia6awJQNvavpSZWgKI5EEUBLwmwbxFSHMQYZd098FSQQD761GMWuK45eYPhYT cKsUJNw4ncIKphI84BJqs5hzKcPy4+q0lVypISNIkk8gPEkmKnsuZCvMHyEi3sMQFnmC6V7Re3Rb 16nFcUnyTwHp50Epmxm3ssnrYeUe0uEHSIoIYg0cKS9cOW5Uw1ClqHEJ5n1q59JWKYhh7tthN0+u 7et2NetKY1pHExyoft4lfYnir1u6sPWL+mUqT9HxE+HxoLhaXS2sND9k5rbdROpJ2INa212OzK1L nn3qhMtMPYU67hThKmEElon+YeHw4e6nFxPblQBKPyY/x5UAt6VLwPZoba3DYbn3knf6qLXVGbcV e4u8QezS02kEjmSTFDHNuXcRxnNDKrG3U4SnRJMSZ/711B0I5OcyhktuxvA37a4suPqRuJ5CecCg ujoEQfCtmOI2rZ0d2vEpEbcKBy2AVcvfS6UgbDnTNJPnSrRK5UmRQKqC9ESDvyoY9Y4q/FlfiQfn G/1hRPWogaYihh1joPRhfEj+Mb/WFBMdSon8DgDyL8emtFZWvUnCRlFYSRIL0+utP7IrKCr9f3/R q2PH90mR/uV170EKKejLAxAP+TbfpGvOv+CMsWpPPEmY/sV1r0Bk/izwPcx7Od/zjQEhlRUNk+6l XEymY5UmyDp4x50uggpHemgj3mZSSRA8aadmEqAVFS9yfmzHhUU8UzvyNB4tpBG4NMLy2YeZWFN8 QeNPA4QdMkT9VIvOHSUhQ3oORulXJl7a5tvbq2tyq3U4FIQBvBAO310bMBsQcKw+4ZIIS2kKSR5V KZ9sPaWm1WqW37i2lSxH0gBBFUTLeamLW+Tg97cBt9StSEcAB/N9aAws4LgLCbTEbrSi3aWLo61d 1Kk7ifQ7+6vXeke2uLdCsvYLf46tThSk2zKuzBH85wjSBVeTeu3rCMPNom/tnFauyUdKfzvETyqb fubpFs2wthtCUCENsSAPACKCk50xPMvt72L4nkt4Xa2SygtuIUkIIjeFHkfKh5huMWdk+W73Bb2y UDu6WSpER4iYq45rwS9duV3Nz7Ssap+ceJ5cAJquM9o2QEIJ7wkKPCgnsHxCzxa1F0w6haSkplJ5 UjdH5tdwe4gCRJj/ABzpo0lq3X2rbKGVLBBKe7J9OdUnpQzULWx+R7R3/KHk6VkH6CefvNBMdEmc 13PSMm1u3W12C3VBgFIlJ8Z84+uuvLIzaoKtyRXzpy7fXFjcpWypTa5ACk7Eedd99HmJt4zlHDbx D6XlKYSFrTwKgN/roJ1xHdFY2J3mt3EmIrUCO7FAqhAVsRtThIAiCD5UigGCZO1bpSqNiaBR3SpH CCKFvWNI/Fne7TLjY/vUTl6tJoXdY0KPRnfGP41sf3qCb6lP+h7h0gDW8J/PTWVp1Ilzk59E8HXf tRWUFX6/5P4NWoKhHyizt/8AS5SnV+bDnRpgaU7xb8PzjSH/AOQNUZfsk+OINH/dOUr1eFhHRlgg 3nsJHl3lUBONvGxTBitUthPGZpVLx3kztz51p2xJ2igTuWu6ajLhvjvUs8XltEttqWI3IrZGFJWl K7l4hSk6uz578KCtLQrXEz4RUknAHT2a1uaFkayI29KnWcIsrZ/tN1rSJgqmo1u5vLpt1p1pTUPE srk7jjvQBvH04hhuarxzELlaGn0Q2hHBJ3gTXOefrwt5oadUl1p9h4HQvYxqkGuoOmF+1U0bJ25Z aeWsKMqEq8QJrm7F8PcxfPTLKy12LSzD6mtaFJBkBQHHwoDBaYld4c004SpbRAKVDiAeVWFrPNi1 aAugrlOgEHcbc6jsv263sBZtbhse0sICXE6eGw5elRSsJtA4/rag8Y+6gaZqzheXlyvsSWrdHdSh KREcKgm8bt0IlxYTGxkQaZZvtW14u0D80yGwVRtJBqCxRy2ClOaU7bJkzvQSmI5mff2YgNjYeJNC /MTy38auHnFd7VzNWW9uDDVtbgLed2QmOE8SagMesfZr0NKJU5pBVvxJoNMLt3HvyoI70zw3/wC9 dP8AQRnVzD7K3wm5uGlWSTpSeaT4ek0CMm4GL68asw65LpCNCB4kcffRz6JMAbX0iXOEBxsJsWUO IhI0qBH+PfQdDsrQ4RpcSraYml9AUkq2EVDW2GO2OKe0hsr1JIUUq2I9KmEPs6ezSpJWRtNAow2C jma2TqA7vCvGFOCQpPpW5PmRHGg8UCUgn30LusgI6Mr8CZ7Rs/3hRWOyBznwoXdZRMdGV/wMuNfr UDvqQz+CNwTw7Z0Ae9FZXvUiH70LiOAedjb/AGdZQC/riY9cZgybaXdx2QWnEGUKDaFIAUGV6tlC eYq1dX1BPRrgRHO3/wCY1WuubhNnhOUrC1sX3LhtF61LjiYUo9k5JMVaur4kDoxwLf8Ak5P95VAT A0rmB8a2w+1L9ylomATvHhSrTetMJBJPKp7B8P7BsFMdqrdRPKg8xK0Qzh6bdlYbkiSBvE71AnCc TxHGRcqAtrdteyie84ANtuQq6KZSmCoaz4mtVpII22oIe2wq2t2tMOOL3lajJ3qDzc37Jhb2lSkF YgL/AJm3GrhpUkn3VSOljMWDZawBy5xd4gOgpbQlGpSj5Cg59zRaP67w35beeZAUwp8gl1JO59QK p/RVgS2XL/GL5am+3LyWg2jVI4QBB47/AAqbxvNb2a1vMYQ+i11KSEQ0QVeEq4/DzqwdGjLlvlkL unHEPquH9bkAqRGqVAcyDvHlQXHEMETbu219aJKA+yhtxB5EDYn7KrGYLY21yEOoLayCFBSYNF+8 w4sXFu+EqubNTRVp4qVpbBA8NzJk1TukZeMu4fdN4ZgaMTcaLIDIZDim9X0zMgwPf6UHP2e03Tt/ bN2rS3HFpACW06lKPkBxppYdGmasQR7RiCRhtvpK4cV84QP6PL311ecm2jFs0xgybSxu1sIcDxZk lJ4jYgmD9tVvFsoWiQ45mDHVvW8QvfsEQDChtufpJI35Gg57Tl/C7ZDjdtcsWz7SE9s65K3EhXPh z4eFVvNtrg15b9ph63W32nEMtoKCS8Oa1K4DyFdE57s8k4060zqQbi2SG0sNgw547jYgcffQr6RL Sywu6ZFtbN2zCXO0LaR/CECdz/jlQVnLWH3RxRoWoLEaVLUtUwf5225H30XeifGlYJnO0tn0ds3f /MG6UgJgkyD6HhVUyW3b3l+wEpSxcusjSDJC522+HD0qy4Thq8VzrY2QcKmRqaeCO4oECAoBQmAR QdTWTYCAVLCieFe3WFWV4pKnWh2iFSlSTBFQfR85eHD3MOxLvXlkoNqWf4xP5KvfVwZbIIOxmgi1 4elgSkrIPDypqtlQE1ZnW0qbg7yKjLxlTKVAJGmOJoItepKR3gDFC/rJKJ6Mr2AdJcb3P9aig4VE bDj4UMOsekp6ML2SILrUfpUEj1JmyjJrytiFOOnj5oH7KyvepPIyfcTzccP95IrKAb9dN9p7J1gb a+urtkXjPefIKpDTgIMbE93iKtHV1LKujjL5fC+z9mIMeSlVS+tZh2L2HRrZt47Zi1vTiTQKO11w OzcgzJnh4mip1YcNb/FTgDrqQrVbE7jh31UBOsLcdhrbZ0JiEDnUvbM9miIk86SsdK3VqaVqbTtE bTzp4J5UGq0kxArRxsngDTkkgbcBWizwmRQNXGjvFBHrLZYxXHUYabJhbzDetC0gx3jEb8uHOjq4 ElMzNUHpcfScEFmq5RbofVC1qXpgDfY+M0HI/YYtl1a3bmyt7C3LnZ26zHarIMaoEmOfhRKyM8nE 7Fm4tihLaFKWHNMBKtCiVkb89yKHXSo7a2+IlouqKWkKTDjsjURsB5wZnzq+9ByG2cCtmlKI1rB4 mSSOBI5GfhQG51SMLwhd9d3wWtNqhaU6YTAG+mPEmn2AN2tvZm9cCW37sh1ZPiYAHwgU6Yw9rEcv 27ToAOgBJT4D9hpZGHNuhDK4T2YiOQoEsVtnlLtvZ7QOuIUdLmqNCSNxHP0oZ490dXl5jryPlJ9F m6kOOslZWFKPE7kxwG1EZnGGnMSvLNKoatEEvOzEE8BNKNOYa7iSk2zrbtwWwpxSSVGOUnnQU+wy Rg+HIQpNqhSkc1JGxoE9OeErv82Iw9huO9Ko2CUwkn7a6rv7VTiDpMeBP21zn0j26lZuvLy3e1Ia CVSonhI24eCaCjWtk0xcW5xC8RbtWoCmm0nvEJMgGOG9WHo4dxRzOthdXGHutoQ+oF7V3QF8IJ3P ED31EvM/InbEqZccvHNRURqCWwIjf3mpfJ79xdZywtCriXC+lxSNR7yAdyYMAbbCg6WbZXa4na4m 2SEqhm4HLSfon3GPjVub2G1QVtbC6wxbau6FoInwqTwl0uWqAsy433HPUUEnJg7CKbvoC0qB4U4k cfqpFwlSjHKgg7pstrIKNPhAoSdZhSm+jW4kDvXLSfrP3Uar5jtGpmKB3WkXp6O3GuZu2p+ugn+p aQrJTp0gaXHUkxx7yT+2sr3qWQMjL8S69z/pJrKCqdfhR/B21bIgG8ZIMcfm3Pvq79WMA9DeXdWw 9nV8A4qqf19Qn8F7NYBKxetD3FDn3Vaeq68X+iDA0L/ikKSNuI1E/toDCNKW+7tXjbknTMKB3pNR BRusSRwpG1bQlZdTqle5M0D8pIRIMzWi0k7kVs26VARuOHpWy9kydtqBvyAoR9PDdy6W0NBOlLJV KuAMnf3UXlQTx40JOmrHLe1vfYLllLluGPnSeRPn8KAA4sxhGY0IwXEbdAcaKSxfJRpVpJAjVzTx MHwFGPLGXLXA+yw6zh1lgobSswZKYG9B3GUWzCEXdmXHGFPoShKR/Bq8FetHnJTbjlip1/6Yuz9L YjvJP7aC9ZYYPyUyoOiQkApSQUp8hG23Davc2JxC3wi7fwlkvXvZHs0AgFR8idp8JpbKaAjCGwmS EqKQeaoP0jtxPH31LPJ7VlSRsY29aClZTwFdnkq3axBhab19IuL8FYUpxziQo8Dvy4VL4Szh7XaN WduGlIjVCCBvy4fZUi2T2imjsJmDSVuy6h59bjoUCrupH5I8KBtjTybTCLl8qCSlohJP84jb6656 zk0FX+IS0pbhW2lKUnc7EzRw6QHpwL2clZ7d1KShIOpQG8SOA2Ek+nOg3iuFrxBV+Ge0S4HHXAAd +4Dtz5GgodlhTuLOXV9blt+3t1qTcIKtK2FA8DOxnl76neizALh3NTd4ppRJCVIIJ+jJ+HCvcq4h 8nZdRamwLpecU4tLg1BaCd9W3Ab8TPCp/oVxC3s80Iw95d2lVwSlhLoEJgcoHODQdA2Dei1QmDJG 9LJZ7N4vN7H8pP8AOrGNSedOAQU7+NB6l1KyIO8SKb3L4QstEwTBn1P214NFvreIJK+fgPCoyzWi /eXfPhaQh5SG0qVACdoMefH30Ek+4lCi2dwQTx50Detesfi6J4TeND6lUYbgqf7VsK0KSe799BDr VOLPR8pCjwvWgY9FUFr6k8fgSuP/AFHj/eR91ZXvUm/0GV/Xe/XTWUFc695P4NtJO6ResHj/AKty rb1YRo6IMvAAd5lZ9/aKqqdexKjgCIE/5Vbn+45Vs6ti9PRLl0Dj7Or/AIi6AqO6QtC1qjkBHE1o HCEKCdlQSJ4DwmtL3UGwtKgkpM1s22OzCn1pJMcOFBtgq1rtRJKlT3jBAJ8p4ipIokAz7qq+FYnj D+OutrtbVOFto2eQ4dQVJATEQdoPvqypdA3kEcaDCxJ5jehL0k4JbX+aF2D4Kn7tslsaZ2jaD4yf qovJeBA9aGGbcYUxmS4XcLW2GwopMDToHL12nagDeMWrOQ8Ut38Te+bfeShTDYSrWUwZUOW870UM MxBD2EX17bgFKrp4twdjCEkfZQn6VMHxDMuZrB/CXbd3tnA0u2dX88gkzqG26efjtRiwWy7DLhY0 gr9ofQTp3nSdx+jQXTLLxUxdJVGpL6jtMQrcRPkR75qQQ+kKUkq3mq1ktSoeSlBQkttqEj6RKASf MST7wamXwWX+2EQdlDwoFApS7wmBoB2ptbG6TdvOOvsG3OzTSEFOnfz4+tN7fEW3seXZNmS0NawP yR51o9d3Vzj6mBZhq3aSfnCoFThkbwOAoIHON2HsXtbALWgBQVISd1KJgTwiEmR6VV8rgqexG9AB 0WjzgJG0mpa5Wm4zViN8hQcRaoXJ0gAdmgAAEHeFFcnjO3KkujlTSbK+uX/4MMLCpH5Ox4e80Alv 7O89rtLK0tlXK1EgISqEK27yp2HAbDyqU6MsLXhucEqWyHHGnNTbhSJIVPCCY9Kl8CtsYvbxzFuz bu7dLmi3t1PFGpO41pjhyEceNPMpl+zzkwzeKLr2IK+ilOlCAlJIgevPnQGFNwrsCofSArXA7x29 Sokd1KiJ9K2QzpSNUwRxpLB22MPs3xqVHaqWeZMkmBQa4u+3fONWdqVrLdylL+gwWiBqTPiOHuNM ct3D14L9zswi1RcuIb1cVadp9Nqe4241h6HMQbMLQytao4KhOxPjwpnktl1rKNqHge2cRrcP9JW5 +2gmrRsItgTBJ8KA3WzRoyUFJ4KvG5+CqP6GyGUgAcKBnWzQT0epIH8ub+xVBYepbIyQ2AQR8/Pl 84Kyk+pPq/A10HcBbo9O8jasoILr1ScBRBgh63j4O1YerOo/ilwHURs0sbf7VdV/r0j9wkqn8u3+ 1yrB1Z5X0TYFwjslgf2q6Aq3X+bSPCeNIYchNywQ5JTBETtTi4SVWy4HLamuEhSElJM78qB1CGG2 7VpCUpnvnkkePrUQ/cXjOYmbFAUbd4FSf6McR9dSmINLUJCCptB1KTzX4D0mqhmPH3MvZgZvbxtR sy+1bKITMLcCt/iAKAjhsJQIiYoW9Ilp+6V44yg3DhQIQ2qTJ2I08440TWHw/bJdSkiUzvQ2xxdy xib7rDalvF8nYctXCaAMYfjWJp6Q8Js8SY1D2xBCwkoIIOwE8eKZo34OV+zW0pK+1vS4QFcAttdD XGMYwu66SLG4xKzadvWLhDbLikFKgeB9QJPwotYez2YwxI4FDK9/zx+2gTynCb20WVhRfsQAmBKA hRmY33Kvqqw4mlCLRxxwEpSJgbk1XsHhl7D5b0Bu5fZU6Y4EylBnfcmdvDzq4ONpXG21AN8i4ZmH D845gfxLsBZ3LiV2znaa3FbniD9EAbRw22qw2bTFniV865dXLj4BUpK1Hs+E90UviDqm8UJgCFAH zBH3iorPVwm1wdy5ceLZdSGUKClJAUo7GR/jhQVxntG8v4ze3LTSHnWtB0DYFaiYnme9x58aQwNt bWT8XdSrQPZwkq8CZn6op1i/zOT20JVPbuBSTxkAEj7BS+VOweyhiDlwvTbuFxKlEcEgQf20AJuF Y1eW9vb2jtxpYK3FqZdG0ERPDxiiV0WYWu8xNGP3ilpdb7gSsGCqIkVBXgwDCMFQ40t5Srx5QZd1 hSVRAkDkB4bVa+iS+YcuHMMR2kBHtACySdzvx5cKAj3t8pi1WUtqWoJkAVC/L9naWKbh1Sbi8WUM 9i2QopWQYkDcDjvUvi94zZYY/dOphttsqJAnYChDlN22ds14xiS/ZXrm+cuWme00rUClPZpUBvJA n86gvrz1zieBui4T2NzeOIY7IHZvUdwPQA1dLVkW7DbKYAQkCqlhlstzMOCWOgDskru3gTMHTpAJ 9VH4VeFp+cMAbeNBouNEBRG24oJdbFH/AIcpM8b5refJVG90d0bbmgp1sEk9Hze8RfN7fmqoJTqU 6fwKfE97tndp80VlJdSrbLFyJnvu/aisoIrrzpP4OoVy7S3+1ypjqvuT0SYIkk7B0f71dRnXlSDl hKo4Lt/f3nKfdV0a+inByPyQ6D69qugM6BLShHEUyw7a4dQDMHantuklBHKmjbfY3ijBAO9BviT7 bPdeMJcQQY/Z9dQV6S4n2e+CXgp5tbIUmSUpgyfMGneeGFuYCt5DikKZUlyQJ2Bk+u01st1tWYbS zCNZuGlKkD+DSIG/hMigstuEhlO0gjaqRmppJvXvY3GW7pSiYdMJkDjPjV8Q2EtgDkIqi52tmlG4 L3dE90zxJHDyoAnjDdwzm21uMX7uKXVwA2yIKUJTxWFDZRO3186NzjqWbXCVE8EIT8Ck/fQlunm3 834azcWVtqt3VJQtLkqEjf7d/Si9i7KU4HaPJ4MupmPAgj7qBtdtqRbX51hPs16h8o2GuTATJ9QR 5gVamHSS2VRCk+FRl9bj2m7HY9qLi2JCAY1KA2G+wM86c2F12+CW7y161pQNStpkcZjnQNcetwhZ dA7y1JHHjVTz5cLdcw2wYQHpd1OTuECISvbeZkDx34xVhz1iAscKNyYKUd4jVHI0PsOujfZgaxC5 uVIbZSFFtPNOkOBRj1IA8vSgks4OMpNtZtAFthWohJHArA4egVTjCbFf4txag6XHWCtfA7rOoj6z UFfF66XcXUavablQSTP0Q2qOJ/pD3ir4y2G8Hbti1rBbCVDgIAoOfsaxFOG481hC3LZ21QrtWm1t hayrgrSeRIMRwgUR+ibCWkdpj3cC32g2hCTPZpHI+e31VRs2ZYtHLxePKJUwpxTSWUcdYPDUOCSI 38iKu/RG4ub+xDehhhSVISeI1DfhtxFARnG27izW28gKQRBBHEUEsFwkXnTJcYeo6rSwWl9CI4bC J8d/so3gAtQKpOG4a3adIuL4i0nvOWaJ24qkxQWzKzYdxbEsVIOnULdonwTx+s/VU8rvKK5nwApD CrX2TDmrfYECVkCJJ3P106aSJ8qBEqIAEDhQZ61xB6PmduF83+qujY62nwoJ9a8j8XzQif8ALkfq roH3UsP72LoT/GO7e9FZWvUtIGWn0wZK3if0kVlAy68IJyxO8Tb/AK6/vpfqquBPRfhQJjvuiDz+ cVSXXg3yuE+HYH++uvOqopA6M7DWB3XHY/TNAemBKCBSdy2lDqSY350lZ3KdxIn7a0xJ0ltCkmCn eg0x1CXMJfbIiUECee1UToGzLe461jFtibRVcWF4WUPkfwiOQnxEVaMRxGbRYWZEGo7oktLS0yym 5YA1XTzjyz4kqNBeytU+NVvMFlbYip5t5TqVEBIUhUaTyPhU4t4p4c6qGbHL9DinLI/OIlQSTHLj QDfOeCsYNmBDlpZEvKR2i7kypSjqg78Ad55UUFLL2WlIUkmWwRtPDehPjeM3t04W7nuBtxsFA2JK l/ZANF7C1JVh7bSu8kpjc0Gl1iLbVrh12tQRKkoJJ2hXdH1kVtgbgFvdWyne1LbihqkHUPHbn4+d QePYbcYng7Fqw5p7J4FSvNCpA+IFLNXTeFIxS+dSUKTal5QJ2UUpMke/9njQD7NmNX+dM7KyvhyU 2+HWw7R+4c37VKFaVCBwEggTE0/dbfQWbewabsy6pWs9nJDadjII2Jnlt4VZzY2eH4cvFbawtkYl fNN+0PBAG8TJ99Nra0Q7jTSyhLa3GktrK3ZEDcpTzJkn4cqDW8swi5wu23AbaU85pHFS1AD6pq2L HzACRAAqKdQh7FiscilA9E//AO1KuL1IMcAKATZrFthgdwnEb1NjhhJdU+133DqV3U6dJj135VNZ CxrK7PZ4Xg945erMBb3YmSTJ75gR68KpvSFdtXV3ibz60e0W1ou5LSRsEAqS2k+oMnwIqMyLauYM 3heI3lk9bKfulWt20pWkgLIgn3EEUHQEd07kCq1lwPO5qxRTgGhCkBJ8QBP2mpe2eXZdnaP6ltaQ lp8qmT/NV4Hz5+tQVveLtMcu9AkuOoB9KAgNqUobEA1oHFa9JFJ27mod0+tK7SFzvzoN1lWmZ4UE +taf3gsTzvkAforo0uujSQN6B/WqcKskWyRzvk/qroJfqYT+DTuw2U9+sisrfqZD97D3kp2fXUis oGHXc3y3Eb6WD/vF1TOr7mvCMJyFbWt3i1nbPJdc1IceCSJVI2NXTrrtzgIUebbH1OL++uNnEkHa RvQd42PSFlkAFWP4bI//AJKPvp09n/La298cw0+l0j764C7yTxPrXhKualUHcGN51wJVo6pvGcOJ 0HhcI++k+jDOGCYZlJi1uscw8LC1qANygEBSiY4+dcSHUocTvXg17gK4UH0Jts/ZXUolzMOGJA8b pG/11Vcx59y6rGJbx2xcbKIMXSAkHj41xBqXEaj8a1UTO5NB1FjucsuqddGH3tj3HWe0V24AUe/M c1RIPGN6I+HdImVm7VtCswYYCEj+Up++uFIJ8ayFfzj4bUHdFp0j5TQ++hWYcNSgr1g+0p58ai84 dIeUVWi1s47h90lbKmXbcXAhaVbevjw8fSuLAFR9I153juSZoO2nek3J5Nuk5isAlpIIT2oIGxHv 2Net9KOUWFBacdwwlcqWQ6JnhwiuJAFExJisg8N6Dta16UMn9sFuZhsATqO7nifup4elTJn5OZLD +1rhzSrzrIJTtsaDoPpMz1gb+NG7sL21uBd4Y7au6FA7laon0mansdz9lrEsh2S14tZ+2pQytbYW NWtIAPv2rl7SRsTNbBJ8aDtW36UMn3Fg2HcespU2nUC5wMVBPdIWWk4s1+7to4nWklwrHAHnXJAC toJrfccKDuWz6UsohO+YLEf/AGU5b6UMnkE/hDYE/wC0rhi1WG7hC3UFxCFAlMxqHhNSPttq46Sq 1S2kuqXAPAEyEjyHCg7Vd6T8omNOYLDf/WULOsDmzBMeyqzbYdidtdOi7SvQ2qSBpUJ+uufFLK3V rQAlKjIA5DwpdqeZJoOu+pjtlh8Rtqd/WTWUr1N2+zyo54rLiuP9NI/ZWUDLrop1YAgaTu21B5fw iv8AtXHrzMmNJ25iuzuuIytzLgUB3UtN/Hta5DW1J+jQRBZAMwTWBid441KlqTFZ2IkkpE0EZ7OJ 3FeKY7xhMVK9l4CvOy24cPKgiCx5D41qWoMQRUuWSREDjNeFkapIoEEi0Fro1Q4EJAIRzBnf69/S lXrlpSrwtdnp1ksgtjgVSeXhtXpYT/NE16GeGw28qDxm4bLlmXez0hep8dn4K25eG1aBVsm00KIL obUmQjiZOx90QeVO2WmFFKFMjUTx1QPftTj2FggkBgDURBe3+ygi7z2Zxp/sVJSlTiS03o3QnfaY 9PXjSeG+zNsuJfSky82RIJOkTq5c5G3OpP2JorCAlkd0GS5sd/t+6tnbW3aX2iW0KSFboS7Mj4UE Y8m2DKezUndrTu3BCtczw8K2U1a+03L6XG9Dpc7NGg93+by293hSzrLanCW29KTwSTMV4liOVBHP WiUL0ocS5wkpBiffSaWNztvUr2G86a9DMDYR40EX2BE6RNbJYPGNvGpMMA8q9LJ8B8KBghnfhSoa QdwmDTzsiTukVuGjEQAaBu22EwIpw0gTsK3S0rbu07tmVTPCg606nrShk5bihA1OJHn3xWVKdUtC R0bJUEgEPLB24nWr9kfCsoNOtVaOXeUEttNqWsplISkknStBP1Sa5NXhS9KdVncSAdXcO/hX0PxG wssRt/Z7+0ZuWpnS6gKE+O9RRyblgmfka2HpI/bQcDDDGAynVZ3Xac1Rtz/7Ui9hqVbtMOpE7yJr 6ADKGWxwwlj4q++tGcmZYZWtbWEMoKzKgFqgnxiYoPn/APJrv/oufCvBhywf4Je3ka+hP4L4BEfJ jUep++vBlfAAZGGNT6q++g+e3yavj2S/gaw4a4Y+Zc/RNfQo5XwAmThjU+p++vPwWy/M/JjXxV99 B89Pkp4meycj+qa2GEvT/Aun8019ChlfAASRhrUnzV99bHLeBnjhrJ+P30Hz2ThL+/zDv6Ne/JFw Zlh39E19Ck5dwVPDDmR8aw5dwX/49r6/voPnl8kvD+Idn+qa1+S3Z/gnI80mvoactYESScOaJPmf vrwZZwEGRhjIPv8AvoPnmMLdH8W5+ia9+TF7y0ufSvoYct4EeOGMH1BNe/g7gn/xzP1/fQfPA4cs H6Cx5RXhw9QH0VV9DXcsYAv6WGMn4/fSasqZdJk4Wz8VffQfPgWPPQfSsNlvOk719Al5Qy2RvhLP xV99aLydlk8cIY+KvvoOChZ4fKtSbgbbcONYLO030h7yMV3j+BuWNR/ce34+f316jKGWwSBhLIB2 O6vvoOE7e0swiXRcSBtoAiYP7Yq69FHRxdZ9xt2zs1qtLW3QHLi5WmQkEwEj+keXoTXXyMpZcSNK cJYA4wCfvp5Z4DhNqoqtrQNEiDoWoftoGeRMrYdlDBk4PhfaezoMhThBUSSSSSAPGsqfSAlISOAr KD//2Q== --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: image/jpeg Content-Disposition: inline; filename="custer.jpg" Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAQAAAQABAAD/4Q1kRXhpZgAASUkqAAgAAAAIABIBCQABAAAAAQAAABoBCQAB AAAAyAAAABsBBQABAAAAbgAAACgBAwABAAAAAgAAADEBAgAOAAAAdgAAADIBAgAUAAAAhAAAABMC CQABAAAAAQAAAGmHBAABAAAAmAAAAOYAAADIAAAAAQAAAGd0aHVtYiAyLjExLjMAMjAwNTowMTox MCAwMDo1NzowMwAGAACQBwAEAAAAMDIyMQGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA //8AAAKgCQABAAAAyAAAAAOgCQABAAAA9gAAAAAAAAAGAAMBAwABAAAABgAAABoBCQABAAAASAAA ABsBCQABAAAASAAAACgBCQABAAAAAgAAAAECBAABAAAANAEAAAICBAABAAAAJwwAAAAAAAD/2P/g ABBKRklGAAEBAAABAAEAAP/bAEMACAYGBwYFCAcHBwkJCAoMFA0MCwsMGRITDxQdGh8eHRocHCAk LicgIiwjHBwoNyksMDE0NDQfJzk9ODI8LjM0Mv/bAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIy MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIAIAAaAMBIgACEQED EQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0B AgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpD REVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEB AQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFR B2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVW V1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrC w8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/ANNRzUiIfSlR DmrUUfrXOajEiz2qZYPap0jqlrOr2+i2RkkZDMR+7jLYJPqfanYCd4OOlQGe2ibbLcQo/ozgGvPL vxRqE6kSXsj+yfu1/JeT+dYsl55m7JUZ6kD+vWiwrnsAlgfG2eI/RxUhjrxlLhgMLKcdua0YPE2q 2mDHfSMB2c7h+tFgueqeVimmPiuO034hKWEepW4APHmw9vqK7G3ube9tlntZUliboymiwXECCgpU oSnGPNIZEqgUVKIz3opAVUXDVajFV4/vVT8R6yNC0SW6THnsfLhB/vHv+AyaaApeKPF0WjK1nZ7Z L0jk9RF9ff2rzC71Ge7naaeZpZWOSzHNVZriSaRndyzsdzMTkk1GFLHABP0qhEhnc5xWjpmg32q5 8oY9M96hs7DzHG4MBjJJGK9j8HW1ta6ckqqHVunHQ0BY8wl8H6hDGWOcjttNYtxbzWxIcHivoXWN X0nS7YzXh5I+WMDJNeY+KTb3umHUotOlhgkfYjkfqaLhY4ESZrb8Oa/caLqCMrFrZyBLH2I9fqKx Y4mkcIilmJwABkk16R4V8BmDy9Q1dfnGGjtj292/woCx2qjIB9aftp+MUvFSMYFopxopDKCDDdK5 L4i2V9dWdlJb28kkEJcylBnaTjBP68116ctWjanFUhHzpit7SLa0t1W7u5lCk4AxmvZbzwvoGoOZ rnS7dpDyXUbCfrjFeZ3WkW0OuXEcMMbQK52RljtA9jTESWNvDqExntAJDu2YEf3R6/413Xhq7itb IW88MgKMct2zmvN47h9C1WCS1uzGGbbIqnPynsexFelLMloUMjF4pVBEh75oAvX50q7+RIXknkwG Pt6c9q0bu00e/wBEk0m/kht4pE2KrOAwPYjPcVmWlza2TSXTbWwMgep9K5zWdavNSmISzS4bOMjA x6AUhm3pHhfRNFVZLG3EkmOLiQ72P0PQfhWnITUVjJNLYQPOmyUoN6+hxUjZzQAwr7UgWpKCMUgG 7RRRzmikxlGNavwDiqiDB6VegGQOKpCJygkhaM9GBBrzHxFp50+5la8tnO4DbIg6LnqP0r0S/v5L K1MtvaS3bZKbYcHDDqD7+3WvONT1u+1W/jbUSVtY3yYEGAB396YjCtLQXt1mOBhAjZDPyT7V3GkX bJbtp92pe1I+V8ZMf19qzta8T6dounx6dplhbySkbi/U4PIJP9K4y88Q6reYMk2yPOQiKFU/h3oA 73VLC6trFrmCVJ7dTn5X6fhWLo3ih31i2N3Bi1iYqWjHTPc+uKg0zxCb+2NrNaHj75hOFI9wKs6j FF9hf7CotmjAZWTj659aAPUQVdA6EMpGQR0IpjCuJ8GeKVNtJYXshd4xuiKLyw7jH9K7SK4guGdI nBePG9Dwy5GRkHkUDFA5HWnMBikIwaKkBo64FFAPNFIZVQVi+IvEk+mSJa2OVnxueQKDt9AM1uov NedeIJkufENyySEAEIMHjgY/mKpCZaOri/hjg1G3jnhRtylB5LofVWXHP1zWpqF1pt1pg8+4jmlS PbA6o32lj/02ydpGOMjr+lcsXliHOJF7jHNQSyAYmiPyA5I9DTEQnTVimMwywHOD6VoafJHp13Hc pZW92gJIhnGUORiljcSJnqDUMTBQ0fdf5UAaFhANDj1STUbP7PJN+9EQHHlnJUKPTJNSW4G4jqCB 1qKKzu0EeoXV0k0N0CqiSXc4C5HI9OaSNsXCIvCg4oAzNbj1O01iDVXhS3km+dPKUKpHTOB61b0j xNLYayl3JGBE6COWNOAQOhFMv7QGCa7N5E489kFuZCXTHOcdhWMGBJJ6CgD3BJEliSVTlXAZT7Gn AA1xvgnXJrxX0+5fd5SAxEjnb0wf0rshjFSMTABopwxRSGVgDn615TNCTNMrctHIwP516xzuGK8z 1FBa+I7yJgNrynH48/1qkJleOZo0GMstMlDy5MKxsx6g8GpGUxSnb9084p5RZUyuFb06UxGfazPE WicFWB4B9Kk3fv8Ad68VDOri4BaNt443D+tEchKsfegDQsVQy3DjJcR/d7dR+Va9lDGLR/NkLOzE gAenTn6iudsJtmoMeoaMg+3IrbtpJVjXa+O/NAGK4Q3M5bG7ewz+NZqAZcdlY5q3bxXOrasba0QN LNIxUFgo7nqa1PCPhttZ1a6S7Vha2rnz9p5Yj+EGgDLsZ7i0nS6t5WikU5BHp6V61o2pxatp8c6M vmYxIgP3W9KI/Bukyu0stmgZkwkSsVWMepx1NcOss/h3Wp47K4WRUfa+4cSD6UmM9IGM0VQ0zVrf VLbzImxIB88Z6r/9aipAn3ZNcB41tGj1gzKMGVFcH3HH9K7pDzXJeNCrTRuzHem1AueMNuOfzX9K pAznLa7juUCSEJMvr3qZwydRx6isi4ty3zJw1TWksjrs3kOOoJpiL0twwXlCwHoKxri6WFj8pG/k A1fe6vIODtKmu1+Gc9vcazNHeRxyZh+RXjVuc8nJ5oA4zwxp91rmpSW1nGGnZMZbgAZ5JP5V6Cvw 213Jb7TYgbcKPMbr/wB816pGsESARpGi+iqAKT7VCPlMi5+tAHisPwc8SLJk39hH/tpI+R/47XWe FvBeqeGtHnhlENxO8hlYRSH5vQDIHP1xXoSzow4ZT9DSvIO1AHiepeMNUWaWCBPsgDFWDrl8jsc9 PpXMajqdxe3BuLt90zAAsABkDp0rq/ixpTWeuwanbDC3kZDr/trwT+IIrziS6uUcJcqGWgDpPDOo pb+ILctJxITGQffp+uKKyNFtjc67YxqdyNMrfgDk/wAqKljR64vB5rK8Q6DaapCtxI0qXC7I0Kth eXAGR3+8fzrTDYNQavKRol465DpEZFI6gryP1FJMbPMEcqxRhytNkwrLInDDriqST/ay3z4lB3A+ tSrIWQq3DjqDVEl9h9oiDIC2e1V4H1KxvEuIGe2KHPmZxtqkbuazYtC5FVJ767vG/eyEr6dKAPQr P4k6tZRHcy3KKeGlXJI98VuQ+OdM1EB3nubN3IASWM4Y+gIzXksN4YcB49yjsank1L7Q4ZvlVfuj 3oC57To2vQX8jJBdJujO0hnCnP0ODXWxC6dBuLL6ZFeP2/izwp4hsIodejmtNREflvdRICjnszY5 OepGOtcvc6vqeh3Ij0jXbnyifl8mZth+maLDPU/i9Cf+EOt53YB4rtcHvypBx+leILdgDbICwrqL 7TvHPiVo7fUTdTxp8y+dIAg9+uM/rXS6D8PbDTQs+pst5cjkJ/yzU/T+L8fyp3AyfAmiXHn/ANrz I0duqkQhurk8Z+mM0V6BI+FCqAFHAA6CiobA/9kA/+EMRWh0dHA6Ly9ucy5hZG9iZS5jb20veGFw LzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQi Pz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUg NC40LjAtRXhpdjIiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5 LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgog ICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgeG1sbnM6dGlm Zj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczpleGlmPSJodHRwOi8v bnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUu Y29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50 cy8xLjEvIgogICB4bXA6Q3JlYXRlRGF0ZT0iMjAwNS0wMS0xMFQwMDowNzoyNyswMTowMCIKICAg eG1wOk1vZGlmeURhdGU9IjIwMDUtMDEtMTBUMDA6NTc6MDMrMDE6MDAiCiAgIHhtcDpNZXRhZGF0 YURhdGU9IjIwMDUtMDEtMTBUMDA6NTc6MDMrMDE6MDAiCiAgIHhtcDpDcmVhdG9yVG9vbD0iQWRv YmUgUGhvdG9zaG9wIENTIFdpbmRvd3MiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHRpZmY6 WFJlc29sdXRpb249IjIwMC8xIgogICB0aWZmOllSZXNvbHV0aW9uPSIyMDAvMSIKICAgdGlmZjpS ZXNvbHV0aW9uVW5pdD0iMiIKICAgZXhpZjpDb2xvclNwYWNlPSI0Mjk0OTY3Mjk1IgogICBleGlm OlBpeGVsWERpbWVuc2lvbj0iNzU1IgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iOTMwIgogICB4 bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6Zjg2ZTcwZTQtNjI5OC0xMWQ5 LTllM2YtZDQyZjM0NjM5ZGJiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ1dWlkOmY4NmU3MGU1LTYy OTgtMTFkOS05ZTNmLWQ0MmYzNDYzOWRiYiIKICAgZGM6Zm9ybWF0PSJpbWFnZS9qcGVnIi8+CiA8 L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAog ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAg ICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz7/2wBDAAUDBAQEAwUEBAQFBQUG BwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUF BQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e Hh4eHh7/wAARCAD2AMgDASIAAhEBAxEB/8QAHQAAAAcBAQEAAAAAAAAAAAAAAAIDBAUGBwEICf/E AEIQAAIBAwIEBAQDBgUBCAMBAAECAwAEEQUhBhIxQQcTUWEicYGRFDKhI0JSscHRCBVicoLwFiQl M5KissJDU9Lh/8QAGQEBAQEBAQEAAAAAAAAAAAAAAAECAwQF/8QAGhEBAQEBAQEBAAAAAAAAAAAA AAERAjEhEv/aAAwDAQACEQMRAD8Am8Hl60dEGN+tAbnalEBIr5+PUIoGcmllXPSgkW9LxxntVBET 2pVY/Y0tFEe+KcpFRDYRDHSjrD/pp4sdHWIUwMHhwOlJcpXr0qVeEEd6aXCxxRtLK6xou7MxwB8z TDSSpjelAtQFxxvwjaSmGXW7dnH/AOtWkH3UEUeLjjhGXATV0z7wyD/61cNTTDbG1AKcdKjBxXww RznWbUA9Mkj+lSem3+n6lE0mnXtvdopwzQyBgp9DjpTDXGQ0mY6fFD6Vzy8DpTDTMR7UOT3p0U9q KF3oaasm1F5PSnTr3pMAb1FJrGwGRRuX1o64xvRtqYG5X2oyp7UpgV1agCKAQSKFKZHLQozUEoNL Rj3oBd+lLxJsKrTsaEmnccW+d65Ehz0pzGp9KuI5HHv0pdU7Yrsa+1LqmSBitSAsabdKrXiLxfb8 IabG6xR3V/O4EVsXweXuxx2/maX4w410PhmJknlWe7A2gRhkf7j2/nWCaxxK9/qs+ohea6mbJuJd 2A7BR0UDoKuJaud54gcYX0Jk/wC46Lbno5T4sf8ALJ/Sqlq+sx3j/wDiOoahq7A5HmSFIwfYf2qA uLxpn55ZGkf1Y5ptLPynAxVxEsdQXcwWNrCvuvM33phM8jS+Z5rZBz1pn+JxuWxRGvxzbDI9aYbE rHfXgGFuX/T+1OrPXdZs3LWt/NCx6lTjP2xUGl3EO9HN3Gds/ah8XnTvEjiiywZLsXKj92UBs/ff 9at+heL9jNiPV7FoG7vCdvsf71izzAocGkWkyD1phuPVuh63o+uRc+mX8NwQMlAcOPmp3p8UA7V5 Gs725sp0mtLiSGRDlWRiCD7EVq3Afi5KrR2HFA8yPoLxB8S/7x+8Pcb/ADqYNfkA3pLlpWKWC6t4 7m1lSaGReZJEbKsD3BoKtYWEwvSjhQe1GA9aOoFA2K4JoAb9Kdcq5zQEagnYVKpALkHrQpyAB0Az 6UKioTlIpxCMikyMiloU2qwLRfMU6iGe4puin0p3Ah6kYHf2rUQsgVFLuyqoGSScACsj8TfFURPJ pXDEo2+GW8Hf2T29/t61D+MHiM+pTy6BoMpFhGeWedT/AOeR2B/h/n8qyiSQjuc961ImndzdzTyN NPK0sjHJLHNNZLk5601klY96SZqrOnRuipJApKWdn70iuTTqztWnkUAEg+gqp9JRrJIQEVmPsM0s LG9dsC2lyenwmtO4D4PZ3jmKeYp64GQPnWoDw8WeBZIrZUbqMj+dTVx5l/yvUACfw0gx1yKQaOaM kNGy/MV6UuuALhIyGRDj2qj8W8E3do3mCDIJ3wOgppjICzD2owmPepnWdKaAtlSCO2KgGBBwe1VM OBJnYUA2BTdTR1zQaD4UceXHDd+thfyNJpE74dTv5JP76+3qO/zr0PHiQB42DKwyrA5BHrXjtc16 l8Lo75OBNIF+GE34cYDdeTJ5P/bisdRqLGq980cLjpXQpB60dQc1lonymuBd8UsFocveoogXahSm 2+1CsqglI6E05twu1NU69KdW/XFaiHcYXOKznx44xOi6QvD2ny8t7fITOyneOE7Y+bbj5A+orRlK Rq0kjBUQFmJ6ADqa8l8ba5LxDxTf6vITi4lJjH8KDZR9ABW+WUY03KMCkC5cnJorHJoA4rQ6elBI 2kOFGTjNGUcx32qZsdAubiESoC+eyjcUQwgspVTmZod+ilxk1a+EtC8/UIeZ+WOVSSmNw3/WKjG0 60tpbeCZJTLIQVKHIO/Tb5Vb7Vbhbxr+1jES82Vjkckuo/d6bHHr7VBtXhrp8ttaxhreGKFfzcw/ X1rTbdomQBQCpGxA61ROFW/EaBDcsQrcgyo3GcdKnLNTHmWV0VTjdTjb5f8A+UaieFjHK/mHBQZJ J6Yqr8XXPD8UDie9tkC5BzKPsKrfGnFGu6jM2k8OlxynlaY/EgPYbd/sKyjTOFeK+JeJmtdUSRgk gDM/wDrjI9fuaiadcY22k3EL3MbRgcpCuB136VjOoIou2ZNlJyK9NeKfhUR4fwS6aZDLYEtcIDu4 I3b6YrzJdczTNzdtvtViEQoNKwxs7qiKWZjgADJJqS4b4e1fiC/Wy0ewmu5m68i/Co9WPQD3NejP C7wtseEuTU9UaK+1gj4SBmO3/wBuerf6vtVXFO8K/CR08nXOKoimCHgsGG59DJ//AD9/Sti5eu1O 5zzHem/b3rPqyCgHNGUUXau5qK6a4a4DXcZqApOxwDQoxWhWK0gsdDinEGAelJMgxS0CgVqMmHHM 7wcDa3LHs4sJsEdvgIryU29ez0tobmNre4iSWGVSkiMMhlIwQazXjHwGtLsyXfCt8LWQ7i0uCSny V+o+ufnXSMvPGN66BVl4m4H4p4clZdW0W7hQHaUJzxn5OuR+tV4Ieb8pG/pVE5w/odxcSiaWNhGm CB6g9/lWn6XprWmnk5jifPQ9MHPX5AVQtK1trTTrlDkyOnlg+m2/9BTF77W7iN3Ms5UAFiW6elRF 6gsNQZ31aOS3gnjCrIzDnJU9cdh9KXKZto52hYWkcgAxj2OGx03qF4D1qOyv4oNSvVCznq+8cfpz fP8AStj/AOxtla6VJDaXLXMDp580gwU5zvzAg7/agsXBN7pqWqWYYK4QOQTsexq4wjTrpArMvKw+ 1ZXw1oyxW8byOfPyc4bOKmJWvLRSPMbH5gcYxmoq5axacP2ulSAzx28e55om5Wyfl3qJ4Jt9Gtbt ruCGeaRsBGlkJyM5O9VaOG8v7oJzFhnY9Rg1oGh6BIlp5WQVC9em+KKstmq3isvLEI5MgqNwfXtW O6j4EcH2/Fd7qF811PBNKZorQNyRIDuVyNzv7jtU5xBxbxhoDfh7Hh6wEEPWV7gF3/2ov9a5oPE2 rcUomoXUFvBaRgoAHLSM/cEY+ED077VUSem2NhpNmLLS7K3srdOkcKBR+nU+9CQk96OxBpN8YqVT dzSecjpSjgE7UFUBTtvQJMoAz61xRntSpAoBRisqIF36Cjcu3SukUBt1oOcu9CjKAaFYq6r2AcZI pxEvTem4SnMKYrUZPrUbipa2bG9RNqN6koelbglElBXBGR6Go/VLezNlPKmnW0kgQkc0S7n7UvET iofjLVrjS9PDWyKZHzueigdT+o+9VHmziHSiuu3d6UjJmdZ1B/KBJvnHrnI9iDUqAlxZLplxp6lF AdnXClhk7j164ovF/Pa6v5v4ZppJCWl5JCNic8uOg3Pak5+JntoRaS2DRyQrkCQbgEk9vcn70ZV/ XtFktLCS4ihcRscDKbjpt/Or34IW8eraeLS4vrkJbyeYsAlbkLDcZXoaoGtahf3/ADT80nk8u4J+ EbdKmPCrU7jh/VI71gTaSEeZj933+VFbglytnfGKdVUMxOMGpaSS3uYEBQZGQDmm2o2FvxBpy3tn MAWXmSRNwfqKqY1HUdKumt7wFcbFlG30qYrQOHohBdBlwA24wOm+f+vrSfGfF8nOdN0+8MKQjMrq uSx9B/aqmeJkS2llRsyKhKgnHNjNQcU97qzNGzRKZy3MScg/P5bUDbiHjp7GZFt9OlupWBBeQkkD tnHqMnFO/Crij8fxBLbRwGOKaIyEZ3LZzzH7kU2nsI9Jljt5ktnuZRhZMZ39fUbZH2rQ+H+GtN0x zeW9nDFdSKFkdR1wP0+lBNk5FEkyRSpGBik2HzqBHFGA964QAcZoZHTNWtDADHWuMpxkGuLgnFK4 GOlZoR5cbmisTncUs4xtSRHM1AF+VCjqu2TQrNVAxg9KdQj1pJBvTmMHpWoyc2y5bYU+jUimtuu1 PIx9a0HMewFR/E1g2oaXJChxJjCt6Zp+uwBpxFvjeqjzxfF9O1r8XeIwYebHKh/ccMcbf7Sp981A Xer8P3Mt3eXrPJMIisUUSgZboC3etw8W4OFE0sXWs31tYXiDML8vNJJ/p5Ruw/lXn6/01dXuGvtO tMO8uJQgPIpYZH3wftREUbt7pvwFkk0dtLgSK+Pi9/b9auOkaekdqEIxt27Va+A/CfWbuwTU4tPE kL5BmdwoGOu27Y+QNPf8hcSyxxhAiNy87HkUn5tj+9Ax4M1274duxDcRmfT2OSq9Y/cf2q7cQaVb a1apcWEsTLIAUYbA59ao2qXXDehoX1PU43cDaKE4P1JGfsPrTXhjiXUtUMknDn+W21ssn/kTO3O5 9cbkfeinXEXDuraYhafSJJ0XOJIGz+lUK/4ku9PZ1tw6GPJIdcEe1bXJxWLfTHg1WExl15SVBIBP p9axbibSdT4g1aX/ACyyldJGCmRl5VC+pJoENB41upOIrK/1W1W8tYHBeHJGR6/Mdd69P6Ve2Wp6 bBfafKJLaZOZGH9fevMs3DtnoFsJNTW6ZCcGeJQVQ+46/fFX3wr4ls9Im/Cxail1pc7ZYdHgb+Ir 6euD70I2Vl+HNJkUvsyBlIKkZBBpJhRSLYwaKuD1FKkY61zAA6Vm0EQKW2pZeXuKSU4pQnbaoorh TSZ5RR96IwqDqnOcChRosAdaFZqocYB6UrG2/Sk1BzS8Y3rcQ7tj7U9i3ppboaF1r3D+l+bFf6xY 296q/s4ZSTgnozKu+PatodalfWmm2Zu76dIYV/ebv7AdSfYVmPFnirO4ktOHoDD+7+JlALf8V7fX 7VfOEtW1K0sNSng4j0Xi7VrlwbeGS9NmkSY/IkfKwXt8+5rJ9S401nT9TOleLPAyzo7HlvIIBDOo /iR1+GQD2OKuMqNftPqd1JPqM81xNJ+aSVizH61evCS+s40l0q5jhWYDy/2oGCMgxsc9gwAPtUzx F4ZTDQouJeGmm1TRp4xMvNHyzRoRnJHRh7j7Vmmr+ZYTW+q2p/aQNhwD+de6mojWPETxMvdE0KG1 1LmFxFzJDaKRGWYE/E6rsFHQD296wHiDjbiTXJna71KZUb/8UR5Fx6bdfrTXiC9u9b1iW/uA3NId lySFHTApyugO9mLiHJI3xVVXzzN8TMSfc0406/u9OnE9pO8TjuO9WCLR47q3DACGdR3GVb2P96tX iN4K8Q8G8LR8QXV5YzxYX8RBEx5oS3pnZgDtQJaD4j3z2fk6laC5jGzSR/mX3q26JxClzah47kTI 35SVwe+29UfwY0NNQur2/uA4ihVY0IPVzv8AI7Dv61aNStX03U5YT5fKeZ4+VcdOQ7j1xn7VApqL /wCZWNzbSr+zmjZcHsazC202WwnVortlukJ/J0BHbPetK06QvEzEHIkI+hFI3fh1+O4e1Pia31uO G5t5ARZsm7DAyQc5/Sg0Dwd4ztdU4Y/C6jMyX1rJycnLn4NsfYnGPf2q6aVq2m6tC02n3ccyI5jf BwUYdQwO4Psa8kcLa3LpGssWeTyXfEgU4Jwa2vWLxdQ4e1oWGnabc6Zcwfio721YJcQuF5v2i7MQ G2BGwzvRZWsOuKTIIqgeC/FjaxpB0q+maS+tF+FnOWkj9fmOn2rQGO9ZsUl0augkVwjfOKAB9qiu 52pNmzttRiDRGU5xQGQ9xQrqKcbihWaIxF70snWklB74paJcmtQRXHHEq8NaA90ih7uU8lup9cbs fYf2rDLvWJ9QvGur6WWWd2y7Mc5q9eOV60Op6XbqhYLA8jHtu2P6VQYruAkeZFsepxW2KfQTI35G Gau/DvG99aWo0zW401vR2xz211livujHdSO3p7VSLdrGX9/lPTI9KXaCXk/ZOjemaDW9M02NpbTi LT/FjVrPhaxbzf8AL7mTnmgYDaH4iQVxtuDt69aoviLrXD+u67LcaLpE1pBJnzWcgLMf4gmAVPr2 /nVMnlBcRXCBJ13QkbA06065FxCWcYdG5WT0IoGI0eISMgUe2RR9OJt5Ws3xjtt1qUB518zG6HtU frScskVygIwd8UHZ7copUCjaxe8bcR6Yuj3V7NeW5OUiCc0jY3+ZG2aXibmUSb4YdRU3wbxqvA2q zak1i9z5kBjDkcxjOf5f2FTQXwqnWz4eOmvGY5orhxICuG5tuvv2ol9rcOs69d+UcpZOsRYfvFld f54qNtOJ5+Jtf1XVpMwySzI/L3/Ly/8A1FHg0aLS9aklslKw3flM8Z3AbzR09tzVEjYcoeULsGAc CmvEOnarqt1aWelreSySK3PBbgnnUYO4HYUdJkSWAKd+XlOPepJ+J9S4VhttZ01Od1k5JEJ/OpBy PuB9hQZLxHpk+n6sySQvE2fiR1wVYdQQad2F5eWkb/h5XiJjaNgDsVbZhj3qa4o1W64nu5tVvIDF M7c2CcmohlBJ2xlaB9wzrl1w/r8Go2ZUyxDHK35WB6g16R4T1634i0SHUYF8st8MkZOeRh1FeW/J WRw2enU1qvgnxTp+nR3Gj6lMtuZ5Q8EjbKSRjlJ7dBUpK2QkD0rg3zRGIJ2O1GSstlFwAc0VsA7V 35UXBzUo6p2oV0KaFZVGKm9LxKM9M0T0NdklENvLLjPIhYD5CukRjfi1qCXHF88eQY7ZFgH2yf1J +1VyGOORMYGKbNK97JPLcMWkkcyOT3J60haieGQqmeXO2a0wkJ7FGHwkKexBpBLqeycJOTy9m7U/ tJo5sebGMinM8VpLGedMqN996COvF/Hw+ZFyyOo6A9fl71H2NwEvg2cGQcrjpuOmf+u1PxZRK7SW NyFx+5UfqyK3/eEBW4jPM4/jH96Im7d/jOGG/auXo57RlO/ptTHS7xJ4lIOT0p60mQ4GxopppdwG QwOfy7UtIshlFuInmD4AULnOegqOGI7wMo2JwfSn8mXQgNysR8LZ70Fp4h8ML/hWK01C51Czb8eq q0ETEtGx3HsR7im96wjWJFlVmjKq3fmxlz/8ahtK1TXdc4jjt9cvGljit2EQXbmIAAz9KXnYWVq8 apjCSSDfJGwTr/yNAiMlFy/7oORvjepDihUk0CGFZFjDToDI/wCUZOMn7060vT7OXQVvpLadCwZY wzBEkPL8JJPQBs5OcHp61E8Xs3+TwW867mQcy47gGgl+P+CtL4d0qyv9L1w6glwgLq6gEZHUY/lv Wb3MmG5c4PKR9akYbaRinPczyRpuiO2QufTNRuqxNHdKvYsD/wBfagPFtEAegFFkbzcKq7A9aDDK igx8qHb8zHCiiPQfhJrX+bcJQiWQyT2rGFyeu3Q/aropX0rzp4e8YS8JXqxPCJ7O5I89R+ZcfvCv QOnXttqFlFeWkgkglUMjDuKzY1DvPegGoLRWqNFFbI7UK4g2O1CshgAcelGRFkUxt0YEH610jAo0 LKD71qDzaI1g1Ke1JB5XZM/I4o1xGY2Db4FLcWRfgOML9OgS8kH0LH+4p75AmgDcwwa2wYYLplNi BtiixzzRH4gWA7EVyM/h5miz8NPIRHMOUAADvnFENpYrO7X87wSnoU/tTC80bUoR5kEgvUG+xww+ h/pUxJawr8cec02FxJE27HlHrQQOkTG2lliZCuGyA2xHtUxFKzfER9qYaxBLPci9tD5g5cSRj8w9 x6ijW8+YsZHTegPLvuMDBp2JAIlbbcY6U2tpPMV8/LOKAceXjB2oJ3hCeN9Z3UMywvjbp0rt9bmS OeeQZeQQrGN8AkFjt26ioLhed4uKIU5iOdXBH/GrLqDfEkaDc3BJ/wCKqv8ASirgbmxtLENZ6W7x ae37cMQFYc+Qq8wOSAASfXvvVP8AEdndhLJzeZ+LfmBbJyc7VJPdW0ziW6hilkAzzFFBHyqJ8RLt brTLec8gkacFiFwT8JoIK2lXlBYbVE8RMFaKT19/ejG5wAq0x1uVnhiB3IbagdwqHVc9MUSP9vdF v3E+FaPptvdX7Q2NhDJPdzEJHGgySTWq2/hOukcMvd6teSC/EZZUhwY1OM4JxknttjrQZqYlDGRx sOgNaX4M8Sw2Usmi6hOscUp5rcucAN3X60Xh3wf4k1e1ju7ua105GHMsUuWk/wCQH5flnNUnUrFo NVms5MB7VzGeX+JTg4+1QemVwRkYxRGJzWd+H3HtrJDBo+sytDdKOSOdz8MnoCexrRRg4IIIPTFZ aHRtutCiEkChWappJkLjJrkaEnJo8rCgjYxvW4MN8XrNYeNr3GR5ipJ09VFRWg3XnQlMnmTYg1dP G+z/APGbO8HSa25D81J/oRWXQztYXvm78j7PVYqw6hFGSJOXp1OKbpzKOZExipGIxXduGT4sjam3 lmJzG+du4NULW84YBZCPfaiXtosykxt13xXFCEEDOR39aNDKUk/KceuelEVq/gu4JcRrICD13okr XBhE8x+IHDEdSOxNW55oHT4lXPqKjr+2je0kjHLlxjtRURZNiPYjfvXWbAIYCkbZuVeQnp1HvSU8 yDIBG+2KIW055DxDp7W7AP5uCcA/Dj4v0zVnhdpZIWOxMZkP/Ni230IqrcMRo2tyTqCTDbyMBnbJ HKP/AJVaLGRBcXDkhkiYRqADsFGB/KosSyxQOg50JPuKqvH7CJrG2RsjDyN+gFS9veTT3W4+HPrV X48uObWljBwI4gMDsTRTbhvTbjXtfstHtZooprqURh5Gwq+5qa8ZeCLjgi6s7d9VttRSUkiSIcpV h1BXP61VrW4ltikttIYZo2DI69Qw70e9utS1/VrSPULlp5ZZVXJwOpAJrSN7/wAO3B6Wul/9qL5R +JuU5bcMu6Reo92/lWo2GmS6hqBv78AJGSLe3JyFH8Te/f2+dNOHEkt+FtOtRcRmNY1AePBGw2pT U9RFoHSW9htI+txcu3KIl74z1JOwHr8qjUTkY/Fo0Vs7RW6bM46k98e9Y54+No6alYWtjbRw3MMZ 82RVwFQ9Ax9Sd9/ep3WvFzQ7G3FhollcXap8IkJ8tSPXfcn3xVP408QdP4k4Yk0M6GLbmkEvmGfn JcHOTsPehqhpb2kjiTl53H72dqv/AADxs9hyaZquTaDaObmyY/Y+1Z000iAsRGF6DkJOPnSE1xME ODzKR2rNiPTqyxTQrLDIskbDKspyCKFZ14IX7XGgXFm0vP5MpIBOcA9PpQrNaXlmwBtXRkkGkQwz SqZyKSireMVh+I4TivAPitJ1JP8ApbY/ry1h+pW3OTgnHbevQPHsN3qOjwaDYcguNTnEILdAoBcn /wBtYMRzKFIww2atxmmGl6nPps3lOeaPO4qz2t5aXyghwD71WLy18zJC71FwzSW8uCSKrK+XFpyn nifJPXemp8xdgjZ+RqOtLucqG5+YEbUs2pyoCWJoHEqyn8sbkkdlNRV4uoKwPkuvtykVIwcQRK2G yKPNrEEzpySFGXpk/Cw9DRVT1qeW1lWQoyCUZx03HWo1LtpG3JrdvBySz1fxAtbDUeG7PUomVjI8 8SukAxs45ts5GPXrXp+z4b4chAaHQ9MjI6FbVB/SkMeG/CzTdS1jiRNOtdPu5TduiGZISyRKrcxZ j6bV6l4a8FuDLSxWO7hvb+U7vJNM0ZLHr8KYx+taqkcUIVI41VewUYArsigSZGKuKoKeEHAMJDpo rhvX8VKf5tTW78DfDW+na5udDleV8Zb8ZMOnyatLyCu/pQQgDqKDNbPwJ8M7W6juU4e8xozkLLcy un1Utg/WlX8EfDsasurW+hm3u0bmUxXEiqp9lzj9K0gkAda7nK7VRVZeDdIeJwqzwzH8s6vzPGfU Bsr+lecPGPh/ifh/iBbLW9Sl1GxkzJY3LfCGGdwR0DDv8/evWb7HOdqpvjLwsvGHh9qGnxIDf26G 4sX7iVRnl/5br9amFeQiOQ4yCPbrTeSLLl4zv1371DQX8yA+YCGHX2NBuJCshR0UgbdKiHdxd8jl dwfSm348xnm5Oai/ibe/bflRj0P96ZXHNBLyMpA96GtG8G9cS34rWDIWO9Qxuv8AqG6n+n1oVV/D S3a7490qNCwVZfNbHooJ/pQrnZjUejBjPSl12IG1Nhkt0pxGD1NSVcQ3F17HpeqcOalNJyRRaj5b tnAAdGWsIlYJql7CSMpcOvX0Y1vvG3DZ4q4Yn0qO4W3nLLJDKwyEZTnt7ZFYxxfwJq3DV9LNNfR6 j+xWeeRFK8nM3IOvXfG9bjNRbJzHAxnGMUwurJJVbb608t5gFywJJ9KcZXBBGRVREabM1rP5E26n cGp0Q288PMNjTC/tkki5kwCB2613SblsCF2APT50AvNMj5TIuB3z3qNSMBvi7Hap65TbP9dqirhV QFsYOelNMW7wn1ltC4uspjPy28kgSTmOwB6H23r2Fpl+rwhieY8ucj+9fP1794mJBAxWhcEeJHF1 vYCCDU3Fra/xgNk9lyRnHtRXtA3S8iuCppvd38ceDg7HtWD6B40W7Wwe9tQZlHxRBsBv9p7VcND4 80ni+ynbTGkt7222ntJtnXPQjsw9xV1V8XX7U8o5XO5H8v70dNcsOflZ+VvQmszvG1Yc7RJzxk5y N+tIQX8sbjmkhBO551II+9T9GNfTUbaTHJMjfI04S4UjGay+zvp5CpJBP+kbCp2y1GRByFmPfc9K foxb2lDE7/ShDIomUZ2OxHzquG8nZQyinNpdyNgEYYGmmPH3jLoFpo3iVrulEGBBcmaEqNuST4wP pzY+lUu60i2EBImVpPbvWwf4z7Q23iBpeoRLgXenAE+pR2/owrCo7iUOAT9KrIvJJbyfDkYNSkFx Hd24jlxzr+U0QeVdDHMFIG5NMJk8p/gl+1X0aj4Eaf5vFNzeFdrW2IB92IA/QGhVn8BdNntOF59U uYyn46UeVnqyLtn5Ek/ahXLq/Wo0LlAbY0dAc9TSJOD70ojjY1iNU7iyM4yKqfF8IvNa1KyfcT8N 3DKD3aN1dftirVDKp9Kz/wAbLq60pdO1ywmWN+SazfftIvp8s1uJWNluQq/NsRnrTtJkbBUjGKj7 OSOeE2rN8QGVNNj51jNljzRnvW2U7JKrKQoyR1phM/lSK64z1NKQOsq8/MObrmkJjzty8h9M1BOW E63UIQMOalZdBkul+J8DtvVTE01pLzwyMuPQU4bi3UUQJkEjbJFMNSl1wzbwjnurxQvcLTC61OGO FdO08IkS+nf5moDUtbvr5irynHtSNoxiIbO9WRFnVWEBfm3A9abadJxE16l3pN7cWk8eyyxyFDj0 yO3tSNpqSD4ZcYPWp231e0t7blgYA43FBYLbxG474Zt+eW/ttULYDrOhJP1BGftVguOK+MNV4cte I7LheG4sp8q01rO+Y5FPxKy9j36dMVlVzfSX1zsMhdzWm/4fePLPhniSTh7XJUj0bWCFLufggnH5 XPoD+Un5HtRZVq4J8Q+GDpbza/FrMFzAc3Kxwq/kL/ER1K57gbbVpPDHFPAuuMo0jjbT5XbcQzv5 T/8Apf8AtTTirwj0niC9N7a6oulXirmKSABiT/qGd1x2715q8SOAZ9D4gm0y4jWw1INlMHFtdL2a Mn8pP8JqZFe1raxkKjlaGZT3RhinSWLKdosZr532mu8UcPXLQ2uralp8sZwViuHT+Rqaj8XPEeKE xJxfqnKRjeXJ+53rWJrYv8cd9YC94ask5Wv44pXch90jJUAEe5B+1eaDKSMgkGlNV1HUNVvZL3Ur ye7uZDl5ZnLMfqaluDODeIeLLvydHsHkjU4kuH+GKP5sf5DJ9qvgghI4YkM2/vWreEXhhfcQSxaz r8Ulto6kMkbfC917DuF9+/b1GkcB+EPDvDix3mphdX1Jfi5pF/Yxn/Snf5tn5Cr9PP22A6Vi9Lhp NHFEkcEEaxRRqFRFGAoHQAUKTnkHPmhXOqTcnNBGNChWI0VjbvgVnX+IiMtwbZzKQDHeA/dWoUK6 c+s1g0N2/mrMuzKas0YS/tQ7LguvN8qFCt1kwV2tJ/KU5XNO2cqSQBkj0oUKBheFmzvudyah51Oe tChViVyOFQfenCxKRvQoVUJSRgHrSTMQ2ATQoVYsOIZ3gBAJpve3Ek5BJ70KFIJXh/i/ibQZEl0r Wbu2KkEKJCV/9J2q73Pi3xPxRZ/5JqENhNPe8tsLqSLLICcbD60KFLCM+4gDw63cWUkjTpaSNApb qQpIrWPD/wAMeG+IuDLW/vWvYbqUNl4ZRjrtsQaFCs9XIs9XHhvwa4M04CS9hudVl5sg3EnKg/4r jP1zWhW8dvY2kdpZW8NtbxjCRRIFVR7AUKFc9tawSSViDTSRsnehQqKbyjLbUKFCs0j/2Q== Cheerio! --Multipart_Sun_Oct_17_10:37:40_2010-1-- ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/1252168370_3.14675.cthulhu!2,S�����������������������������������������0000644�0001750�0001750�00000001633�13020504332�016531� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <dfgh@floppydisk.nl> X-Spam-Checker-Version: SpamAssassin 3.1.0 (2005-09-13) on mindcrime X-Spam-Level: Delivered-To: dfgh@floppydisk.nl Message-ID: <43A09C49.9040902@euler.org> Date: Wed, 14 Dec 2005 23:27:21 +0100 From: Fred Flintstone <fred@euler.org> User-Agent: Mozilla Thunderbird 1.0.7 (X11/20051010) X-Accept-Language: nl-NL, nl, en MIME-Version: 1.0 To: dfgh@floppydisk.nl List-Id: =?utf-8?q?Example_of_List_Id?= Subject: Re: xyz References: <439C1136.90504@euler.org> <4399DD94.5070309@euler.org> <20051209233303.GA13812@gauss.org> <439B41ED.2080402@euler.org> <4399DD94.5070309@euler.org> <20051209233303.GA13812@gauss.org> <439A1E03.3090604@euler.org> <20051211184308.GB13513@gauss.org> In-Reply-To: <20051211184308.GB13513@gauss.org> X-Enigmail-Version: 0.92.0.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-UIDL: T<?"!%LG"!cAK"!_j(#! Content-Length: 1879 Test 123. �����������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/encrypted!2,S����������������������������������������������������������0000644�0001750�0001750�00000004523�12605152237�015076� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-path: <> Envelope-to: peter@example.com Delivery-date: Fri, 11 May 2012 16:22:03 +0300 Received: from localhost.example.com ([127.0.0.1] helo=borealis) by borealis with esmtp (Exim 4.77) id 1SSpnB-00038a-Ux for djcb@localhost; Fri, 11 May 2012 16:21:58 +0300 Delivered-To: peter@example.com From: Brian <brian@example.com> To: Peter <peter@example.com> Subject: encrypted User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:21:42 +0300 Message-ID: <877gwi97kp.fsf@example.com> MIME-Version: 1.0 Content-Type: multipart/encrypted; boundary="=-=-="; protocol="application/pgp-encrypted" --=-=-= Content-Type: application/pgp-encrypted Version: 1 --=-=-= Content-Type: application/octet-stream -----BEGIN PGP MESSAGE----- Version: GnuPG v1.4.12 (GNU/Linux) hQQOA1T38TPQrHD6EA//YXkUB4Dy09ngCRyHWbXmV3XBjuKTr8xrak5ML1kwurav gyagOHKLMU+5CKvObChiKtXhtgU0od7IC8o+ALlHevQ0XXcqNYA2KUfX8R7akq7d Xx9mA6D8P7Y/P8juUCLBpfrCi2GC42DtvPZSUu3bL/ctUJ3InPHIfHibKF2HMm7/ gUHAKY8VPJF39dLP8GLcfki6qFdeWbxgtzmuyzHfCBCLnDL0J9vpEQBpGDFMcc4v cCbmMJaiPOmRb6U4WOuRVnuXuTztLiIn0jMslzOSFDcLTVBAsrC01r71O+XZKfN4 mIfcpcWJYKM2NQW8Jwf+8Hr84uznBqs8uTTlrmppjkAHZGqGMjiQDxLhDVaCQzMy O8PSV4xT6HPlKXOwV1OLc+vm0A0RAdSBctgZg40oFn4XdB1ur8edwAkLvc0hJKaz gyTQiPaXm2Uh2cDeEx4xNgXmwCKasqc9jAlnDC2QwA33+pw3OqgZT5h1obn0fAeR mgB+iW1503DIi/96p8HLZcr2EswLEH9ViHIEaFj/vlR5BaOncsLB0SsNV/MHRvym Xg5GUjzPIiyBZ3KaR9OIBiZ5eXw+bSrPAo/CAs0Zwxag7W3CH//oK39Qo1GnkYpc 4IQxhx4IwkzqtCnripltV/kfpGu0yA/OdK8lOjkUqCwvL97o73utXIxm21Zd3mEP /iLNrduZjMCq+goz1pDAQa9Dez6VjwRuRPTqeAac8Fx/nzrVzIoIEAt36hpuaH1l KpbmHpKgsUWcrE5iYT0RRlRRtRF4PfJg8PUmP1hvw8TaEmNfT+0HgzcJB/gRsVdy gTzkzUDzGZLhRcpmM5eW4BkuUmIO7625pM6Jd3HOGyfCGSXyEZGYYeVKzv8xbzYf QM6YYKooRN9Ya2jdcWguW0sCSJO/RZ9eaORpTeOba2+Fp6w5L7lga+XM9GLfgref Cf39XX1RsmRBsrJTw0z5COf4bT8G3/IfQP0QyKWIFITiFjGmpZhLsKQ3KT4vSe/d gTY1xViVhkjvMFn3cgSOSrvktQpAhsXx0IRazN0T7pTU33a5K0SrZajY9ynFDIw9 we7XYyVwZzYEXjGih5mTH1PhWYK5fZZEKKqaz5TyYv9SeWJ+8FrHeXUKD38SQEHM qkpl9Iv17RF4Qy9uASWwRoobhKO+GykTaBSTyw8R8ctG/hfAlnaZxQ3TwNyHWyvU 9SVJsp27ulv/W9MLZtGpEMK0ckAR164Vyou1KOn200BqxbC2tJpegNeD2TP5ZtdY HIcxkgKr0haYcDnVEf1ulSxv23pZWIexbgvVCG7dRL0eB+6O28f9CWehle10MDyM 0AYyw8Da2cu7PONMovqt4nayScyGTacFBp7c2KXR9DGZ0mcBwOjL/mGRKcVWN3MG 2auCrwn2KVWmKZI3Jp0T8KhfGBnFs9lUElpDTOiED1/2bKz6Yoc385QtWx99DFMZ IWiH5wMxkWFpzjE+GHiJ09vSbTTL4JY9eu2n5nxQmtjYMBVxQm7S7qwH =0Paa -----END PGP MESSAGE----- --=-=-=-- �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/1305664394.2171_402.cthulhu!2,�����������������������������������������0000644�0001750�0001750�00000001156�12605152237�016476� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������From: =?UTF-8?B?TcO8?= <testmu@testmu.xx> To: Helmut =?UTF-8?B?S3LDtmdlcg==?= <hk@testmu.xxx> Subject: =?UTF-8?B?TW90w7ZyaGVhZA==?= User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/24.0 Mule/6.0 (HANACHIRUSATO) References: <non-exist-01@msg.id> <non-exist-02@msg.id> <non-exist-03@msg.id> <non-exist-04@msg.id> 1n-Reply-To: <non-exist-04@msg.id> MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test for issue #38, where apparently searching for accented words in subject, to etc. fails. What about here? Queensrÿche. Mötley Crüe. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir4/special!2,Sabc���������������������������������������������������������0000644�0001750�0001750�00000000536�12605152237�015167� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Date: Thu, 1 Jun 2012 14:57:25 -0200 From: "Rocky Balboa" <rocky@example.com> To: "Ivan Drago" <ivan@example.com> Subject: currying and tail optimization Message-id: <3BE9E653ef345@emss35m06.us.lmco.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Test 123. I'm a special message with special flags. ������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/test-mu-msg-fields.c������������������������������������������������������������0000644�0001750�0001750�00000006655�12605152237�014674� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <locale.h> #include "test-mu-common.h" #include "mu-msg-fields.h" static void test_mu_msg_field_body (void) { MuMsgFieldId field; field = MU_MSG_FIELD_ID_BODY_TEXT; g_assert_cmpstr (mu_msg_field_name(field),==, "body"); g_assert_cmpuint (mu_msg_field_shortcut(field),==, 'b'); g_assert_cmpuint (mu_msg_field_xapian_prefix(field),==, 'B'); g_assert_cmpuint (mu_msg_field_is_numeric(field), ==, FALSE); } static void test_mu_msg_field_subject (void) { MuMsgFieldId field; field = MU_MSG_FIELD_ID_SUBJECT; g_assert_cmpstr (mu_msg_field_name(field),==, "subject"); g_assert_cmpuint (mu_msg_field_shortcut(field),==, 's'); g_assert_cmpuint (mu_msg_field_xapian_prefix(field),==, 'S'); g_assert_cmpuint (mu_msg_field_is_numeric(field), ==,FALSE); } static void test_mu_msg_field_to (void) { MuMsgFieldId field; field = MU_MSG_FIELD_ID_TO; g_assert_cmpstr (mu_msg_field_name(field),==, "to"); g_assert_cmpuint (mu_msg_field_shortcut(field),==, 't'); g_assert_cmpuint (mu_msg_field_xapian_prefix(field),==, 'T'); g_assert_cmpuint (mu_msg_field_is_numeric(field), ==, FALSE); } static void test_mu_msg_field_prio (void) { MuMsgFieldId field; field = MU_MSG_FIELD_ID_PRIO; g_assert_cmpstr (mu_msg_field_name(field),==, "prio"); g_assert_cmpuint (mu_msg_field_shortcut(field),==, 'p'); g_assert_cmpuint (mu_msg_field_xapian_prefix(field),==, 'P'); g_assert_cmpuint (mu_msg_field_is_numeric(field), ==, TRUE); } static void test_mu_msg_field_flags (void) { MuMsgFieldId field; field = MU_MSG_FIELD_ID_FLAGS; g_assert_cmpstr (mu_msg_field_name(field),==, "flag"); g_assert_cmpuint (mu_msg_field_shortcut(field),==, 'g'); g_assert_cmpuint (mu_msg_field_xapian_prefix(field),==, 'G'); g_assert_cmpuint (mu_msg_field_is_numeric(field),==, TRUE); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_msg_str_date */ g_test_add_func ("/mu-msg-fields/mu-msg-field-body", test_mu_msg_field_body); g_test_add_func ("/mu-msg-fields/mu-msg-field-subject", test_mu_msg_field_subject); g_test_add_func ("/mu-msg-fields/mu-msg-field-to", test_mu_msg_field_to); g_test_add_func ("/mu-msg-fields/mu-msg-field-prio", test_mu_msg_field_prio); g_test_add_func ("/mu-msg-fields/mu-msg-field-flags", test_mu_msg_field_flags); /* FIXME: add tests for mu_msg_str_flags; but note the * function simply calls mu_msg_field_str */ g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } �����������������������������������������������������������������������������������mu-0.9.18/lib/tests/test-mu-container.c�������������������������������������������������������������0000644�0001750�0001750�00000004332�13020504332�014577� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2014 Jakub Sitnicki <jsitnicki@gmail.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 3, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include "test-mu-common.h" #include "mu-container.h" static gboolean container_has_children (const MuContainer *c) { return c && c->child; } static gboolean container_is_sibling_of (const MuContainer *c, const MuContainer *sibling) { const MuContainer *cur; for (cur = c; cur; cur = cur->next) { if (cur == sibling) return TRUE; } return container_is_sibling_of (sibling, c); } static void test_mu_container_splice_children_when_parent_has_no_siblings (void) { MuContainer *child, *parent, *root_set; child = mu_container_new (NULL, 0, "child"); parent = mu_container_new (NULL, 0, "parent"); parent = mu_container_append_children (parent, child); root_set = parent; root_set = mu_container_splice_children (root_set, parent); g_assert (root_set != NULL); g_assert (!container_has_children(parent)); g_assert (container_is_sibling_of (root_set, child)); mu_container_destroy(parent); mu_container_destroy(child); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/mu-container/mu-container-splice-children-when-parent-has-no-siblings", test_mu_container_splice_children_when_parent_has_no_siblings); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/Makefile.am���������������������������������������������������������������������0000644�0001750�0001750�00000015374�13020504332�013121� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������## Copyright (C) 2008-2015 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ## ## 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, 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. include $(top_srcdir)/gtest.mk AM_CPPFLAGS=$(XAPIAN_CXXFLAGS) \ $(GMIME_CFLAGS) \ $(GLIB_CFLAGS) \ -I ${top_srcdir} \ -I ${top_srcdir}/lib \ -DMU_TESTMAILDIR=\"${abs_srcdir}/testdir\" \ -DMU_TESTMAILDIR2=\"${abs_srcdir}/testdir2\" \ -DMU_TESTMAILDIR3=\"${abs_srcdir}/testdir3\" \ -DMU_TESTMAILDIR4=\"${abs_srcdir}/testdir4\" \ -DABS_CURDIR=\"${abs_builddir}\" \ -DABS_SRCDIR=\"${abs_srcdir}\" # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter noinst_PROGRAMS= $(TEST_PROGS) noinst_LTLIBRARIES=libtestmucommon.la TEST_PROGS += test-mu-util test_mu_util_SOURCES= test-mu-util.c dummy.cc test_mu_util_LDADD= libtestmucommon.la TEST_PROGS += test-mu-str test_mu_str_SOURCES= test-mu-str.c dummy.cc test_mu_str_LDADD= libtestmucommon.la TEST_PROGS += test-mu-maildir test_mu_maildir_SOURCES= test-mu-maildir.c dummy.cc test_mu_maildir_LDADD= libtestmucommon.la TEST_PROGS += test-mu-msg-fields test_mu_msg_fields_SOURCES= test-mu-msg-fields.c dummy.cc test_mu_msg_fields_LDADD= libtestmucommon.la TEST_PROGS += test-mu-msg test_mu_msg_SOURCES= test-mu-msg.c dummy.cc test_mu_msg_LDADD= libtestmucommon.la TEST_PROGS += test-mu-store test_mu_store_SOURCES= test-mu-store.c dummy.cc test_mu_store_LDADD= libtestmucommon.la TEST_PROGS += test-mu-date test_mu_date_SOURCES= test-mu-date.c dummy.cc test_mu_date_LDADD= libtestmucommon.la TEST_PROGS += test-mu-flags test_mu_flags_SOURCES= test-mu-flags.c dummy.cc test_mu_flags_LDADD= libtestmucommon.la TEST_PROGS += test-mu-container test_mu_container_SOURCES= test-mu-container.c dummy.cc test_mu_container_LDADD= libtestmucommon.la # we need to use dummy.cc to enforce c++ linking... BUILT_SOURCES= \ dummy.cc dummy.cc: touch dummy.cc libtestmucommon_la_SOURCES= \ test-mu-common.c \ test-mu-common.h libtestmucommon_la_LIBADD= ../libmu.la # note the question marks; make does not like files with ':', so we # use the (also supported) version with '!' instead. We could escape # the : with \: but automake does not recognize that.... # test messages, the '.ignore' message should be ignored # when indexing EXTRA_DIST= \ testdir/tmp/1220863087.12663.ignore \ testdir/new/1220863087.12663_9.mindcrime \ testdir/new/1220863087.12663_25.mindcrime \ testdir/new/1220863087.12663_21.mindcrime \ testdir/new/1220863087.12663_23.mindcrime \ testdir/cur/1220863087.12663_5.mindcrime!2,S \ testdir/cur/1220863087.12663_7.mindcrime!2,RS \ testdir/cur/1220863087.12663_15.mindcrime!2,PS \ testdir/cur/1220863087.12663_19.mindcrime!2,S \ testdir/cur/1220863042.12663_1.mindcrime!2,S \ testdir/cur/1220863060.12663_3.mindcrime!2,S \ testdir/cur/1283599333.1840_11.cthulhu!2, \ testdir/cur/1305664394.2171_402.cthulhu!2, \ testdir/cur/1252168370_3.14675.cthulhu!2,S \ testdir/cur/encrypted!2,S \ testdir/cur/multimime!2,FS \ testdir/cur/signed!2,S \ testdir/cur/signed-encrypted!2,S \ testdir/cur/special!2,Sabc \ testdir/cur/multirecip!2,S \ testdir2/bar/cur/mail1 \ testdir2/bar/cur/mail2 \ testdir2/bar/cur/mail3 \ testdir2/bar/cur/mail4 \ testdir2/bar/cur/mail5 \ testdir2/bar/cur/181736.eml \ testdir2/bar/cur/mail6 \ testdir2/bar/tmp/.noindex \ testdir2/bar/new/.noindex \ testdir2/Foo/cur/mail5 \ testdir2/Foo/cur/arto.eml \ testdir2/Foo/cur/fraiche.eml \ testdir2/Foo/tmp/.noindex \ testdir2/Foo/new/.noindex \ testdir2/wom_bat/cur/atomic \ testdir2/wom_bat/cur/rfc822.1 \ testdir2/wom_bat/cur/rfc822.2 \ testdir3/cycle \ testdir3/cycle/new/.noindex \ testdir3/cycle/cur/rogue0 \ testdir3/cycle/cur/cycle0 \ testdir3/cycle/cur/cycle0.0 \ testdir3/cycle/cur/cycle0.0.0 \ testdir3/cycle/tmp/.noindex \ testdir3/tree/new/.noindex \ testdir3/tree/cur/child0.0 \ testdir3/tree/cur/child4.0 \ testdir3/tree/cur/root2 \ testdir3/tree/cur/root1 \ testdir3/tree/cur/child3.0.0.0.0 \ testdir3/tree/cur/root0 \ testdir3/tree/cur/child2.0.0 \ testdir3/tree/cur/child0.1 \ testdir3/tree/cur/child0.1.0 \ testdir3/tree/cur/child4.1 \ testdir3/tree/tmp/.noindex \ testdir3/sort/1st-child-promotes-thread/cur/A \ testdir3/sort/1st-child-promotes-thread/cur/B \ testdir3/sort/1st-child-promotes-thread/cur/C \ testdir3/sort/1st-child-promotes-thread/cur/D \ testdir3/sort/2nd-child-promotes-thread/cur/A \ testdir3/sort/2nd-child-promotes-thread/cur/B \ testdir3/sort/2nd-child-promotes-thread/cur/C \ testdir3/sort/2nd-child-promotes-thread/cur/D \ testdir3/sort/2nd-child-promotes-thread/cur/E \ testdir3/sort/child-does-not-promote-thread/cur/A \ testdir3/sort/child-does-not-promote-thread/cur/X \ testdir3/sort/child-does-not-promote-thread/cur/Y \ testdir3/sort/child-does-not-promote-thread/cur/Z \ testdir3/sort/grandchild-promotes-only-subthread/cur/A \ testdir3/sort/grandchild-promotes-only-subthread/cur/B \ testdir3/sort/grandchild-promotes-only-subthread/cur/C \ testdir3/sort/grandchild-promotes-only-subthread/cur/D \ testdir3/sort/grandchild-promotes-only-subthread/cur/E \ testdir3/sort/grandchild-promotes-only-subthread/cur/F \ testdir3/sort/grandchild-promotes-only-subthread/cur/G \ testdir3/sort/grandchild-promotes-thread/cur/A \ testdir3/sort/grandchild-promotes-thread/cur/B \ testdir3/sort/grandchild-promotes-thread/cur/C \ testdir3/sort/grandchild-promotes-thread/cur/D \ testdir3/sort/grandchild-promotes-thread/cur/E \ testdir4/1220863087.12663_19.mindcrime!2,S \ testdir4/1220863042.12663_1.mindcrime!2,S \ testdir4/1283599333.1840_11.cthulhu!2, \ testdir4/1305664394.2171_402.cthulhu!2, \ testdir4/1252168370_3.14675.cthulhu!2,S \ testdir4/mail1 \ testdir4/mail5 \ testdir4/181736.eml \ testdir4/encrypted!2,S \ testdir4/multimime!2,FS \ testdir4/signed!2,S \ testdir4/signed-bad!2,S \ testdir4/signed-encrypted!2,S \ testdir4/special!2,Sabc ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/test-mu-maildir.c���������������������������������������������������������������0000644�0001750�0001750�00000040606�13020504332�014242� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2016 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <glib/gstdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include "test-mu-common.h" #include "mu-maildir.h" #include "mu-util.h" static void test_mu_maildir_mkdir_01 (void) { int i; gchar *tmpdir, *mdir, *tmp; const gchar *subs[] = {"tmp", "cur", "new"}; tmpdir = test_mu_common_get_random_tmpdir (); mdir = g_strdup_printf ("%s%c%s", tmpdir, G_DIR_SEPARATOR, "cuux"); g_assert_cmpuint (mu_maildir_mkdir (mdir, 0755, FALSE, NULL), ==, TRUE); for (i = 0; i != G_N_ELEMENTS(subs); ++i) { gchar* dir; dir = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, subs[i]); g_assert_cmpuint (g_access (dir, R_OK), ==, 0); g_assert_cmpuint (g_access (dir, W_OK), ==, 0); g_free (dir); } tmp = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, ".noindex"); g_assert_cmpuint (g_access (tmp, F_OK), !=, 0); g_free (tmp); g_free (tmpdir); g_free (mdir); } static void test_mu_maildir_mkdir_02 (void) { int i; gchar *tmpdir, *mdir, *tmp; const gchar *subs[] = {"tmp", "cur", "new"}; tmpdir = test_mu_common_get_random_tmpdir (); mdir = g_strdup_printf ("%s%c%s", tmpdir, G_DIR_SEPARATOR, "cuux"); g_assert_cmpuint (mu_maildir_mkdir (mdir, 0755, TRUE, NULL), ==, TRUE); for (i = 0; i != G_N_ELEMENTS(subs); ++i) { gchar* dir; dir = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, subs[i]); g_assert_cmpuint (g_access (dir, R_OK), ==, 0); g_assert_cmpuint (g_access (dir, W_OK), ==, 0); g_free (dir); } tmp = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, ".noindex"); g_assert_cmpuint (g_access (tmp, F_OK), ==, 0); g_free (tmp); g_free (tmpdir); g_free (mdir); } static void test_mu_maildir_mkdir_03 (void) { int i; gchar *tmpdir, *mdir, *tmp; const gchar *subs[] = {"tmp", "cur", "new"}; tmpdir = test_mu_common_get_random_tmpdir (); mdir = g_strdup_printf ("%s%c%s", tmpdir, G_DIR_SEPARATOR, "cuux"); /* create part of the structure already... */ { gchar *dir; dir = g_strdup_printf ("%s%ccur", mdir, G_DIR_SEPARATOR); g_assert_cmpuint (g_mkdir_with_parents (dir, 0755), ==, 0); g_free (dir); } /* this should still work */ g_assert_cmpuint (mu_maildir_mkdir (mdir, 0755, FALSE, NULL), ==, TRUE); for (i = 0; i != G_N_ELEMENTS(subs); ++i) { gchar* dir; dir = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, subs[i]); g_assert_cmpuint (g_access (dir, R_OK), ==, 0); g_assert_cmpuint (g_access (dir, W_OK), ==, 0); g_free (dir); } tmp = g_strdup_printf ("%s%c%s", mdir, G_DIR_SEPARATOR, ".noindex"); g_assert_cmpuint (g_access (tmp, F_OK), !=, 0); g_free (tmp); g_free (tmpdir); g_free (mdir); } static void test_mu_maildir_mkdir_04 (void) { gchar *tmpdir, *mdir; tmpdir = test_mu_common_get_random_tmpdir (); mdir = g_strdup_printf ("%s%c%s", tmpdir, G_DIR_SEPARATOR, "cuux"); /* create part of the structure already... */ { gchar *dir; g_assert_cmpuint (g_mkdir_with_parents (mdir, 0755), ==, 0); dir = g_strdup_printf ("%s%ccur", mdir, G_DIR_SEPARATOR); g_assert_cmpuint (g_mkdir_with_parents (dir, 0000), ==, 0); g_free (dir); } /* this should fail now, because cur is not read/writable */ g_assert_cmpuint (mu_maildir_mkdir (mdir, 0755, FALSE, NULL), ==, (geteuid()==0 ? TRUE : FALSE)); g_free (tmpdir); g_free (mdir); } static gboolean ignore_error (const char* log_domain, GLogLevelFlags log_level, const gchar* msg, gpointer user_data) { return FALSE; /* don't abort */ } static void test_mu_maildir_mkdir_05 (void) { /* this must fail */ g_test_log_set_fatal_handler ((GTestLogFatalFunc)ignore_error, NULL); g_assert_cmpuint (mu_maildir_mkdir (NULL, 0755, TRUE, NULL), ==, FALSE); } static gchar* copy_test_data (void) { gchar *dir, *cmd; dir = test_mu_common_get_random_tmpdir(); cmd = g_strdup_printf ("mkdir -m 0700 %s", dir); if (g_test_verbose()) g_print ("cmd: %s\n", cmd); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); cmd = g_strdup_printf ("cp -R %s %s", MU_TESTMAILDIR, dir); if (g_test_verbose()) g_print ("cmd: %s\n", cmd); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); return dir; } typedef struct { int _file_count; int _dir_entered; int _dir_left; } WalkData; static MuError dir_cb (const char *fullpath, gboolean enter, WalkData *data) { if (enter) ++data->_dir_entered; else ++data->_dir_left; if (g_test_verbose()) g_print ("%s: %s: %s (%u)\n", __func__, enter ? "entering" : "leaving", fullpath, enter ? data->_dir_entered : data->_dir_left); return MU_OK; } static MuError msg_cb (const char *fullpath, const char* mdir, struct stat *statinfo, WalkData *data) { ++data->_file_count; return MU_OK; } static void test_mu_maildir_walk_01 (void) { char *tmpdir; WalkData data; MuError rv; tmpdir = copy_test_data (); memset (&data, 0, sizeof(WalkData)); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, TRUE, &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 19); g_assert_cmpuint (data._dir_entered,==, 5); g_assert_cmpuint (data._dir_left,==, 5); g_free (tmpdir); } static void test_mu_maildir_walk (void) { char *tmpdir, *cmd, *dir; WalkData data; MuError rv; tmpdir = copy_test_data (); memset (&data, 0, sizeof(WalkData)); /* mark the 'new' dir with '.noindex', to ignore it */ dir = g_strdup_printf ("%s%ctestdir%cnew", tmpdir, G_DIR_SEPARATOR, G_DIR_SEPARATOR); cmd = g_strdup_printf ("chmod 700 %s", dir); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); cmd = g_strdup_printf ("touch %s%c.noindex", dir, G_DIR_SEPARATOR); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); g_free (dir); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, TRUE, &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 15); g_assert_cmpuint (data._dir_entered,==, 4); g_assert_cmpuint (data._dir_left,==, 4); g_free (tmpdir); } static void test_mu_maildir_walk_with_noupdate (void) { char *tmpdir, *cmd, *dir; WalkData data; MuError rv; tmpdir = copy_test_data (); /* mark the 'new' dir with '.noindex', to ignore it */ dir = g_strdup_printf ("%s%ctestdir%cnew", tmpdir, G_DIR_SEPARATOR, G_DIR_SEPARATOR); cmd = g_strdup_printf ("chmod 700 %s", dir); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); memset (&data, 0, sizeof(WalkData)); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, FALSE, /* ie., non-full update */ &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 19); g_assert_cmpuint (data._dir_entered,==, 5); g_assert_cmpuint (data._dir_left,==, 5); /* again, full update. results should be the same, since there * is no noupdate yet */ memset (&data, 0, sizeof(WalkData)); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, TRUE, /* ie., full update */ &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 19); g_assert_cmpuint (data._dir_entered,==, 5); g_assert_cmpuint (data._dir_left,==, 5); /* add a '.noupdate' file; this affects the outcome when the * 4th arg to mu_maildir_walk is FALSE */ cmd = g_strdup_printf ("touch %s%c.noupdate", dir, G_DIR_SEPARATOR); g_assert (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL)); g_free (cmd); memset (&data, 0, sizeof(WalkData)); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, FALSE, /* non-full update */ &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 15); g_assert_cmpuint (data._dir_entered,==, 4); g_assert_cmpuint (data._dir_left,==, 4); /* now run again, but do a full update */ memset (&data, 0, sizeof(WalkData)); rv = mu_maildir_walk (tmpdir, (MuMaildirWalkMsgCallback)msg_cb, (MuMaildirWalkDirCallback)dir_cb, TRUE, /* full update */ &data); g_assert_cmpuint (MU_OK, ==, rv); g_assert_cmpuint (data._file_count, ==, 19); g_assert_cmpuint (data._dir_entered,==, 5); g_assert_cmpuint (data._dir_left,==, 5); g_free (dir); g_free (tmpdir); } static void test_mu_maildir_get_flags_from_path (void) { int i; struct { const char *path; MuFlags flags; } paths[] = { { "/home/foo/Maildir/test/cur/123456:2,FSR", MU_FLAG_REPLIED | MU_FLAG_SEEN | MU_FLAG_FLAGGED }, { "/home/foo/Maildir/test/new/123456", MU_FLAG_NEW }, { /* NOTE: when in new/, the :2,.. stuff is ignored */ "/home/foo/Maildir/test/new/123456:2,FR", MU_FLAG_NEW }, { "/home/foo/Maildir/test/cur/123456:2,DTP", MU_FLAG_DRAFT | MU_FLAG_TRASHED | MU_FLAG_PASSED }, { "/home/foo/Maildir/test/cur/123456:2,S", MU_FLAG_SEEN } }; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { MuFlags flags; flags = mu_maildir_get_flags_from_path(paths[i].path); g_assert_cmpuint(flags, ==, paths[i].flags); } } static void assert_matches_regexp (const char *str, const char *rx) { if (!g_regex_match_simple (rx, str, 0, 0)) { if (g_test_verbose ()) g_print ("%s does not match %s", str, rx); g_assert (0); } } static void test_mu_maildir_get_new_path_new (void) { int i; struct { const char *oldpath; MuFlags flags; const char *newpath; } paths[] = { { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_REPLIED, "/home/foo/Maildir/test/cur/123456:2,R" }, { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_NEW, "/home/foo/Maildir/test/new/123456" }, { "/home/foo/Maildir/test/new/123456:2,FR", MU_FLAG_SEEN | MU_FLAG_REPLIED, "/home/foo/Maildir/test/cur/123456:2,RS" }, { "/home/foo/Maildir/test/new/1313038887_0.697:2,", MU_FLAG_SEEN | MU_FLAG_FLAGGED | MU_FLAG_PASSED, "/home/foo/Maildir/test/cur/1313038887_0.697:2,FPS" }, { "/home/djcb/Maildir/trash/new/1312920597.2206_16.cthulhu", MU_FLAG_SEEN, "/home/djcb/Maildir/trash/cur/1312920597.2206_16.cthulhu:2,S" } }; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { char *str, *newbase; str = mu_maildir_get_new_path (paths[i].oldpath, NULL, paths[i].flags, TRUE); newbase = g_path_get_basename (str); assert_matches_regexp (newbase, "\\d+\\." "[[:xdigit:]]{16}\\." "[[:alpha:]]+(:2,.*)?"); g_free (newbase); g_free(str); } } static void test_mu_maildir_get_new_path_01 (void) { int i; struct { const char *oldpath; MuFlags flags; const char *newpath; } paths[] = { { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_REPLIED, "/home/foo/Maildir/test/cur/123456:2,R" }, { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_NEW, "/home/foo/Maildir/test/new/123456" }, { "/home/foo/Maildir/test/new/123456:2,FR", MU_FLAG_SEEN | MU_FLAG_REPLIED, "/home/foo/Maildir/test/cur/123456:2,RS" }, { "/home/foo/Maildir/test/new/1313038887_0.697:2,", MU_FLAG_SEEN | MU_FLAG_FLAGGED | MU_FLAG_PASSED, "/home/foo/Maildir/test/cur/1313038887_0.697:2,FPS" }, { "/home/djcb/Maildir/trash/new/1312920597.2206_16.cthulhu", MU_FLAG_SEEN, "/home/djcb/Maildir/trash/cur/1312920597.2206_16.cthulhu:2,S" } }; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { gchar *str; str = mu_maildir_get_new_path(paths[i].oldpath, NULL, paths[i].flags, FALSE); g_assert_cmpstr(str, ==, paths[i].newpath); g_free(str); } } static void test_mu_maildir_get_new_path_02 (void) { int i; struct { const char *oldpath; MuFlags flags; const char *targetdir; const char *newpath; } paths[] = { { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_REPLIED, "/home/foo/Maildir/blabla", "/home/foo/Maildir/blabla/cur/123456:2,R" }, { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_NEW, "/home/bar/Maildir/coffee", "/home/bar/Maildir/coffee/new/123456" }, { "/home/foo/Maildir/test/new/123456", MU_FLAG_SEEN | MU_FLAG_REPLIED, "/home/cuux/Maildir/tea", "/home/cuux/Maildir/tea/cur/123456:2,RS" }, { "/home/foo/Maildir/test/new/1313038887_0.697:2,", MU_FLAG_SEEN | MU_FLAG_FLAGGED | MU_FLAG_PASSED, "/home/boy/Maildir/stuff", "/home/boy/Maildir/stuff/cur/1313038887_0.697:2,FPS" } }; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { gchar *str; str = mu_maildir_get_new_path(paths[i].oldpath, paths[i].targetdir, paths[i].flags, FALSE); g_assert_cmpstr(str, ==, paths[i].newpath); g_free(str); } } static void test_mu_maildir_get_new_path_custom (void) { int i; struct { const char *oldpath; MuFlags flags; const char *targetdir; const char *newpath; } paths[] = { { "/home/foo/Maildir/test/cur/123456:2,FR", MU_FLAG_REPLIED, "/home/foo/Maildir/blabla", "/home/foo/Maildir/blabla/cur/123456:2,R" }, { "/home/foo/Maildir/test/cur/123456:2,hFeRllo123", MU_FLAG_FLAGGED, "/home/foo/Maildir/blabla", "/home/foo/Maildir/blabla/cur/123456:2,Fhello123" }, { "/home/foo/Maildir/test/cur/123456:2,abc", MU_FLAG_PASSED, "/home/foo/Maildir/blabla", "/home/foo/Maildir/blabla/cur/123456:2,Pabc" } }; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { gchar *str; str = mu_maildir_get_new_path(paths[i].oldpath, paths[i].targetdir, paths[i].flags, FALSE); g_assert_cmpstr(str, ==, paths[i].newpath); g_free(str); } } static void test_mu_maildir_get_maildir_from_path (void) { unsigned u; struct { const char *path, *exp; } cases[] = { {"/home/foo/Maildir/test/cur/123456:2,FR", "/home/foo/Maildir/test"}, {"/home/foo/Maildir/lala/new/1313038887_0.697:2,", "/home/foo/Maildir/lala"} }; for (u = 0; u != G_N_ELEMENTS(cases); ++u) { gchar *mdir; mdir = mu_maildir_get_maildir_from_path (cases[u].path); g_assert_cmpstr(mdir,==,cases[u].exp); g_free (mdir); } } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_util_maildir_mkmdir */ g_test_add_func ("/mu-maildir/mu-maildir-mkdir-01", test_mu_maildir_mkdir_01); g_test_add_func ("/mu-maildir/mu-maildir-mkdir-02", test_mu_maildir_mkdir_02); g_test_add_func ("/mu-maildir/mu-maildir-mkdir-03", test_mu_maildir_mkdir_03); g_test_add_func ("/mu-maildir/mu-maildir-mkdir-04", test_mu_maildir_mkdir_04); g_test_add_func ("/mu-maildir/mu-maildir-mkdir-05", test_mu_maildir_mkdir_05); /* mu_util_maildir_walk */ g_test_add_func ("/mu-maildir/mu-maildir-walk-01", test_mu_maildir_walk_01); g_test_add_func ("/mu-maildir/mu-maildir-walk", test_mu_maildir_walk); g_test_add_func ("/mu-maildir/mu-maildir-walk-with-noupdate", test_mu_maildir_walk_with_noupdate); /* get/set flags */ g_test_add_func("/mu-maildir/mu-maildir-get-new-path-new", test_mu_maildir_get_new_path_new); g_test_add_func("/mu-maildir/mu-maildir-get-new-path-01", test_mu_maildir_get_new_path_01); g_test_add_func("/mu-maildir/mu-maildir-get-new-path-02", test_mu_maildir_get_new_path_02); g_test_add_func("/mu-maildir/mu-maildir-get-new-path-custom", test_mu_maildir_get_new_path_custom); g_test_add_func("/mu-maildir/mu-maildir-get-flags-from-path", test_mu_maildir_get_flags_from_path); g_test_add_func("/mu-maildir/mu-maildir-get-maildir-from-path", test_mu_maildir_get_maildir_from_path); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } ��������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/test-mu-common.c����������������������������������������������������������������0000644�0001750�0001750�00000003523�13020504332�014106� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <glib/gstdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <langinfo.h> #include <locale.h> #include "test-mu-common.h" char* test_mu_common_get_random_tmpdir (void) { char*dir; int res; dir = g_strdup_printf ( "%s%cmu-test-%d%ctest-%x", g_get_tmp_dir(), G_DIR_SEPARATOR, getuid(), G_DIR_SEPARATOR, (int)random()*getpid()*(int)time(NULL)); res = g_mkdir_with_parents (dir, 0700); g_assert (res != -1); return dir; } const char* set_tz (const char* tz) { static const char* oldtz; oldtz = getenv ("TZ"); if (tz) setenv ("TZ", tz, 1); else unsetenv ("TZ"); tzset (); return oldtz; } gboolean set_en_us_utf8_locale (void) { setenv ("LC_ALL", "en_US.utf8", 1); setlocale (LC_ALL, "en_US.utf8"); if (strcmp (nl_langinfo(CODESET), "UTF-8") != 0) { g_print ("Note: Unit tests require the en_US.utf8 locale. " "Ignoring test cases.\n"); return FALSE; } return TRUE; } void black_hole (void) { return; /* do nothing */ } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/test-mu-flags.c�����������������������������������������������������������������0000644�0001750�0001750�00000014153�13020504332�013713� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include "mu-flags.h" #include "test-mu-common.h" static void test_mu_flag_char (void) { g_assert_cmpuint (mu_flag_char (MU_FLAG_DRAFT), ==, 'D'); g_assert_cmpuint (mu_flag_char (MU_FLAG_FLAGGED), ==, 'F'); g_assert_cmpuint (mu_flag_char (MU_FLAG_PASSED), ==, 'P'); g_assert_cmpuint (mu_flag_char (MU_FLAG_REPLIED), ==, 'R'); g_assert_cmpuint (mu_flag_char (MU_FLAG_SEEN), ==, 'S'); g_assert_cmpuint (mu_flag_char (MU_FLAG_TRASHED), ==, 'T'); g_assert_cmpuint (mu_flag_char (MU_FLAG_NEW), ==, 'N'); g_assert_cmpuint (mu_flag_char (MU_FLAG_SIGNED), ==, 'z'); g_assert_cmpuint (mu_flag_char (MU_FLAG_ENCRYPTED), ==, 'x'); g_assert_cmpuint (mu_flag_char (MU_FLAG_HAS_ATTACH), ==, 'a'); g_assert_cmpuint (mu_flag_char (MU_FLAG_UNREAD), ==, 'u'); g_assert_cmpuint (mu_flag_char (12345), ==, 0); } static void test_mu_flag_name (void) { g_assert_cmpstr (mu_flag_name (MU_FLAG_DRAFT), ==, "draft"); g_assert_cmpstr (mu_flag_name (MU_FLAG_FLAGGED), ==, "flagged"); g_assert_cmpstr (mu_flag_name (MU_FLAG_PASSED), ==, "passed"); g_assert_cmpstr (mu_flag_name (MU_FLAG_REPLIED), ==, "replied"); g_assert_cmpstr (mu_flag_name (MU_FLAG_SEEN), ==, "seen"); g_assert_cmpstr (mu_flag_name (MU_FLAG_TRASHED), ==, "trashed"); g_assert_cmpstr (mu_flag_name (MU_FLAG_NEW), ==, "new"); g_assert_cmpstr (mu_flag_name (MU_FLAG_SIGNED), ==, "signed"); g_assert_cmpstr (mu_flag_name (MU_FLAG_ENCRYPTED), ==, "encrypted"); g_assert_cmpstr (mu_flag_name (MU_FLAG_HAS_ATTACH), ==, "attach"); g_assert_cmpstr (mu_flag_name (MU_FLAG_UNREAD), ==, "unread"); g_assert_cmpstr (mu_flag_name (12345), ==, NULL); } static void test_mu_flags_to_str_s (void) { g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_PASSED|MU_FLAG_SIGNED, MU_FLAG_TYPE_ANY), ==, "Pz"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_NEW, MU_FLAG_TYPE_ANY), ==, "N"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_HAS_ATTACH|MU_FLAG_TRASHED, MU_FLAG_TYPE_ANY), ==, "Ta"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_NONE, MU_FLAG_TYPE_ANY), ==, ""); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_PASSED|MU_FLAG_SIGNED, MU_FLAG_TYPE_CONTENT), ==, "z"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_NEW, MU_FLAG_TYPE_MAILDIR), ==, "N"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_HAS_ATTACH|MU_FLAG_TRASHED, MU_FLAG_TYPE_MAILFILE), ==, "T"); g_assert_cmpstr (mu_flags_to_str_s(MU_FLAG_NONE, MU_FLAG_TYPE_PSEUDO), ==, ""); } static void test_mu_flags_from_str (void) { /* note, the 3rd arg to mu_flags_from_str determines whether * invalid flags will be ignored (if TRUE) or MU_FLAG_INVALID (if FALSE) */ g_assert_cmpuint (mu_flags_from_str ("RP", MU_FLAG_TYPE_ANY, TRUE), ==, MU_FLAG_REPLIED | MU_FLAG_PASSED); g_assert_cmpuint (mu_flags_from_str ("Nz", MU_FLAG_TYPE_ANY, TRUE), ==, MU_FLAG_NEW | MU_FLAG_SIGNED); g_assert_cmpuint (mu_flags_from_str ("axD", MU_FLAG_TYPE_ANY, TRUE), ==, MU_FLAG_HAS_ATTACH | MU_FLAG_ENCRYPTED | MU_FLAG_DRAFT); g_assert_cmpuint (mu_flags_from_str ("RP", MU_FLAG_TYPE_MAILFILE, TRUE), ==, MU_FLAG_REPLIED | MU_FLAG_PASSED); g_assert_cmpuint (mu_flags_from_str ("Nz", MU_FLAG_TYPE_MAILFILE, TRUE), ==, MU_FLAG_NONE); /* ignore errors or not */ g_assert_cmpuint (mu_flags_from_str ("qwi", MU_FLAG_TYPE_MAILFILE, FALSE), ==, MU_FLAG_INVALID); g_assert_cmpuint (mu_flags_from_str ("qwi", MU_FLAG_TYPE_MAILFILE, TRUE), ==, 0); } static void test_mu_flags_from_str_delta (void) { g_assert_cmpuint (mu_flags_from_str_delta ("+S-R", MU_FLAG_REPLIED | MU_FLAG_DRAFT, MU_FLAG_TYPE_ANY),==, MU_FLAG_SEEN | MU_FLAG_DRAFT); g_assert_cmpuint (mu_flags_from_str_delta ("", MU_FLAG_REPLIED | MU_FLAG_DRAFT, MU_FLAG_TYPE_ANY),==, MU_FLAG_REPLIED | MU_FLAG_DRAFT); g_assert_cmpuint (mu_flags_from_str_delta ("-N+P+S-D", MU_FLAG_SIGNED | MU_FLAG_DRAFT, MU_FLAG_TYPE_ANY),==, MU_FLAG_PASSED | MU_FLAG_SEEN | MU_FLAG_SIGNED); } static void test_mu_flags_custom_from_str (void) { unsigned u; struct { const char *str; const char *expected; } cases[] = { { "ABC", "ABC" }, { "PAF", "A" }, { "ShelloPwoFrDldR123", "helloworld123" }, { "SPD", NULL } }; for (u = 0; u != G_N_ELEMENTS(cases); ++u) { char *cust; cust = mu_flags_custom_from_str (cases[u].str); if (g_test_verbose()) g_print ("%s: str:%s; got:%s; expected:%s\n", __func__, cases[u].str, cust, cases[u].expected); g_assert_cmpstr (cust, ==, cases[u].expected); g_free (cust); } } int main (int argc, char *argv[]) { int rv; g_test_init (&argc, &argv, NULL); /* mu_msg_str_date */ g_test_add_func ("/mu-flags/test-mu-flag-char", test_mu_flag_char); g_test_add_func ("/mu-flags/test-mu-flag-name",test_mu_flag_name); g_test_add_func ("/mu-flags/test-mu-flags-to-str-s",test_mu_flags_to_str_s); g_test_add_func ("/mu-flags/test-mu-flags-from-str",test_mu_flags_from_str); g_test_add_func ("/mu-flags/test-mu-flags-from-str-delta",test_mu_flags_from_str_delta ); g_test_add_func ("/mu-flags/test-mu-flags-custom-from-str", test_mu_flags_custom_from_str); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); rv = g_test_run (); return rv; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/test-mu-store.c�����������������������������������������������������������������0000644�0001750�0001750�00000012351�13020504332�013751� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <locale.h> #include "test-mu-common.h" #include "mu-store.h" static void test_mu_store_new_destroy (void) { MuStore *store; gchar* tmpdir; GError *err; tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); err = NULL; store = mu_store_new_writable (tmpdir, NULL, FALSE, &err); g_assert_no_error (err); g_assert (store); g_assert_cmpuint (0,==,mu_store_count (store, NULL)); mu_store_flush (store); mu_store_unref (store); g_free (tmpdir); } static void test_mu_store_version (void) { MuStore *store; gchar* tmpdir; GError *err; tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); err = NULL; store = mu_store_new_writable (tmpdir, NULL, FALSE, &err); g_assert (store); mu_store_unref (store); store = mu_store_new_read_only (tmpdir, &err); g_assert (store); g_assert (err == NULL); g_assert_cmpuint (0,==,mu_store_count (store, NULL)); g_assert_cmpstr (MU_STORE_SCHEMA_VERSION,==, mu_store_version(store)); mu_store_unref (store); g_free (tmpdir); } G_GNUC_UNUSED static void test_mu_store_store_msg_and_count (void) { MuMsg *msg; MuStore *store; gchar* tmpdir; tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); store = mu_store_new_writable (tmpdir, NULL, FALSE, NULL); g_assert (store); g_free (tmpdir); g_assert_cmpuint (0,==,mu_store_count (store, NULL)); /* add one */ /* XXX this passes, but not make-dist; investigate */ msg = mu_msg_new_from_file ( MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,", NULL, NULL); g_assert (msg); g_assert_cmpuint (mu_store_add_msg (store, msg, NULL), !=, MU_STORE_INVALID_DOCID); g_assert_cmpuint (1,==,mu_store_count (store, NULL)); g_assert_cmpuint (TRUE,==,mu_store_contains_message (store, MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,", NULL)); mu_msg_unref (msg); /* add another one */ msg = mu_msg_new_from_file (MU_TESTMAILDIR2 "/bar/cur/mail3", NULL, NULL); g_assert (msg); g_assert_cmpuint (mu_store_add_msg (store, msg, NULL), !=, MU_STORE_INVALID_DOCID); g_assert_cmpuint (2,==,mu_store_count (store, NULL)); g_assert_cmpuint (TRUE,==, mu_store_contains_message (store, MU_TESTMAILDIR2 "/bar/cur/mail3", NULL)); mu_msg_unref (msg); /* try to add the first one again. count should be 2 still */ msg = mu_msg_new_from_file (MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,", NULL, NULL); g_assert (msg); g_assert_cmpuint (mu_store_add_msg (store, msg, NULL), !=, MU_STORE_INVALID_DOCID); g_assert_cmpuint (2,==,mu_store_count (store, NULL)); mu_msg_unref (msg); mu_store_unref (store); } G_GNUC_UNUSED static void test_mu_store_store_msg_remove_and_count (void) { MuMsg *msg; MuStore *store; gchar* tmpdir; GError *err; tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); store = mu_store_new_writable (tmpdir, NULL, FALSE, NULL); g_assert (store); g_assert_cmpuint (0,==,mu_store_count (store, NULL)); /* add one */ err = NULL; msg = mu_msg_new_from_file ( MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,", NULL, &err); g_assert (msg); g_assert_cmpuint (mu_store_add_msg (store, msg, NULL), !=, MU_STORE_INVALID_DOCID); g_assert_cmpuint (1,==,mu_store_count (store, NULL)); mu_msg_unref (msg); /* remove one */ mu_store_remove_path (store, MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,"); g_assert_cmpuint (0,==,mu_store_count (store, NULL)); g_assert_cmpuint (FALSE,==,mu_store_contains_message (store, MU_TESTMAILDIR "/cur/1283599333.1840_11.cthulhu!2,", NULL)); g_free (tmpdir); mu_store_unref (store); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_runtime_init/uninit */ g_test_add_func ("/mu-store/mu-store-new-destroy", test_mu_store_new_destroy); g_test_add_func ("/mu-store/mu-store-version", test_mu_store_version); #if 0 /* XXX this passes, but not make-dist; investigate */ g_test_add_func ("/mu-store/mu-store-store-and-count", test_mu_store_store_msg_and_count); g_test_add_func ("/mu-store/mu-store-store-remove-and-count", test_mu_store_store_msg_remove_and_count); #endif if (!g_test_verbose()) g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/test-mu-str.c�������������������������������������������������������������������0000644�0001750�0001750�00000030741�13020504332�013430� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include <glib.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <locale.h> #include "test-mu-common.h" #include "mu-str.h" #include "mu-msg-prio.h" static void test_mu_str_size_01 (void) { struct lconv *lc; char *tmp2; lc = localeconv(); tmp2 = g_strdup_printf ("0%s0 kB", lc->decimal_point); g_assert_cmpstr (mu_str_size_s (0), ==, tmp2); g_free (tmp2); tmp2 = g_strdup_printf ("100%s0 kB", lc->decimal_point); g_assert_cmpstr (mu_str_size_s (100000), ==, tmp2); g_free (tmp2); tmp2 = g_strdup_printf ("1%s1 MB", lc->decimal_point); g_assert_cmpstr (mu_str_size_s (1100*1000), ==, tmp2); g_free (tmp2); } static void test_mu_str_size_02 (void) { struct lconv *lc; char *tmp1, *tmp2; lc = localeconv(); tmp2 = g_strdup_printf ("1%s0 MB", lc->decimal_point); tmp1 = mu_str_size (999999); g_assert_cmpstr (tmp1, !=, tmp2); g_free (tmp1); g_free (tmp2); } static void test_mu_str_prio_01 (void) { g_assert_cmpstr(mu_msg_prio_name(MU_MSG_PRIO_LOW), ==, "low"); g_assert_cmpstr(mu_msg_prio_name(MU_MSG_PRIO_NORMAL), ==, "normal"); g_assert_cmpstr(mu_msg_prio_name(MU_MSG_PRIO_HIGH), ==, "high"); } static gboolean ignore_error (const char* log_domain, GLogLevelFlags log_level, const gchar* msg, gpointer user_data) { return FALSE; /* don't abort */ } static void test_mu_str_prio_02 (void) { /* this must fail */ g_test_log_set_fatal_handler ((GTestLogFatalFunc)ignore_error, NULL); g_assert_cmpstr (mu_msg_prio_name(666), ==, NULL); } static void test_mu_str_flatten (void) { int i; struct { const char* word; const char* norm; } words [] = { { "dantès", "dantes"}, { "foo", "foo" }, { "Föö", "foo" }, { "číslo", "cislo" }, { "hÆvý mëÐal ümláõt", "hævy_meðal_umlaot"} }; for (i = 0; i != G_N_ELEMENTS(words); ++i) { gchar *str; str = mu_str_process_term (words[i].word); g_assert_cmpstr (str, ==, words[i].norm); g_free (str); } } static void test_parse_arglist (void) { const char *args; GHashTable *hash; GError *err; args = "cmd:find query:\"maildir:\\\"/sent items\\\"\" maxnum:500"; err = NULL; hash = mu_str_parse_arglist (args, &err); g_assert_no_error (err); g_assert (hash); g_assert_cmpstr (g_hash_table_lookup (hash, "cmd"), ==, "find"); g_assert_cmpstr (g_hash_table_lookup (hash, "query"), ==, "maildir:\"/sent items\""); g_assert_cmpstr (g_hash_table_lookup (hash, "maxnum"), ==, "500"); g_hash_table_destroy (hash); } static void test_mu_str_esc_to_list (void) { int i; struct { const char* str; const char* strs[3]; } strings [] = { { "maildir:foo", {"maildir:foo", NULL, NULL}}, { "maildir:sent items", {"maildir:sent", "items", NULL}}, { "\"maildir:sent items\"", {"maildir:sent items", NULL, NULL}}, }; for (i = 0; i != G_N_ELEMENTS(strings); ++i) { GSList *lst, *cur; unsigned u; lst = mu_str_esc_to_list (strings[i].str); for (cur = lst, u = 0; cur; cur = g_slist_next(cur), ++u) g_assert_cmpstr ((const char*)cur->data,==, strings[i].strs[u]); mu_str_free_list (lst); } } static void test_mu_str_process_query_term (void) { int i; struct { const char* word; const char* esc; } words [] = { { "aap@noot.mies", "aap_noot_mies" }, { "Foo..Bar", "foo__bar" }, { "Foo.Bar", "foo_bar" }, { "Foo Bar", "foo_bar" }, { "\\foo", "_foo" }, { "subject:test@foo", "subject:test_foo" }, { "xxx:test@bar", "xxx:test_bar" }, { "aa$bb$cc", "aa_bb_cc" }, { "date:2010..2012", "date:2010..2012"}, { "d:2010..2012", "d:2010..2012"}, { "size:10..20", "size:10..20"}, { "x:2010..2012", "x:2010__2012"}, { "q:2010..2012", "q:2010__2012"}, { "subject:2010..2012", "subject:2010__2012"}, { "(maildir:foo)", "(maildir:foo)"}, { "Тесла, Никола", "тесла__никола"}, { "Masha@Аркона.ru", "masha_аркона_ru" }, { "foo:ελληνικά", "foo:ελληνικα" }, { "日本語!!", "日本語__" }, { "£", "_" } }; for (i = 0; i != G_N_ELEMENTS(words); ++i) { gchar *s; s = mu_str_process_query_term (words[i].word); if (g_test_verbose()) g_print ("expected: '%s' <=> got: '%s'\n", words[i].esc, s); g_assert_cmpstr (s, ==, words[i].esc); g_free (s); } } static void test_mu_str_process_term (void) { int i; struct { const char* word; const char* esc; } words [] = { { "aap@noot.mies", "aap_noot_mies" }, { "A&B", "a_b" }, { "Foo..Bar", "foo__bar" }, { "Foo.Bar", "foo_bar" }, { "Foo Bar", "foo_bar" }, { "\\foo", "_foo" }, { "subject:test@foo", "subject_test_foo" }, { "xxx:test@bar", "xxx_test_bar" }, { "aa$bb$cc", "aa_bb_cc" }, { "date:2010..2012", "date_2010__2012"}, { "subject:2010..2012", "subject_2010__2012"}, { "(maildir:foo)", "_maildir_foo_"}, { "Тесла, Никола", "тесла__никола"}, { "Masha@Аркона.ru", "masha_аркона_ru" }, { "foo:ελληνικά", "foo_ελληνικα" }, { "日本語!!", "日本語__" }, { "£", "_" }, /* invalid utf8 */ { "Hello\xC3\x2EWorld", "hello__world" } }; for (i = 0; i != G_N_ELEMENTS(words); ++i) { gchar *s; s = mu_str_process_term (words[i].word); if (g_test_verbose()) g_print ("expected: '%s' <=> got: '%s'\n", words[i].esc, s); g_assert_cmpstr (s, ==, words[i].esc); g_free (s); } } static void test_mu_str_process_text (void) { int i; struct { const char* word; const char* esc; } words [] = { { "aap@noot.mies", "aap@noot.mies" }, { "A&B", "a&b" }, { "Foo..Bar", "foo..bar" }, { "Foo.Bar", "foo.bar" }, { "Foo Bar", "foo bar" }, { "\\foo", "\\foo" }, { "subject:test@foo", "subject:test@foo" }, { "xxx:test@bar", "xxx:test@bar" }, { "aa$bb$cc", "aa$bb$cc" }, { "date:2010..2012", "date:2010..2012"}, { "subject:2010..2012", "subject:2010..2012"}, { "(maildir:foo)", "(maildir:foo)"}, { "Тесла, Никола", "тесла, никола"}, { "Masha@Аркона.ru", "masha@аркона.ru" }, { "foo:ελληνικά", "foo:ελληνικα" }, { "日本語!!", "日本語!!" } }; for (i = 0; i != G_N_ELEMENTS(words); ++i) { gchar *s; s = mu_str_process_text (words[i].word); if (g_test_verbose()) g_print ("expected: '%s' <=> got: '%s'\n", words[i].esc, s); g_assert_cmpstr (s, ==, words[i].esc); g_free (s); } } static void test_mu_str_display_contact (void) { int i; struct { const char* word; const char* disp; } words [] = { { "\"Foo Bar\" <aap@noot.mies>", "Foo Bar"}, { "Foo Bar <aap@noot.mies>", "Foo Bar" }, { "<aap@noot.mies>", "aap@noot.mies" }, { "foo@bar.nl", "foo@bar.nl" } }; for (i = 0; i != G_N_ELEMENTS(words); ++i) g_assert_cmpstr (mu_str_display_contact_s (words[i].word), ==, words[i].disp); } static void assert_cmplst (GSList *lst, const char *items[]) { int i; if (!lst) g_assert (!items); for (i = 0; lst; lst = g_slist_next(lst), ++i) g_assert_cmpstr ((char*)lst->data,==,items[i]); g_assert (items[i] == NULL); } static GSList* create_list (const char *items[]) { GSList *lst; lst = NULL; while (items && *items) { lst = g_slist_prepend (lst, g_strdup(*items)); ++items; } return g_slist_reverse (lst); } static void test_mu_str_from_list (void) { { const char *strs[] = {"aap", "noot", "mies", NULL}; GSList *lst = create_list (strs); gchar *str = mu_str_from_list (lst, ','); g_assert_cmpstr ("aap,noot,mies", ==, str); mu_str_free_list (lst); g_free (str); } { const char *strs[] = {"aap", "no,ot", "mies", NULL}; GSList *lst = create_list (strs); gchar *str = mu_str_from_list (lst, ','); g_assert_cmpstr ("aap,no,ot,mies", ==, str); mu_str_free_list (lst); g_free (str); } { const char *strs[] = {NULL}; GSList *lst = create_list (strs); gchar *str = mu_str_from_list (lst,'@'); g_assert_cmpstr (NULL, ==, str); mu_str_free_list (lst); g_free (str); } } static void test_mu_str_to_list (void) { { const char *items[]= {"foo", "bar ", "cuux", NULL}; GSList *lst = mu_str_to_list ("foo@bar @cuux",'@', FALSE); assert_cmplst (lst, items); mu_str_free_list (lst); } { GSList *lst = mu_str_to_list (NULL,'x',FALSE); g_assert (lst == NULL); mu_str_free_list (lst); } } static void test_mu_str_to_list_strip (void) { const char *items[]= {"foo", "bar", "cuux", NULL}; GSList *lst = mu_str_to_list ("foo@bar @cuux",'@', TRUE); assert_cmplst (lst, items); mu_str_free_list (lst); } static void test_mu_str_subject_normalize (void) { int i; struct { char *src, *exp; } tests[] = { { "test123", "test123" }, { "Re:test123", "test123" }, { "Re: Fwd: test123", "test123" }, { "Re[3]: Fwd: test123", "test123" }, { "operation: mindcrime", "operation: mindcrime" }, /*...*/ { "", "" } }; for (i = 0; i != G_N_ELEMENTS(tests); ++i) g_assert_cmpstr (mu_str_subject_normalize (tests[i].src), ==, tests[i].exp); } static void test_mu_term_fixups (void) { unsigned u; struct { const gchar *expr, *expected; } testcases [] = { { "date:19700101", "date:19700101..19700101" }, { "date:19700101..19700101", "date:19700101..19700101" }, { "(date:20121107))", "(date:20121107..20121107))" }, { "maildir:/somepath", "maildir:/somepath" }, { "([maildir:/somepath]", "([maildir:/somepath]" }, /* add more */ { "({", "({" }, { "({abc", "({abc" }, { "abc)}", "abc)}" }, { "", "" } }; for (u = 0; u != G_N_ELEMENTS(testcases); ++u) { gchar *prep; prep = mu_str_xapian_fixup_terms (testcases[u].expr); g_assert_cmpstr (prep, ==, testcases[u].expected); g_free (prep); } } static void test_mu_str_replace (void) { unsigned u; struct { const char* str; const char* sub; const char *repl; const char *exp; } strings [] = { { "hello", "ll", "xx", "hexxo" }, { "hello", "hello", "hi", "hi" }, { "hello", "foo", "bar", "hello" } }; for (u = 0; u != G_N_ELEMENTS(strings); ++u) { char *res; res = mu_str_replace (strings[u].str, strings[u].sub, strings[u].repl); g_assert_cmpstr (res,==,strings[u].exp); g_free (res); } } int main (int argc, char *argv[]) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); /* mu_str_size */ g_test_add_func ("/mu-str/mu-str-size-01", test_mu_str_size_01); g_test_add_func ("/mu-str/mu-str-size-02", test_mu_str_size_02); /* mu_str_prio */ g_test_add_func ("/mu-str/mu-str-prio-01", test_mu_str_prio_01); g_test_add_func ("/mu-str/mu-str-prio-02", test_mu_str_prio_02); g_test_add_func ("/mu-str/mu-str-flatten", test_mu_str_flatten); g_test_add_func ("/mu-str/process-query-term", test_mu_str_process_query_term); g_test_add_func ("/mu-str/process-term", test_mu_str_process_term); g_test_add_func ("/mu-str/process-text", test_mu_str_process_text); g_test_add_func ("/mu-str/mu-str-display_contact", test_mu_str_display_contact); g_test_add_func ("/mu-str/mu-str-from-list", test_mu_str_from_list); g_test_add_func ("/mu-str/mu-str-to-list", test_mu_str_to_list); g_test_add_func ("/mu-str/mu-str-to-list-strip", test_mu_str_to_list_strip); g_test_add_func ("/mu-str/mu-str-parse-arglist", test_parse_arglist); g_test_add_func ("/mu-str/mu-str-replace", test_mu_str_replace); g_test_add_func ("/mu-str/mu-str-esc-to-list", test_mu_str_esc_to_list); g_test_add_func ("/mu-str/mu_str_subject_normalize", test_mu_str_subject_normalize); /* mu_str_xapian_fixup_terms */ g_test_add_func ("/mu-str/mu_term_fixups", test_mu_term_fixups); /* FIXME: add tests for mu_str_flags; but note the * function simply calls mu_msg_field_str */ g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, (GLogFunc)black_hole, NULL); return g_test_run (); } �������������������������������mu-0.9.18/lib/tests/testdir/������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�012616� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir/new/��������������������������������������������������������������������0000755�0001750�0001750�00000000000�13021065721�013407� 5����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir/new/1220863087.12663_21.mindcrime���������������������������������������0000644�0001750�0001750�00000012701�12605152237�017275� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 6389969CB2 for <xxxx@localhost>; Thu, 7 Aug 2008 08:10:07 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Thu, 07 Aug 2008 08:10:07 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs34769wfh; Wed, 6 Aug 2008 13:38:53 -0700 (PDT) Received: by 10.100.6.13 with SMTP id 13mr4103508anf.83.1218055131215; Wed, 06 Aug 2008 13:38:51 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id b32si10199298ana.34.2008.08.06.13.38.49; Wed, 06 Aug 2008 13:38:51 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; DomainKey-Status: good (test mode) Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org; domainkeys=pass (test mode) header.From=juanma_bellon@yahoo.es Received: from localhost ([127.0.0.1]:55648 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQpmT-0005W9-AQ for xxxx.klub@gmail.com; Wed, 06 Aug 2008 16:38:49 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KQplz-0005U5-Pk for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:38:19 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KQplw-0005Nw-OG for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:38:19 -0400 Received: from [199.232.76.173] (port=45465 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQplw-0005NX-I6 for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:38:16 -0400 Received: from n74a.bullet.mail.sp1.yahoo.com ([98.136.45.21]:29868) by monty-python.gnu.org with smtp (Exim 4.60) (envelope-from <juanma_bellon@yahoo.es>) id 1KQplw-0007EF-7Z for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:38:16 -0400 Received: from [216.252.122.216] by n74.bullet.mail.sp1.yahoo.com with NNFMP; 06 Aug 2008 20:38:14 -0000 Received: from [68.142.237.89] by t1.bullet.sp1.yahoo.com with NNFMP; 06 Aug 2008 20:38:14 -0000 Received: from [69.147.75.180] by t5.bullet.re3.yahoo.com with NNFMP; 06 Aug 2008 20:38:14 -0000 Received: from [127.0.0.1] by omp101.mail.re1.yahoo.com with NNFMP; 06 Aug 2008 20:38:14 -0000 X-Yahoo-Newman-Id: 778995.62909.bm@omp101.mail.re1.yahoo.com Received: (qmail 43643 invoked from network); 6 Aug 2008 20:38:14 -0000 DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=s1024; d=yahoo.es; h=Received:X-YMail-OSG:X-Yahoo-Newman-Property:From:To:Subject:Date:User-Agent:References:In-Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-Disposition:Message-Id; b=ThdHlND5CNUsLPGuk+XhCWkdUA9w7lg4hiAgx8F8egsmQteMpwUlV/Y5tfe6K3O2jzHjtsklkzWqm7WY3VAcxxD/QgxLnianK5ZQHoelDAiGaFRqu8Y42XMZso2ccCBFWUQaKo9C+KIfa3e3ci73qehVxTtmr7bxLjurcSYEBPo= ; Received: from unknown (HELO 212251170160.customer.cdi.no) (juanma_bellon@212.251.170.160 with plain) by smtp109.plus.mail.re1.yahoo.com with SMTP; 6 Aug 2008 20:38:14 -0000 X-YMail-OSG: k86L54kVM1kiZbUlYx7gayoBrCLYMFIRDL.KJLBKetNucAbwU4RjeeE1vhjw33hREaUig0CCjG7BTwIfbeZZpRmUcHbxl6gR0z6Sd3lYqA-- X-Yahoo-Newman-Property: ymail-3 From: anon@example.com To: help-gnu-emacs@gnu.org Date: Wed, 6 Aug 2008 22:38:15 +0200 User-Agent: KMail/1.9.6 (enterprise 0.20070907.709405) References: <mailman.15123.1216681940.18990.help-gnu-emacs@gnu.org> <mailman.15143.1216715014.18990.help-gnu-emacs@gnu.org> <9bc17528-8ea9-49f7-8e9d-07f5ede91415@p31g2000prf.googlegroups.com> In-Reply-To: <9bc17528-8ea9-49f7-8e9d-07f5ede91415@p31g2000prf.googlegroups.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline Message-Id: <200808062238.15634.juanma_bellon@yahoo.es> X-detected-kernel: by monty-python.gnu.org: FreeBSD 6.x (1) Subject: Re: basic question: going back to dired X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor <help-gnu-emacs.gnu.org> List-Unsubscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=unsubscribe> List-Archive: <http://lists.gnu.org/pipermail/help-gnu-emacs> List-Post: <mailto:help-gnu-emacs@gnu.org> List-Help: <mailto:help-gnu-emacs-request@gnu.org?subject=help> List-Subscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=subscribe> Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 361 On Thursday 31 July 2008, Xah wrote: > what's the logic of =E2=80=9COK=E2=80=9D? =46or all I know, it comes from "0 Knock-outs" (from USA civil war times, IIRC), i.e., all went really well. But this is really off-topic. =2D-=20 Juanma "Having a smoking section in a restaurant is like having a peeing section in a swimming pool." -- Edward Burr ���������������������������������������������������������������mu-0.9.18/lib/tests/testdir/new/1220863087.12663_25.mindcrime���������������������������������������0000644�0001750�0001750�00000010173�12605152237�017302� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-3.6 required=3.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id D68E769CB5 for <xxxx@localhost>; Fri, 8 Aug 2008 20:56:25 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Fri, 08 Aug 2008 20:56:25 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs71287wfh; Fri, 8 Aug 2008 07:40:46 -0700 (PDT) Received: by 10.100.122.8 with SMTP id u8mr3824321anc.77.1218206446062; Fri, 08 Aug 2008 07:40:46 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id d35si2718351and.38.2008.08.08.07.40.45; Fri, 08 Aug 2008 07:40:46 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Received: from localhost ([127.0.0.1]:47349 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KRT93-0006Po-A3 for xxxx.klub@gmail.com; Fri, 08 Aug 2008 10:40:45 -0400 Path: news.stanford.edu!headwall.stanford.edu!newshub.sdsu.edu!news-out.readnews.com!news-xxxfer.readnews.com!panix!not-for-mail From: anon@example.com Newsgroups: gnu.emacs.help Date: Fri, 08 Aug 2008 10:07:30 -0400 Organization: PANIX Public Access Internet and UNIX, NYC Message-ID: <uwsireh25.fsf@one.dot.net> References: <mailman.15123.1216681940.18990.help-gnu-emacs@gnu.org> <mailman.15143.1216715014.18990.help-gnu-emacs@gnu.org> <9bc17528-8ea9-49f7-8e9d-07f5ede91415@p31g2000prf.googlegroups.com> <200808062238.15634.juanma_bellon@yahoo.es> <mailman.15958.1218056266.18990.help-gnu-emacs@gnu.org> NNTP-Posting-Host: panix5.panix.com Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: reader1.panix.com 1218204439 22850 166.84.1.5 (8 Aug 2008 14:07:19 GMT) X-Complaints-To: abuse@panix.com NNTP-Posting-Date: Fri, 8 Aug 2008 14:07:19 +0000 (UTC) User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.2 (windows-nt) Cancel-Lock: sha1:Ckkp5oJPIMuAVgEHGnS/9MkZsEs= Xref: news.stanford.edu gnu.emacs.help:160963 To: help-gnu-emacs@gnu.org Subject: Re: basic question: going back to dired X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor <help-gnu-emacs.gnu.org> List-Unsubscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=unsubscribe> List-Archive: <http://lists.gnu.org/pipermail/help-gnu-emacs> List-Post: <mailto:help-gnu-emacs@gnu.org> List-Help: <mailto:help-gnu-emacs-request@gnu.org?subject=help> List-Subscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=subscribe> Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 710 Lines: 27 I seem to remember from my early school days it was a campaign slogan for someone nick-named Kinderhook that went something like Old Kinderhook is OK - Chris "Juanma Barranquero" <lekktu@gmail.com> writes: > On Wed, Aug 6, 2008 at 22:38, Juanma <juanma_bellon@yahoo.es> wrote: > >> For all I know, it comes from "0 Knock-outs" (from USA civil war times, >> IIRC), i.e., all went really well. > > See http://en.wikipedia.org/wiki/Okay#Etymology > > "0 knock-outs" is among the "Improbable or refuted etymologies". > > Juanma > > -- (. .) =ooO=(_)=Ooo===================================== Chris McMahan | first_initiallastname@one.dot.net ================================================= �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir/new/1220863087.12663_23.mindcrime���������������������������������������0000644�0001750�0001750�00000012340�12605152237�017276� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id C3EF069CB3 for <xxxx@localhost>; Thu, 7 Aug 2008 08:10:10 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Thu, 07 Aug 2008 08:10:10 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs35153wfh; Wed, 6 Aug 2008 13:58:17 -0700 (PDT) Received: by 10.100.166.10 with SMTP id o10mr4182182ane.0.1218056296101; Wed, 06 Aug 2008 13:58:16 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id d34si13875743and.3.2008.08.06.13.58.14; Wed, 06 Aug 2008 13:58:16 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org; dkim=pass (test mode) header.i=@gmail.com Received: from localhost ([127.0.0.1]:33418 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQq5G-0001aY-Cr for xxxx.klub@gmail.com; Wed, 06 Aug 2008 16:58:14 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KQq4n-0001Z9-06 for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:57:45 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KQq4l-0001V8-6c for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:57:44 -0400 Received: from [199.232.76.173] (port=46438 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQq4k-0001Un-V2 for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:57:42 -0400 Received: from ik-out-1112.google.com ([66.249.90.180]:17562) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from <lekktu@gmail.com>) id 1KQq4k-0001fk-OW for help-gnu-emacs@gnu.org; Wed, 06 Aug 2008 16:57:42 -0400 Received: by ik-out-1112.google.com with SMTP id c21so94956ika.2 for <help-gnu-emacs@gnu.org>; Wed, 06 Aug 2008 13:57:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:message-id:date:from:to :subject:cc:in-reply-to:mime-version:content-type :content-transfer-encoding:content-disposition:references; bh=TTNY9749hpg1+TXOwdaCr+zbQGhBUt3IvsjLWp+pxp0=; b=BOfudUT/SiW9V4e9+k3dXDzwm+ogdrq4m5OlO+f1H+oE6OAYGIm8dbdqDAOwUewBoS jRpfZo07YamP9rkko79SeFdQnf7UAPFAw9x7DFCm3x6muSlCcJBR7vYs1rgHOSINAn2B vQx2//lKR4fXfKNURNu+B30KrvoEmw6m2C8dI= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:to:subject:cc:in-reply-to:mime-version :content-type:content-transfer-encoding:content-disposition :references; b=UMDBulH/LwxDywEH0pfK3DbJ4u2kIZCVDLIM++PqrdcR82HjcS/O3Jhf5OFrf7Fnyj GH76xmc7zkTG/3aQy2WY6DeWCJaFarEItmhxy3h/xS+kUKeDARzNox0OzK6lIv/u9bdy f2LnFlYRJ7Q5vy3lxpxAWB4v0qCwtF9LjWFg4= Received: by 10.210.47.7 with SMTP id u7mr3100239ebu.30.1218056261587; Wed, 06 Aug 2008 13:57:41 -0700 (PDT) Received: by 10.210.71.14 with HTTP; Wed, 6 Aug 2008 13:57:41 -0700 (PDT) Message-ID: <f7ccd24b0808061357t453f5962w8b61f9a453b684d0@mail.gmail.com> Date: Wed, 6 Aug 2008 22:57:41 +0200 From: anon@example.com To: Juanma <juanma_bellon@yahoo.es> In-Reply-To: <200808062238.15634.juanma_bellon@yahoo.es> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit Content-Disposition: inline References: <mailman.15123.1216681940.18990.help-gnu-emacs@gnu.org> <mailman.15143.1216715014.18990.help-gnu-emacs@gnu.org> <9bc17528-8ea9-49f7-8e9d-07f5ede91415@p31g2000prf.googlegroups.com> <200808062238.15634.juanma_bellon@yahoo.es> X-detected-kernel: by monty-python.gnu.org: Linux 2.6 (newer, 2) Cc: help-gnu-emacs@gnu.org Subject: Re: basic question: going back to dired X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor <help-gnu-emacs.gnu.org> List-Unsubscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=unsubscribe> List-Archive: <http://lists.gnu.org/pipermail/help-gnu-emacs> List-Post: <mailto:help-gnu-emacs@gnu.org> List-Help: <mailto:help-gnu-emacs-request@gnu.org?subject=help> List-Subscribe: <http://lists.gnu.org/mailman/listinfo/help-gnu-emacs>, <mailto:help-gnu-emacs-request@gnu.org?subject=subscribe> Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 309 On Wed, Aug 6, 2008 at 22:38, Juanma <juanma_bellon@yahoo.es> wrote: > For all I know, it comes from "0 Knock-outs" (from USA civil war times, > IIRC), i.e., all went really well. See http://en.wikipedia.org/wiki/Okay#Etymology "0 knock-outs" is among the "Improbable or refuted etymologies". Juanma ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mu-0.9.18/lib/tests/testdir/new/1220863087.12663_9.mindcrime����������������������������������������0000644�0001750�0001750�00000021747�12605152237�017235� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Return-Path: <sqlite-dev-bounces@sqlite.org> X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-1.2 required=3.0 tests=BAYES_00,HTML_MESSAGE, MIME_QP_LONG_LINE autolearn=no version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 4E3CF6963B for <xxxx@localhost>; Mon, 4 Aug 2008 21:49:37 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for <xxxx@localhost> (single-drop); Mon, 04 Aug 2008 21:49:37 +0300 (EEST) Received: by 10.142.51.12 with SMTP id y12cs94317wfy; Mon, 4 Aug 2008 05:48:28 -0700 (PDT) Received: by 10.150.152.17 with SMTP id z17mr1245909ybd.194.1217854107583; Mon, 04 Aug 2008 05:48:27 -0700 (PDT) Received: from sqlite.org (sqlite.org [67.18.92.124]) by mx.google.com with ESMTP id 9si6334793yws.5.2008.08.04.05.47.57; Mon, 04 Aug 2008 05:48:27 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) client-ip=67.18.92.124; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) smtp.mail=sqlite-dev-bounces@sqlite.org Received: from sqlite.org (localhost [127.0.0.1]) by sqlite.org (Postfix) with ESMTP id 4FBC111C6F; Mon, 4 Aug 2008 08:47:54 -0400 (EDT) X-Original-To: sqlite-dev@sqlite.org Delivered-To: sqlite-dev@sqlite.org Received: from cpsmtpo-eml02.kpnxchange.com (cpsmtpo-eml02.kpnxchange.com [213.75.38.151]) by sqlite.org (Postfix) with ESMTP id AA4F111C10 for <sqlite-dev@sqlite.org>; Mon, 4 Aug 2008 08:47:51 -0400 (EDT) Received: from hpsmtp-eml21.kpnxchange.com ([213.75.38.121]) by cpsmtpo-eml02.kpnxchange.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 4 Aug 2008 14:47:50 +0200 Received: from cpbrm-eml13.kpnsp.local ([195.121.247.250]) by hpsmtp-eml21.kpnxchange.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 4 Aug 2008 14:47:50 +0200 Received: from hpsmtp-eml30.kpnxchange.com ([10.94.53.250]) by cpbrm-eml13.kpnsp.local with Microsoft SMTPSVC(6.0.3790.1830); Mon, 4 Aug 2008 14:47:50 +0200 Received: from localhost ([10.94.53.250]) by hpsmtp-eml30.kpnxchange.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 4 Aug 2008 14:47:49 +0200 Content-class: urn:content-classes:message MIME-Version: 1.0 X-MimeOLE: Produced By Microsoft Exchange V6.5 Date: Mon, 4 Aug 2008 14:46:06 +0200 Message-ID: <F687EC042917A94E8BB4B0902946453AE17D6C@CPEXBE-EML18.kpnsp.local> X-MS-Has-Attach: X-MS-TNEF-Correlator: Thread-Topic: [sqlite-dev] VM optimization inside sqlite3VdbeExec Thread-Index: Acj2FjkWvteFtLHTTYeVz4ES7E2ggAAGRxeI References: <83B5AF40-DBFA-4578-A043-04C80276E195@sqlabs.net> From: anon@example.com To: <sqlite-dev@sqlite.org> X-OriginalArrivalTime: 04 Aug 2008 12:47:49.0650 (UTC) FILETIME=[4D577720:01C8F630] Subject: Re: [sqlite-dev] VM optimization inside sqlite3VdbeExec X-BeenThere: sqlite-dev@sqlite.org X-Mailman-Version: 2.1.9 Precedence: list Reply-To: sqlite-dev@sqlite.org List-Id: <sqlite-dev.sqlite.org> List-Unsubscribe: <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>, <mailto:sqlite-dev-request@sqlite.org?subject=unsubscribe> List-Archive: <http://sqlite.org:8080/cgi-bin/mailman/private/sqlite-dev> List-Post: <mailto:sqlite-dev@sqlite.org> List-Help: <mailto:sqlite-dev-request@sqlite.org?subject=help> List-Subscribe: <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>, <mailto:sqlite-dev-request@sqlite.org?subject=subscribe> Content-Type: multipart/mixed; boundary="===============1911358387==" Mime-version: 1.0 Sender: sqlite-dev-bounces@sqlite.org Errors-To: sqlite-dev-bounces@sqlite.org Content-Length: 5318 This is a multi-part message in MIME format. --===============1911358387== Content-class: urn:content-classes:message Content-Type: multipart/alternative; boundary="----_=_NextPart_001_01C8F630.0FC2EC1E" This is a multi-part message in MIME format. ------_=_NextPart_001_01C8F630.0FC2EC1E Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Actually, almost every C compiler will already do what you suggest: if = the range of case labels is compact, the switch will be compiled using a = jump table. Only if the range is limited and/or sparse other techniques = will be used, such as linear search and binary search. =20 I'm pretty sure, if you perform the tests suggested by Mihai, that you = will find zero performance difference, neither better, nor worse. =20 Paul =20 ________________________________ From: anon@example.com Sent: Mon 8/4/2008 11:40 AM To: sqlite-dev@sqlite.org Subject: [sqlite-dev] VM optimization inside sqlite3VdbeExec Inside sqlite3VdbeExec there is a very big switch statement. In order to increase performance with few modifications to the=20 original code, why not use this technique ? http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html = <http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html>=20 With a properly defined "instructions" array, instead of the switch=20 statement you can use something like: goto * instructions[pOp->opcode]; --- Marco Bambini http://www.sqlabs.net <http://www.sqlabs.net/>=20 http://www.sqlabs.net/blog/ <http://www.sqlabs.net/blog/>=20 http://www.sqlabs.net/realsqlserver/ = <http://www.sqlabs.net/realsqlserver/>=20 _______________________________________________ sqlite-dev mailing list sqlite-dev@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev = <http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev>=20 ------_=_NextPart_001_01C8F630.0FC2EC1E Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable <HTML dir=3Dltr><HEAD><TITLE>[sqlite-dev] VM optimization inside = sqlite3VdbeExec=0A= =0A= =0A= =0A=
=0A=
Actually, = almost every C compiler will already do what you suggest: if the range = of case labels is compact, the switch will be compiled using a jump = table. Only if the range is limited and/or sparse other techniques will = be used, such as linear search and binary search.
=0A=
 
=0A=
I'm pretty sure, if you = perform the tests suggested by Mihai, that you will find zero = performance difference, neither better, nor worse.
=0A=
 
=0A=
Paul
=0A=
 
=0A=
=0A=
=0A=
=0A=
From: = sqlite-dev-bounces@sqlite.org on behalf of Marco Bambini
Sent: = Mon 8/4/2008 11:40 AM
To: = sqlite-dev@sqlite.org
Subject: [sqlite-dev] VM optimization = inside sqlite3VdbeExec

=0A=
=0A=

Inside sqlite3VdbeExec there is a very = big switch statement.
In order to increase performance with few = modifications to the 
original code, why not use this technique = ?
= http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html<= /FONT>

With a properly defined = "instructions" array, instead of the switch 
statement you can = use something like:
goto * = instructions[pOp->opcode];
---
Marco Bambini
http://www.sqlabs.net
http://www.sqlabs.net/blog/
http://www.sqlabs.net/realsqlserver/



<= FONT face=3DArial = size=3D2>_______________________________________________
sqlite-dev = mailing list
sqlite-dev@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev

------_=_NextPart_001_01C8F630.0FC2EC1E-- --===============1911358387== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ sqlite-dev mailing list sqlite-dev@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev --===============1911358387==-- mu-0.9.18/lib/tests/testdir/cur/0000755000175000017500000000000013021065721013407 500000000000000mu-0.9.18/lib/tests/testdir/cur/1220863087.12663_7.mindcrime!2,RS0000644000175000017500000001262612605152237017673 00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 3EBAB6963B for ; Mon, 4 Aug 2008 21:49:35 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for (single-drop); Mon, 04 Aug 2008 21:49:35 +0300 (EEST) Received: by 10.142.51.12 with SMTP id y12cs89536wfy; Mon, 4 Aug 2008 02:48:56 -0700 (PDT) Received: by 10.150.134.21 with SMTP id h21mr7950048ybd.181.1217843335665; Mon, 04 Aug 2008 02:48:55 -0700 (PDT) Received: from sqlite.org (sqlite.org [67.18.92.124]) by mx.google.com with ESMTP id 6si5897081ywi.1.2008.08.04.02.48.35; Mon, 04 Aug 2008 02:48:55 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) client-ip=67.18.92.124; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) smtp.mail=sqlite-dev-bounces@sqlite.org Received: from sqlite.org (localhost [127.0.0.1]) by sqlite.org (Postfix) with ESMTP id ED01611C4E; Mon, 4 Aug 2008 05:48:31 -0400 (EDT) X-Original-To: sqlite-dev@sqlite.org Delivered-To: sqlite-dev@sqlite.org Received: from mx0.security.ro (mx0.security.ro [80.96.72.194]) by sqlite.org (Postfix) with ESMTP id EB3F51192C for ; Mon, 4 Aug 2008 05:48:28 -0400 (EDT) Received: (qmail 348 invoked from network); 4 Aug 2008 12:48:03 +0300 Received: from dev.security.ro (HELO ?192.168.1.70?) (192.168.1.70) by mx0.security.ro with SMTP; 4 Aug 2008 12:48:03 +0300 Message-ID: <4896D06A.8000901@security.ro> Date: Mon, 04 Aug 2008 12:48:26 +0300 From: anon@example.com User-Agent: Thunderbird 2.0.0.16 (Windows/20080708) MIME-Version: 1.0 To: sqlite-dev@sqlite.org References: <83B5AF40-DBFA-4578-A043-04C80276E195@sqlabs.net> In-Reply-To: <83B5AF40-DBFA-4578-A043-04C80276E195@sqlabs.net> Content-Type: multipart/mixed; boundary="------------000207070200050102060301" X-BitDefender-Scanner: Clean, Agent: BitDefender qmail 2.0.0 on mx0.security.ro X-BitDefender-Spam: No (0) X-BitDefender-SpamStamp: v1, whitelisted, total: 0 Subject: Re: [sqlite-dev] VM optimization inside sqlite3VdbeExec X-BeenThere: sqlite-dev@sqlite.org X-Mailman-Version: 2.1.9 Precedence: high Reply-To: sqlite-dev@sqlite.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: sqlite-dev-bounces@sqlite.org Errors-To: sqlite-dev-bounces@sqlite.org Content-Length: 2212 This is a multi-part message in MIME format. --------------000207070200050102060301 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Marco Bambini wrote: > Inside sqlite3VdbeExec there is a very big switch statement. > In order to increase performance with few modifications to the > original code, why not use this technique ? > http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html > > With a properly defined "instructions" array, instead of the switch > statement you can use something like: > goto * instructions[pOp->opcode]; > --- > Marco Bambini > http://www.sqlabs.net > http://www.sqlabs.net/blog/ > http://www.sqlabs.net/realsqlserver/ > > > > _______________________________________________ > sqlite-dev mailing list > sqlite-dev@sqlite.org > http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev > All the world's not a VAX. This technique is GCC-specific. The SQLite source must be as portable as possible thus tying it to a specific compiler is out of the question. While one could conceivably use some preprocessor magic to provide alternate implementations, that would be impractical considering the sheer size of the code affected. On the other hand - perhaps you could benchmark the change and provide some data on whether this actually improves performance? --------------000207070200050102060301 Content-Type: text/x-vcard; charset=utf-8; name="mihailim.vcf" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="mihailim.vcf" begin:vcard fn:Mihai Limbasan n:Limbasan;Mihai org:SC SECPRAL COM SRL adr:;;str. Actorului nr. 9;Cluj-Napoca;Cluj;400441;Romania email;internet:mihailim@security.ro title:SoftwareDeveloper tel;work:+40 264 449579 tel;fax:+40 264 418594 tel;cell:+40 729 038302 url:http://secpral.ro/ version:2.1 end:vcard --------------000207070200050102060301 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ sqlite-dev mailing list sqlite-dev@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev --------------000207070200050102060301-- mu-0.9.18/lib/tests/testdir/cur/multimime!2,FS0000644000175000017500000000116012605152237015710 00000000000000Return-path: <> Envelope-to: djcb@localhost Delivery-date: Sun, 20 May 2012 09:59:51 +0300 From: Steve Jobs To: Bill Gates Subject: multimime User-agent: mu4e 0.9.8.4; emacs 23.3.1 Date: Sat, 19 May 2012 20:57:56 +0100 Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" --=-=-= Content-Type: text/plain abc --=-=-= Content-Type: application/octet-stream Content-Disposition: attachment; filename="test1.C" Content-Transfer-Encoding: base64 aGVyZSBpcyBhIHNpbXBsZSB0ZXN0IGZpbGUuCg== --=-=-= Content-Type: text/plain def --=-=-=-- mu-0.9.18/lib/tests/testdir/cur/1283599333.1840_11.cthulhu!2,0000644000175000017500000000075213020504332017114 00000000000000From: Frodo Baggins To: Bilbo Baggins Subject: Greetings from =?UTF-8?B?TG90aGzDs3JpZW4=?= User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/24.0 Mule/6.0 (HANACHIRUSATO) Fcc: .sent Organization: The Fellowship of the Ring MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message-Id: Let's write some fünkÿ text using umlauts. Foo. mu-0.9.18/lib/tests/testdir/cur/multirecip!2,S0000644000175000017500000000036313020504332015746 00000000000000Date: Thu, 15 May 2016 14:57:25 -0200 From: To: a@example.com,b@example.com,c@example.com Cc: d@example.com,e@example.com Subject: test with multi to and cc Message-id: <3BE9E652343245@emss35m06.us.lmco.com> Message with multi cc and to. mu-0.9.18/lib/tests/testdir/cur/1220863042.12663_1.mindcrime!2,S0000644000175000017500000001432512605152237017530 00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-4.9 required=3.0 tests=BAYES_00,DATE_IN_PAST_96_XX, RCVD_IN_DNSWL_MED autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 5123469CB3 for ; Thu, 7 Aug 2008 08:10:19 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for (single-drop); Thu, 07 Aug 2008 08:10:19 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs39272wfh; Wed, 6 Aug 2008 20:15:17 -0700 (PDT) Received: by 10.65.133.8 with SMTP id k8mr2071878qbn.7.1218078916289; Wed, 06 Aug 2008 20:15:16 -0700 (PDT) Received: from sourceware.org (sourceware.org [209.132.176.174]) by mx.google.com with SMTP id 28si7904461qbw.0.2008.08.06.20.15.15; Wed, 06 Aug 2008 20:15:16 -0700 (PDT) Received-SPF: neutral (google.com: 209.132.176.174 is neither permitted nor denied by domain of gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org) client-ip=209.132.176.174; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.132.176.174 is neither permitted nor denied by domain of gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org) smtp.mail=gcc-help-return-33661-xxxx.klub=gmail.com@gcc.gnu.org Received: (qmail 13493 invoked by alias); 7 Aug 2008 03:15:13 -0000 Received: (qmail 13485 invoked by uid 22791); 7 Aug 2008 03:15:12 -0000 Received: from mailgw1a.lmco.com (HELO mailgw1a.lmco.com) (192.31.106.7) by sourceware.org (qpsmtpd/0.31) with ESMTP; Thu, 07 Aug 2008 03:14:27 +0000 Received: from emss07g01.ems.lmco.com (relay5.ems.lmco.com [166.29.2.16])by mailgw1a.lmco.com (LM-6) with ESMTP id m773EPZH014730for ; Wed, 6 Aug 2008 21:14:25 -0600 (MDT) Received: from CONVERSION2-DAEMON.lmco.com by lmco.com (PMDF V6.3-x14 #31428) id <0K5700601NO18J@lmco.com> for gcc-help@gcc.gnu.org; Wed, 06 Aug 2008 21:14:25 -0600 (MDT) Received: from EMSS04I00.us.lmco.com ([166.17.13.135]) by lmco.com (PMDF V6.3-x14 #31428) with ESMTP id <0K5700H5MNNWGX@lmco.com> for gcc-help@gcc.gnu.org; Wed, 06 Aug 2008 21:14:20 -0600 (MDT) Received: from EMSS35M06.us.lmco.com ([158.187.107.143]) by EMSS04I00.us.lmco.com with Microsoft SMTPSVC(5.0.2195.6713); Wed, 06 Aug 2008 23:14:20 -0400 Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "Mickey Mouse" Subject: gcc include search order To: "Donald Duck" Message-id: <3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Content-class: urn:content-classes:message Mailing-List: contact gcc-help-help@gcc.gnu.org; run by ezmlm Precedence: klub List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-help-owner@gcc.gnu.org Delivered-To: mailing list gcc-help@gcc.gnu.org Content-Length: 3024 Hi. In my unit testing I need to change some header files (target is vxWorks, which supports some things that the sun does not). So, what I do is fetch the development tree, and then in a new unit test directory I attempt to compile the unit under test. Since this is NOT vxworks, I use sed to change some of the .h files and put them in a ./changed directory. When I try to compile the file, it is still using the .h file from the original location, even though I have listed the include path for ./changed before the include path for the development tree. Here is a partial output from gcc using the -v option GNU CPP version 3.1 (cpplib) (sparc ELF) GNU C++ version 3.1 (sparc-sun-solaris2.8) compiled by GNU C version 3.1. ignoring nonexistent directory "NONE/include" #include "..." search starts here: #include <...> search starts here: . changed /export/home4/xxx/yyyy/builds/int_rel5_latest/src/mp/interface /export/home4/xxx/yyyy/builds/int_rel5_latest/src/ap/app /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/interface /usr/local/include/g++-v3 /usr/local/include/g++-v3/sparc-sun-solaris2.8 /usr/local/include/g++-v3/backward /usr/local/include /usr/local/lib/gcc-lib/sparc-sun-solaris2.8/3.1/include /usr/local/sparc-sun-solaris2.8/include /usr/include End of search list. I know the changed file is correct and that the include is not working as expected, because when I copy the file from ./changed, back into the development tree, the compilation works as expected. One more bit of information. The source that I cam compiling is in /export/home4/xxx/yyyy/builds/int_rel5_latest/src/ap/app And it is including files from /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common These include files should be including the files from ./changed (when they exist) but they are ignoring the .h files in the ./changed directory and are instead using other, unchanged files in the /export/home4/xxx/yyyy/builds/int_rel5_latest/src/shared/common directory. The gcc command line is something like TEST_DIR="." CHANGED_DIR_NAME=changed CHANGED_FILES_DIR=${TEST_DIR}/${CHANGED_DIR_NAME} CICU_HEADER_FILES="-I ${AP_INTERFACE_FILES} -I ${AP_APP_FILES} -I ${SHARED_COMMON_FILES} -I ${SHARED_INTERFACE_FILES}" HEADERS="-I ./ -I ${CHANGED_FILES_DIR} ${CICU_HEADER_FILES}" DEFINES="-DSUNRUN -DA10_DEBUG -DJOETEST" CFLAGS="-v -c -g -O1 -pipe -Wformat -Wunused -Wuninitialized -Wshadow -Wmissing-prototypes -Wmissing-declarations" printf "Compiling the UUT File\n" gcc -fprofile-arcs -ftest-coverage ${CFLAGS} ${HEADERS} ${DEFINES} ${AP_APP_FILES}/unitUnderTest.cpp I hope this explanation is clear. If anyone knows how to fix the command line so that it gets the .h files in the "changed" directory are used instead of files in the other include directories. Thanks Joe ---------------------------------------------------- Time Flies like an Arrow. Fruit Flies like a Banana mu-0.9.18/lib/tests/testdir/cur/1220863087.12663_15.mindcrime!2,PS0000644000175000017500000001464312605152237017751 00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-3.6 required=3.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, SPF_PASS,WHOIS_NETSOLPR autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 1A6CD69CB6 for ; Tue, 12 Aug 2008 21:42:38 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.109] by mindcrime with IMAP (fetchmail-6.3.8) for (single-drop); Tue, 12 Aug 2008 21:42:38 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs123119wfh; Sun, 10 Aug 2008 22:06:31 -0700 (PDT) Received: by 10.100.166.10 with SMTP id o10mr9327844ane.0.1218431190107; Sun, 10 Aug 2008 22:06:30 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id c29si10110392anc.13.2008.08.10.22.06.29; Sun, 10 Aug 2008 22:06:30 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Received: from localhost ([127.0.0.1]:45637 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KSPbx-0006dj-96 for xxxx.klub@gmail.com; Mon, 11 Aug 2008 01:06:29 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KSPbE-0006cQ-Nd for help-gnu-emacs@gnu.org; Mon, 11 Aug 2008 01:05:44 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KSPbD-0006bs-Px for help-gnu-emacs@gnu.org; Mon, 11 Aug 2008 01:05:44 -0400 Received: from [199.232.76.173] (port=37426 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KSPbD-0006bk-HT for help-gnu-emacs@gnu.org; Mon, 11 Aug 2008 01:05:43 -0400 Received: from main.gmane.org ([80.91.229.2]:46446 helo=ciao.gmane.org) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1KSPbD-0003Kl-CA for help-gnu-emacs@gnu.org; Mon, 11 Aug 2008 01:05:43 -0400 Received: from list by ciao.gmane.org with local (Exim 4.43) id 1KSPb9-00080r-CX for help-gnu-emacs@gnu.org; Mon, 11 Aug 2008 05:05:39 +0000 Received: from bas2-toronto63-1088792724.dsl.bell.ca ([64.229.168.148]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Mon, 11 Aug 2008 05:05:39 +0000 Received: from cpchan by bas2-toronto63-1088792724.dsl.bell.ca with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Mon, 11 Aug 2008 05:05:39 +0000 X-Injected-Via-Gmane: http://gmane.org/ To: help-gnu-emacs@gnu.org From: anon@example.com Date: Mon, 11 Aug 2008 01:03:22 -0400 Organization: Linux Private Site Message-ID: <87bq00nnxh.fsf@MagnumOpus.Mercurius> References: <877iav5s49.fsf@163.com> <86hc9yc5sj.fsf@timbral.net> <877iat7udd.fsf@163.com> <87fxphcsxi.fsf@lion.rapttech.com.au> <8504ddd4-5e3b-4ed5-bf77-aa9cce81b59a@1g2000pre.googlegroups.com> <87k5es59we.fsf@lion.rapttech.com.au> <63c824e3-62b1-4a93-8fa8-2813e1f9397f@v13g2000pro.googlegroups.com> <874p5vsgg8.fsf@nonospaz.fatphil.org> <8250972e-1886-4021-80bc-376e34881c80@v39g2000pro.googlegroups.com> <87zlnnqvvs.fsf@nonospaz.fatphil.org> <57add0e0-b39d-4c71-8d2c-d3b9ddfaa1a9@1g2000pre.googlegroups.com> <87sktfnz5p.fsf@atthis.clsnet.nl> <562e1111-d9e7-4b6a-b661-3f9af13fea17@b30g2000prf.googlegroups.com> <87d4khoq97.fsf@atthis.clsnet.nl> <0fe404c5-cab8-4692-8a27-532e737a7813@i24g2000prf.googlegroups.com> Mime-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha1; protocol="application/pgp-signature" X-Complaints-To: usenet@ger.gmane.org X-Gmane-NNTP-Posting-Host: bas2-toronto63-1088792724.dsl.bell.ca X-Face: G; Z,`sm>)4t4LB/GUrgH$W`!AmfHMj,LG)Z}X0ax@s9:0>0)B&@vcm{v-le)wng)?|o]D\Z}0:6X User-Agent: Gnus/5.110011 (No Gnus v0.11) Emacs/23.0.60 (gnu/linux) Cancel-Lock: sha1:IKyfrl5drOw6HllHFSmWHAKEeC8= X-detected-kernel: by monty-python.gnu.org: Linux 2.6, seldom 2.4 (older, 4) Subject: Re: Can anybody tell me how to send HTML-format mail in gnus X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 1229 Lines: 36 --=-=-= Content-Type: text/plain Xah writes: > So, i was reading about it in Wikipedia. Although i don't have a TV, > and haven't had since 2000, but i still enjoyed the festive spirits > anyhow. After all, i'm Chinese by blood. So, in my wandering, i ran > into this welcome song on youtube: > > http://www.youtube.com/watch?v=1HEndNYVhZo What is your point? Your email is in plain text and I can click on the link just fine- it is not exactly rocket science to implement parsing of URL's to workable links in an Email program (a lot of programs does that, including Gnus). Images can be included inline if you want. Also mail markups such as *this*, **this** and _this_ have been around since the Usenet days and displayed appropriately by a number of mailers. Like others have said, most html messages that I have seen either contains useless information, or are plain spam and can introduce a host of security problems in some mailers. Charles --=-=-= Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.4-svn0 (GNU/Linux) iD8DBQFIn8gm3epPyyKbwPYRApbvAKDRirXwzMzI+NHV77+QcP3EgTPaCgCfb/6m GtNVKdYAeftaYm1nwRVoCDA= =ULo3 -----END PGP SIGNATURE----- --=-=-=-- mu-0.9.18/lib/tests/testdir/cur/1220863060.12663_3.mindcrime!2,S0000644000175000017500000002707313020504332017523 00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00,HTML_MESSAGE autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id D724F6963B for ; Mon, 4 Aug 2008 21:49:27 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for (single-drop); Mon, 04 Aug 2008 21:49:27 +0300 (EEST) Received: by 10.142.51.12 with SMTP id y12cs86537wfy; Mon, 4 Aug 2008 00:38:51 -0700 (PDT) Received: by 10.151.113.5 with SMTP id q5mr272266ybm.37.1217835529913; Mon, 04 Aug 2008 00:38:49 -0700 (PDT) Received: from sqlite.org (sqlite.org [67.18.92.124]) by mx.google.com with ESMTP id 5si5754915ywd.8.2008.08.04.00.38.30; Mon, 04 Aug 2008 00:38:50 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) client-ip=67.18.92.124; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) smtp.mail=sqlite-dev-bounces@sqlite.org Received: from sqlite.org (localhost [127.0.0.1]) by sqlite.org (Postfix) with ESMTP id 765A511C46; Mon, 4 Aug 2008 03:38:27 -0400 (EDT) X-Original-To: sqlite-dev@sqlite.org Delivered-To: sqlite-dev@sqlite.org Received: from ik-out-1112.google.com (ik-out-1112.google.com [66.249.90.176]) by sqlite.org (Postfix) with ESMTP id 4C59511C41 for ; Mon, 4 Aug 2008 03:38:23 -0400 (EDT) Received: by ik-out-1112.google.com with SMTP id b32so2163423ika.0 for ; Mon, 04 Aug 2008 00:38:23 -0700 (PDT) Received: by 10.210.54.19 with SMTP id c19mr14589042eba.107.1217835502549; Mon, 04 Aug 2008 00:38:22 -0700 (PDT) Received: by 10.210.115.10 with HTTP; Mon, 4 Aug 2008 00:38:22 -0700 (PDT) Message-ID: <477821040808040038s381bf382p7411451e3c1a2e4e@mail.gmail.com> Date: Mon, 4 Aug 2008 10:38:22 +0300 From: anon@example.com To: sqlite-dev@sqlite.org In-Reply-To: <73d4fc50808030747g303a170ieac567723c2d4f24@mail.gmail.com> MIME-Version: 1.0 References: <477821040808030533y41f1501dq32447b568b6e6ca5@mail.gmail.com> <73d4fc50808030747g303a170ieac567723c2d4f24@mail.gmail.com> Subject: Re: [sqlite-dev] SQLite exception A&B X-BeenThere: sqlite-dev@sqlite.org X-Mailman-Version: 2.1.9 Priority: normal Reply-To: sqlite-dev@sqlite.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: multipart/mixed; boundary="===============2123623832==" Mime-version: 1.0 Sender: sqlite-dev-bounces@sqlite.org Errors-To: sqlite-dev-bounces@sqlite.org Content-Length: 8475 --===============2123623832== Content-Type: multipart/alternative; boundary="----=_Part_29556_25702991.1217835502493" ------=_Part_29556_25702991.1217835502493 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline Hi Grant, Thanks for your reply. I am using a different session for each thread, whenever a thread wishes to access the database it gets a session from the session pool and works with that session until its work is done. Most of the actions the threads are doing on the database are quite complicated and are required to be fully committed or completely ignored, so yes, I am (most of the time) explicitly beginning and committing my transactions. Regarding the SQLiteStatementImpl, I believe the Poco manual explains that sessions and statements for that matter cannot be shared between threads, therefore if you are using a session via one thread only it should work fine. My first impression was that the problem was in the Poco infrastructure (I have found several Poco related bugs in the past), but the problem ALWAYS occurs when I perform the "BEGIN IMMEDIATE" action, if it were a Poco related bug, I would expect to see it here and there without any relation to this specific statement, but that is not the case. None the less, I will also post my question on the Poco forums. Nadav. On Sun, Aug 3, 2008 at 5:47 PM, Grant Gatchel wrote: > Are you using the same Poco::Session for every thread or does each call > create a new session/handle to the database? > > Are you explicitly BEGINning and COMMITting your transactions? > > In looking at the 1.3.2 branch of Poco::Data::SQLite, there appears to be a > race condition in the SQLiteStatementImpl::next() method in which the member > _nextResponse is being accessed before the SQLiteStatementImpl::hasNext() > method has a chance to interpret that value and throw an exception. > > This question might be more suitable in the Poco forums or mailinglist. > > - Grant > > On Sun, Aug 3, 2008 at 8:33 AM, nadav g wrote: > >> Hi All, >> >> I have been using SQLite with Poco (www.appinf.com) as my infrastructure. >> The program is running several threads that access this database very >> often and are synchronized by SQLite itself. >> Everything seems to work just fine most of time (usually days - weeks) but >> I do get an occasional exception: >> >> Exception: SQL error or missing database: Iterator Error: trying to check >> if there is a next value >> >> The backtrace leads to this statement: >> *"BEGIN IMMEDIATE"* >> >> This specific code runs numerous times before an exception occurs (if >> occurs at all) and I cannot think of any reason for it to fail later rather >> than sooner. >> It is pretty obvious that this situation occurs due to some rare thread >> state, but I could not find any information that gives me any hint as to >> what this state might be. >> >> So what I am asking is: >> 1) Does anyone know why this sort of exception occurs? >> 2) Can anyone think of a reason for such an exception to occur in the >> situation I have described? >> >> Thanks in advance, >> Nadav. >> >> >> _______________________________________________ >> sqlite-dev mailing list >> sqlite-dev@sqlite.org >> http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev >> >> > > _______________________________________________ > sqlite-dev mailing list > sqlite-dev@sqlite.org > http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev > > ------=_Part_29556_25702991.1217835502493 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline
Hi Grant,

Thanks for your reply.
I am using a different session for each thread, whenever a thread wishes to access the database it gets a session from the session pool and works with that session until its work is done.

Most of the actions the threads are doing on the database are quite complicated and are required to be fully committed or completely ignored, so yes, I am (most of the time) explicitly beginning and committing my transactions.

Regarding the SQLiteStatementImpl, I believe the Poco manual explains that sessions and statements for that matter cannot be shared between threads, therefore if you are using a session via one thread only it should work fine.

My first impression was that the problem was in the Poco infrastructure (I have found several Poco related bugs in the past), but the problem ALWAYS occurs when I perform the "BEGIN IMMEDIATE" action, if it were a Poco related bug, I would expect to see it here and there without any relation to this specific statement, but that is not the case.

None the less, I will also post my question on the Poco forums.

Nadav.

On Sun, Aug 3, 2008 at 5:47 PM, Grant Gatchel <grant.gatchel@gmail.com> wrote:
Are you using the same Poco::Session for every thread or does each call create a new session/handle to the database?

Are you explicitly BEGINning and COMMITting your transactions?

In looking at the 1.3.2 branch of Poco::Data::SQLite, there appears to be a race condition in the SQLiteStatementImpl::next() method in which the member _nextResponse is being accessed before the SQLiteStatementImpl::hasNext() method has a chance to interpret that value and throw an exception.

This question might be more suitable in the Poco forums or mailinglist.

- Grant

On Sun, Aug 3, 2008 at 8:33 AM, nadav g <nadav.gr@gmail.com> wrote:
Hi All,

I have been using SQLite with Poco (www.appinf.com) as my infrastructure.
The program is running several threads that access this database very often and are synchronized by SQLite itself.
Everything seems to work just fine most of time (usually days - weeks) but I do get an occasional exception:

Exception: SQL error or missing database: Iterator Error: trying to check if there is a next value

The backtrace leads to this statement:
"BEGIN IMMEDIATE"

This specific code runs numerous times before an exception occurs (if occurs at all) and I cannot think of any reason for it to fail later rather than sooner.
It is pretty obvious that this situation occurs due to some rare thread state, but I could not find any information that gives me any hint as to what this state might be.

So what I am asking is:
1) Does anyone know why this sort of exception occurs?
2) Can anyone think of a reason for such an exception to occur in the situation I have described?

Thanks in advance,
Nadav.


_______________________________________________
sqlite-dev mailing list
sqlite-dev@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev



_______________________________________________
sqlite-dev mailing list
sqlite-dev@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev


------=_Part_29556_25702991.1217835502493-- --===============2123623832== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ sqlite-dev mailing list sqlite-dev@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev --===============2123623832==-- mu-0.9.18/lib/tests/testdir/cur/1220863087.12663_19.mindcrime!2,S0000644000175000017500000000713612605152237017634 00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id C4D6569CB3 for ; Thu, 7 Aug 2008 08:10:08 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [66.249.91.109] by mindcrime with IMAP (fetchmail-6.3.8) for (single-drop); Thu, 07 Aug 2008 08:10:08 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs34794wfh; Wed, 6 Aug 2008 13:40:29 -0700 (PDT) Received: by 10.100.33.13 with SMTP id g13mr1093301ang.79.1218055228418; Wed, 06 Aug 2008 13:40:28 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id d19si15908789and.17.2008.08.06.13.40.27; Wed, 06 Aug 2008 13:40:28 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Received: from localhost ([127.0.0.1]:56316 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KQpo3-0007Pc-Qk for xxxx.klub@gmail.com; Wed, 06 Aug 2008 16:40:27 -0400 From: anon@example.com Newsgroups: gnu.emacs.help Date: Wed, 6 Aug 2008 20:38:35 +0100 Message-ID: References: <55dbm5-qcl.ln1@news.ducksburg.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Trace: individual.net bABVU1hcJwWAuRwe/097AAoOXnGGeYR8G1In635iFGIyfDLPUv X-Orig-Path: news.ducksburg.com!news Cancel-Lock: sha1:wK7dsPRpNiVxpL/SfvmNzlvUR94= sha1:oepBoM0tJBLN52DotWmBBvW5wbg= User-Agent: slrn/pre0.9.9-120/mm/ao (Ubuntu Hardy) Path: news.stanford.edu!headwall.stanford.edu!newshub.sdsu.edu!feeder.erje.net!proxad.net!feeder1-2.proxad.net!feed.ac-versailles.fr!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail Xref: news.stanford.edu gnu.emacs.help:160868 To: help-gnu-emacs@gnu.org Subject: Re: Learning LISP; Scheme vs elisp. X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 417 Lines: 11 On 2008-08-01, Thien-Thi Nguyen wrote: > warriors attack, felling foe after foe, > few growing old til they realize: to know > what deceit is worth deflection; > such receipt reversed rejection! > then their heavy arms, e'er transformed to shields: > balanced hooked charms, ploughed deep, rich yields. Aha: the exercise for the reader is to place the parens correctly. Might take me a while to solve this puzzle. mu-0.9.18/lib/tests/testdir/cur/signed!2,S0000644000175000017500000000164412605152237015060 00000000000000Return-path: <> Envelope-to: skipio@localhost Delivery-date: Fri, 11 May 2012 16:21:57 +0300 Received: from localhost.roma.net([127.0.0.1] helo=borealis) by borealis with esmtp (Exim 4.77) id 1SSpnB-00038a-55 for djcb@localhost; Fri, 11 May 2012 16:21:57 +0300 Delivered-To: diggler@gmail.com From: Skipio To: Hannibal Subject: signed User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:20:45 +0300 Message-ID: <878vgy97ma.fsf@roma.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha1; protocol="application/pgp-signature" --=-=-= Content-Type: text/plain I am signed! --=-=-= Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iEYEARECAAYFAk+tEi0ACgkQ6WrHoQF92jxTzACeKd/XxY+P7bpymWL3JBRHaW9p DpwAoKw7PDW4z/lNTkWjndVTjoO9jGhs =blXz -----END PGP SIGNATURE----- --=-=-=-- mu-0.9.18/lib/tests/testdir/cur/signed-encrypted!2,S0000644000175000017500000000440112605152237017045 00000000000000Return-path: <> Envelope-to: karjala@localhost Delivery-date: Fri, 11 May 2012 16:37:57 +0300 From: karjala@example.com To: lapinkulta@example.com Subject: signed + encrypted User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:36:08 +0300 Message-ID: <874nrm96wn.fsf@example.com> MIME-Version: 1.0 Content-Type: multipart/encrypted; boundary="=-=-="; protocol="application/pgp-encrypted" --=-=-= Content-Type: application/pgp-encrypted Version: 1 --=-=-= Content-Type: application/octet-stream -----BEGIN PGP MESSAGE----- Version: GnuPG v1.4.12 (GNU/Linux) hQQOA1T38TPQrHD6EA/+K4kSpMa7zk+qihUkQnHSq28xYxisNQx6X5DVNjA/Qx16 uZj/40ae+PoSMTVfklP+B2S/IomuTW6dwVqS7aQ3u4MTzi+YOi11k1lEMD7hR0Wb L0i48o3/iCPuCTpnOsaLZvRL06g+oTi0BF2pgz/YdsgsBTGrTb3pkDGSlLIhvh/J P8eE3OuzkXS6d8ymJKx2S2wQJrc1AFf1BgJfgc5T0iAvcV+zIMG+PIYcVd04zVpj cORFEfvGgfxWkeX+Ks3tu/l5PA1EesnoqFdNFZm+RKBg3RFsOm8tBlJ46xJjfeHg zLgifeSLy3tOX7CvWYs9torrx7s7UOI2gV8kzBqz+a7diyCMezceeQ9l0nIRybwW C9Egp8Bpfb02iXTOGdE/vRiNItQH14GKmXf4nCSwdtQUm3yzaqY9yL3xBxAlW53e YOFfPMESt+E7IlPn0c7llWGrcdrhJbUEoGOIPezES7kdeNPzi8G1lLtvT04/SSZJ QxPH5FNzSFaYFAQSdI7TR69P7L7vtLL8ndkjY49HfLFXochQQzsqrzVxzRCruHxA zbZSRptNf9SuXEaX9buO1vlFHheGvrCKzEWa6O7JD/DiyrE/zqy4jdlh9abMCouQ GWGSbn8jk6SMTQQ2Yv/VOyFqifHZp0UJD59tyIdenpxoYu5M0lwHLNVDlRjLEwUQ AIDz1tbLoM7lxs2FOKGr8QqbKIeMfL+NUmbvVIDc4mJrOlRnHh+cZYm4Z49iTl1v bYNMYgR5nY7W6rqh0ae7ZOW0h2NzpkAwTzuf1YrSjNavd9KBwOCFtAoZhRwfwFVx ju+ByHFNnf7g/R6DekHS0pSiatM0cPDJT05atEZb+13CRHHznonmLHi+VahXjrpg cIUA8Lhjdfm6Fsabo7gNZnTTRxNBqUXKK2vJF/XLbNrH5K2BH2dCCmUNtm3yFWiM DOzaw3665Y3S6MvZdyKpatbNrVoJdBpRgPxJ1YCSEituFUqHJBStay+aRb5fVkQR w3+9hWw+Ob0+2EumKbgfQ7iMwTZBCZP4VOxkoqdHvs9aWm4N7wHtXsyCew3icbJx lyUWsDx/FI+HlQRfOqeAMxmp8kKybmHNw8oGiw+uPPUHSD1NFYVm2DtwhYll3Fvs YY7r5s3yP1ZnwxMqWI3OsExVUXs8MS4UTAgO+cggO7YidPcANbBDihBFP8mTXtni Oo5n5v+/eRoLfHmnsGcaK8EkKsfFHpbqn4gxXGcBuHaTTJ/ZhbW6bi1WWZA9ExaJ IeTDtp5Bks1pJvTjCDacvgwl3rEBM6yaeIvB7575Y/GPMTOZhawhfOxV1smMmTKI JOWYb3+PuN2cvWetkjFgH8re4sRXq22DKBZHJEWYU8sH0sACAePnIr+pkrOtGeJB t1zBqZUnrupH6ptk9n/AjbQ+XSMTEKu55gSjYLAYx1EHApx52QLkdh+ej5xCIVeY 6wS1Iipkoc6/r6F7CKctupXurNY2AlD4uQIOfD6kQgkqK4PY3hsRHQA+Zqj6oRfr kxysFJZvhgt26IeBVapFs10WuYt9iHfpbPUBQUIZCLyPAh08UdVW64Uc2DvUPy+I C+3RrmTHQPP/YNKgDQaZ3ySVEDkqjaDPmXr5K0Ibaib2dtPCLcA= =pv03 -----END PGP MESSAGE----- --=-=-=-- mu-0.9.18/lib/tests/testdir/cur/1252168370_3.14675.cthulhu!2,S0000644000175000017500000000156312605152237017253 00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.1.0 (2005-09-13) on mindcrime X-Spam-Level: Delivered-To: dfgh@floppydisk.nl Message-ID: <43A09C49.9040902@euler.org> Date: Wed, 14 Dec 2005 23:27:21 +0100 From: Fred Flintstone User-Agent: Mozilla Thunderbird 1.0.7 (X11/20051010) X-Accept-Language: nl-NL, nl, en MIME-Version: 1.0 To: dfgh@floppydisk.nl Subject: Re: xyz References: <439C1136.90504@euler.org> <4399DD94.5070309@euler.org> <20051209233303.GA13812@gauss.org> <439B41ED.2080402@euler.org> <4399DD94.5070309@euler.org> <20051209233303.GA13812@gauss.org> <439A1E03.3090604@euler.org> <20051211184308.GB13513@gauss.org> In-Reply-To: <20051211184308.GB13513@gauss.org> X-Enigmail-Version: 0.92.0.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-UIDL: T Envelope-to: peter@example.com Delivery-date: Fri, 11 May 2012 16:22:03 +0300 Received: from localhost.example.com ([127.0.0.1] helo=borealis) by borealis with esmtp (Exim 4.77) id 1SSpnB-00038a-Ux for djcb@localhost; Fri, 11 May 2012 16:21:58 +0300 Delivered-To: peter@example.com From: Brian To: Peter Subject: encrypted User-agent: mu4e 0.9.8.5-dev1; emacs 24.1.50.8 Date: Fri, 11 May 2012 16:21:42 +0300 Message-ID: MIME-Version: 1.0 Content-Type: multipart/encrypted; boundary="=-=-="; protocol="application/pgp-encrypted" --=-=-= Content-Type: application/pgp-encrypted Version: 1 --=-=-= Content-Type: application/octet-stream -----BEGIN PGP MESSAGE----- Version: GnuPG v1.4.12 (GNU/Linux) hQQOA1T38TPQrHD6EA//YXkUB4Dy09ngCRyHWbXmV3XBjuKTr8xrak5ML1kwurav gyagOHKLMU+5CKvObChiKtXhtgU0od7IC8o+ALlHevQ0XXcqNYA2KUfX8R7akq7d Xx9mA6D8P7Y/P8juUCLBpfrCi2GC42DtvPZSUu3bL/ctUJ3InPHIfHibKF2HMm7/ gUHAKY8VPJF39dLP8GLcfki6qFdeWbxgtzmuyzHfCBCLnDL0J9vpEQBpGDFMcc4v cCbmMJaiPOmRb6U4WOuRVnuXuTztLiIn0jMslzOSFDcLTVBAsrC01r71O+XZKfN4 mIfcpcWJYKM2NQW8Jwf+8Hr84uznBqs8uTTlrmppjkAHZGqGMjiQDxLhDVaCQzMy O8PSV4xT6HPlKXOwV1OLc+vm0A0RAdSBctgZg40oFn4XdB1ur8edwAkLvc0hJKaz gyTQiPaXm2Uh2cDeEx4xNgXmwCKasqc9jAlnDC2QwA33+pw3OqgZT5h1obn0fAeR mgB+iW1503DIi/96p8HLZcr2EswLEH9ViHIEaFj/vlR5BaOncsLB0SsNV/MHRvym Xg5GUjzPIiyBZ3KaR9OIBiZ5eXw+bSrPAo/CAs0Zwxag7W3CH//oK39Qo1GnkYpc 4IQxhx4IwkzqtCnripltV/kfpGu0yA/OdK8lOjkUqCwvL97o73utXIxm21Zd3mEP /iLNrduZjMCq+goz1pDAQa9Dez6VjwRuRPTqeAac8Fx/nzrVzIoIEAt36hpuaH1l KpbmHpKgsUWcrE5iYT0RRlRRtRF4PfJg8PUmP1hvw8TaEmNfT+0HgzcJB/gRsVdy gTzkzUDzGZLhRcpmM5eW4BkuUmIO7625pM6Jd3HOGyfCGSXyEZGYYeVKzv8xbzYf QM6YYKooRN9Ya2jdcWguW0sCSJO/RZ9eaORpTeOba2+Fp6w5L7lga+XM9GLfgref Cf39XX1RsmRBsrJTw0z5COf4bT8G3/IfQP0QyKWIFITiFjGmpZhLsKQ3KT4vSe/d gTY1xViVhkjvMFn3cgSOSrvktQpAhsXx0IRazN0T7pTU33a5K0SrZajY9ynFDIw9 we7XYyVwZzYEXjGih5mTH1PhWYK5fZZEKKqaz5TyYv9SeWJ+8FrHeXUKD38SQEHM qkpl9Iv17RF4Qy9uASWwRoobhKO+GykTaBSTyw8R8ctG/hfAlnaZxQ3TwNyHWyvU 9SVJsp27ulv/W9MLZtGpEMK0ckAR164Vyou1KOn200BqxbC2tJpegNeD2TP5ZtdY HIcxkgKr0haYcDnVEf1ulSxv23pZWIexbgvVCG7dRL0eB+6O28f9CWehle10MDyM 0AYyw8Da2cu7PONMovqt4nayScyGTacFBp7c2KXR9DGZ0mcBwOjL/mGRKcVWN3MG 2auCrwn2KVWmKZI3Jp0T8KhfGBnFs9lUElpDTOiED1/2bKz6Yoc385QtWx99DFMZ IWiH5wMxkWFpzjE+GHiJ09vSbTTL4JY9eu2n5nxQmtjYMBVxQm7S7qwH =0Paa -----END PGP MESSAGE----- --=-=-=-- mu-0.9.18/lib/tests/testdir/cur/1220863087.12663_5.mindcrime!2,S0000644000175000017500000000716512605152237017551 00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=BAYES_00 autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id 32F276963F for ; Mon, 4 Aug 2008 21:49:34 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for (single-drop); Mon, 04 Aug 2008 21:49:34 +0300 (EEST) Received: by 10.142.51.12 with SMTP id y12cs89397wfy; Mon, 4 Aug 2008 02:41:16 -0700 (PDT) Received: by 10.150.156.20 with SMTP id d20mr963580ybe.104.1217842875596; Mon, 04 Aug 2008 02:41:15 -0700 (PDT) Received: from sqlite.org (sqlite.org [67.18.92.124]) by mx.google.com with ESMTP id 6si3605185ywi.1.2008.08.04.02.40.57; Mon, 04 Aug 2008 02:41:15 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) client-ip=67.18.92.124; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of sqlite-dev-bounces@sqlite.org designates 67.18.92.124 as permitted sender) smtp.mail=sqlite-dev-bounces@sqlite.org Received: from sqlite.org (localhost [127.0.0.1]) by sqlite.org (Postfix) with ESMTP id 7147F11C45; Mon, 4 Aug 2008 05:40:55 -0400 (EDT) X-Original-To: sqlite-dev@sqlite.org Delivered-To: sqlite-dev@sqlite.org Received: from relay00.pair.com (relay00.pair.com [209.68.5.9]) by sqlite.org (Postfix) with SMTP id B5F901192C for ; Mon, 4 Aug 2008 05:40:52 -0400 (EDT) Received: (qmail 59961 invoked from network); 4 Aug 2008 09:40:50 -0000 Received: from unknown (HELO ?192.168.0.17?) (unknown) by unknown with SMTP; 4 Aug 2008 09:40:50 -0000 X-pair-Authenticated: 87.13.75.164 Message-Id: <83B5AF40-DBFA-4578-A043-04C80276E195@sqlabs.net> From: anon@example.com To: sqlite-dev@sqlite.org Mime-Version: 1.0 (Apple Message framework v926) Date: Mon, 4 Aug 2008 11:40:49 +0200 X-Mailer: Apple Mail (2.926) Subject: [sqlite-dev] VM optimization inside sqlite3VdbeExec X-BeenThere: sqlite-dev@sqlite.org X-Mailman-Version: 2.1.9 Precedence: list Reply-To: sqlite-dev@sqlite.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: sqlite-dev-bounces@sqlite.org Errors-To: sqlite-dev-bounces@sqlite.org Content-Length: 639 Inside sqlite3VdbeExec there is a very big switch statement. In order to increase performance with few modifications to the original code, why not use this technique ? http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html With a properly defined "instructions" array, instead of the switch statement you can use something like: goto * instructions[pOp->opcode]; --- Marco Bambini http://www.sqlabs.net http://www.sqlabs.net/blog/ http://www.sqlabs.net/realsqlserver/ _______________________________________________ sqlite-dev mailing list sqlite-dev@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-dev mu-0.9.18/lib/tests/testdir/cur/1305664394.2171_402.cthulhu!2,0000644000175000017500000000115612605152237017203 00000000000000From: =?UTF-8?B?TcO8?= To: Helmut =?UTF-8?B?S3LDtmdlcg==?= Subject: =?UTF-8?B?TW90w7ZyaGVhZA==?= User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/24.0 Mule/6.0 (HANACHIRUSATO) References: 1n-Reply-To: MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test for issue #38, where apparently searching for accented words in subject, to etc. fails. What about here? Queensrÿche. Mötley Crüe. mu-0.9.18/lib/tests/testdir/cur/special!2,Sabc0000644000175000017500000000053612605152237015674 00000000000000Date: Thu, 1 Jun 2012 14:57:25 -0200 From: "Rocky Balboa" To: "Ivan Drago" Subject: currying and tail optimization Message-id: <3BE9E653ef345@emss35m06.us.lmco.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Test 123. I'm a special message with special flags. mu-0.9.18/lib/tests/testdir/tmp/0000755000175000017500000000000013021065721013416 500000000000000mu-0.9.18/lib/tests/testdir/tmp/1220863087.12663.ignore0000644000175000017500000001017312605152237016317 00000000000000Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on mindcrime X-Spam-Level: X-Spam-Status: No, score=-3.6 required=3.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.2.5 X-Original-To: xxxx@localhost Delivered-To: xxxx@localhost Received: from mindcrime (localhost [127.0.0.1]) by mail.xxxxsoftware.nl (Postfix) with ESMTP id D68E769CB5 for ; Fri, 8 Aug 2008 20:56:25 +0300 (EEST) Delivered-To: xxxx.klub@gmail.com Received: from gmail-imap.l.google.com [72.14.221.111] by mindcrime with IMAP (fetchmail-6.3.8) for (single-drop); Fri, 08 Aug 2008 20:56:25 +0300 (EEST) Received: by 10.142.237.21 with SMTP id k21cs71287wfh; Fri, 8 Aug 2008 07:40:46 -0700 (PDT) Received: by 10.100.122.8 with SMTP id u8mr3824321anc.77.1218206446062; Fri, 08 Aug 2008 07:40:46 -0700 (PDT) Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by mx.google.com with ESMTP id d35si2718351and.38.2008.08.08.07.40.45; Fri, 08 Aug 2008 07:40:46 -0700 (PDT) Received-SPF: pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) client-ip=199.232.76.165; Authentication-Results: mx.google.com; spf=pass (google.com: domain of help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org designates 199.232.76.165 as permitted sender) smtp.mail=help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Received: from localhost ([127.0.0.1]:47349 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KRT93-0006Po-A3 for xxxx.klub@gmail.com; Fri, 08 Aug 2008 10:40:45 -0400 Path: news.stanford.edu!headwall.stanford.edu!newshub.sdsu.edu!news-out.readnews.com!news-xxxfer.readnews.com!panix!not-for-mail From: anon@example.com Newsgroups: gnu.emacs.help Date: Fri, 08 Aug 2008 10:07:30 -0400 Organization: PANIX Public Access Internet and UNIX, NYC Message-ID: References: <9bc17528-8ea9-49f7-8e9d-07f5ede91415@p31g2000prf.googlegroups.com> <200808062238.15634.juanma_bellon@yahoo.es> NNTP-Posting-Host: panix5.panix.com Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: reader1.panix.com 1218204439 22850 166.84.1.5 (8 Aug 2008 14:07:19 GMT) X-Complaints-To: abuse@panix.com NNTP-Posting-Date: Fri, 8 Aug 2008 14:07:19 +0000 (UTC) User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.2 (windows-nt) Cancel-Lock: sha1:Ckkp5oJPIMuAVgEHGnS/9MkZsEs= Xref: news.stanford.edu gnu.emacs.help:160963 To: help-gnu-emacs@gnu.org Subject: Re: basic question: going back to dired X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Errors-To: help-gnu-emacs-bounces+xxxx.klub=gmail.com@gnu.org Content-Length: 710 Lines: 27 I seem to remember from my early school days it was a campaign slogan for someone nick-named Kinderhook that went something like Old Kinderhook is OK - Chris "Juanma Barranquero" writes: > On Wed, Aug 6, 2008 at 22:38, Juanma wrote: > >> For all I know, it comes from "0 Knock-outs" (from USA civil war times, >> IIRC), i.e., all went really well. > > See http://en.wikipedia.org/wiki/Okay#Etymology > > "0 knock-outs" is among the "Improbable or refuted etymologies". > > Juanma > > -- (. .) =ooO=(_)=Ooo===================================== Chris McMahan | first_initiallastname@one.dot.net ================================================= mu-0.9.18/lib/tests/dummy.cc0000644000175000017500000000000012605152237012516 00000000000000mu-0.9.18/lib/tests/test-mu-util.c0000644000175000017500000001451113020504332013572 00000000000000/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include "test-mu-common.h" #include "lib/mu-util.h" static void test_mu_util_dir_expand_00 (void) { #ifdef HAVE_WORDEXP_H gchar *got, *expected; got = mu_util_dir_expand ("~/IProbablyDoNotExist"); expected = g_strdup_printf ("%s%cIProbablyDoNotExist", getenv("HOME"), G_DIR_SEPARATOR); g_assert_cmpstr (got,==,expected); g_free (got); g_free (expected); #endif /*HAVE_WORDEXP_H*/ } static void test_mu_util_dir_expand_01 (void) { /* XXXX: the testcase does not work when using some dir * setups; (see issue #585), although the code should still * work. Turn of the test for now */ return; #ifdef HAVE_WORDEXP_H { gchar *got, *expected; got = mu_util_dir_expand ("~/Desktop"); expected = g_strdup_printf ("%s%cDesktop", getenv("HOME"), G_DIR_SEPARATOR); g_assert_cmpstr (got,==,expected); g_free (got); g_free (expected); } #endif /*HAVE_WORDEXP_H*/ } static void test_mu_util_guess_maildir_01 (void) { char *got; const char *expected; /* skip the test if there's no /tmp */ if (access ("/tmp", F_OK)) return; g_setenv ("MAILDIR", "/tmp", TRUE); got = mu_util_guess_maildir (); expected = "/tmp"; g_assert_cmpstr (got,==,expected); g_free (got); } static void test_mu_util_guess_maildir_02 (void) { char *got, *mdir; g_unsetenv ("MAILDIR"); mdir = g_strdup_printf ("%s%cMaildir", getenv("HOME"), G_DIR_SEPARATOR); got = mu_util_guess_maildir (); if (access (mdir, F_OK) == 0) g_assert_cmpstr (got, ==, mdir); else g_assert_cmpstr (got, == , NULL); g_free (got); g_free (mdir); } static void test_mu_util_check_dir_01 (void) { if (g_access ("/usr/bin", F_OK) == 0) { g_assert_cmpuint ( mu_util_check_dir ("/usr/bin", TRUE, FALSE) == TRUE, ==, g_access ("/usr/bin", R_OK) == 0); } } static void test_mu_util_check_dir_02 (void) { if (g_access ("/tmp", F_OK) == 0) { g_assert_cmpuint ( mu_util_check_dir ("/tmp", FALSE, TRUE) == TRUE, ==, g_access ("/tmp", W_OK) == 0); } } static void test_mu_util_check_dir_03 (void) { if (g_access (".", F_OK) == 0) { g_assert_cmpuint ( mu_util_check_dir (".", TRUE, TRUE) == TRUE, ==, g_access (".", W_OK | R_OK) == 0); } } static void test_mu_util_check_dir_04 (void) { /* not a dir, so it must be false */ g_assert_cmpuint ( mu_util_check_dir ("test-util.c", TRUE, TRUE), ==, FALSE); } static void test_mu_util_str_from_strv_01 (void) { const gchar *strv[] = { "aap", "noot", "mies", NULL }; gchar *str = mu_util_str_from_strv (strv); g_assert_cmpstr (str, ==, "aap noot mies"); g_free (str); } static void test_mu_util_str_from_strv_02 (void) { const gchar *strv[] = { "test", NULL }; gchar *str = mu_util_str_from_strv (strv); g_assert_cmpstr (str, ==, "test"); g_free (str); } static void test_mu_util_str_from_strv_03 (void) { const gchar *strv[] = { NULL }; gchar *str = mu_util_str_from_strv (strv); g_assert_cmpstr (str, ==, ""); g_free (str); } static void test_mu_util_get_dtype_with_lstat (void) { g_assert_cmpuint ( mu_util_get_dtype_with_lstat (MU_TESTMAILDIR), ==, DT_DIR); g_assert_cmpuint ( mu_util_get_dtype_with_lstat (MU_TESTMAILDIR2), ==, DT_DIR); g_assert_cmpuint ( mu_util_get_dtype_with_lstat (MU_TESTMAILDIR2 "/Foo/cur/mail5"), ==, DT_REG); } static void test_mu_util_supports (void) { gboolean has_guile; gchar *path; has_guile = FALSE; #ifdef BUILD_GUILE has_guile = TRUE; #endif /*BUILD_GUILE*/ g_assert_cmpuint (mu_util_supports (MU_FEATURE_GUILE), == ,has_guile); g_assert_cmpuint (mu_util_supports (MU_FEATURE_CRYPTO), == ,TRUE); path = g_find_program_in_path ("gnuplot"); g_free (path); g_assert_cmpuint (mu_util_supports (MU_FEATURE_GNUPLOT),==,path ? TRUE : FALSE); g_assert_cmpuint ( mu_util_supports (MU_FEATURE_GNUPLOT|MU_FEATURE_GUILE|MU_FEATURE_CRYPTO), ==, has_guile && path ? TRUE : FALSE); } static void test_mu_util_program_in_path (void) { g_assert_cmpuint (mu_util_program_in_path("ls"),==,TRUE); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_util_dir_expand */ g_test_add_func ("/mu-util/mu-util-dir-expand-00", test_mu_util_dir_expand_00); g_test_add_func ("/mu-util/mu-util-dir-expand-01", test_mu_util_dir_expand_01); /* mu_util_guess_maildir */ g_test_add_func ("/mu-util/mu-util-guess-maildir-01", test_mu_util_guess_maildir_01); g_test_add_func ("/mu-util/mu-util-guess-maildir-02", test_mu_util_guess_maildir_02); /* mu_util_check_dir */ g_test_add_func ("/mu-util/mu-util-check-dir-01", test_mu_util_check_dir_01); g_test_add_func ("/mu-util/mu-util-check-dir-02", test_mu_util_check_dir_02); g_test_add_func ("/mu-util/mu-util-check-dir-03", test_mu_util_check_dir_03); g_test_add_func ("/mu-util/mu-util-check-dir-04", test_mu_util_check_dir_04); /* test_mu_util_str_from_strv */ g_test_add_func ("/mu-util/mu-util-str-from-strv-01", test_mu_util_str_from_strv_01); g_test_add_func ("/mu-util/mu-util-str-from-strv-02", test_mu_util_str_from_strv_02); g_test_add_func ("/mu-util/mu-util-str-from-strv-03", test_mu_util_str_from_strv_03); g_test_add_func ("/mu-util/mu-util-get-dtype-with-lstat", test_mu_util_get_dtype_with_lstat); g_test_add_func ("/mu-util/mu-util-supports", test_mu_util_supports); g_test_add_func ("/mu-util/mu-util-program-in-path", test_mu_util_program_in_path); g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG| G_LOG_LEVEL_MESSAGE| G_LOG_LEVEL_INFO, (GLogFunc)black_hole, NULL); return g_test_run (); } mu-0.9.18/lib/tests/testdir2/0000755000175000017500000000000013021065721012700 500000000000000mu-0.9.18/lib/tests/testdir2/wom_bat/0000755000175000017500000000000013021065721014330 500000000000000mu-0.9.18/lib/tests/testdir2/wom_bat/cur/0000755000175000017500000000000013021065721015121 500000000000000mu-0.9.18/lib/tests/testdir2/wom_bat/cur/rfc822.20000644000175000017500000000266112605152237016145 00000000000000From: dwarf@siblings.net To: root@eruditorum.org Subject: Fwd: test abc References: <8639ddr9wu.fsf@cthulhu.djcbsoftware> User-agent: mu 0.98pre; emacs 24.0.91.9 Date: Thu, 24 Nov 2011 14:24:00 +0200 Message-ID: <861usxr9nj.fsf@cthulhu.djcbsoftware> Content-Type: multipart/mixed; boundary="=-=-=" MIME-Version: 1.0 --=-=-= Content-Type: text/plain Saw the website. Am willing to stipulate that you are not RIST 9E03. Suspect that you are the Dentist, who yearns for honest exchange of views. Anonymous, digitally signed e-mail is the only safe vehicle for same. If you want me to believe you are not the Dentist, provide plausible explanation for your question regarding why we are building the Crypt. Yours truly, --=-=-= Content-Type: message/rfc822 Content-Disposition: inline; filename= "1322137188_3.11919.foo:2,S" Content-Description: rfc822 From: dwarf@siblings.net To: root@eruditorum.org Subject: test abc User-agent: mu 0.98pre; emacs 24.0.91.9 Date: Thu, 24 Nov 2011 14:18:25 +0200 Message-ID: <8639ddr9wu.fsf@cthulhu.djcbsoftware> Content-Type: text/plain MIME-Version: 1.0 As I stepped on this unknown middle-aged Filipina's feet during an ill-advised ballroom dancing foray, she leaned close to me and uttered some latitude and longitude figures with a conspicuously large number of significant digits of precision, implying a maximum positional error on the order of the size of a dinner plate. Gosh, was I ever curious! --=-=-=-- mu-0.9.18/lib/tests/testdir2/wom_bat/cur/rfc822.10000644000175000017500000000324512605152237016143 00000000000000Return-Path: Subject: Fwd: rfc822 From: foobar To: martin Content-Type: multipart/mixed; boundary="=-XHhVx/BCC6tJB87HLPqF" Message-Id: <1077300332.871.27.camel@example.com> Mime-Version: 1.0 X-Mailer: Ximian Evolution 1.4.5 Date: Fri, 20 Feb 2004 19:05:33 +0100 --=-XHhVx/BCC6tJB87HLPqF Content-Type: text/plain Content-Transfer-Encoding: 7bit Hello world, forwarding some RFC822 message --=-XHhVx/BCC6tJB87HLPqF Content-Disposition: inline Content-Type: message/rfc822 Return-Path: Message-ID: <9A01B19D0D605D478E8B72E1367C66340141B9C5@example.com> From: frob@example.com To: foo@example.com Subject: hopjesvla Date: Sat, 13 Dec 2003 19:35:56 +0100 MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 7bit The ship drew on and had safely passed the strait, which some volcanic shock has made between the Calasareigne and Jaros islands; had doubled Pomegue, and approached the harbor under topsails, jib, and spanker, but so slowly and sedately that the idlers, with that instinct which is the forerunner of evil, asked one another what misfortune could have happened on board. However, those experienced in navigation saw plainly that if any accident had occurred, it was not to the vessel herself, for she bore down with all the evidence of being skilfully handled, the anchor a-cockbill, the jib-boom guys already eased off, and standing by the side of the pilot, who was steering the Pharaon towards the narrow entrance of the inner port, was a young man, who, with activity and vigilant eye, watched every motion of the ship, and repeated each direction of the pilot. --=-XHhVx/BCC6tJB87HLPqF-- mu-0.9.18/lib/tests/testdir2/wom_bat/cur/atomic0000644000175000017500000000166012605152237016251 00000000000000Date: Sat, 12 Nov 2011 12:06:23 -0400 From: "Richard P. Feynman" Subject: atoms To: "Democritus" Message-id: <3BE9E6535E302944823E7A1A20D852173@msg.id> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high If, in some cataclysm, all scientific knowledge were to be destroyed, and only one sentence passed on to the next generation of creatures, what statement would contain the most information in the fewest words? I believe it is the atomic hypothesis (or atomic fact, or whatever you wish to call it) that all things are made of atoms — little particles that move around in perpetual motion, attracting each other when they are a little distance apart, but repelling upon being squeezed into one another. In that one sentence you will see an enormous amount of information about the world, if just a little imagination and thinking are applied. mu-0.9.18/lib/tests/testdir2/bar/0000755000175000017500000000000013021065721013444 500000000000000mu-0.9.18/lib/tests/testdir2/bar/new/0000755000175000017500000000000013021065721014235 500000000000000mu-0.9.18/lib/tests/testdir2/bar/new/.noindex0000644000175000017500000000000012605152237015616 00000000000000mu-0.9.18/lib/tests/testdir2/bar/cur/0000755000175000017500000000000013021065721014235 500000000000000mu-0.9.18/lib/tests/testdir2/bar/cur/mail60000644000175000017500000000106212605152237015115 00000000000000Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "Geoff Tate" Subject: eyes of a stranger To: "Enrico Fermi" Message-id: <3BE9E6535E302944823E7A1A20D852173@msg.id> MIME-version: 1.0 X-label: @NextActions operation:mindcrime Queensrÿche Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high And I raise my head and stare Into the eyes of a stranger I've always known that the mirror never lies People always turn away From the eyes of a stranger Afraid to know what Lies behind the stare mu-0.9.18/lib/tests/testdir2/bar/cur/mail40000644000175000017500000000205613020504332015104 00000000000000Return-Path: Delivered-To: foo@example.com Received: from [128.88.204.56] by freemailng0304.web.de with HTTP; Mon, 07 May 2005 00:27:52 +0200 Date: Mon, 07 May 2005 00:27:52 +0200 Message-Id: <293847329847@web.de> MIME-Version: 1.0 From: =?iso-8859-1?Q? "=F6tzi" ?= To: foo@example.com Subject: =?iso-8859-1?Q?Re:=20der=20b=E4r=20und=20das=20m=E4dchen?= Precedence: fm-user Organization: http://freemail.web.de/ Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 8bit X-MIME-Autoconverted: from quoted-printable to 8bit by mailhost6.ladot.com id j48MScQ30791 X-Label: \backslash X-UIDL: 93h!!\i <87hbblwelr.fsf@sapphire.mobileactivedefense.com> <8762s0jreh.fsf@sapphire.mobileactivedefense.com> <87hbbjc5jt.fsf@sapphire.mobileactivedefense.com> <8ioh48-8mu.ln1@leafnode-msgid.gclare.org.uk> Organization: UseNetServer - www.usenetserver.com X-Complaints-To: abuse@usenetserver.com Message-ID: Date: 08 Mar 2011 17:04:20 GMT Lines: 27 Xref: uutiset.elisa.fi comp.unix.programmer:181736 John Denver writes: >Eric the Red wrote: > >>> There _IS_ a requirement that all reads and writes to regular files >>> be atomic. There is also an ordering guarantee. Any implementation >>> that doesn't provide both atomicity and ordering guarantees is broken. >> >> But where is it specified? > >The place where it is stated most explicitly is in XSH7 2.9.7 >Thread Interactions with Regular File Operations: > > All of the following functions shall be atomic with respect to each > other in the effects specified in POSIX.1-2008 when they operate on > regular files or symbolic links: > > [List of functions that includes read() and write()] > > If two threads each call one of these functions, each call shall > either see all of the specified effects of the other call, or none > of them. > And, for the purposes of this paragraph, the two threads need not be part of the same process. jimbo mu-0.9.18/lib/tests/testdir2/bar/cur/mail10000644000175000017500000000277212605152237015121 00000000000000Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "John Milton" Subject: Fere libenter homines id quod volunt credunt To: "Julius Caesar" Message-id: <3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com> MIME-version: 1.0 x-label: Paradise losT X-Keywords: milton,john Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high OF Mans First Disobedience, and the Fruit Of that Forbidden Tree, whose mortal tast Brought Death into the World, and all our woe, With loss of Eden, till one greater Man Restore us, and regain the blissful Seat, [ 5 ] Sing Heav'nly Muse,that on the secret top Of Oreb, or of Sinai, didst inspire That Shepherd, who first taught the chosen Seed, In the Beginning how the Heav'ns and Earth Rose out of Chaos: Or if Sion Hill [ 10 ] Delight thee more, and Siloa's Brook that flow'd Fast by the Oracle of God; I thence Invoke thy aid to my adventrous Song, That with no middle flight intends to soar Above th' Aonian Mount, while it pursues [ 15 ] Things unattempted yet in Prose or Rhime. And chiefly Thou O Spirit, that dost prefer Before all Temples th' upright heart and pure, Instruct me, for Thou know'st; Thou from the first Wast present, and with mighty wings outspread [ 20 ] Dove-like satst brooding on the vast Abyss And mad'st it pregnant: What in me is dark Illumin, what is low raise and support; That to the highth of this great Argument I may assert Eternal Providence, [ 25 ] And justifie the wayes of God to men. mu-0.9.18/lib/tests/testdir2/bar/cur/mail20000644000175000017500000000064612605152237015120 00000000000000Date: Thu, 31 Jul 2008 14:57:25 -0400 From: "Socrates" Subject: cool stuff To: "Alcibiades" Message-id: <3BE9E6535E0D852173@emss35m06.us.lmco.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high The hour of departure has arrived, and we go our ways—I to die, and you to live. Which is better God only knows. http-emacs mu-0.9.18/lib/tests/testdir2/bar/cur/mail50000644000175000017500000000015712605152237015120 00000000000000Date: Mon, 13 Jun 2011 14:57:25 -0400 From: xyz@123.xx Subject: abc To: foo@bar.cx Message-id: 123 mu-0.9.18/lib/tests/testdir2/bar/cur/mail30000644000175000017500000000361312605152237015116 00000000000000From: Napoleon Bonaparte To: Edmond =?UTF-8?B?RGFudMOocw==?= Subject: rock on dude User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/24.0 Mule/6.0 (HANACHIRUSATO) Fcc: .sent MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le 24 février 1815, la vigie de Notre-Dame de la Garde signala le trois-mâts le Pharaon, venant de Smyrne, Trieste et Naples. Comme d'habitude, un pilote côtier partit aussitôt du port, rasa le château d'If, et alla aborder le navire entre le cap de Morgion et l'île de Rion. Aussitôt, comme d'habitude encore, la plate-forme du fort Saint-Jean s'était couverte de curieux; car c'est toujours une grande affaire à Marseille que l'arrivée d'un bâtiment, surtout quand ce bâtiment, comme le Pharaon, a été construit, gréé, arrimé sur les chantiers de la vieille Phocée, et appartient à un armateur de la ville. Cependant ce bâtiment s'avançait; il avait heureusement franchi le détroit que quelque secousse volcanique a creusé entre l'île de Calasareigne et l'île de Jaros; il avait doublé Pomègue, et il s'avançait sous ses trois huniers, son grand foc et sa brigantine, mais si lentement et d'une allure si triste, que les curieux, avec cet instinct qui pressent un malheur, se demandaient quel accident pouvait être arrivé à bord. Néanmoins les experts en navigation reconnaissaient que si un accident était arrivé, ce ne pouvait être au bâtiment lui-même; car il s'avançait dans toutes les conditions d'un navire parfaitement gouverné: son ancre était en mouillage, ses haubans de beaupré décrochés; et près du pilote, qui s'apprêtait à diriger le Pharaon par l'étroite entrée du port de Marseille, était un jeune homme au geste rapide et à l'œil actif, qui surveillait chaque mouvement du navire et répétait chaque ordre du pilote. mu-0.9.18/lib/tests/testdir2/bar/tmp/0000755000175000017500000000000013021065721014244 500000000000000mu-0.9.18/lib/tests/testdir2/bar/tmp/.noindex0000644000175000017500000000000012605152237015625 00000000000000mu-0.9.18/lib/tests/testdir2/Foo/0000755000175000017500000000000013021065721013423 500000000000000mu-0.9.18/lib/tests/testdir2/Foo/new/0000755000175000017500000000000013021065721014214 500000000000000mu-0.9.18/lib/tests/testdir2/Foo/new/.noindex0000644000175000017500000000000012605152237015575 00000000000000mu-0.9.18/lib/tests/testdir2/Foo/cur/0000755000175000017500000000000013021065721014214 500000000000000mu-0.9.18/lib/tests/testdir2/Foo/cur/fraiche.eml0000644000175000017500000000052012605152237016237 00000000000000From: Sender To: Recip Subject: search accents Date: 2012-12-08 00:48 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit line 1: Глокая куздра штеко будланула бокра и курдячит бокрёнка line 2: crème fraîche mu-0.9.18/lib/tests/testdir2/Foo/cur/arto.eml0000644000175000017500000005617012605152237015617 00000000000000Return-Path: <> X-Original-To: f00f@localhost Delivered-To: f00f@localhost Received: from puppet (puppet [127.0.0.1]) by f00fmachines.nl (Postfix) with ESMTP id A534D39C7F1 for ; Mon, 23 May 2011 20:30:05 +0300 (EEST) Delivered-To: diggler@gmail.com Received: from ew-in-f109.1e100.net [174.15.27.101] by puppet with POP3 (fetchmail-6.3.18) for (single-drop); Mon, 23 May 2011 20:30:05 +0300 (EEST) Received: by 10.142.147.13 with SMTP id u13cs87252wfd; Mon, 23 May 2011 01:54:10 -0700 (PDT) Received: by 10.204.7.74 with SMTP id c10mr1984197bkc.104.1306140849326; Mon, 23 May 2011 01:54:09 -0700 (PDT) Received: from MTX4.mbn1.net (mtx4.mbn1.net [213.188.129.252]) by mx.google.com with ESMTP id e6si18117551bkw.39.2011.05.23.01.54.07; Mon, 23 May 2011 01:54:08 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of MTX4.mbn1.net designates 213.188.129.252 as permitted sender) client-ip=213.188.129.252; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of MTX4.mbn1.net designates 213.188.129.252 as permitted sender) smtp.mail= Resent-From: X-Default-Received-SPF: pass (skip=forwardok (res=PASS)) x-ip-name=192.168.10.123; From: ArtOlive To: "f00f@f00fmachines.nl" Reply-To: Date: Mon, 23 May 2011 10:53:45 +0200 Subject: NIEUWSBRIEF ART OLIVE | juni exposite in galerie ArtOlive MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="_=aspNetEmail=_5ed4592191214c7a99bd7f6a3a0f077d" X-Mailer: aspNetEmail ver 3.5.2.10 X-Sender: 87.93.13.24 X-RemoteIP: 87.93.13.24 Originating-IP: 87.93.13.24 X-MAILINGLIJST-ID: <10374608.109906.11909.2011523105345.MSGID@mailinglijst.nl> Message-ID: <10374608.109906.11909.2011523105345.MSGID@mailinglijst.nl> X-Authenticated-User: guest@mailinglijst.eu X-STA-Metric: 0 (engine=030) X-STA-NotSpam: geinformeerd vormen spec:usig:3.8.2 twee samen X-STA-Spam: 2011 &bull • e-mailing subject:juni X-BTI-AntiSpam: score:0,sta:0/030,dnsbl:passed,sw:passed,bsn:10/passed,spf:off,bsctr:passed/1,dk:off,pbmf:none,ipr:0/3,trusted:no,ts:no,bs:no,ubl:passed X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply Resent-Message-Id: <19740414233016.EB6835A132F5FCF4@MTX4.mbn1.net> Resent-Date: Mon, 23 May 2011 10:54:07 +0200 (CEST) --_=aspNetEmail=_5ed4592191214c7a99bd7f6a3a0f077d Content-Type: text/plain; charset="iso-8859-15" Content-Transfer-Encoding: quoted-printable ART-O-NEWS; juni 2011 Westergasfabriekterrein Polonceaukade 17 10= 14 DA Amsterdam tel: 020-6758504 info@artolive.nl www.artolive.nlJuni= expositie bij ArtOlive: Peter van den Akker en Marinel Vieleers Zondag 5 juni Elke maand vindt er in de galerie van ArtOlive een expositie plaats. We = lichten enkele kunstenaars uit (die je misschien al kent van onze website= ), waarbij we een spannende mix van materiaal en techniek presenteren. Ti= jdens de expositie staan we klaar om elke vraag over ons kunstaanbod te b= eantwoorden.=20 De exposities zijn te bezoeken van maandag t/m vrijdag, tussen 10:00 en 1= 7:00 uur. De opening is altijd op de eerste zondag van de maand. Dit valt= samen met de Sunday Market die elke maand op het Cultuurpark Westergasfa= briek georganiseerd wordt. De Sunday Market is gratis te bezoeken en staa= t vol met kunst, design, mode en heerlijke hapjes, en er hangt altijd een= vrolijke sfeer. Een ideaal moment dus om in te haken en deze maand twee = kunstenaars te presenteren: Peter van den Akker en Marinel Vieleers. We verwelkomen je graag op zondag 5 juni 2011, van 12:00 t/m 17:00 uur op= de Polonceaukade 17 van het Cultuurpark Westergasfabriek in Amsterdam!=20= bekijk meer werk op www.artolive.nl... Peter van den Akker "In mijn beelden en schilderijen staat het mensbeeld centraal; niet als i= ndividu maar als universele gestalte, waarbij ik op transparante wijze ti= jdsbeelden en gelaagdheid in het menselijke handelen naar voren breng. Ve= rhoudingen tussen mensen, verschuivingen in wereldculturen en verandering= en in techniek, architectuur, natuur en mensbeeld vormen mijn inspiratieb= ronnen. Het zijn allemaal beelden en sferen die naast en met elkaar besta= an. Mijn werkwijze omvat vele technieken in verschillende materialen: sch= ilderijen, gemengde technieken op papier/collages, zeefdrukken, beelden i= n cortenstaal, keramische objecten." Peter van den Akker exposeert regelmatig in binnen- en buitenland bij gal= erie=EBn en musea en is in verschillende kunstinstellingen en bedrijfscol= lecties opgenomen. lees meer over Peter... Marinel Vieleers Marinel Vieleers probeert het menselijke in de bouwwerken - en ook vaak i= ets van het karakter van de bouwer of bewoner - te laten zien. Het zijn m= aar subtiele details die dat alles weergeven. De 'tand des tijds' of invloed van mensen op de gebouwen spelen vaak mee = in het werk. Koper, cement, lood en andere materialen worden in haar nieu= we werk gebruikt. Op deze manier kan ze gemakkelijker improviseren en nog= directer op haar gevoel afgaan. Marinel is gefascineerd door de schoonheid van het imperfecte. De gelaagd= heid van ouderdom, het verval. De imperfectie die ontstaat door toevallig= e omstandigheden maakt een huis, een muur, een schutting, hout of steen b= oeiend. Het is doorleefd en het toont een stukje van zijn geschiedenis. lees meer over Marinel... =20 ZONDAG 5 MEI - Juni expositie in de galerie van ArtOlive met Marinel Viel= eers en Peter van den Akker Opening op zondag 5 mei, tijdens de Sunday Market op het Cultuurpark West= ergasfabriek in Amsterdam. Je bent van harte uitgenodigd om tussen 12:00 = en 17:00 uur langs te komen! Daarna is de expositie te zien op werkdagen (ma - vrij) tussen 10:00 en 1= 7:00. De expositie duurt tot 24 juni 2011. wil je niet langer door artolive ge=EFnformeerd worden? Klik dan hier om= je af te melden.=20 kreeg je dit mailtje doorgestuurd en wil je voortaan zelf ook graag de n= ieuwsbrief ontvangen?=20 klik dan hier om je aan te melden.=20 Deze e-mailing is verzorgd met MailingLijst --_=aspNetEmail=_5ed4592191214c7a99bd7f6a3a0f077d Content-Type: text/html; charset="iso-8859-15" Content-Transfer-Encoding: quoted-printable Artolive
3D"artolive"
ART-O-NEWS • juni 2011
Westergasfabriekterrein   Polonceau= kade 17 1014 DA Amsterdam   tel: 020-6758504  info@artolive.nl<= /a>  www.artolive.nl
Juni= expositie bij ArtOlive: Peter van den Akker en Marinel Vieleers

Zondag 5 juni
Elke maand vindt er in de galerie van Art= Olive een expositie plaats. We lichten enkele kunstenaars uit (die je mis= schien al kent van onze website), waarbij we een spannende mix van materi= aal en techniek presenteren. Tijdens de expositie staan we klaar om elke = vraag over ons kunstaanbod te beantwoorden.

De exposities zijn te bezoeken van maa= ndag t/m vrijdag, tussen 10:00 en 17:00 uur. De opening is altijd op de e= erste zondag van de maand. Dit valt samen met de Sunday Market die elke m= aand op het Cultuurpark Westergasfabriek georganiseerd wordt. De Sunday M= arket is gratis te bezoeken en staat vol met kunst, design, mode en heerl= ijke hapjes, en er hangt altijd een vrolijke sfeer. Een ideaal moment dus= om in te haken en deze maand twee kunstenaars te presenteren: Peter van = den Akker en Marinel Vieleers.

We verwelkomen je graag op zondag 5 ju= ni 2011, van 12:00 t/m 17:00 uur op de Polonceaukade 17 van het Cultuurpa= rk Westergasfabriek in Amsterdam!


3D""  = bekijk= meer werk op www.artolive.nl...   

=
3D""
<= a target=3D"_blank" href=3D"http://www.mailinglijst.eu/redirect.aspx?l=3D= 154043&a=3D10374608&t=3DH"> <= /td> Peter van den Akker

"In mijn beelden en schild= erijen staat het mensbeeld centraal; niet als individu maar als universel= e gestalte, waarbij ik op transparante wijze tijdsbeelden en gelaagdheid = in het menselijke handelen naar voren breng. Verhoudingen tussen mensen, = verschuivingen in wereldculturen en veranderingen in techniek, architectu= ur, natuur en mensbeeld vormen mijn inspiratiebronnen. Het zijn allemaal = beelden en sferen die naast en met elkaar bestaan. Mijn werkwijze omvat v= ele technieken in verschillende materialen: schilderijen, gemengde techni= eken op papier/collages, zeefdrukken, beelden in cortenstaal, keramische = objecten.”

Peter van den Akker expose= ert regelmatig in binnen- en buitenland bij galerieën en musea en is= in verschillende kunstinstellingen en bedrijfscollecties opgenomen.


3D""  = lees meer over Peter...   

= Marinel Vieleers

Marinel Vieleers probeert = het menselijke in de bouwwerken - en ook vaak iets van het karakter van d= e bouwer of bewoner - te laten zien. Het zijn maar subtiele details die d= at alles weergeven.

De ‘tand des tijds&r= squo; of invloed van mensen op de gebouwen spelen vaak mee in het werk. K= oper, cement, lood en andere materialen worden in haar nieuwe werk gebrui= kt. Op deze manier kan ze gemakkelijker improviseren en nog directer op h= aar gevoel afgaan.

Marinel is gefascineerd do= or de schoonheid van het imperfecte. De gelaagdheid van ouderdom, het ver= val. De imperfectie die ontstaat door toevallige omstandigheden maakt een= huis, een muur, een schutting, hout of steen boeiend. Het is doorleefd e= n het toont een stukje van zijn geschiedenis.


3D""  = lees meer ov= er Marinel...   

3D""
3D""

ZONDAG 5 MEI - Juni expositie in de galerie van ArtOlive met = Marinel Vieleers en Peter van den Akker

Opening op zondag 5 mei, = tijdens de Sunday Market op het Cultuurpark Westergasfabriek in Amsterdam= . Je bent van harte uitgenodigd om tussen 12:00 en 17:00 uur langs te kom= en!

Daarna is de expositie te zien op werkdagen (ma - vrij= ) tussen 10:00 en 17:00. De expositie duurt tot 24 juni 2011.
3D"Kunst wil je niet langer door artolive geïnformeerd worden? Klik= dan hier om je af te melden.
kreeg je dit mailtje doorgestuurd en wil je voortaan = zelf ook graag de nieuwsbrief ontvangen?
klik dan hier om= je aan te melden.

<= HR SIZE=3D1 STYLE=3D"COLOR:#d3d3d3" SIZE=3D1>Deze e-mailing is verzorgd m= et MailingLijst

--_=aspNetEmail=_5ed4592191214c7a99bd7f6a3a0f077d-- mu-0.9.18/lib/tests/testdir2/Foo/cur/mail50000644000175000017500000013234413020504332015070 00000000000000From: Sitting Bull To: George Custer Subject: pics for you Mail-Reply-To: djcb@djcbsoftware.nl User-Agent: Hunkpapa/2.15.9 (Almost Unreal) Message-Id: CAHSaMxZ9rk5ASjqsbXizjTQuSk583=M6TORHz=bfogtmbGGs5A@mail.gmail.com Fcc: .sent MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: multipart/mixed; boundary="Multipart_Sun_Oct_17_10:37:40_2010-1" --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: text/plain; charset=US-ASCII Dude! Here are some pics! --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: image/jpeg Content-Disposition: inline; filename="sittingbull.jpg" Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAQAAAQABAAD/4QvoRXhpZgAASUkqAAgAAAAIABIBCQABAAAAAQAAABoBCQAB AAAASAAAABsBCQABAAAASAAAACgBCQABAAAAAgAAADEBAgAOAAAAbgAAADIBAgAUAAAAfAAAABMC CQABAAAAAQAAAGmHBAABAAAAkAAAAN4AAABndGh1bWIgMi4xMS4zADIwMTA6MTA6MTcgMTA6MzM6 MzcABgAAkAcABAAAADAyMjEBkQcABAAAAAECAwAAoAcABAAAADAxMDABoAkAAQAAAAEAAAACoAkA AQAAAMgAAAADoAkAAQAAAGsBAAAAAAAABgADAQMAAQAAAAYAAAAaAQkAAQAAAEgAAAAbAQkAAQAA AEgAAAAoAQkAAQAAAAIAAAABAgQAAQAAACwBAAACAgQAAQAAALMKAAAAAAAA/9j/4AAQSkZJRgAB AQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwc KDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIy MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCACAAEcDASIAAhEBAxEB/8QAHwAA AQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIh MUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpT VFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5 usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAA AAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEI FEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVm Z2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK 0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDq77xdrX/CQ6laRXjRxQTF ECovA/EUg8Sa6W/5CUuP9xP8K5yWQnxjrw9Lwj9BWjkgZHFAG6mu6yV51OXP+4n/AMTUq61rBB/4 mU2f9xP/AImsJJTuAJFW0YDnfmgCTUPFGqWFq882p3G1eyqmT/47VfRfGGpawkgGp3CyIeg2cj1+ 7XK+O7zybCGMNjzHyR6gD/69ZvgG8zqU67vvRZH4EUAesJe6m/XVLv8ANf8A4mpf7Qvl/wCX+6b6 uP8ACs+ObKdaeh3Hg9aANTw/4gurjxTLpU7tIv2cTKzHpgkH+n5UVheHGI+KWzJwdNP/AKFRQBzD 7f8AhMfEDEHH24j/AMdWrs0oCkDrVKJs+NfEsZ+79u/9kWrd5GqKTmgCstwwkyT0p5uzu61mOzbj zSFn3DmgDB8ePLPe2MEQZyykhRzk9/5Va8D6Vd2Mz3d3CYxJHiPd16+la0hhMybkUvxhj1HWr5uM uB0wMYoA3YJARjvV+DBPasC2lYsOuK3LVunWgCLQRj4sIPXTGP8A4/RSaF/yV2P30tv/AEOigDmY QD408UE9ftw/9AFXpv3iFT9Kgs4t3jXxV6C+H/oAq5cxkMcCgDFltXVyMVVv7iGwtzNcNsQfiT+F a8jbAxdgFAzmuTZZfEV81vG+xTyX/uIPT3P9aAIBr9vdNHcQI/lxk5DDBrfsLuK+jE0MqupPOOx9 KzNY8L6fbaaYrdGXb3BOcnuT+FcpodzN4c8RRRyylrW4IDE9MdM/UUAes2wbOAK27PhRms6CJlwc VrWowRkUAV9CP/F3YffSm/8AQ6Kfo+P+FuWp9dLf/wBDooAxrH/kd/Ff/X8P/Ra1evUOcgVW01Qf G/izIz/py/8Aota2LqPK4xQBxniWc2mi3MxBGFA/Mgf1rmtEF/Z6HNqMNuzvPnY+7G1V6Hoe+T0r qfGmnT3Xhm8WNWJVQ+B/skH+lUPBt3d3PhuzXyBM6xBY0YfKDnALewxmgDE1BfEDaPaXNzMRJPIQ +TjCgDHb69u9ZGt2Us2lrdNDtMLAgq27Kng84Fd74qnaMwWB8qWRTnzUcfePGSOx4ziuf1kzT6S9 tuRHlVUG5sDJOMA+lAHofh5/tvh3T7k4ZnhXcfcDB/UVuRQEdqzvDelPo/hywsJGDSRRjeR0yeTj 2ya3I8/3aAMXSU2/FmzJ/wCgbJ/6FRUunf8AJV7H/sGy/wDoQooAyNJXf448XYPS+X/0Wtb8ynyj 0rm/DIll8W+KDKQ0pvF3FehPlr0rvINMzbfN8rsc7upH0oA5ie3mktZSI1ICn5W43e1ec6ZrDwax facIj9liUNtUcgE8j0IzXrHiqS20rQJbiadoyBsWQjc2T2HvXnvhbREuzeXTbvMlfILcsF6D6jFA GJr+pWE1ymFkwFzhlwo+i1xevazLd3Fva2+UiQhh7kdPyr0jVfA8t0BeXNybe35UK2EJAJwST/QG uS1Pw7HYalbKHUIxYxyDd8wHUnNAHsnhXVBrGhWkrBlmEYVww6sAATXQInA5rn/AOZtIa3mQHZI+ xwfvAnJ6d8n9a6yazEKhlzgUAc1YAr8WbH302X/0IUU6xBPxYsSe2my/+hUUAV/Bdj5fi3xWJJDJ JHeopY8bj5a5OK9AUArwARXEeFjjxh4xbub5f/RYrsIZgJhGTjcuQMGgDnfHiwnw1KJoVkUuB8yg hfeuZ+HemTLpjx3OCZNzKUbPy54/Sut8Z263OlJE1wYgzkkjvgH86yfBb+XYWuIGiEithWzn9aAN loTcO0ctuGjV9oMg5JGCSOOnp9K8/wDH1qH1iERrukRAqqB3Jzj9BXpsk6F+oyCuRjJ54rhNcg+3 Ge5XiUSL5ZGc87sdPagDQ+HlvJHoAdo9h85mUY7dK7WSRCoB6HiuV8IiW10JYs7yszDJ7fN/k1tG Rpb4xj7qpnj3Iwfx5oAwLMgfF+1UHI/suTH/AH3RTLJNnxltx2Olvj/vuigB3hgf8Vp4vH/T8v8A 6AK6aRWFk2CA2CPSua8M4/4T3xcp/wCftD/45XR6q32e1JjUySCRdqA4J3HH9aAKHiJTceH4mliK r5e5lDfMpx2Iqp4eQR6Zp75Y4jX7xyfTn8q29djjbS/LMqxYGFdugNZWlskOh2pKgYj2AqO4OB/M 0AW7+NLQ3Fwi/O6hsk5yRwOO3WuS1qGJtNuvN3iNJkX5e+EIxn8f1re1e4ubq8jSOMiBArZJ/wBY xOcfQcZ+tVNTsYh4dnjmG9PMJIP8XYUAQ20z2Hg6OeJGTYQzd+N3Le+RzXQ6TGwtjLLkuxAy3XAH f8Saw9Mlt7vwsI4yZI9m07xtyM/y5rqodqxIFAIx1oA5iDj4w2ZHfS3/APQjRSw8/GGzx20x/wD0 I0UAee+I/GV/4S+IXiAWlvFKJ7gM28njC+1Ubn4v6xclC1hbAq6vwzdjn+lXviB4X1O88b6lPBYX EkUkgZXWJiDwPQVzH/CH61zjSbwj1EDf4UAbV78YdZvYPJbT7UA+7HP61HbfFXXLW3SFdOtSqZ67 v8fesg+Ddbzn+yL3P/XBv8Kcvg3Xc5Oj3x/7YP8A4UAaY+KuvIQP7PtM5JXKt/jUF78Udcu7F7WS ytEVv4grZHPB61VPg/Ws/wDIGvs9v3T/AOFMPg7XcHOk32P+uD/4UAWLb4l6vb2zQJZ2m1gP4WGC FAz19q17f4va0sSobS04GB8rf41z3/CIayOuk3g/7d2/wqRfCWr8f8S27/78P/hQB33w78Q3fib4 jR3l3HHG6WTxgR5xjOe/1oq78JvCmo6dq8+qXUBhhETQqJAVYsSDkA8496KAP//ZAP/bAEMABQME BAQDBQQEBAUFBQYHDAgHBwcHDwsLCQwRDxISEQ8RERMWHBcTFBoVEREYIRgaHR0fHx8TFyIkIh4k HB4fHv/bAEMBBQUFBwYHDggIDh4UERQeHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e Hh4eHh4eHh4eHh4eHh4eHv/AABEIAWsAyAMBIgACEQEDEQH/xAAdAAABBAMBAQAAAAAAAAAAAAAH AwQFBgACCAEJ/8QAVhAAAQMCBAIHAwYHCwoGAgMAAQIDEQAEBQYSITFBBwgTIlFhcRSBkRUjMqGx 0RYXQoKSssElJjNDRFJicpOi8CQ0NmNzo7PC0uEnNVNUVYMJRWR08f/EABQBAQAAAAAAAAAAAAAA AAAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwDqjNudMEyw6lvE3HEqUnUS kDSmeEkkCTB241Aq6X8mgSL5J/PT99DPrX3S232GwohPbp5/6v8A70PMsrSuwYKgkgtjiONB0Ovp lykFQLhJ8+0Fefjoyh/6/wDfFBctsJWkpt0wRzSK9ft7cwVMNmf6IoDOOmjJ5/lB/SFejpmyeT/n I/TFBdFqwACGGR6IFOWbRhZ3Ya349wUBgT0y5QP8qT+n/wBq3T0x5OPG8A/OFCy3tbZCRpt2j+YK cotLMjvWjM/1BQEs9MWTR/LUn0VWfjiyb/72hki1sw5q9kaHL6ApZDFspQHsze3DuCgI6umLJqUg +2Eg+AJ/ZSR6aMmj+UOfon7qozjTACUhlsCP5opo402AQWkeumgIK+mrKAA0uuK8e6R+ykj03ZU5 JePx+6h6gaTGlPHmkUwzDjlrhDKVvtIWXVaUIjj4+6gKKOm7KZUQrtU+YCj/AMteHpuyvvpbeV7l f9NAHEc3Ls8ed9iYZLTZIAI4+vpRAy1i7WKYS3dhtIDghQHIjiKC9npwy3O1rcH3K/6a1V034Ef4 OyfPqlf/AE1WG1KKxAAHhFO0LgTCfhQTf47cGPCwuJ/qr/6a9R004as7YXcn0bX/ANNRLbqhsAOF OWnVgEnh4UEm30wWbhhvBL5e3Jpz/ppwOlMLSC3lzElT4suD/kqHN642YT9tIu3T7h2UYHnQWW36 S9YleXcQA8Qhf7U05b6SMPXiFpYGwfZfu19myl9XZ6leEkR9dVBy7XEFXLcUN+mPEn7VnCrplZQ6 zclaFeCgAQfqoOqbO4L6VBxvsnURrRqConcbisqo9FOYE5mw1zGENdkLllpZQSCUkApO/wCbWUAY 63i9N0x3v5SkHy+aqj5QBXhFqRzbFWrrjrWm5a1ER7Unh/saqOSXP3Bst/4pNBbWhLY1nvCt1NyZ mRWtuoKESCOdKjSOB99BiEjYHYU4ZQpKpTwpArTxpVt8g7RQPmlQkpP2UoFE8VkU0buEkK4A+NbN rCzHDagfQCmN69TtsPjTRLoBA1b0oHFaZPwoH6IPODFJPwOG9asklJJ228a8egp9aBspcHhvQf6T b55eZHWVu6m2kgISDsnaT76Kt46hlpx1WyUJKj7q59xzEFXV9dXC1krccK9/M0CybtYUTrMnj50R uiPGLl69OHBTaWG2isgjdRkffQgFyoqO441eOiW4WM1W8D6aVBXpFAfGCVGQmnDZO07jmaYW74G0 yKfNuJUBB50D0ISAIAiNyK3UUkQIpDtgUhAAIApNToAKQACedAotaZgCfOvQrUCAIpsVAma31kEE bbUG60Dwn1oW9O6S3h2HqnYvK291E1SlEkzvQr6f1qRg1gVHc3B4+lAaeq2rXkFhcz3NPwcXWU06 pzqnchtQ4CkBQjT/AKxVZQDXrpOEXLJO0XYj+xFVDI6lHAbEeLSfsq1dd2UPWyxMG7HHhPZCqtkX /wAhw9Svo9gj7KC4Wx2MCKdt7ncTTaxbGmBz3p4hISNzHhQJrQSYnjXiUkQOYpZzupJJpFK5g7n3 0ChQSkbxSluSFDfam5WACokj1NetPAqA29KCXb0HdQ9TSwQlSduVR6XBpJnetkXWgCTQSTQCRpMk Uk8djzpsm9ExNaOP6pOqBQRWcblNtly/eI3SwoTPiIrnbEXAl7j4UaulrEHLbJ9z2ZEuKS2fQneg Fevy62ASQYoHCHkqXsqrl0U3aWs52mpelLgU3v5jah20/C9zO/hVmyc8Bj+HjfvXCB9dB09bq0mD zqQZUkAVDtriOdO7ZzSYO8UEoHBw3r0KB3mm6XUxERWdpO0UDhJBVvSulKh9kU2bUdzvvSzKu9tI mgXShOjfaTQk6xZ/cOw4GLk7/m0XlqlGxEcCDzoP9Y1KhgNhI/lR/VNAWuqGpJyG3pBHdVPn84qs rTqgqJyKyCI+bXHp2qqygH/XoQEW9i5M6r0CJ/1VVnJKP3Aw8QP4BvYf1RVh69K1lm0BMgXyQPL5 moPJ50YDY+TCP1RQW+yRoaC44HhSygNXe99NbW7lCUKA48K3cdgbk8KDa5IAMcKj3XiklIMTSrj0 zCppi8STwNAo5cEo4RXtq53pn1pke8NIUR5UoydWoA+lBMdrCdjSLr5G8yfCmJdWE6ZpNx2NiZoH 4uZjlThKitJ35TUOl4DntUlZOgoiaAe9Nl+tnDbS1QAULdlzxHgftoMLdm4VzAmKKvTsvsrywUqS y8y42seBBBB9xoSWTa7hVwocGmyrh7qDZpUKFXzoptG77NdoFkwiXD7uFUvCMOu7+5SxbNLccO8A TRd6Nsq3eB5mbVezq9l7ThsCTEUBcQvhtuKdWy0qVJ40wS4NqcNOTvA+NBKpMokRSiFKnx8opo07 KAAYNOGwrko0DlB/JO005Y0gTNNW9pkzTm3SDJJoHIQhIlKZ50IesgFDLdgYgC74/mmjC03KaEXW WBTlmykH/Ox+qqgJnVAV+8S3hMbOA78fnVVlZ1Oio5BbnkHI/tVVlAOOvSIbtlSN8QSD/Y0yyayh 3Ldg4BBNs37+6KedfERaWqo2+UE/8E1FdGdwF5Yw0kgzatmPzRQWdhnRClCTWPAEHeKkENodbSpO xpBy3IJJjegjFwjh6U1eWSoxw50+uGQHPE86j7qEqiY9KBstYSe6B61gfLapB5QaaPOQs77V4twR wNA8TcFyPKsdd25UyS6RBAivXXJSDQOm1jyinlq/CoSY22qDDh1AVI22mUqmNqCpdI+GXOZb9Ng1 I9jZL+qNiVSAPiKiMhZQFixcHGGgkvJPaJJ4JBolsKaTdO9zdTcKV4CdqalLSrtevcQEkeVA7ytl /CcOm7sLVKFugb+VSt06k3WgfSSO9HKss30NsagAkAd0GminCXSr8onegfNKSDO/nJp22tCSDUYF xEJpVDkkwDPrQTTCoGxBmnjDp4KG9QVu4rVxjepS3WY1KVPpQSbSiT508tBvBImo9hJICt96f2yS TwIigkWht6UHes8ojK9mJE+2Dj/UVRhaEpn7aDnWhH71LP8A/uj9VVATupmZyCiePzn/ABVVledT OfxfszG6XYj/AGyqygHfX3A+TrPmTiKZ3/1JqvdFX+h+EqPO2TU91+NrK1Mf/sER/YmoLopSpWR8 II/9sPtNARbNSdAKdzHCtnY0Eq4zypnbFaEAqImnRUFt6p93OgjbyEEn6qhrxQUo+FTGIp7m23rU HeGKBi6E6orRcRtWyiSoxWp4ERQeoAI3NeLSnnW7Q2341s5skcOMUCSEpMk08to0gA/Gmu4HCvW1 qoPMQWtOJIDR/I3HiKyycacfU4o86Y4qpxBcutiUphIpLLy1LGtSN1RsTwoLS0SQpx3uoG4HlSHa gq1JMSa9ullVulJAA5+dN0wYiKCQbcJ+lNLoVzmmjKoEmndudSoVAHpQOrbVq3mIqWtYME7Co9oE xHAeFPWVqCQY250EsyuBsSPWn7C1CI4+tRdq4F7gjzFSduoCBHoKCRYUYAG1CHrQJjKFqqB/nqf1 FUWbck96NO3jNCXrPmMm23Ha9R+oqgJPUvM9H6PLtR/vTWVt1MkoT0eMaQAVpdUYPPtSPurKAb9f ZQNkwnf/AMwR/wAA1GdD7QOQMHUeduPtNSfX3H7n2y4mcSSPT5g1G9D69XR9gxCeFvHHzNBczGkR tWqXIBjlW4HdO0U1XqQsGOe9ApcAKTHEGoe7tyJqWWSBv61roQ6nvCgrD7cEyPqpNCYn0qavrMhW w5U1NsYAAoGLAEmaUcAinItSZ8R4VjjJ0Qo+tBHK+NeoI0kVXs3ZptMIS42wk3FwB9EGAPU0KMWz zma6uCtm67BoHZLSQEx6nc0BdzRfBq0UhKu8BwFI4BchttK1EyrlPCqjhmJOXlrbquHUPOrbBJJ5 8+FaXC32NTguLi2MmCYU2fLjPxoCel0vDUFFQPCnSAQaH+Vc625fRhuKpbtX+CHAruL+6iC0QtKV JUFA8CKByyFE8KeI2CTzpG2E8h8acpQSRCRH20DxlRMGPqp2zOkJB2prZtlRIg/Gn7bSUwNW9A+t BAO4B8Kk7UeJmo20SUkmeHlUnbK2G1A+aiZjfxoS9Z8k5Ltwrleo4f1VUV0d6IG/OhR1mkg5HZkz /lqP1VUBI6l5H4vmRJOzvHl86aykOpOf3iEb/Sdj+0rKCgdfUj5NtxzGJI9/zBqP6Gkj8XuDDn2H /Malev6lHybakQD8oNg7f6hVMehVIV0dYMqJhj/mNBcm29Q24U3u2gDtUohvS2dtzTO6H+IoI93d PmBTcq0xBmnDoM7b7U1KZUQCBvQLKcQtuFRSCwAmBFIvBxJ2MGaVAUUhQIngaBNUg7kVV81YoUhV rbuweCiOPpUxmC9Rh9it5Su+dkDmTQ9Nz2q1PrO5JCd5JNAxxyztBaOOXyktsNp1rUefqaH7dpc5 hvlDCLJabVJgOLG5H2D0q7YhhV5nDFrfBLYrTbA9reOAwAkHYep/YKL+X8qWGHYe1aWjCUBtMDQn c+cmgDtvlq7scEQ2grTdtnVqKdhVXx3EscYTpumoIkdogbKHmDXRWJ2NotlxpbjRUOYVJ9N6G+bc Mt3EdghaFx9IAUAXfxB25bIe0haOHd+qiR0QZ1dRdIwXFXdbayEsOqP0T/NNU/MuXH7Qret0KKUn dIE/CoCxUW3UnfcyCOINB2BaiRI8Nop+0gxP21R+iPHF45gCUPq1XVsezd347bGr8wlQERQLWp0r Gw41IoAKp2k0xQkg/Qnwp9bSYJSZFA7YSfjUhbN6uMAimTBIUCakbZW44UD1hkJAoUdZ9pKMgNqH H25H6qqLTR7oPOhZ1ngD0dSOIvGz9SqC4dSZJGQyeRU7H6YrK36lBH4vk7flvQfzxWUFE6/oBwm1 Vz+UWh7+wVTboPSR0cYKkCf8nk/pGpHr/o/e9aLjf5SbH+4XTPoOUk9G2CyCT7PwA/pGgvaR3eAN M71E8qkkJBRsDvTa7QNBFBCuoHGKaqbGuZEU/udIERvTbUmSNO9A0ukjSTG9Nm1BC5PDnUg8pIHC ozEn0tMLcCeCSfqoB9nrElXOLi2bJ0IOkeE86rVzcBDKnAoBLRgCNyfL/HjXmO4koPaoPaOEmf5s 86aYHbPY7mmywdk6mmyHbjwKZkz60Bl6NcGRhWX0XDrWm6vPnnIHeAPAe4VZx2iEFSVrKYgpI0/X UJeYuzhrae0Qt9SANmwdIH7ajh0j4WrW204dYEEE8NqCXW6+7dP2yUMNnTKdZJ24cao2ZGCVO9ol tzcmUnh/j7qXTm3tbC8xdcKQnsWRJ7pIUoq+qKqeMZ3YUp9xxxsJcUCQngOJ2+NAmptDxKHEqSCN vGhnnCxOHY8sISEpe76YECecfb76J2GXDV8wLj2a4DDhlDikEA+h8Kr/AEk4St7Chdtd4sHUI4xz Hw391At0JY38mZntmnHdLV38y4nlP5J/x4102ywVAEbiuLMDfcYeauGjpUlQUDHMGu1MpXPyll+x vkbh5hKifOKBZtuCRoiKWbQoGYpx2KkmYO/lSiGjI2EeYoMaQSSDTplJA2rxtJG1OmUgD/tQKM6h 50MOsv3ujlwgzF02ftopJMAAxt5ULeslKujq5I/9w2frNBb+pM6Dkfsuep8n9NH31lJ9SUfvOBgb F/cf10VlBVuv7q/B61g7fKTW3/0Lpn0IjT0d4IN/82HPzNO+v8Vfg/apkR8osn/crpr0HEno5wUE cLeJnzNARGSCmCD8ab3IBkAcqctjbhwrV1oqTMAUEBdpABnemRAHvqYu7WASQDNMOw7xAKfHjQMn YjvDaq/m64RaYFevcNLSj9VWp+3IEyKo3S8U2+RcSXIkthI9SQKAKXV4u5ue21BLY3JngImrb0S2 TrmCXuYVuKb9pfUkr0nZpA4Ty3maHDSXsQLOHW/8PdOJZbHLvH7AAfjXUOW8rWWGZbssMTZtutMt BGpbQMniVRE7mTQDzMfSJctYXcpwbLq30MoSFP3KinXO3cQBJ9SRQpu/lvE8WS+jCQXXVAyylaUu T68TXQubsNNpZK1YwLVsDV3mSSPSFCoPJeTH3rv5dvO2UiYtu1RpKxH0o4xvzNBHXuX0WnRpcGzQ supWlSyqZMjccfGN6FGG2+Iv4u5cW+ALxNLSoaZUfmwoc1Abq9Jrq3GsPtrbI12gcFNHaI3oJW9q 7Z9qbEpSpapg+NA2ezHnJ4rZxLCrZLVshHYWzdutBXPECCoAjzqRtlKxNpxp22U0tSTLS4kCPXen +XLW5xR2EN3iXE/SCmwUj3wD9tTSsHLPdUtK1cYiaDnq1bXa4jcWLuy23FAe7b7q616v1yb/ACFa JMyySg78INc19IViLHNAuG0hPa94jzTxo79Vq/DtjiWHDcJWHk+iuXxFAXrls7xO1etJkCCaevNy NXOvGGxG6QDQaIbM8AactohMRWyWyD605aSInwoGwTIgCD40MOsa3/4cXh8Hm/1qLSkhMqNDHrHN T0Y38Dg43+sKCd6k4H4EHxDj36yKyvOpTIyZpJPF79dFZQVLr/T+Dtt4DEmf+Cuk+g1uejfAzEk2 wn4ml+v8EjK9sYknE2Y8vmXKT6BQ4OjTAyT/ACfh+caAiBoDYCaxxOlAGnjSralQJg7V4+sdmBtQ Rd2gaZM7nwqLdShCzE1NXS2wmJmot5CSdSQregY3KthE+kUNOnjtF5Hf7PVHatlfoFCim+hOkwDI 86p3SRhyr/Kt/apaKipklIHGRuKDmjKV81ZZuwi6cWSlNzpE8BtE/EiuqLbMPtFi2WVDVA2rjXEy 5bX1skApW2AQPA6ia6awJQNvavpSZWgKI5EEUBLwmwbxFSHMQYZd098FSQQD761GMWuK45eYPhYT cKsUJNw4ncIKphI84BJqs5hzKcPy4+q0lVypISNIkk8gPEkmKnsuZCvMHyEi3sMQFnmC6V7Re3Rb 16nFcUnyTwHp50Epmxm3ssnrYeUe0uEHSIoIYg0cKS9cOW5Uw1ClqHEJ5n1q59JWKYhh7tthN0+u 7et2NetKY1pHExyoft4lfYnir1u6sPWL+mUqT9HxE+HxoLhaXS2sND9k5rbdROpJ2INa212OzK1L nn3qhMtMPYU67hThKmEElon+YeHw4e6nFxPblQBKPyY/x5UAt6VLwPZoba3DYbn3knf6qLXVGbcV e4u8QezS02kEjmSTFDHNuXcRxnNDKrG3U4SnRJMSZ/711B0I5OcyhktuxvA37a4suPqRuJ5CecCg ujoEQfCtmOI2rZ0d2vEpEbcKBy2AVcvfS6UgbDnTNJPnSrRK5UmRQKqC9ESDvyoY9Y4q/FlfiQfn G/1hRPWogaYihh1joPRhfEj+Mb/WFBMdSon8DgDyL8emtFZWvUnCRlFYSRIL0+utP7IrKCr9f3/R q2PH90mR/uV170EKKejLAxAP+TbfpGvOv+CMsWpPPEmY/sV1r0Bk/izwPcx7Od/zjQEhlRUNk+6l XEymY5UmyDp4x50uggpHemgj3mZSSRA8aadmEqAVFS9yfmzHhUU8UzvyNB4tpBG4NMLy2YeZWFN8 QeNPA4QdMkT9VIvOHSUhQ3oORulXJl7a5tvbq2tyq3U4FIQBvBAO310bMBsQcKw+4ZIIS2kKSR5V KZ9sPaWm1WqW37i2lSxH0gBBFUTLeamLW+Tg97cBt9StSEcAB/N9aAws4LgLCbTEbrSi3aWLo61d 1Kk7ifQ7+6vXeke2uLdCsvYLf46tThSk2zKuzBH85wjSBVeTeu3rCMPNom/tnFauyUdKfzvETyqb fubpFs2wthtCUCENsSAPACKCk50xPMvt72L4nkt4Xa2SygtuIUkIIjeFHkfKh5huMWdk+W73Bb2y UDu6WSpER4iYq45rwS9duV3Nz7Ssap+ceJ5cAJquM9o2QEIJ7wkKPCgnsHxCzxa1F0w6haSkplJ5 UjdH5tdwe4gCRJj/ABzpo0lq3X2rbKGVLBBKe7J9OdUnpQzULWx+R7R3/KHk6VkH6CefvNBMdEmc 13PSMm1u3W12C3VBgFIlJ8Z84+uuvLIzaoKtyRXzpy7fXFjcpWypTa5ACk7Eedd99HmJt4zlHDbx D6XlKYSFrTwKgN/roJ1xHdFY2J3mt3EmIrUCO7FAqhAVsRtThIAiCD5UigGCZO1bpSqNiaBR3SpH CCKFvWNI/Fne7TLjY/vUTl6tJoXdY0KPRnfGP41sf3qCb6lP+h7h0gDW8J/PTWVp1Ilzk59E8HXf tRWUFX6/5P4NWoKhHyizt/8AS5SnV+bDnRpgaU7xb8PzjSH/AOQNUZfsk+OINH/dOUr1eFhHRlgg 3nsJHl3lUBONvGxTBitUthPGZpVLx3kztz51p2xJ2igTuWu6ajLhvjvUs8XltEttqWI3IrZGFJWl K7l4hSk6uz578KCtLQrXEz4RUknAHT2a1uaFkayI29KnWcIsrZ/tN1rSJgqmo1u5vLpt1p1pTUPE srk7jjvQBvH04hhuarxzELlaGn0Q2hHBJ3gTXOefrwt5oadUl1p9h4HQvYxqkGuoOmF+1U0bJ25Z aeWsKMqEq8QJrm7F8PcxfPTLKy12LSzD6mtaFJBkBQHHwoDBaYld4c004SpbRAKVDiAeVWFrPNi1 aAugrlOgEHcbc6jsv263sBZtbhse0sICXE6eGw5elRSsJtA4/rag8Y+6gaZqzheXlyvsSWrdHdSh KREcKgm8bt0IlxYTGxkQaZZvtW14u0D80yGwVRtJBqCxRy2ClOaU7bJkzvQSmI5mff2YgNjYeJNC /MTy38auHnFd7VzNWW9uDDVtbgLed2QmOE8SagMesfZr0NKJU5pBVvxJoNMLt3HvyoI70zw3/wC9 dP8AQRnVzD7K3wm5uGlWSTpSeaT4ek0CMm4GL68asw65LpCNCB4kcffRz6JMAbX0iXOEBxsJsWUO IhI0qBH+PfQdDsrQ4RpcSraYml9AUkq2EVDW2GO2OKe0hsr1JIUUq2I9KmEPs6ezSpJWRtNAow2C jma2TqA7vCvGFOCQpPpW5PmRHGg8UCUgn30LusgI6Mr8CZ7Rs/3hRWOyBznwoXdZRMdGV/wMuNfr UDvqQz+CNwTw7Z0Ae9FZXvUiH70LiOAedjb/AGdZQC/riY9cZgybaXdx2QWnEGUKDaFIAUGV6tlC eYq1dX1BPRrgRHO3/wCY1WuubhNnhOUrC1sX3LhtF61LjiYUo9k5JMVaur4kDoxwLf8Ak5P95VAT A0rmB8a2w+1L9ylomATvHhSrTetMJBJPKp7B8P7BsFMdqrdRPKg8xK0Qzh6bdlYbkiSBvE71AnCc TxHGRcqAtrdteyie84ANtuQq6KZSmCoaz4mtVpII22oIe2wq2t2tMOOL3lajJ3qDzc37Jhb2lSkF YgL/AJm3GrhpUkn3VSOljMWDZawBy5xd4gOgpbQlGpSj5Cg59zRaP67w35beeZAUwp8gl1JO59QK p/RVgS2XL/GL5am+3LyWg2jVI4QBB47/AAqbxvNb2a1vMYQ+i11KSEQ0QVeEq4/DzqwdGjLlvlkL unHEPquH9bkAqRGqVAcyDvHlQXHEMETbu219aJKA+yhtxB5EDYn7KrGYLY21yEOoLayCFBSYNF+8 w4sXFu+EqubNTRVp4qVpbBA8NzJk1TukZeMu4fdN4ZgaMTcaLIDIZDim9X0zMgwPf6UHP2e03Tt/ bN2rS3HFpACW06lKPkBxppYdGmasQR7RiCRhtvpK4cV84QP6PL311ecm2jFs0xgybSxu1sIcDxZk lJ4jYgmD9tVvFsoWiQ45mDHVvW8QvfsEQDChtufpJI35Gg57Tl/C7ZDjdtcsWz7SE9s65K3EhXPh z4eFVvNtrg15b9ph63W32nEMtoKCS8Oa1K4DyFdE57s8k4060zqQbi2SG0sNgw547jYgcffQr6RL Sywu6ZFtbN2zCXO0LaR/CECdz/jlQVnLWH3RxRoWoLEaVLUtUwf5225H30XeifGlYJnO0tn0ds3f /MG6UgJgkyD6HhVUyW3b3l+wEpSxcusjSDJC522+HD0qy4Thq8VzrY2QcKmRqaeCO4oECAoBQmAR QdTWTYCAVLCieFe3WFWV4pKnWh2iFSlSTBFQfR85eHD3MOxLvXlkoNqWf4xP5KvfVwZbIIOxmgi1 4elgSkrIPDypqtlQE1ZnW0qbg7yKjLxlTKVAJGmOJoItepKR3gDFC/rJKJ6Mr2AdJcb3P9aig4VE bDj4UMOsekp6ML2SILrUfpUEj1JmyjJrytiFOOnj5oH7KyvepPIyfcTzccP95IrKAb9dN9p7J1gb a+urtkXjPefIKpDTgIMbE93iKtHV1LKujjL5fC+z9mIMeSlVS+tZh2L2HRrZt47Zi1vTiTQKO11w OzcgzJnh4mip1YcNb/FTgDrqQrVbE7jh31UBOsLcdhrbZ0JiEDnUvbM9miIk86SsdK3VqaVqbTtE bTzp4J5UGq0kxArRxsngDTkkgbcBWizwmRQNXGjvFBHrLZYxXHUYabJhbzDetC0gx3jEb8uHOjq4 ElMzNUHpcfScEFmq5RbofVC1qXpgDfY+M0HI/YYtl1a3bmyt7C3LnZ26zHarIMaoEmOfhRKyM8nE 7Fm4tihLaFKWHNMBKtCiVkb89yKHXSo7a2+IlouqKWkKTDjsjURsB5wZnzq+9ByG2cCtmlKI1rB4 mSSOBI5GfhQG51SMLwhd9d3wWtNqhaU6YTAG+mPEmn2AN2tvZm9cCW37sh1ZPiYAHwgU6Yw9rEcv 27ToAOgBJT4D9hpZGHNuhDK4T2YiOQoEsVtnlLtvZ7QOuIUdLmqNCSNxHP0oZ490dXl5jryPlJ9F m6kOOslZWFKPE7kxwG1EZnGGnMSvLNKoatEEvOzEE8BNKNOYa7iSk2zrbtwWwpxSSVGOUnnQU+wy Rg+HIQpNqhSkc1JGxoE9OeErv82Iw9huO9Ko2CUwkn7a6rv7VTiDpMeBP21zn0j26lZuvLy3e1Ia CVSonhI24eCaCjWtk0xcW5xC8RbtWoCmm0nvEJMgGOG9WHo4dxRzOthdXGHutoQ+oF7V3QF8IJ3P ED31EvM/InbEqZccvHNRURqCWwIjf3mpfJ79xdZywtCriXC+lxSNR7yAdyYMAbbCg6WbZXa4na4m 2SEqhm4HLSfon3GPjVub2G1QVtbC6wxbau6FoInwqTwl0uWqAsy433HPUUEnJg7CKbvoC0qB4U4k cfqpFwlSjHKgg7pstrIKNPhAoSdZhSm+jW4kDvXLSfrP3Uar5jtGpmKB3WkXp6O3GuZu2p+ugn+p aQrJTp0gaXHUkxx7yT+2sr3qWQMjL8S69z/pJrKCqdfhR/B21bIgG8ZIMcfm3Pvq79WMA9DeXdWw 9nV8A4qqf19Qn8F7NYBKxetD3FDn3Vaeq68X+iDA0L/ikKSNuI1E/toDCNKW+7tXjbknTMKB3pNR BRusSRwpG1bQlZdTqle5M0D8pIRIMzWi0k7kVs26VARuOHpWy9kydtqBvyAoR9PDdy6W0NBOlLJV KuAMnf3UXlQTx40JOmrHLe1vfYLllLluGPnSeRPn8KAA4sxhGY0IwXEbdAcaKSxfJRpVpJAjVzTx MHwFGPLGXLXA+yw6zh1lgobSswZKYG9B3GUWzCEXdmXHGFPoShKR/Bq8FetHnJTbjlip1/6Yuz9L YjvJP7aC9ZYYPyUyoOiQkApSQUp8hG23Davc2JxC3wi7fwlkvXvZHs0AgFR8idp8JpbKaAjCGwmS EqKQeaoP0jtxPH31LPJ7VlSRsY29aClZTwFdnkq3axBhab19IuL8FYUpxziQo8Dvy4VL4Szh7XaN WduGlIjVCCBvy4fZUi2T2imjsJmDSVuy6h59bjoUCrupH5I8KBtjTybTCLl8qCSlohJP84jb6656 zk0FX+IS0pbhW2lKUnc7EzRw6QHpwL2clZ7d1KShIOpQG8SOA2Ek+nOg3iuFrxBV+Ge0S4HHXAAd +4Dtz5GgodlhTuLOXV9blt+3t1qTcIKtK2FA8DOxnl76neizALh3NTd4ppRJCVIIJ+jJ+HCvcq4h 8nZdRamwLpecU4tLg1BaCd9W3Ab8TPCp/oVxC3s80Iw95d2lVwSlhLoEJgcoHODQdA2Dei1QmDJG 9LJZ7N4vN7H8pP8AOrGNSedOAQU7+NB6l1KyIO8SKb3L4QstEwTBn1P214NFvreIJK+fgPCoyzWi /eXfPhaQh5SG0qVACdoMefH30Ek+4lCi2dwQTx50Detesfi6J4TeND6lUYbgqf7VsK0KSe799BDr VOLPR8pCjwvWgY9FUFr6k8fgSuP/AFHj/eR91ZXvUm/0GV/Xe/XTWUFc695P4NtJO6ResHj/AKty rb1YRo6IMvAAd5lZ9/aKqqdexKjgCIE/5Vbn+45Vs6ti9PRLl0Dj7Or/AIi6AqO6QtC1qjkBHE1o HCEKCdlQSJ4DwmtL3UGwtKgkpM1s22OzCn1pJMcOFBtgq1rtRJKlT3jBAJ8p4ipIokAz7qq+FYnj D+OutrtbVOFto2eQ4dQVJATEQdoPvqypdA3kEcaDCxJ5jehL0k4JbX+aF2D4Kn7tslsaZ2jaD4yf qovJeBA9aGGbcYUxmS4XcLW2GwopMDToHL12nagDeMWrOQ8Ut38Te+bfeShTDYSrWUwZUOW870UM MxBD2EX17bgFKrp4twdjCEkfZQn6VMHxDMuZrB/CXbd3tnA0u2dX88gkzqG26efjtRiwWy7DLhY0 gr9ofQTp3nSdx+jQXTLLxUxdJVGpL6jtMQrcRPkR75qQQ+kKUkq3mq1ktSoeSlBQkttqEj6RKASf MST7wamXwWX+2EQdlDwoFApS7wmBoB2ptbG6TdvOOvsG3OzTSEFOnfz4+tN7fEW3seXZNmS0NawP yR51o9d3Vzj6mBZhq3aSfnCoFThkbwOAoIHON2HsXtbALWgBQVISd1KJgTwiEmR6VV8rgqexG9AB 0WjzgJG0mpa5Wm4zViN8hQcRaoXJ0gAdmgAAEHeFFcnjO3KkujlTSbK+uX/4MMLCpH5Ox4e80Alv 7O89rtLK0tlXK1EgISqEK27yp2HAbDyqU6MsLXhucEqWyHHGnNTbhSJIVPCCY9Kl8CtsYvbxzFuz bu7dLmi3t1PFGpO41pjhyEceNPMpl+zzkwzeKLr2IK+ilOlCAlJIgevPnQGFNwrsCofSArXA7x29 Sokd1KiJ9K2QzpSNUwRxpLB22MPs3xqVHaqWeZMkmBQa4u+3fONWdqVrLdylL+gwWiBqTPiOHuNM ct3D14L9zswi1RcuIb1cVadp9Nqe4241h6HMQbMLQytao4KhOxPjwpnktl1rKNqHge2cRrcP9JW5 +2gmrRsItgTBJ8KA3WzRoyUFJ4KvG5+CqP6GyGUgAcKBnWzQT0epIH8ub+xVBYepbIyQ2AQR8/Pl 84Kyk+pPq/A10HcBbo9O8jasoILr1ScBRBgh63j4O1YerOo/ilwHURs0sbf7VdV/r0j9wkqn8u3+ 1yrB1Z5X0TYFwjslgf2q6Aq3X+bSPCeNIYchNywQ5JTBETtTi4SVWy4HLamuEhSElJM78qB1CGG2 7VpCUpnvnkkePrUQ/cXjOYmbFAUbd4FSf6McR9dSmINLUJCCptB1KTzX4D0mqhmPH3MvZgZvbxtR sy+1bKITMLcCt/iAKAjhsJQIiYoW9Ilp+6V44yg3DhQIQ2qTJ2I08440TWHw/bJdSkiUzvQ2xxdy xib7rDalvF8nYctXCaAMYfjWJp6Q8Js8SY1D2xBCwkoIIOwE8eKZo34OV+zW0pK+1vS4QFcAttdD XGMYwu66SLG4xKzadvWLhDbLikFKgeB9QJPwotYez2YwxI4FDK9/zx+2gTynCb20WVhRfsQAmBKA hRmY33Kvqqw4mlCLRxxwEpSJgbk1XsHhl7D5b0Bu5fZU6Y4EylBnfcmdvDzq4ONpXG21AN8i4ZmH D845gfxLsBZ3LiV2znaa3FbniD9EAbRw22qw2bTFniV865dXLj4BUpK1Hs+E90UviDqm8UJgCFAH zBH3iorPVwm1wdy5ceLZdSGUKClJAUo7GR/jhQVxntG8v4ze3LTSHnWtB0DYFaiYnme9x58aQwNt bWT8XdSrQPZwkq8CZn6op1i/zOT20JVPbuBSTxkAEj7BS+VOweyhiDlwvTbuFxKlEcEgQf20AJuF Y1eW9vb2jtxpYK3FqZdG0ERPDxiiV0WYWu8xNGP3ilpdb7gSsGCqIkVBXgwDCMFQ40t5Srx5QZd1 hSVRAkDkB4bVa+iS+YcuHMMR2kBHtACySdzvx5cKAj3t8pi1WUtqWoJkAVC/L9naWKbh1Sbi8WUM 9i2QopWQYkDcDjvUvi94zZYY/dOphttsqJAnYChDlN22ds14xiS/ZXrm+cuWme00rUClPZpUBvJA n86gvrz1zieBui4T2NzeOIY7IHZvUdwPQA1dLVkW7DbKYAQkCqlhlstzMOCWOgDskru3gTMHTpAJ 9VH4VeFp+cMAbeNBouNEBRG24oJdbFH/AIcpM8b5refJVG90d0bbmgp1sEk9Hze8RfN7fmqoJTqU 6fwKfE97tndp80VlJdSrbLFyJnvu/aisoIrrzpP4OoVy7S3+1ypjqvuT0SYIkk7B0f71dRnXlSDl hKo4Lt/f3nKfdV0a+inByPyQ6D69qugM6BLShHEUyw7a4dQDMHantuklBHKmjbfY3ijBAO9BviT7 bPdeMJcQQY/Z9dQV6S4n2e+CXgp5tbIUmSUpgyfMGneeGFuYCt5DikKZUlyQJ2Bk+u01st1tWYbS zCNZuGlKkD+DSIG/hMigstuEhlO0gjaqRmppJvXvY3GW7pSiYdMJkDjPjV8Q2EtgDkIqi52tmlG4 L3dE90zxJHDyoAnjDdwzm21uMX7uKXVwA2yIKUJTxWFDZRO3186NzjqWbXCVE8EIT8Ck/fQlunm3 834azcWVtqt3VJQtLkqEjf7d/Si9i7KU4HaPJ4MupmPAgj7qBtdtqRbX51hPs16h8o2GuTATJ9QR 5gVamHSS2VRCk+FRl9bj2m7HY9qLi2JCAY1KA2G+wM86c2F12+CW7y161pQNStpkcZjnQNcetwhZ dA7y1JHHjVTz5cLdcw2wYQHpd1OTuECISvbeZkDx34xVhz1iAscKNyYKUd4jVHI0PsOujfZgaxC5 uVIbZSFFtPNOkOBRj1IA8vSgks4OMpNtZtAFthWohJHArA4egVTjCbFf4txag6XHWCtfA7rOoj6z UFfF66XcXUavablQSTP0Q2qOJ/pD3ir4y2G8Hbti1rBbCVDgIAoOfsaxFOG481hC3LZ21QrtWm1t hayrgrSeRIMRwgUR+ibCWkdpj3cC32g2hCTPZpHI+e31VRs2ZYtHLxePKJUwpxTSWUcdYPDUOCSI 38iKu/RG4ub+xDehhhSVISeI1DfhtxFARnG27izW28gKQRBBHEUEsFwkXnTJcYeo6rSwWl9CI4bC J8d/so3gAtQKpOG4a3adIuL4i0nvOWaJ24qkxQWzKzYdxbEsVIOnULdonwTx+s/VU8rvKK5nwApD CrX2TDmrfYECVkCJJ3P106aSJ8qBEqIAEDhQZ61xB6PmduF83+qujY62nwoJ9a8j8XzQif8ALkfq roH3UsP72LoT/GO7e9FZWvUtIGWn0wZK3if0kVlAy68IJyxO8Tb/AK6/vpfqquBPRfhQJjvuiDz+ cVSXXg3yuE+HYH++uvOqopA6M7DWB3XHY/TNAemBKCBSdy2lDqSY350lZ3KdxIn7a0xJ0ltCkmCn eg0x1CXMJfbIiUECee1UToGzLe461jFtibRVcWF4WUPkfwiOQnxEVaMRxGbRYWZEGo7oktLS0yym 5YA1XTzjyz4kqNBeytU+NVvMFlbYip5t5TqVEBIUhUaTyPhU4t4p4c6qGbHL9DinLI/OIlQSTHLj QDfOeCsYNmBDlpZEvKR2i7kypSjqg78Ad55UUFLL2WlIUkmWwRtPDehPjeM3t04W7nuBtxsFA2JK l/ZANF7C1JVh7bSu8kpjc0Gl1iLbVrh12tQRKkoJJ2hXdH1kVtgbgFvdWyne1LbihqkHUPHbn4+d QePYbcYng7Fqw5p7J4FSvNCpA+IFLNXTeFIxS+dSUKTal5QJ2UUpMke/9njQD7NmNX+dM7KyvhyU 2+HWw7R+4c37VKFaVCBwEggTE0/dbfQWbewabsy6pWs9nJDadjII2Jnlt4VZzY2eH4cvFbawtkYl fNN+0PBAG8TJ99Nra0Q7jTSyhLa3GktrK3ZEDcpTzJkn4cqDW8swi5wu23AbaU85pHFS1AD6pq2L HzACRAAqKdQh7FiscilA9E//AO1KuL1IMcAKATZrFthgdwnEb1NjhhJdU+133DqV3U6dJj135VNZ CxrK7PZ4Xg945erMBb3YmSTJ75gR68KpvSFdtXV3ibz60e0W1ou5LSRsEAqS2k+oMnwIqMyLauYM 3heI3lk9bKfulWt20pWkgLIgn3EEUHQEd07kCq1lwPO5qxRTgGhCkBJ8QBP2mpe2eXZdnaP6ltaQ lp8qmT/NV4Hz5+tQVveLtMcu9AkuOoB9KAgNqUobEA1oHFa9JFJ27mod0+tK7SFzvzoN1lWmZ4UE +taf3gsTzvkAforo0uujSQN6B/WqcKskWyRzvk/qroJfqYT+DTuw2U9+sisrfqZD97D3kp2fXUis oGHXc3y3Eb6WD/vF1TOr7mvCMJyFbWt3i1nbPJdc1IceCSJVI2NXTrrtzgIUebbH1OL++uNnEkHa RvQd42PSFlkAFWP4bI//AJKPvp09n/La298cw0+l0j764C7yTxPrXhKualUHcGN51wJVo6pvGcOJ 0HhcI++k+jDOGCYZlJi1uscw8LC1qANygEBSiY4+dcSHUocTvXg17gK4UH0Jts/ZXUolzMOGJA8b pG/11Vcx59y6rGJbx2xcbKIMXSAkHj41xBqXEaj8a1UTO5NB1FjucsuqddGH3tj3HWe0V24AUe/M c1RIPGN6I+HdImVm7VtCswYYCEj+Up++uFIJ8ayFfzj4bUHdFp0j5TQ++hWYcNSgr1g+0p58ai84 dIeUVWi1s47h90lbKmXbcXAhaVbevjw8fSuLAFR9I153juSZoO2nek3J5Nuk5isAlpIIT2oIGxHv 2Net9KOUWFBacdwwlcqWQ6JnhwiuJAFExJisg8N6Dta16UMn9sFuZhsATqO7nifup4elTJn5OZLD +1rhzSrzrIJTtsaDoPpMz1gb+NG7sL21uBd4Y7au6FA7laon0mansdz9lrEsh2S14tZ+2pQytbYW NWtIAPv2rl7SRsTNbBJ8aDtW36UMn3Fg2HcespU2nUC5wMVBPdIWWk4s1+7to4nWklwrHAHnXJAC toJrfccKDuWz6UsohO+YLEf/AGU5b6UMnkE/hDYE/wC0rhi1WG7hC3UFxCFAlMxqHhNSPttq46Sq 1S2kuqXAPAEyEjyHCg7Vd6T8omNOYLDf/WULOsDmzBMeyqzbYdidtdOi7SvQ2qSBpUJ+uufFLK3V rQAlKjIA5DwpdqeZJoOu+pjtlh8Rtqd/WTWUr1N2+zyo54rLiuP9NI/ZWUDLrop1YAgaTu21B5fw iv8AtXHrzMmNJ25iuzuuIytzLgUB3UtN/Hta5DW1J+jQRBZAMwTWBid441KlqTFZ2IkkpE0EZ7OJ 3FeKY7xhMVK9l4CvOy24cPKgiCx5D41qWoMQRUuWSREDjNeFkapIoEEi0Fro1Q4EJAIRzBnf69/S lXrlpSrwtdnp1ksgtjgVSeXhtXpYT/NE16GeGw28qDxm4bLlmXez0hep8dn4K25eG1aBVsm00KIL obUmQjiZOx90QeVO2WmFFKFMjUTx1QPftTj2FggkBgDURBe3+ygi7z2Zxp/sVJSlTiS03o3QnfaY 9PXjSeG+zNsuJfSky82RIJOkTq5c5G3OpP2JorCAlkd0GS5sd/t+6tnbW3aX2iW0KSFboS7Mj4UE Y8m2DKezUndrTu3BCtczw8K2U1a+03L6XG9Dpc7NGg93+by293hSzrLanCW29KTwSTMV4liOVBHP WiUL0ocS5wkpBiffSaWNztvUr2G86a9DMDYR40EX2BE6RNbJYPGNvGpMMA8q9LJ8B8KBghnfhSoa QdwmDTzsiTukVuGjEQAaBu22EwIpw0gTsK3S0rbu07tmVTPCg606nrShk5bihA1OJHn3xWVKdUtC R0bJUEgEPLB24nWr9kfCsoNOtVaOXeUEttNqWsplISkknStBP1Sa5NXhS9KdVncSAdXcO/hX0PxG wssRt/Z7+0ZuWpnS6gKE+O9RRyblgmfka2HpI/bQcDDDGAynVZ3Xac1Rtz/7Ui9hqVbtMOpE7yJr 6ADKGWxwwlj4q++tGcmZYZWtbWEMoKzKgFqgnxiYoPn/APJrv/oufCvBhywf4Je3ka+hP4L4BEfJ jUep++vBlfAAZGGNT6q++g+e3yavj2S/gaw4a4Y+Zc/RNfQo5XwAmThjU+p++vPwWy/M/JjXxV99 B89Pkp4meycj+qa2GEvT/Aun8019ChlfAASRhrUnzV99bHLeBnjhrJ+P30Hz2ThL+/zDv6Ne/JFw Zlh39E19Ck5dwVPDDmR8aw5dwX/49r6/voPnl8kvD+Idn+qa1+S3Z/gnI80mvoactYESScOaJPmf vrwZZwEGRhjIPv8AvoPnmMLdH8W5+ia9+TF7y0ufSvoYct4EeOGMH1BNe/g7gn/xzP1/fQfPA4cs H6Cx5RXhw9QH0VV9DXcsYAv6WGMn4/fSasqZdJk4Wz8VffQfPgWPPQfSsNlvOk719Al5Qy2RvhLP xV99aLydlk8cIY+KvvoOChZ4fKtSbgbbcONYLO030h7yMV3j+BuWNR/ce34+f316jKGWwSBhLIB2 O6vvoOE7e0swiXRcSBtoAiYP7Yq69FHRxdZ9xt2zs1qtLW3QHLi5WmQkEwEj+keXoTXXyMpZcSNK cJYA4wCfvp5Z4DhNqoqtrQNEiDoWoftoGeRMrYdlDBk4PhfaezoMhThBUSSSSSAPGsqfSAlISOAr KD//2Q== --Multipart_Sun_Oct_17_10:37:40_2010-1 Content-Type: image/jpeg Content-Disposition: inline; filename="custer.jpg" Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAQAAAQABAAD/4Q1kRXhpZgAASUkqAAgAAAAIABIBCQABAAAAAQAAABoBCQAB AAAAyAAAABsBBQABAAAAbgAAACgBAwABAAAAAgAAADEBAgAOAAAAdgAAADIBAgAUAAAAhAAAABMC CQABAAAAAQAAAGmHBAABAAAAmAAAAOYAAADIAAAAAQAAAGd0aHVtYiAyLjExLjMAMjAwNTowMTox MCAwMDo1NzowMwAGAACQBwAEAAAAMDIyMQGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA //8AAAKgCQABAAAAyAAAAAOgCQABAAAA9gAAAAAAAAAGAAMBAwABAAAABgAAABoBCQABAAAASAAA ABsBCQABAAAASAAAACgBCQABAAAAAgAAAAECBAABAAAANAEAAAICBAABAAAAJwwAAAAAAAD/2P/g ABBKRklGAAEBAAABAAEAAP/bAEMACAYGBwYFCAcHBwkJCAoMFA0MCwsMGRITDxQdGh8eHRocHCAk LicgIiwjHBwoNyksMDE0NDQfJzk9ODI8LjM0Mv/bAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIy MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIAIAAaAMBIgACEQED EQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0B AgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpD REVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEB AQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFR B2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVW V1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrC w8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/ANNRzUiIfSlR DmrUUfrXOajEiz2qZYPap0jqlrOr2+i2RkkZDMR+7jLYJPqfanYCd4OOlQGe2ibbLcQo/ozgGvPL vxRqE6kSXsj+yfu1/JeT+dYsl55m7JUZ6kD+vWiwrnsAlgfG2eI/RxUhjrxlLhgMLKcdua0YPE2q 2mDHfSMB2c7h+tFgueqeVimmPiuO034hKWEepW4APHmw9vqK7G3ube9tlntZUliboymiwXECCgpU oSnGPNIZEqgUVKIz3opAVUXDVajFV4/vVT8R6yNC0SW6THnsfLhB/vHv+AyaaApeKPF0WjK1nZ7Z L0jk9RF9ff2rzC71Ge7naaeZpZWOSzHNVZriSaRndyzsdzMTkk1GFLHABP0qhEhnc5xWjpmg32q5 8oY9M96hs7DzHG4MBjJJGK9j8HW1ta6ckqqHVunHQ0BY8wl8H6hDGWOcjttNYtxbzWxIcHivoXWN X0nS7YzXh5I+WMDJNeY+KTb3umHUotOlhgkfYjkfqaLhY4ESZrb8Oa/caLqCMrFrZyBLH2I9fqKx Y4mkcIilmJwABkk16R4V8BmDy9Q1dfnGGjtj292/woCx2qjIB9aftp+MUvFSMYFopxopDKCDDdK5 L4i2V9dWdlJb28kkEJcylBnaTjBP68116ctWjanFUhHzpit7SLa0t1W7u5lCk4AxmvZbzwvoGoOZ rnS7dpDyXUbCfrjFeZ3WkW0OuXEcMMbQK52RljtA9jTESWNvDqExntAJDu2YEf3R6/413Xhq7itb IW88MgKMct2zmvN47h9C1WCS1uzGGbbIqnPynsexFelLMloUMjF4pVBEh75oAvX50q7+RIXknkwG Pt6c9q0bu00e/wBEk0m/kht4pE2KrOAwPYjPcVmWlza2TSXTbWwMgep9K5zWdavNSmISzS4bOMjA x6AUhm3pHhfRNFVZLG3EkmOLiQ72P0PQfhWnITUVjJNLYQPOmyUoN6+hxUjZzQAwr7UgWpKCMUgG 7RRRzmikxlGNavwDiqiDB6VegGQOKpCJygkhaM9GBBrzHxFp50+5la8tnO4DbIg6LnqP0r0S/v5L K1MtvaS3bZKbYcHDDqD7+3WvONT1u+1W/jbUSVtY3yYEGAB396YjCtLQXt1mOBhAjZDPyT7V3GkX bJbtp92pe1I+V8ZMf19qzta8T6dounx6dplhbySkbi/U4PIJP9K4y88Q6reYMk2yPOQiKFU/h3oA 73VLC6trFrmCVJ7dTn5X6fhWLo3ih31i2N3Bi1iYqWjHTPc+uKg0zxCb+2NrNaHj75hOFI9wKs6j FF9hf7CotmjAZWTj659aAPUQVdA6EMpGQR0IpjCuJ8GeKVNtJYXshd4xuiKLyw7jH9K7SK4guGdI nBePG9Dwy5GRkHkUDFA5HWnMBikIwaKkBo64FFAPNFIZVQVi+IvEk+mSJa2OVnxueQKDt9AM1uov NedeIJkufENyySEAEIMHjgY/mKpCZaOri/hjg1G3jnhRtylB5LofVWXHP1zWpqF1pt1pg8+4jmlS PbA6o32lj/02ydpGOMjr+lcsXliHOJF7jHNQSyAYmiPyA5I9DTEQnTVimMwywHOD6VoafJHp13Hc pZW92gJIhnGUORiljcSJnqDUMTBQ0fdf5UAaFhANDj1STUbP7PJN+9EQHHlnJUKPTJNSW4G4jqCB 1qKKzu0EeoXV0k0N0CqiSXc4C5HI9OaSNsXCIvCg4oAzNbj1O01iDVXhS3km+dPKUKpHTOB61b0j xNLYayl3JGBE6COWNOAQOhFMv7QGCa7N5E489kFuZCXTHOcdhWMGBJJ6CgD3BJEliSVTlXAZT7Gn AA1xvgnXJrxX0+5fd5SAxEjnb0wf0rshjFSMTABopwxRSGVgDn615TNCTNMrctHIwP516xzuGK8z 1FBa+I7yJgNrynH48/1qkJleOZo0GMstMlDy5MKxsx6g8GpGUxSnb9084p5RZUyuFb06UxGfazPE WicFWB4B9Kk3fv8Ad68VDOri4BaNt443D+tEchKsfegDQsVQy3DjJcR/d7dR+Va9lDGLR/NkLOzE gAenTn6iudsJtmoMeoaMg+3IrbtpJVjXa+O/NAGK4Q3M5bG7ewz+NZqAZcdlY5q3bxXOrasba0QN LNIxUFgo7nqa1PCPhttZ1a6S7Vha2rnz9p5Yj+EGgDLsZ7i0nS6t5WikU5BHp6V61o2pxatp8c6M vmYxIgP3W9KI/Bukyu0stmgZkwkSsVWMepx1NcOss/h3Wp47K4WRUfa+4cSD6UmM9IGM0VQ0zVrf VLbzImxIB88Z6r/9aipAn3ZNcB41tGj1gzKMGVFcH3HH9K7pDzXJeNCrTRuzHem1AueMNuOfzX9K pAznLa7juUCSEJMvr3qZwydRx6isi4ty3zJw1TWksjrs3kOOoJpiL0twwXlCwHoKxri6WFj8pG/k A1fe6vIODtKmu1+Gc9vcazNHeRxyZh+RXjVuc8nJ5oA4zwxp91rmpSW1nGGnZMZbgAZ5JP5V6Cvw 213Jb7TYgbcKPMbr/wB816pGsESARpGi+iqAKT7VCPlMi5+tAHisPwc8SLJk39hH/tpI+R/47XWe FvBeqeGtHnhlENxO8hlYRSH5vQDIHP1xXoSzow4ZT9DSvIO1AHiepeMNUWaWCBPsgDFWDrl8jsc9 PpXMajqdxe3BuLt90zAAsABkDp0rq/ixpTWeuwanbDC3kZDr/trwT+IIrziS6uUcJcqGWgDpPDOo pb+ILctJxITGQffp+uKKyNFtjc67YxqdyNMrfgDk/wAqKljR64vB5rK8Q6DaapCtxI0qXC7I0Kth eXAGR3+8fzrTDYNQavKRol465DpEZFI6gryP1FJMbPMEcqxRhytNkwrLInDDriqST/ay3z4lB3A+ tSrIWQq3DjqDVEl9h9oiDIC2e1V4H1KxvEuIGe2KHPmZxtqkbuazYtC5FVJ767vG/eyEr6dKAPQr P4k6tZRHcy3KKeGlXJI98VuQ+OdM1EB3nubN3IASWM4Y+gIzXksN4YcB49yjsank1L7Q4ZvlVfuj 3oC57To2vQX8jJBdJujO0hnCnP0ODXWxC6dBuLL6ZFeP2/izwp4hsIodejmtNREflvdRICjnszY5 OepGOtcvc6vqeh3Ij0jXbnyifl8mZth+maLDPU/i9Cf+EOt53YB4rtcHvypBx+leILdgDbICwrqL 7TvHPiVo7fUTdTxp8y+dIAg9+uM/rXS6D8PbDTQs+pst5cjkJ/yzU/T+L8fyp3AyfAmiXHn/ANrz I0duqkQhurk8Z+mM0V6BI+FCqAFHAA6CiobA/9kA/+EMRWh0dHA6Ly9ucy5hZG9iZS5jb20veGFw LzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQi Pz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUg NC40LjAtRXhpdjIiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5 LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgog ICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgeG1sbnM6dGlm Zj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczpleGlmPSJodHRwOi8v bnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUu Y29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50 cy8xLjEvIgogICB4bXA6Q3JlYXRlRGF0ZT0iMjAwNS0wMS0xMFQwMDowNzoyNyswMTowMCIKICAg eG1wOk1vZGlmeURhdGU9IjIwMDUtMDEtMTBUMDA6NTc6MDMrMDE6MDAiCiAgIHhtcDpNZXRhZGF0 YURhdGU9IjIwMDUtMDEtMTBUMDA6NTc6MDMrMDE6MDAiCiAgIHhtcDpDcmVhdG9yVG9vbD0iQWRv YmUgUGhvdG9zaG9wIENTIFdpbmRvd3MiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHRpZmY6 WFJlc29sdXRpb249IjIwMC8xIgogICB0aWZmOllSZXNvbHV0aW9uPSIyMDAvMSIKICAgdGlmZjpS ZXNvbHV0aW9uVW5pdD0iMiIKICAgZXhpZjpDb2xvclNwYWNlPSI0Mjk0OTY3Mjk1IgogICBleGlm OlBpeGVsWERpbWVuc2lvbj0iNzU1IgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iOTMwIgogICB4 bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6Zjg2ZTcwZTQtNjI5OC0xMWQ5 LTllM2YtZDQyZjM0NjM5ZGJiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ1dWlkOmY4NmU3MGU1LTYy OTgtMTFkOS05ZTNmLWQ0MmYzNDYzOWRiYiIKICAgZGM6Zm9ybWF0PSJpbWFnZS9qcGVnIi8+CiA8 L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAog ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAg ICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz7/2wBDAAUDBAQEAwUEBAQFBQUG BwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUF BQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e Hh4eHh7/wAARCAD2AMgDASIAAhEBAxEB/8QAHQAAAAcBAQEAAAAAAAAAAAAAAAIDBAUGBwEICf/E AEIQAAIBAwIEBAQDBgUBCAMBAAECAwAEEQUhBhIxQQcTUWEicYGRFDKhI0JSscHRCBVicoLwFiQl M5KissJDU9Lh/8QAGQEBAQEBAQEAAAAAAAAAAAAAAAECAwQF/8QAGhEBAQEBAQEBAAAAAAAAAAAA AAERAjEhEv/aAAwDAQACEQMRAD8Am8Hl60dEGN+tAbnalEBIr5+PUIoGcmllXPSgkW9LxxntVBET 2pVY/Y0tFEe+KcpFRDYRDHSjrD/pp4sdHWIUwMHhwOlJcpXr0qVeEEd6aXCxxRtLK6xou7MxwB8z TDSSpjelAtQFxxvwjaSmGXW7dnH/AOtWkH3UEUeLjjhGXATV0z7wyD/61cNTTDbG1AKcdKjBxXww RznWbUA9Mkj+lSem3+n6lE0mnXtvdopwzQyBgp9DjpTDXGQ0mY6fFD6Vzy8DpTDTMR7UOT3p0U9q KF3oaasm1F5PSnTr3pMAb1FJrGwGRRuX1o64xvRtqYG5X2oyp7UpgV1agCKAQSKFKZHLQozUEoNL Rj3oBd+lLxJsKrTsaEmnccW+d65Ehz0pzGp9KuI5HHv0pdU7Yrsa+1LqmSBitSAsabdKrXiLxfb8 IabG6xR3V/O4EVsXweXuxx2/maX4w410PhmJknlWe7A2gRhkf7j2/nWCaxxK9/qs+ohea6mbJuJd 2A7BR0UDoKuJaud54gcYX0Jk/wC46Lbno5T4sf8ALJ/Sqlq+sx3j/wDiOoahq7A5HmSFIwfYf2qA uLxpn55ZGkf1Y5ptLPynAxVxEsdQXcwWNrCvuvM33phM8jS+Z5rZBz1pn+JxuWxRGvxzbDI9aYbE rHfXgGFuX/T+1OrPXdZs3LWt/NCx6lTjP2xUGl3EO9HN3Gds/ah8XnTvEjiiywZLsXKj92UBs/ff 9at+heL9jNiPV7FoG7vCdvsf71izzAocGkWkyD1phuPVuh63o+uRc+mX8NwQMlAcOPmp3p8UA7V5 Gs725sp0mtLiSGRDlWRiCD7EVq3Afi5KrR2HFA8yPoLxB8S/7x+8Pcb/ADqYNfkA3pLlpWKWC6t4 7m1lSaGReZJEbKsD3BoKtYWEwvSjhQe1GA9aOoFA2K4JoAb9Kdcq5zQEagnYVKpALkHrQpyAB0Az 6UKioTlIpxCMikyMiloU2qwLRfMU6iGe4puin0p3Ah6kYHf2rUQsgVFLuyqoGSScACsj8TfFURPJ pXDEo2+GW8Hf2T29/t61D+MHiM+pTy6BoMpFhGeWedT/AOeR2B/h/n8qyiSQjuc961ImndzdzTyN NPK0sjHJLHNNZLk5601klY96SZqrOnRuipJApKWdn70iuTTqztWnkUAEg+gqp9JRrJIQEVmPsM0s LG9dsC2lyenwmtO4D4PZ3jmKeYp64GQPnWoDw8WeBZIrZUbqMj+dTVx5l/yvUACfw0gx1yKQaOaM kNGy/MV6UuuALhIyGRDj2qj8W8E3do3mCDIJ3wOgppjICzD2owmPepnWdKaAtlSCO2KgGBBwe1VM OBJnYUA2BTdTR1zQaD4UceXHDd+thfyNJpE74dTv5JP76+3qO/zr0PHiQB42DKwyrA5BHrXjtc16 l8Lo75OBNIF+GE34cYDdeTJ5P/bisdRqLGq980cLjpXQpB60dQc1lonymuBd8UsFocveoogXahSm 2+1CsqglI6E05twu1NU69KdW/XFaiHcYXOKznx44xOi6QvD2ny8t7fITOyneOE7Y+bbj5A+orRlK Rq0kjBUQFmJ6ADqa8l8ba5LxDxTf6vITi4lJjH8KDZR9ABW+WUY03KMCkC5cnJorHJoA4rQ6elBI 2kOFGTjNGUcx32qZsdAubiESoC+eyjcUQwgspVTmZod+ilxk1a+EtC8/UIeZ+WOVSSmNw3/WKjG0 60tpbeCZJTLIQVKHIO/Tb5Vb7Vbhbxr+1jES82Vjkckuo/d6bHHr7VBtXhrp8ttaxhreGKFfzcw/ X1rTbdomQBQCpGxA61ROFW/EaBDcsQrcgyo3GcdKnLNTHmWV0VTjdTjb5f8A+UaieFjHK/mHBQZJ J6Yqr8XXPD8UDie9tkC5BzKPsKrfGnFGu6jM2k8OlxynlaY/EgPYbd/sKyjTOFeK+JeJmtdUSRgk gDM/wDrjI9fuaiadcY22k3EL3MbRgcpCuB136VjOoIou2ZNlJyK9NeKfhUR4fwS6aZDLYEtcIDu4 I3b6YrzJdczTNzdtvtViEQoNKwxs7qiKWZjgADJJqS4b4e1fiC/Wy0ewmu5m68i/Co9WPQD3NejP C7wtseEuTU9UaK+1gj4SBmO3/wBuerf6vtVXFO8K/CR08nXOKoimCHgsGG59DJ//AD9/Sti5eu1O 5zzHem/b3rPqyCgHNGUUXau5qK6a4a4DXcZqApOxwDQoxWhWK0gsdDinEGAelJMgxS0CgVqMmHHM 7wcDa3LHs4sJsEdvgIryU29ez0tobmNre4iSWGVSkiMMhlIwQazXjHwGtLsyXfCt8LWQ7i0uCSny V+o+ufnXSMvPGN66BVl4m4H4p4clZdW0W7hQHaUJzxn5OuR+tV4Ieb8pG/pVE5w/odxcSiaWNhGm CB6g9/lWn6XprWmnk5jifPQ9MHPX5AVQtK1trTTrlDkyOnlg+m2/9BTF77W7iN3Ms5UAFiW6elRF 6gsNQZ31aOS3gnjCrIzDnJU9cdh9KXKZto52hYWkcgAxj2OGx03qF4D1qOyv4oNSvVCznq+8cfpz fP8AStj/AOxtla6VJDaXLXMDp580gwU5zvzAg7/agsXBN7pqWqWYYK4QOQTsexq4wjTrpArMvKw+ 1ZXw1oyxW8byOfPyc4bOKmJWvLRSPMbH5gcYxmoq5axacP2ulSAzx28e55om5Wyfl3qJ4Jt9Gtbt ruCGeaRsBGlkJyM5O9VaOG8v7oJzFhnY9Rg1oGh6BIlp5WQVC9em+KKstmq3isvLEI5MgqNwfXtW O6j4EcH2/Fd7qF811PBNKZorQNyRIDuVyNzv7jtU5xBxbxhoDfh7Hh6wEEPWV7gF3/2ov9a5oPE2 rcUomoXUFvBaRgoAHLSM/cEY+ED077VUSem2NhpNmLLS7K3srdOkcKBR+nU+9CQk96OxBpN8YqVT dzSecjpSjgE7UFUBTtvQJMoAz61xRntSpAoBRisqIF36Cjcu3SukUBt1oOcu9CjKAaFYq6r2AcZI pxEvTem4SnMKYrUZPrUbipa2bG9RNqN6koelbglElBXBGR6Go/VLezNlPKmnW0kgQkc0S7n7UvET iofjLVrjS9PDWyKZHzueigdT+o+9VHmziHSiuu3d6UjJmdZ1B/KBJvnHrnI9iDUqAlxZLplxp6lF AdnXClhk7j164ovF/Pa6v5v4ZppJCWl5JCNic8uOg3Pak5+JntoRaS2DRyQrkCQbgEk9vcn70ZV/ XtFktLCS4ihcRscDKbjpt/Or34IW8eraeLS4vrkJbyeYsAlbkLDcZXoaoGtahf3/ADT80nk8u4J+ EbdKmPCrU7jh/VI71gTaSEeZj933+VFbglytnfGKdVUMxOMGpaSS3uYEBQZGQDmm2o2FvxBpy3tn MAWXmSRNwfqKqY1HUdKumt7wFcbFlG30qYrQOHohBdBlwA24wOm+f+vrSfGfF8nOdN0+8MKQjMrq uSx9B/aqmeJkS2llRsyKhKgnHNjNQcU97qzNGzRKZy3MScg/P5bUDbiHjp7GZFt9OlupWBBeQkkD tnHqMnFO/Crij8fxBLbRwGOKaIyEZ3LZzzH7kU2nsI9Jljt5ktnuZRhZMZ39fUbZH2rQ+H+GtN0x zeW9nDFdSKFkdR1wP0+lBNk5FEkyRSpGBik2HzqBHFGA964QAcZoZHTNWtDADHWuMpxkGuLgnFK4 GOlZoR5cbmisTncUs4xtSRHM1AF+VCjqu2TQrNVAxg9KdQj1pJBvTmMHpWoyc2y5bYU+jUimtuu1 PIx9a0HMewFR/E1g2oaXJChxJjCt6Zp+uwBpxFvjeqjzxfF9O1r8XeIwYebHKh/ccMcbf7Sp981A Xer8P3Mt3eXrPJMIisUUSgZboC3etw8W4OFE0sXWs31tYXiDML8vNJJ/p5Ruw/lXn6/01dXuGvtO tMO8uJQgPIpYZH3wftREUbt7pvwFkk0dtLgSK+Pi9/b9auOkaekdqEIxt27Va+A/CfWbuwTU4tPE kL5BmdwoGOu27Y+QNPf8hcSyxxhAiNy87HkUn5tj+9Ax4M1274duxDcRmfT2OSq9Y/cf2q7cQaVb a1apcWEsTLIAUYbA59ao2qXXDehoX1PU43cDaKE4P1JGfsPrTXhjiXUtUMknDn+W21ssn/kTO3O5 9cbkfeinXEXDuraYhafSJJ0XOJIGz+lUK/4ku9PZ1tw6GPJIdcEe1bXJxWLfTHg1WExl15SVBIBP p9axbibSdT4g1aX/ACyyldJGCmRl5VC+pJoENB41upOIrK/1W1W8tYHBeHJGR6/Mdd69P6Ve2Wp6 bBfafKJLaZOZGH9fevMs3DtnoFsJNTW6ZCcGeJQVQ+46/fFX3wr4ls9Im/Cxail1pc7ZYdHgb+Ir 6euD70I2Vl+HNJkUvsyBlIKkZBBpJhRSLYwaKuD1FKkY61zAA6Vm0EQKW2pZeXuKSU4pQnbaoorh TSZ5RR96IwqDqnOcChRosAdaFZqocYB6UrG2/Sk1BzS8Y3rcQ7tj7U9i3ppboaF1r3D+l+bFf6xY 296q/s4ZSTgnozKu+PatodalfWmm2Zu76dIYV/ebv7AdSfYVmPFnirO4ktOHoDD+7+JlALf8V7fX 7VfOEtW1K0sNSng4j0Xi7VrlwbeGS9NmkSY/IkfKwXt8+5rJ9S401nT9TOleLPAyzo7HlvIIBDOo /iR1+GQD2OKuMqNftPqd1JPqM81xNJ+aSVizH61evCS+s40l0q5jhWYDy/2oGCMgxsc9gwAPtUzx F4ZTDQouJeGmm1TRp4xMvNHyzRoRnJHRh7j7Vmmr+ZYTW+q2p/aQNhwD+de6mojWPETxMvdE0KG1 1LmFxFzJDaKRGWYE/E6rsFHQD296wHiDjbiTXJna71KZUb/8UR5Fx6bdfrTXiC9u9b1iW/uA3NId lySFHTApyugO9mLiHJI3xVVXzzN8TMSfc0406/u9OnE9pO8TjuO9WCLR47q3DACGdR3GVb2P96tX iN4K8Q8G8LR8QXV5YzxYX8RBEx5oS3pnZgDtQJaD4j3z2fk6laC5jGzSR/mX3q26JxClzah47kTI 35SVwe+29UfwY0NNQur2/uA4ihVY0IPVzv8AI7Dv61aNStX03U5YT5fKeZ4+VcdOQ7j1xn7VApqL /wCZWNzbSr+zmjZcHsazC202WwnVortlukJ/J0BHbPetK06QvEzEHIkI+hFI3fh1+O4e1Pia31uO G5t5ARZsm7DAyQc5/Sg0Dwd4ztdU4Y/C6jMyX1rJycnLn4NsfYnGPf2q6aVq2m6tC02n3ccyI5jf BwUYdQwO4Psa8kcLa3LpGssWeTyXfEgU4Jwa2vWLxdQ4e1oWGnabc6Zcwfio721YJcQuF5v2i7MQ G2BGwzvRZWsOuKTIIqgeC/FjaxpB0q+maS+tF+FnOWkj9fmOn2rQGO9ZsUl0augkVwjfOKAB9qiu 52pNmzttRiDRGU5xQGQ9xQrqKcbihWaIxF70snWklB74paJcmtQRXHHEq8NaA90ih7uU8lup9cbs fYf2rDLvWJ9QvGur6WWWd2y7Mc5q9eOV60Op6XbqhYLA8jHtu2P6VQYruAkeZFsepxW2KfQTI35G Gau/DvG99aWo0zW401vR2xz211livujHdSO3p7VSLdrGX9/lPTI9KXaCXk/ZOjemaDW9M02NpbTi LT/FjVrPhaxbzf8AL7mTnmgYDaH4iQVxtuDt69aoviLrXD+u67LcaLpE1pBJnzWcgLMf4gmAVPr2 /nVMnlBcRXCBJ13QkbA06065FxCWcYdG5WT0IoGI0eISMgUe2RR9OJt5Ws3xjtt1qUB518zG6HtU frScskVygIwd8UHZ7copUCjaxe8bcR6Yuj3V7NeW5OUiCc0jY3+ZG2aXibmUSb4YdRU3wbxqvA2q zak1i9z5kBjDkcxjOf5f2FTQXwqnWz4eOmvGY5orhxICuG5tuvv2ol9rcOs69d+UcpZOsRYfvFld f54qNtOJ5+Jtf1XVpMwySzI/L3/Ly/8A1FHg0aLS9aklslKw3flM8Z3AbzR09tzVEjYcoeULsGAc CmvEOnarqt1aWelreSySK3PBbgnnUYO4HYUdJkSWAKd+XlOPepJ+J9S4VhttZ01Od1k5JEJ/OpBy PuB9hQZLxHpk+n6sySQvE2fiR1wVYdQQad2F5eWkb/h5XiJjaNgDsVbZhj3qa4o1W64nu5tVvIDF M7c2CcmohlBJ2xlaB9wzrl1w/r8Go2ZUyxDHK35WB6g16R4T1634i0SHUYF8st8MkZOeRh1FeW/J WRw2enU1qvgnxTp+nR3Gj6lMtuZ5Q8EjbKSRjlJ7dBUpK2QkD0rg3zRGIJ2O1GSstlFwAc0VsA7V 35UXBzUo6p2oV0KaFZVGKm9LxKM9M0T0NdklENvLLjPIhYD5CukRjfi1qCXHF88eQY7ZFgH2yf1J +1VyGOORMYGKbNK97JPLcMWkkcyOT3J60haieGQqmeXO2a0wkJ7FGHwkKexBpBLqeycJOTy9m7U/ tJo5sebGMinM8VpLGedMqN996COvF/Hw+ZFyyOo6A9fl71H2NwEvg2cGQcrjpuOmf+u1PxZRK7SW NyFx+5UfqyK3/eEBW4jPM4/jH96Im7d/jOGG/auXo57RlO/ptTHS7xJ4lIOT0p60mQ4GxopppdwG QwOfy7UtIshlFuInmD4AULnOegqOGI7wMo2JwfSn8mXQgNysR8LZ70Fp4h8ML/hWK01C51Czb8eq q0ETEtGx3HsR7im96wjWJFlVmjKq3fmxlz/8ahtK1TXdc4jjt9cvGljit2EQXbmIAAz9KXnYWVq8 apjCSSDfJGwTr/yNAiMlFy/7oORvjepDihUk0CGFZFjDToDI/wCUZOMn7060vT7OXQVvpLadCwZY wzBEkPL8JJPQBs5OcHp61E8Xs3+TwW867mQcy47gGgl+P+CtL4d0qyv9L1w6glwgLq6gEZHUY/lv Wb3MmG5c4PKR9akYbaRinPczyRpuiO2QufTNRuqxNHdKvYsD/wBfagPFtEAegFFkbzcKq7A9aDDK igx8qHb8zHCiiPQfhJrX+bcJQiWQyT2rGFyeu3Q/aropX0rzp4e8YS8JXqxPCJ7O5I89R+ZcfvCv QOnXttqFlFeWkgkglUMjDuKzY1DvPegGoLRWqNFFbI7UK4g2O1CshgAcelGRFkUxt0YEH610jAo0 LKD71qDzaI1g1Ke1JB5XZM/I4o1xGY2Db4FLcWRfgOML9OgS8kH0LH+4p75AmgDcwwa2wYYLplNi BtiixzzRH4gWA7EVyM/h5miz8NPIRHMOUAADvnFENpYrO7X87wSnoU/tTC80bUoR5kEgvUG+xww+ h/pUxJawr8cec02FxJE27HlHrQQOkTG2lliZCuGyA2xHtUxFKzfER9qYaxBLPci9tD5g5cSRj8w9 x6ijW8+YsZHTegPLvuMDBp2JAIlbbcY6U2tpPMV8/LOKAceXjB2oJ3hCeN9Z3UMywvjbp0rt9bmS OeeQZeQQrGN8AkFjt26ioLhed4uKIU5iOdXBH/GrLqDfEkaDc3BJ/wCKqv8ASirgbmxtLENZ6W7x ae37cMQFYc+Qq8wOSAASfXvvVP8AEdndhLJzeZ+LfmBbJyc7VJPdW0ziW6hilkAzzFFBHyqJ8RLt brTLec8gkacFiFwT8JoIK2lXlBYbVE8RMFaKT19/ejG5wAq0x1uVnhiB3IbagdwqHVc9MUSP9vdF v3E+FaPptvdX7Q2NhDJPdzEJHGgySTWq2/hOukcMvd6teSC/EZZUhwY1OM4JxknttjrQZqYlDGRx sOgNaX4M8Sw2Usmi6hOscUp5rcucAN3X60Xh3wf4k1e1ju7ua105GHMsUuWk/wCQH5flnNUnUrFo NVms5MB7VzGeX+JTg4+1QemVwRkYxRGJzWd+H3HtrJDBo+sytDdKOSOdz8MnoCexrRRg4IIIPTFZ aHRtutCiEkChWappJkLjJrkaEnJo8rCgjYxvW4MN8XrNYeNr3GR5ipJ09VFRWg3XnQlMnmTYg1dP G+z/APGbO8HSa25D81J/oRWXQztYXvm78j7PVYqw6hFGSJOXp1OKbpzKOZExipGIxXduGT4sjam3 lmJzG+du4NULW84YBZCPfaiXtosykxt13xXFCEEDOR39aNDKUk/KceuelEVq/gu4JcRrICD13okr XBhE8x+IHDEdSOxNW55oHT4lXPqKjr+2je0kjHLlxjtRURZNiPYjfvXWbAIYCkbZuVeQnp1HvSU8 yDIBG+2KIW055DxDp7W7AP5uCcA/Dj4v0zVnhdpZIWOxMZkP/Ni230IqrcMRo2tyTqCTDbyMBnbJ HKP/AJVaLGRBcXDkhkiYRqADsFGB/KosSyxQOg50JPuKqvH7CJrG2RsjDyN+gFS9veTT3W4+HPrV X48uObWljBwI4gMDsTRTbhvTbjXtfstHtZooprqURh5Gwq+5qa8ZeCLjgi6s7d9VttRSUkiSIcpV h1BXP61VrW4ltikttIYZo2DI69Qw70e9utS1/VrSPULlp5ZZVXJwOpAJrSN7/wAO3B6Wul/9qL5R +JuU5bcMu6Reo92/lWo2GmS6hqBv78AJGSLe3JyFH8Te/f2+dNOHEkt+FtOtRcRmNY1AePBGw2pT U9RFoHSW9htI+txcu3KIl74z1JOwHr8qjUTkY/Fo0Vs7RW6bM46k98e9Y54+No6alYWtjbRw3MMZ 82RVwFQ9Ax9Sd9/ep3WvFzQ7G3FhollcXap8IkJ8tSPXfcn3xVP408QdP4k4Yk0M6GLbmkEvmGfn JcHOTsPehqhpb2kjiTl53H72dqv/AADxs9hyaZquTaDaObmyY/Y+1Z000iAsRGF6DkJOPnSE1xME ODzKR2rNiPTqyxTQrLDIskbDKspyCKFZ14IX7XGgXFm0vP5MpIBOcA9PpQrNaXlmwBtXRkkGkQwz SqZyKSireMVh+I4TivAPitJ1JP8ApbY/ry1h+pW3OTgnHbevQPHsN3qOjwaDYcguNTnEILdAoBcn /wBtYMRzKFIww2atxmmGl6nPps3lOeaPO4qz2t5aXyghwD71WLy18zJC71FwzSW8uCSKrK+XFpyn nifJPXemp8xdgjZ+RqOtLucqG5+YEbUs2pyoCWJoHEqyn8sbkkdlNRV4uoKwPkuvtykVIwcQRK2G yKPNrEEzpySFGXpk/Cw9DRVT1qeW1lWQoyCUZx03HWo1LtpG3JrdvBySz1fxAtbDUeG7PUomVjI8 8SukAxs45ts5GPXrXp+z4b4chAaHQ9MjI6FbVB/SkMeG/CzTdS1jiRNOtdPu5TduiGZISyRKrcxZ j6bV6l4a8FuDLSxWO7hvb+U7vJNM0ZLHr8KYx+taqkcUIVI41VewUYArsigSZGKuKoKeEHAMJDpo rhvX8VKf5tTW78DfDW+na5udDleV8Zb8ZMOnyatLyCu/pQQgDqKDNbPwJ8M7W6juU4e8xozkLLcy un1Utg/WlX8EfDsasurW+hm3u0bmUxXEiqp9lzj9K0gkAda7nK7VRVZeDdIeJwqzwzH8s6vzPGfU Bsr+lecPGPh/ifh/iBbLW9Sl1GxkzJY3LfCGGdwR0DDv8/evWb7HOdqpvjLwsvGHh9qGnxIDf26G 4sX7iVRnl/5br9amFeQiOQ4yCPbrTeSLLl4zv1371DQX8yA+YCGHX2NBuJCshR0UgbdKiHdxd8jl dwfSm348xnm5Oai/ibe/bflRj0P96ZXHNBLyMpA96GtG8G9cS34rWDIWO9Qxuv8AqG6n+n1oVV/D S3a7490qNCwVZfNbHooJ/pQrnZjUejBjPSl12IG1Nhkt0pxGD1NSVcQ3F17HpeqcOalNJyRRaj5b tnAAdGWsIlYJql7CSMpcOvX0Y1vvG3DZ4q4Yn0qO4W3nLLJDKwyEZTnt7ZFYxxfwJq3DV9LNNfR6 j+xWeeRFK8nM3IOvXfG9bjNRbJzHAxnGMUwurJJVbb608t5gFywJJ9KcZXBBGRVREabM1rP5E26n cGp0Q288PMNjTC/tkki5kwCB2613SblsCF2APT50AvNMj5TIuB3z3qNSMBvi7Hap65TbP9dqirhV QFsYOelNMW7wn1ltC4uspjPy28kgSTmOwB6H23r2Fpl+rwhieY8ucj+9fP1794mJBAxWhcEeJHF1 vYCCDU3Fra/xgNk9lyRnHtRXtA3S8iuCppvd38ceDg7HtWD6B40W7Wwe9tQZlHxRBsBv9p7VcND4 80ni+ynbTGkt7222ntJtnXPQjsw9xV1V8XX7U8o5XO5H8v70dNcsOflZ+VvQmszvG1Yc7RJzxk5y N+tIQX8sbjmkhBO551II+9T9GNfTUbaTHJMjfI04S4UjGay+zvp5CpJBP+kbCp2y1GRByFmPfc9K foxb2lDE7/ShDIomUZ2OxHzquG8nZQyinNpdyNgEYYGmmPH3jLoFpo3iVrulEGBBcmaEqNuST4wP pzY+lUu60i2EBImVpPbvWwf4z7Q23iBpeoRLgXenAE+pR2/owrCo7iUOAT9KrIvJJbyfDkYNSkFx Hd24jlxzr+U0QeVdDHMFIG5NMJk8p/gl+1X0aj4Eaf5vFNzeFdrW2IB92IA/QGhVn8BdNntOF59U uYyn46UeVnqyLtn5Ek/ahXLq/Wo0LlAbY0dAc9TSJOD70ojjY1iNU7iyM4yKqfF8IvNa1KyfcT8N 3DKD3aN1dftirVDKp9Kz/wAbLq60pdO1ywmWN+SazfftIvp8s1uJWNluQq/NsRnrTtJkbBUjGKj7 OSOeE2rN8QGVNNj51jNljzRnvW2U7JKrKQoyR1phM/lSK64z1NKQOsq8/MObrmkJjzty8h9M1BOW E63UIQMOalZdBkul+J8DtvVTE01pLzwyMuPQU4bi3UUQJkEjbJFMNSl1wzbwjnurxQvcLTC61OGO FdO08IkS+nf5moDUtbvr5irynHtSNoxiIbO9WRFnVWEBfm3A9abadJxE16l3pN7cWk8eyyxyFDj0 yO3tSNpqSD4ZcYPWp231e0t7blgYA43FBYLbxG474Zt+eW/ttULYDrOhJP1BGftVguOK+MNV4cte I7LheG4sp8q01rO+Y5FPxKy9j36dMVlVzfSX1zsMhdzWm/4fePLPhniSTh7XJUj0bWCFLufggnH5 XPoD+Un5HtRZVq4J8Q+GDpbza/FrMFzAc3Kxwq/kL/ER1K57gbbVpPDHFPAuuMo0jjbT5XbcQzv5 T/8Apf8AtTTirwj0niC9N7a6oulXirmKSABiT/qGd1x2715q8SOAZ9D4gm0y4jWw1INlMHFtdL2a Mn8pP8JqZFe1raxkKjlaGZT3RhinSWLKdosZr532mu8UcPXLQ2uralp8sZwViuHT+Rqaj8XPEeKE xJxfqnKRjeXJ+53rWJrYv8cd9YC94ask5Wv44pXch90jJUAEe5B+1eaDKSMgkGlNV1HUNVvZL3Ur ye7uZDl5ZnLMfqaluDODeIeLLvydHsHkjU4kuH+GKP5sf5DJ9qvgghI4YkM2/vWreEXhhfcQSxaz r8Ulto6kMkbfC917DuF9+/b1GkcB+EPDvDix3mphdX1Jfi5pF/Yxn/Snf5tn5Cr9PP22A6Vi9Lhp NHFEkcEEaxRRqFRFGAoHQAUKTnkHPmhXOqTcnNBGNChWI0VjbvgVnX+IiMtwbZzKQDHeA/dWoUK6 c+s1g0N2/mrMuzKas0YS/tQ7LguvN8qFCt1kwV2tJ/KU5XNO2cqSQBkj0oUKBheFmzvudyah51Oe tChViVyOFQfenCxKRvQoVUJSRgHrSTMQ2ATQoVYsOIZ3gBAJpve3Ek5BJ70KFIJXh/i/ibQZEl0r Wbu2KkEKJCV/9J2q73Pi3xPxRZ/5JqENhNPe8tsLqSLLICcbD60KFLCM+4gDw63cWUkjTpaSNApb qQpIrWPD/wAMeG+IuDLW/vWvYbqUNl4ZRjrtsQaFCs9XIs9XHhvwa4M04CS9hudVl5sg3EnKg/4r jP1zWhW8dvY2kdpZW8NtbxjCRRIFVR7AUKFc9tawSSViDTSRsnehQqKbyjLbUKFCs0j/2Q== Cheerio! --Multipart_Sun_Oct_17_10:37:40_2010-1-- mu-0.9.18/lib/tests/testdir2/Foo/tmp/0000755000175000017500000000000013021065721014223 500000000000000mu-0.9.18/lib/tests/testdir2/Foo/tmp/.noindex0000644000175000017500000000000012605152237015604 00000000000000mu-0.9.18/lib/mu-str.h0000644000175000017500000002324113020504332011313 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_STR_H__ #define __MU_STR_H__ #include #include #include #include G_BEGIN_DECLS /** * @addtogroup MuStr * Various string utilities * @{ */ /** * create a 'display contact' from an email header To/Cc/Bcc/From-type address * ie., turn * "Foo Bar" * into * Foo Bar * Note that this is based on some simple heuristics. Max length is 255 bytes. * * mu_str_display_contact_s returns a statically allocated * buffer (ie, non-reentrant), while mu_str_display_contact * returns a newly allocated string that you must free with g_free * when done with it. * * @param str a 'contact str' (ie., what is in the To/Cc/Bcc/From * fields), or NULL * * @return a newly allocated string with a display contact */ const char* mu_str_display_contact_s (const char *str) G_GNUC_CONST; char *mu_str_display_contact (const char *str) G_GNUC_WARN_UNUSED_RESULT; /** * get a display size for a given size_t; uses M for sizes > * 1000*1000, k for smaller sizes. Note: this function use the * 10-based SI units, _not_ the powers-of-2 based ones. * * mu_str_size_s returns a ptr to a static buffer, * while mu_str_size returns dynamically allocated * memory that must be freed after use. * * @param t the size as an size_t * * @return a string representation of the size; see above * for what to do with it */ const char* mu_str_size_s (size_t s) G_GNUC_CONST; char* mu_str_size (size_t s) G_GNUC_WARN_UNUSED_RESULT; /** * Replace all occurences of substr in str with repl * * @param str a string * @param substr some string to replace * @param repl a replacement string * * @return a newly allocated string with the substr replaced by repl; free with g_free */ char *mu_str_replace (const char *str, const char *substr, const char *repl); /** * get a display string for a given set of flags, OR'ed in * @param flags; one character per flag: * D=draft,F=flagged,N=new,P=passed,R=replied,S=seen,T=trashed * a=has-attachment,s=signed, x=encrypted * * mu_str_file_flags_s returns a ptr to a static buffer, * while mu_str_file_flags returns dynamically allocated * memory that must be freed after use. * * @param flags file flags * * @return a string representation of the flags; see above * for what to do with it */ const char* mu_str_flags_s (MuFlags flags) G_GNUC_CONST; char* mu_str_flags (MuFlags flags) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * get a 'summary' of the string, ie. the first /n/ lines of the * strings, with all newlines removed, replaced by single spaces * * @param str the source string * @param max_lines the maximum number of lines to include in the summary * * @return a newly allocated string with the summary. use g_free to free it. */ char* mu_str_summarize (const char* str, size_t max_lines) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Process some text (e.g. message bodies) -- flatten (remove accents * etc.), and remove some punctuation. * * @param text some text * * @return the processed text, free with g_free */ char* mu_str_process_text (const char *text) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Process some term (e.g., an e-mail address, subject field): * remove accents, replace some punctuation by _ * * @param term some term * * @return the processed text, free with g_free */ char* mu_str_process_term (const char *term) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Process some query term (e.g., an e-mail address, subject field): * remove accents, replace some punctuation by _, but leave some query * metachars alone. * * @param qterm some query term * * @return the processed text, free with g_free */ char* mu_str_process_query_term (const char *qterm) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Handle the message-id in a special way * * @param str the message-id str * @param query is this a query? * * @return the massaged message-id */ char* mu_str_process_msgid (const char *str, gboolean query); /** * Fixup values for some fields in the DWIM manner: * - if term is date:YYYYMMDD, replace it with the range * date:YYYYMMDD..YYYYMMDD. * * @param query a query string * * @return the fixup'd string that must be g_free()d * after use or NULL in case of error. */ gchar* mu_str_xapian_fixup_terms (const gchar *term); /** * parse a byte size; a size is a number, with optionally a * unit. Units recognized are b/B (bytes) k/K (1000) and m/M * (1000*1000). Only the first letter is checked and the function is * not case-sensitive, so 1000Kb, 3M will work equally well. Note, * for kB, MB etc., we then follow the SI standards, not 2^10 etc. The * 'b' may be omitted. * * practical sizes for email messages are in terms of Mb; even in * extreme cases it should be under 100 Mb. Function return * GUINT64_MAX if there a parsing error * * @param str a string with a size, such a "100", "100Kb", "1Mb" * * @return the corresponding size in bytes, or -1 in case of error */ gint64 mu_str_size_parse_bkm (const char* str); /** * create a full path from a path + a filename. function is _not_ * reentrant. * * @param path a path (!= NULL) * @param name a name (may be NULL) * * @return the path as a statically allocated buffer. don't free. */ const char* mu_str_fullpath_s (const char* path, const char* name); /** * escape a string like a string literal in C; ie. replace \ with \\, * and " with \" * * @param str a non-NULL str * @param in_quotes whether the result should be enclosed in "" * * @return the escaped string, newly allocated (free with g_free) */ char* mu_str_escape_c_literal (const gchar* str, gboolean in_quotes) G_GNUC_WARN_UNUSED_RESULT; /** * turn a string into plain ascii by replacing each non-ascii * character with a dot ('.'). Replacement is done in-place. * * @param buf a buffer to asciify * * @return the buf ptr (as to allow for function composition) */ char* mu_str_asciify_in_place (char *buf); /** * turn string in buf into valid utf8. If this string is not valid * utf8 already, the function massages the offending characters. * * @param buf a buffer to utf8ify * * @return a newly allocated utf8 string */ char* mu_str_utf8ify (const char *buf); /** * convert a string in a certain charset into utf8 * * @param buffer a buffer to convert * @param charset source character set. * * @return a UTF8 string (which you need to g_free when done with it), * or NULL in case of error */ gchar* mu_str_convert_to_utf8 (const char* buffer, const char *charset); /** * macro to check whether the string is empty, ie. if it's NULL or * it's length is 0 * * @param S a string * * @return TRUE if the string is empty, FALSE otherwise */ #define mu_str_is_empty(S) ((!(S)||!(*S))?TRUE:FALSE) /** * convert a GSList of strings to a #sepa-separated list * * @param lst a GSList * @param the separator character * * @return a newly allocated string */ char* mu_str_from_list (const GSList *lst, char sepa); /** * convert a #sepa-separated list of strings in to a GSList * * @param str a #sepa-separated list of strings * @param the separator character * @param remove leading/trailing whitespace from the string * * @return a newly allocated GSList (free with mu_str_free_list) */ GSList* mu_str_to_list (const char *str, char sepa, gboolean strip); /** * convert a string (with possible escaping) to a list. list items are * separated by one or more spaces. list items can be quoted (using * '"'). * * @param str a string * * @return a list of elements or NULL in case of error, free with * mu_str_free_list */ GSList* mu_str_esc_to_list (const char *str); /** * Parse a list of : arguments, where supports * quoting and escaping. * * @param args a list of arguments * @param err receives error information * * @return a hash table with key->value, or NULL in case of * error. Free with g_hash_table_destroy. */ GHashTable* mu_str_parse_arglist (const char *args, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * free a GSList consisting of allocated strings * * @param lst a GSList */ void mu_str_free_list (GSList *lst); /** * strip the subject of Re:, Fwd: etc. * * @param str a subject string * * @return a new string -- this is pointing somewhere inside the @str; * no copy is made, don't free */ const gchar* mu_str_subject_normalize (const gchar* str); /** * take a list of strings, and return the concatenation of their * quoted forms * * @param params NULL-terminated array of strings * * @return the quoted concatenation of the strings */ gchar* mu_str_quoted_from_strv (const gchar **params); /** * Remove control characters from a string * * @param str a string * * @return the str with control characters removed */ char* mu_str_remove_ctrl_in_place (char *str); /** @} */ G_END_DECLS #endif /*__MU_STR_H__*/ mu-0.9.18/lib/mu-maildir.h0000644000175000017500000002025013020504332012121 00000000000000/* ** Copyright (C) 2008-2015 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_MAILDIR_H__ #define __MU_MAILDIR_H__ #include #include #include /* for mode_t */ #include #include G_BEGIN_DECLS /** * create a new maildir. if parts of the maildir already exists, those * will simply be ignored. IOW, if you try to create the same maildir * twice, the second will simply be a no-op (without any errors). * Note, if the function fails 'halfway', it will *not* try to remove * the parts the were created. it *will* create any parent dirs that * are not yet existent. * * * @param path the path (missing components will be created, as in 'mkdir -p') * @param mode the file mode (e.g., 0755) * @param noindex add a .noindex file to the maildir, so it will be excluded * from indexing by 'mu index' * @param err if function returns FALSE, receives error * information. err may be NULL. * * @return TRUE if creation succeeded (or already existed), FALSE otherwise */ gboolean mu_maildir_mkdir (const char* path, mode_t mode, gboolean noindex, GError **err); /** * create a symbolic link to a mail message * * @param src the full path to the source message * @param targetpath the path to the target maildir; ie., *not* * MyMaildir/cur, but just MyMaildir/. The function will figure out * the correct subdir then. * @param err if function returns FALSE, err may contain extra * information. if err is NULL, does nothing * * @return */ gboolean mu_maildir_link (const char* src, const char *targetpath, GError **err); /** * MuMaildirWalkMsgCallback -- callback function for * mu_path_walk_maildir; see the documentation there. It will be * called for each message found, with fullpath containing the full * path to the message, mdir containing the maildir -- that is, when * indexing ~/Maildir, a message ~/Maildir/foo/bar/cur/msg would have * the maildir "foo/bar". Then, the information from 'stat' of this * file (see stat(3)), and a user_data pointer */ typedef MuError (*MuMaildirWalkMsgCallback) (const char* fullpath, const char* mdir, struct stat *statinfo, void *user_data); /** * MuPathWalkDirCallback -- callback function for mu_path_walk_maildir; see the * documentation there. It will be called each time a dir is entered or left, * with 'enter' being TRUE upon entering, FALSE otherwise */ typedef MuError (*MuMaildirWalkDirCallback) (const char* fullpath, gboolean enter, void *user_data); /** * start a recursive walk of a maildir; for each file found, we call * callback with the path (with the Maildir path of scanner_new as * root), the filename, the timestamp (mtime) of the file,and the * *data pointer, for user data. dot-files are ignored, as well as * files outside cur/ and new/ dirs and unreadable files; however, * dotdirs are visited (ie. '.dotdir/cur'), so this enables Maildir++. * (http://www.inter7.com/courierimap/README.maildirquota.html, search * for 'Mission statement'). In addition, dirs containing a file named * '.noindex' are ignored, as are their subdirectories, and dirs * containing a file called '.noupdate' are ignored, unless @param * full is TRUE. * * mu_walk_maildir stops if the callbacks return something different * from MU_OK. For example, it can return MU_STOP to stop the scan, or * some error. * * @param path the maildir path to scan * @param cb_msg the callback function called for each msg * @param cb_dir the callback function called for each dir * @param full whether do a full scan, i.e., to ignore .noupdate files * @param data user data pointer * * @return a scanner result; MU_OK if everything went ok, * MU_STOP if we want to stop, or MU_ERROR in * case of error */ MuError mu_maildir_walk (const char *path, MuMaildirWalkMsgCallback cb_msg, MuMaildirWalkDirCallback cb_dir, gboolean full, void *data); /** * recursively delete all the symbolic links in a directory tree * * @param dir top dir * @param err if function returns FALSE, err may contain extra * information. if err is NULL, does nothing * * @return TRUE if it worked, FALSE in case of error */ gboolean mu_maildir_clear_links (const gchar* dir, GError **err); /** * whether the directory path ends in '/cur/' or '/new/' * * @param path some path */ gboolean mu_maildir_is_leaf_dir (const char *path); /** * get the Maildir flags from the full path of a mailfile. The flags * are as specified in http://cr.yp.to/proto/maildir.html, plus * MU_MSG_FLAG_NEW for new messages, ie the ones that live in * new/. The flags are logically OR'ed. Note that the file does not * have to exist; the flags are based on the path only. * * @param pathname of a mailfile; it does not have to refer to an * actual message * * @return the flags, or MU_MSG_FILE_FLAG_UNKNOWN in case of error */ MuFlags mu_maildir_get_flags_from_path (const char* pathname); /** * get the new pathname for a message, based on the old path and the * new flags and (optionally) a new maildir. Note that * setting/removing the MU_FLAG_NEW will change the directory in which * a message lives. The flags are as specified in * http://cr.yp.to/proto/maildir.html, plus MU_FLAG_NEW for new * messages, ie the ones that live in new/. The flags are logically * OR'ed. Note that the file does not have to exist; the flags are * based on the path only. * * * @param oldpath the old (current) full path to the message * (including the filename) * @param new_mdir the new maildir for this message, or NULL to keep * it in the current one. The maildir is the absolute file system * path, without the 'cur' or 'new' * @param new_flags the new flags for this message * @param new_name whether to create a new unique name, or keep the * old one * * @return a new path name; use g_free when done with. NULL in case of * error. */ char* mu_maildir_get_new_path (const char *oldpath, const char *new_mdir, MuFlags new_flags, gboolean new_name) G_GNUC_WARN_UNUSED_RESULT; /** * get the maildir for a certain message path, ie, the path *before* * cur/ or new/ * * @param path path for some message * * @return the maildir (free with g_free), or NULL in case of error */ char* mu_maildir_get_maildir_from_path (const char* path) G_GNUC_WARN_UNUSED_RESULT; /** * move a message file to another maildir; the function returns the full path to * the new message. if the target file already exists, it is overwritten. * * @param msgpath an absolute file system path to an existing message in an * actual maildir * @param targetmdir the target maildir; note that this the base-level * Maildir, ie. /home/user/Maildir/archive, and must _not_ include the * 'cur' or 'new' part. Note that the target maildir must be on the * same filesystem. If you specify NULL for targetmdir, only the flags * of the message are affected; note that this may still involve a * moved to another directory (say, from new/ to cur/) * @param flags to set for the target (influences the filename, path) * @param ignore_dups whether to silently ignore the src=target case * (and return TRUE) * @param new_name whether to create a new unique name, or keep the * old one * @param err receives error information * * @return return the full path name of the target file (g_free) if * the move succeeded, NULL otherwise */ gchar* mu_maildir_move_message (const char* oldpath, const char* targetmdir, MuFlags newflags, gboolean ignore_dups, gboolean new_name, GError **err) G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS #endif /*__MU_MAILDIR_H__*/ mu-0.9.18/lib/mu-index.h0000644000175000017500000001642113020504331011613 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_INDEX_H__ #define __MU_INDEX_H__ #include #include #include /* for MuResult */ #include G_BEGIN_DECLS /* opaque structure */ struct _MuIndex; typedef struct _MuIndex MuIndex; struct _MuIndexStats { unsigned _processed; /* number of msgs processed or counted */ unsigned _updated; /* number of msgs new or updated */ unsigned _cleaned_up; /* number of msgs cleaned up */ unsigned _uptodate; /* number of msgs already uptodate */ }; typedef struct _MuIndexStats MuIndexStats; /** * create a new MuIndex instance. NOTE(1): the database does not have * to exist yet, but the directory must already exist; NOTE(2): before * doing anything with the returned Index object, make sure you haved * called g_type_init, and mu_msg_init somewhere in your code. * * @param store a writable MuStore object * @param err to receive error or NULL; there are only errors when this * function returns NULL. Possible errors: see mu-error.h * * @return a new MuIndex instance, or NULL in case of error */ MuIndex* mu_index_new (MuStore *store, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * destroy the index instance * * @param index a MuIndex instance, or NULL */ void mu_index_destroy (MuIndex *index); /** * change the maximum file size that mu-index considers from its * default (MU_INDEX_MAX_FILE_SIZE). Note that the maximum size is a * protection against mu (or the libraries it uses) allocating too * much memory, which can lead to problems * * @param index a mu index object * @param max_size the maximum msg size, or 0 to reset to the default */ void mu_index_set_max_msg_size (MuIndex *index, guint max_size); /** * change batch size for Xapian store transaction (see * 'mu_store_set_batch_size') * * @param index a mu index object * @param max_size the batch size, or 0 to reset to the default */ void mu_index_set_xbatch_size (MuIndex *index, guint xbatchsize); /** * callback function for mu_index_(run|stats|cleanup), for each message * * @param stats pointer to structure to receive statistics data * @param user_data pointer to user data * * @return MU_OK to continue, MU_STOP to stop, or MU_ERROR in * case of some error. */ typedef MuError (*MuIndexMsgCallback) (MuIndexStats* stats, void *user_data); /** * callback function for mu_index_(run|stats|cleanup), for each dir enter/leave * * @param path dirpath we just entered / left * @param enter did we enter (TRUE) or leave(FALSE) the dir? * @param user_data pointer to user data * * @return MU_OK to contiue, MU_STOP to stopd or MU_ERROR in * case of some error. */ typedef MuError (*MuIndexDirCallback) (const char* path, gboolean enter, void *user_data); /** * start the indexing process * * @param index a valid MuIndex instance * @param path the path to index. This must be an absolute path * @param force if != 0, force re-indexing already index messages; this is * obviously a lot slower than only indexing new/changed messages * @param lazycheck whether ignore subdirectoryies that have up-to-date * timestamps. * @param stats a structure with some statistics about the results; * note that this function does *not* reset the struct values to allow * for cumulative stats from multiple calls. If needed, you can use * @mu_index_stats_clear before calling this function * @param cb_msg a callback function called for every msg indexed; * @param cb_dir a callback function called for every dir entered/left or NULL * @param user_data a user pointer that will be passed to the callback function * * @return MU_OK if the stats gathering was completed succesfully, * MU_STOP if the user stopped or MU_ERROR in * case of some error. */ MuError mu_index_run (MuIndex *index, const char *path, gboolean force, gboolean lazycheck, MuIndexStats *stats, MuIndexMsgCallback msg_cb, MuIndexDirCallback dir_cb, void *user_data); /** * gather some statistics about the Maildir; this is usually much faster than * mu_index_run, and can thus be used to provide some information to the user * note though that the statistics may be different from the reality that * mu_index_run sees, when there are updates in the Maildir * * @param index a valid MuIndex instance * @param path the path to get stats for; this must be an absolute path * @param stats a structure with some statistics about the results; * note that this function does *not* reset the struct values to allow * for cumulative stats from multiple calls. If needed, you can use * @mu_index_stats_clear before calling this function * @param msg_cb a callback function which will be called for every msg; * @param dir_cb a callback function which will be called for every dir or NULL * @param user_data a user pointer that will be passed to the callback function * xb * @return MU_OK if the stats gathering was completed succesfully, * MU_STOP if the user stopped or MU_ERROR in * case of some error. */ MuError mu_index_stats (MuIndex *index, const char *path, MuIndexStats *stats, MuIndexMsgCallback msg_cb, MuIndexDirCallback dir_cb, void *user_data); /** * callback function called for each message * * @param MuIndexCleanupCallback * * @return a MuResult */ typedef MuError (*MuIndexCleanupDeleteCallback) (MuIndexStats *stats, void *user_data); /** * cleanup the database; ie. remove entries for which no longer a corresponding * file exists in the maildir * * @param index a valid MuIndex instance * @param stats a structure with some statistics about the results; * note that this function does *not* reset the struct values to allow * for cumulative stats from multiple calls. If needed, you can use * @mu_index_stats_clear before calling this function * @param cb a callback function which will be called for every msg; * @param user_data a user pointer that will be passed to the callback function * @param err to receive error info or NULL. err->code is MuError value * * @return MU_OK if the stats gathering was completed succesfully, * MU_STOP if the user stopped or MU_ERROR in * case of some error. */ MuError mu_index_cleanup (MuIndex *index, MuIndexStats *stats, MuIndexCleanupDeleteCallback cb, void *user_data, GError **err); /** * clear the stats structure * * @param stats a MuIndexStats object * * @return TRUE if stats != NULL, FALSE otherwise */ gboolean mu_index_stats_clear (MuIndexStats *stats); G_END_DECLS #endif /*__MU_INDEX_H__*/ mu-0.9.18/lib/mu-flags.c0000644000175000017500000001110313020504331011563 00000000000000/* ** Copyright (C) 2011-2012 ** ** 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, 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. ** */ #include #include "mu-flags.h" struct _FlagInfo { MuFlags flag; char kar; const char *name; MuFlagType flag_type; }; typedef struct _FlagInfo FlagInfo; static const FlagInfo FLAG_INFO[] = { /* NOTE: order of this is significant, due to optimizations * below */ { MU_FLAG_DRAFT, 'D', "draft", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_FLAGGED, 'F', "flagged", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_PASSED, 'P', "passed", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_REPLIED, 'R', "replied", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_SEEN, 'S', "seen", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_TRASHED, 'T', "trashed", MU_FLAG_TYPE_MAILFILE }, { MU_FLAG_NEW, 'N', "new", MU_FLAG_TYPE_MAILDIR }, { MU_FLAG_SIGNED, 'z', "signed", MU_FLAG_TYPE_CONTENT }, { MU_FLAG_ENCRYPTED, 'x', "encrypted", MU_FLAG_TYPE_CONTENT }, { MU_FLAG_HAS_ATTACH, 'a', "attach", MU_FLAG_TYPE_CONTENT }, { MU_FLAG_LIST, 'l', "list", MU_FLAG_TYPE_CONTENT }, { MU_FLAG_UNREAD, 'u', "unread", MU_FLAG_TYPE_PSEUDO } }; MuFlagType mu_flag_type (MuFlags flag) { unsigned u; for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u) if (FLAG_INFO[u].flag == flag) return FLAG_INFO[u].flag_type; return MU_FLAG_TYPE_INVALID; } char mu_flag_char (MuFlags flag) { unsigned u; for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u) if (FLAG_INFO[u].flag == flag) return FLAG_INFO[u].kar; return 0; } static MuFlags mu_flag_from_char (char kar) { unsigned u; for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u) if (FLAG_INFO[u].kar == kar) return FLAG_INFO[u].flag; return MU_FLAG_INVALID; } const char* mu_flag_name (MuFlags flag) { unsigned u; for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u) if (FLAG_INFO[u].flag == flag) return FLAG_INFO[u].name; return NULL; } const char* mu_flags_to_str_s (MuFlags flags, MuFlagType types) { unsigned u,v; static char str[sizeof(FLAG_INFO) + 1]; for (u = 0, v = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) if (flags & FLAG_INFO[u].flag && types & FLAG_INFO[u].flag_type) str[v++] = FLAG_INFO[u].kar; str[v] = '\0'; return str; } MuFlags mu_flags_from_str (const char *str, MuFlagType types, gboolean ignore_invalid) { const char *cur; MuFlags flag; g_return_val_if_fail (str, MU_FLAG_INVALID); for (cur = str, flag = MU_FLAG_NONE; *cur; ++cur) { MuFlags f; f = mu_flag_from_char (*cur); if (f == MU_FLAG_INVALID) { if (ignore_invalid) continue; return MU_FLAG_INVALID; } if (mu_flag_type (f) & types) flag |= f; } return flag; } char* mu_flags_custom_from_str (const char *str) { char *custom; const char* cur; unsigned u; g_return_val_if_fail (str, NULL); for (cur = str, u = 0, custom = NULL; *cur; ++cur) { MuFlags flag; flag = mu_flag_from_char (*cur); /* if it's a valid file flag, ignore it */ if (flag != MU_FLAG_INVALID && mu_flag_type (flag) == MU_FLAG_TYPE_MAILFILE) continue; /* otherwise, add it to our custom string */ if (!custom) custom = g_new0 (char, strlen(str) + 1); custom[u++] = *cur; } return custom; } void mu_flags_foreach (MuFlagsForeachFunc func, gpointer user_data) { unsigned u; g_return_if_fail (func); for (u = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) func (FLAG_INFO[u].flag, user_data); } MuFlags mu_flags_from_str_delta (const char *str, MuFlags oldflags, MuFlagType types) { const char *cur; MuFlags newflags; g_return_val_if_fail (str, MU_FLAG_INVALID); for (cur = str, newflags = oldflags; *cur; ++cur) { MuFlags f; if (*cur == '+' || *cur == '-') { f = mu_flag_from_char (cur[1]); if (f == 0) goto error; if (*cur == '+') newflags |= f; else newflags &= ~f; ++cur; continue; } goto error; } return newflags; error: g_warning ("invalid flag string"); return MU_FLAG_INVALID; } mu-0.9.18/lib/Makefile.am0000644000175000017500000000457413020504331011756 00000000000000## Copyright (C) 2010-2013 Dirk-Jan C. Binnema ## ## 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, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk # enforce compiling guile (optionally) first,then this dir first # before decending into tests/ SUBDIRS= . tests AM_CPPFLAGS=$(XAPIAN_CXXFLAGS) $(GMIME_CFLAGS) $(GLIB_CFLAGS) $(GUILE_CFLAGS) # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter \ -Wdeclaration-after-statement -Wno-variadic-macros AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter noinst_LTLIBRARIES= \ libmu.la libmu_la_SOURCES= \ mu-bookmarks.c \ mu-bookmarks.h \ mu-contacts.c \ mu-contacts.h \ mu-container.c \ mu-container.h \ mu-date.c \ mu-date.h \ mu-flags.h \ mu-flags.c \ mu-index.c \ mu-index.h \ mu-log.c \ mu-log.h \ mu-maildir.c \ mu-maildir.h \ mu-msg-crypto.c \ mu-msg-doc.cc \ mu-msg-doc.h \ mu-msg-fields.c \ mu-msg-fields.h \ mu-msg-file.c \ mu-msg-file.h \ mu-msg-iter.cc \ mu-msg-iter.h \ mu-msg-part.c \ mu-msg-part.h \ mu-msg-prio.c \ mu-msg-prio.h \ mu-msg-priv.h \ mu-msg-sexp.c \ mu-msg.c \ mu-msg.h \ mu-msg.h \ mu-query.cc \ mu-query.h \ mu-runtime.c \ mu-runtime.h \ mu-script.c \ mu-script.h \ mu-store.cc \ mu-store.h \ mu-store-read.cc \ mu-store-write.cc \ mu-store-priv.hh \ mu-str.c \ mu-str.h \ mu-threader.c \ mu-threader.h \ mu-util.c \ mu-util.h libmu_la_LIBADD= \ $(XAPIAN_LIBS) \ $(GMIME_LIBS) \ $(GLIB_LIBS) \ $(GUILE_LIBS) EXTRA_DIST= \ mu-msg-crypto.c \ doxyfile.in mu-0.9.18/lib/mu-threader.c0000644000175000017500000003124213020504332012274 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #include /* for log, ceil */ #include /* for memset */ #include "mu-threader.h" #include "mu-container.h" #include "mu-str.h" /* msg threading implementation based on JWZ's algorithm, as described in: * http://www.jwz.org/doc/threading.html * * the implementation follows the terminology from that doc, so should * be understandable from that... I did change things a bit though * * the end result of the threading operation is a hashtable which maps * docids (ie., Xapian documents == messages) to 'thread paths'; a * thread path is a string denoting the 2-dimensional place of a * message in a list of messages, * * Msg1 => 00000 * Msg2 => 00001 * Msg3 (child of Msg2) => 00001:00000 * Msg4 (child of Msg2) => 00001:00001 * Msg5 (child of Msg4) => 00001:00001:00000 * Msg6 => 00002 * * the padding-0's are added to make them easy to sort using strcmp; * the number hexadecimal numbers, and the length of the 'segments' * (the parts separated by the ':') is equal to ceil(log_16(matchnum)) * */ /* step 1 */ static GHashTable* create_containers (MuMsgIter *iter); /* step 2 */ static MuContainer *find_root_set (GHashTable *ids); static MuContainer* prune_empty_containers (MuContainer *root); /* static void group_root_set_by_subject (GSList *root_set); */ GHashTable* create_doc_id_thread_path_hash (MuContainer *root, size_t match_num); /* msg threading algorithm, based on JWZ's algorithm, * http://www.jwz.org/doc/threading.html */ GHashTable* mu_threader_calculate (MuMsgIter *iter, size_t matchnum, MuMsgFieldId sortfield, gboolean descending) { GHashTable *id_table, *thread_ids; MuContainer *root_set; g_return_val_if_fail (iter, FALSE); g_return_val_if_fail (mu_msg_field_id_is_valid (sortfield) || sortfield == MU_MSG_FIELD_ID_NONE, FALSE); /* step 1 */ id_table = create_containers (iter); if (matchnum == 0) return id_table; /* just return an empty table */ /* step 2 -- the root_set is the list of children without parent */ root_set = find_root_set (id_table); /* step 3: skip until the end; we still need to containers */ /* step 4: prune empty containers */ root_set = prune_empty_containers (root_set); /* sort root set */ if (sortfield != MU_MSG_FIELD_ID_NONE) root_set = mu_container_sort (root_set, sortfield, descending, NULL); /* step 5: group root set by subject */ /* group_root_set_by_subject (root_set); */ /* sort */ mu_msg_iter_reset (iter); /* go all the way back */ /* finally, deliver the docid => thread-path hash */ thread_ids = mu_container_thread_info_hash_new (root_set, matchnum); g_hash_table_destroy (id_table); /* step 3*/ return thread_ids; } G_GNUC_UNUSED static void check_dup (const char *msgid, MuContainer *c, GHashTable *hash) { if (g_hash_table_lookup (hash, c)) { g_warning ("ALREADY!!"); mu_container_dump (c, FALSE); g_assert (0); } else g_hash_table_insert (hash, c, GUINT_TO_POINTER(TRUE)); } G_GNUC_UNUSED static void assert_no_duplicates (GHashTable *ids) { GHashTable *hash; hash = g_hash_table_new (g_direct_hash, g_direct_equal); g_hash_table_foreach (ids, (GHFunc)check_dup, hash); g_hash_table_destroy (hash); } /* a referred message is a message that is refered by some other * message */ static MuContainer* find_or_create_referred (GHashTable *id_table, const char *msgid, gboolean *created) { MuContainer *c; g_return_val_if_fail (msgid, NULL); c = g_hash_table_lookup (id_table, msgid); *created = !c; if (!c) { c = mu_container_new (NULL, 0, msgid); g_hash_table_insert (id_table, (gpointer)msgid, c); /* assert_no_duplicates (id_table); */ } return c; } /* find a container for the given msgid; if it does not exist yet, * create a new one, and register it */ static MuContainer* find_or_create (GHashTable *id_table, MuMsg *msg, guint docid) { MuContainer *c; const char* msgid; char fake[32]; g_return_val_if_fail (msg, NULL); g_return_val_if_fail (docid != 0, NULL); msgid = mu_msg_get_msgid (msg); if (!msgid) msgid = mu_msg_get_path (msg); /* fake it */ if (!msgid) { /* no path either? seems to happen... */ g_warning ("message without path"); snprintf (fake, sizeof(fake), "fake:%p", (gpointer)msg); msgid = fake; } /* XXX the '' works around a crash; find a better * solution */ c = g_hash_table_lookup (id_table, msgid); /* If id_table contains an empty MuContainer for this ID: * * * Store this message in the MuContainer's message slot. */ if (c) { if (!c->msg) { c->msg = mu_msg_ref (msg); c->docid = docid; return c; } else { /* special case, not in the JWZ algorithm: the * container exists already and has a message; this * means that we are seeing *another message* with a * message-id we already saw... create this message, * and mark it as a duplicate, and a child of the one * we saw before; use its path as a fake message-id * */ MuContainer *c2; const char* fake_msgid; fake_msgid = mu_msg_get_path (msg); c2 = mu_container_new (msg, docid, fake_msgid); c2->flags = MU_CONTAINER_FLAG_DUP; /*c = */ mu_container_append_children (c, c2); g_hash_table_insert (id_table, (gpointer)fake_msgid, c2); return NULL; /* don't process this message further */ } } else { /* Else: Create a new MuContainer object holding this message; Index the MuContainer by Message-ID in id_table. */ c = mu_container_new (msg, docid, msgid); g_hash_table_insert (id_table, (gpointer)msgid, c); /* assert_no_duplicates (id_table); */ return c; } } static gboolean child_elligible (MuContainer *parent, MuContainer *child, gboolean created) { if (!parent || !child) return FALSE; if (child->parent) return FALSE; /* if (created) */ /* return TRUE; */ if (mu_container_reachable (parent, child)) return FALSE; if (mu_container_reachable (child, parent)) return FALSE; return TRUE; } static void /* 1B */ handle_references (GHashTable *id_table, MuContainer *c) { const GSList *refs, *cur; MuContainer *parent; gboolean created; refs = mu_msg_get_references (c->msg); if (!refs) return; /* nothing to do */ /* For each element in the message's References field: Find a MuContainer object for the given Message-ID: If there's one in id_table use that; Otherwise, make (and index) one with a null Message. */ /* go over over our list of refs, until 1 before the last... */ created = FALSE; for (parent = NULL, cur = refs; cur; cur = g_slist_next (cur)) { MuContainer *child; child = find_or_create_referred (id_table, (gchar*)cur->data, &created); /* if we find the current message in their own refs, break now so that parent != c in next step */ if (child == c) break; /*Link the References field's MuContainers together in * the order implied by the References header. If they are already linked, don't change the existing links. Do not add a link if adding that link would introduce a loop: that is, before asserting A->B, search down the children of B to see if A is reachable, and also search down the children of A to see if B is reachable. If either is already reachable as a child of the other, don't add the link. */ if (child_elligible (parent, child, created)) /*parent =*/ mu_container_append_children (parent, child); parent = child; } /* 'parent' points to the last ref: our direct parent; Set the parent of this message to be the last element in References. Note that this message may have a parent already: this can happen because we saw this ID in a References field, and presumed a parent based on the other entries in that field. Now that we have the actual message, we can be more definitive, so throw away the old parent and use this new one. Find this MuContainer in the parent's children list, and unlink it. Note that this could cause this message to now have no parent, if it has no references field, but some message referred to it as the non-first element of its references. (Which would have been some kind of lie...) Note that at all times, the various ``parent'' and ``child'' fields must be kept inter-consistent. */ /* optimization: if the the message was newly added, it's by definition not reachable yet */ /* So, we move c and its descendants to become a child of parent if: * both are not NULL * parent is not a descendant of c. * both are different from each other (guaranteed in last loop) */ if (parent && c && !(c->child && mu_container_reachable (c->child, parent))) { /* if c already has a parent, remove c from its parent children and reparent it, as now we know who is c's parent reliably */ if (c->parent) { mu_container_remove_child(c->parent, c); c->next = c->last = c->parent = NULL; } /*parent = */mu_container_append_children (parent, c); } } /* step 1: create the containers, connect them, and fill the id_table */ static GHashTable* create_containers (MuMsgIter *iter) { GHashTable *id_table; id_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)mu_container_destroy); for (mu_msg_iter_reset (iter); !mu_msg_iter_is_done (iter); mu_msg_iter_next (iter)) { MuContainer *c; MuMsg *msg; unsigned docid; /* 1.A */ msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */ docid = mu_msg_iter_get_docid (iter); c = find_or_create (id_table, msg, docid); /* 1.B and C */ if (c) handle_references (id_table, c); } return id_table; } static void filter_root_set (const gchar *msgid, MuContainer *c, MuContainer **root_set) { /* ignore children */ if (c->parent) return; /* ignore duplicates */ if (c->flags & MU_CONTAINER_FLAG_DUP) return; if (*root_set == NULL) { *root_set = c; return; } else *root_set = mu_container_append_siblings (*root_set, c); } /* 2. Walk over the elements of id_table, and gather a list of the MuContainer objects that have no parents, but do have children */ static MuContainer* find_root_set (GHashTable *ids) { MuContainer *root_set; root_set = NULL; g_hash_table_foreach (ids, (GHFunc)filter_root_set, &root_set); return root_set; } static gboolean prune_maybe (MuContainer *c) { MuContainer *cur; for (cur = c->child; cur; cur = cur->next) { if (cur->flags & MU_CONTAINER_FLAG_DELETE) { c = mu_container_remove_child (c, cur); } else if (cur->flags & MU_CONTAINER_FLAG_SPLICE) { c = mu_container_splice_grandchildren (c, cur); c = mu_container_remove_child (c, cur); } } g_return_val_if_fail (c, FALSE); /* don't touch containers with messages */ if (c->msg) return TRUE; /* A. If it is an msg-less container with no children, mark it for * deletion. */ if (!c->child) { c->flags |= MU_CONTAINER_FLAG_DELETE; return TRUE; } /* B. If the MuContainer has no Message, but does have * children, remove this container but promote its * children to this level (that is, splice them in to * the current child list.) * * Do not promote the children if doing so would * promote them to the root set -- unless there is * only one child, in which case, do. */ if (c->child->next) /* ie., > 1 child */ return TRUE; c->flags |= MU_CONTAINER_FLAG_SPLICE; return TRUE; } static MuContainer* prune_empty_containers (MuContainer *root_set) { MuContainer *cur; mu_container_foreach (root_set, (MuContainerForeachFunc)prune_maybe, NULL); /* and prune the root_set itself... */ for (cur = root_set; cur; cur = cur->next) { if (cur->flags & MU_CONTAINER_FLAG_DELETE) { root_set = mu_container_remove_sibling (root_set, cur); } else if (cur->flags & MU_CONTAINER_FLAG_SPLICE) { root_set = mu_container_splice_children (root_set, cur); root_set = mu_container_remove_sibling (root_set, cur); } } return root_set; } mu-0.9.18/lib/mu-msg.h0000644000175000017500000003777013020504332011305 00000000000000/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2010-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_H__ #define __MU_MSG_H__ #include #include #include #include /* for MuError and XapianDocument */ G_BEGIN_DECLS struct _MuMsg; typedef struct _MuMsg MuMsg; /* options for various functions */ enum _MuMsgOptions { MU_MSG_OPTION_NONE = 0, /* 1 << 0 is still free! */ /* for -> sexp conversion */ MU_MSG_OPTION_HEADERS_ONLY = 1 << 1, MU_MSG_OPTION_EXTRACT_IMAGES = 1 << 2, /* below options are for checking signatures; only effective * if mu was built with crypto support */ MU_MSG_OPTION_VERIFY = 1 << 4, MU_MSG_OPTION_AUTO_RETRIEVE = 1 << 5, MU_MSG_OPTION_USE_AGENT = 1 << 6, /* MU_MSG_OPTION_USE_PKCS7 = 1 << 7, /\* gpg is the default *\/ */ /* get password from console if needed */ MU_MSG_OPTION_CONSOLE_PASSWORD = 1 << 7, MU_MSG_OPTION_DECRYPT = 1 << 8, /* misc */ MU_MSG_OPTION_OVERWRITE = 1 << 9, MU_MSG_OPTION_USE_EXISTING = 1 << 10, /* recurse into submessages */ MU_MSG_OPTION_RECURSE_RFC822 = 1 << 11 }; typedef enum _MuMsgOptions MuMsgOptions; /** * create a new MuMsg* instance which parses a message and provides * read access to its properties; call mu_msg_unref when done with it. * * @param path full path to an email message file * @param mdir the maildir for this message; ie, if the path is * ~/Maildir/foo/bar/cur/msg, the maildir would be foo/bar; you can * pass NULL for this parameter, in which case some maildir-specific * information is not available. * @param err receive error information (MU_ERROR_FILE or * MU_ERROR_GMIME), or NULL. There will only be err info if the * function returns NULL * * @return a new MuMsg instance or NULL in case of error; call * mu_msg_unref when done with this message */ MuMsg *mu_msg_new_from_file (const char* filepath, const char *maildir, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * create a new MuMsg* instance based on a Xapian::Document * * @param store a MuStore ptr * @param doc a ptr to a Xapian::Document (but cast to XapianDocument, * because this is C not C++). MuMsg takes _ownership_ of this pointer; * don't touch it afterwards * @param err receive error information, or NULL. There * will only be err info if the function returns NULL * * @return a new MuMsg instance or NULL in case of error; call * mu_msg_unref when done with this message */ MuMsg *mu_msg_new_from_doc (XapianDocument* doc, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * if we don't have a message file yet (because this message is * database-backed), load it. * * @param msg a MuMsg * @param err receives error information * * @return TRUE if this succceeded, FALSE in case of error */ gboolean mu_msg_load_msg_file (MuMsg *msg, GError **err); /** * close the file-backend, if any; this function is for the use case * where you have a large amount of messages where you need some * file-backed field (body or attachments). If you don't close the * file-backend after retrieving the desired field, you'd quickly run * out of file descriptors. If this message does not have a * file-backend, do nothing. * * @param msg a message object */ void mu_msg_unload_msg_file (MuMsg *msg); /** * increase the reference count for this message * * @param msg a message * * @return the message with its reference count increased, or NULL in * case of error. */ MuMsg *mu_msg_ref (MuMsg *msg); /** * decrease the reference count for this message. if the reference * count reaches 0, the message will be destroyed. * * @param msg a message */ void mu_msg_unref (MuMsg *msg); /** * cache the values from the backend (file or db), so we don't the * backend anymore * * @param self a message */ void mu_msg_cache_values (MuMsg *self); /** * get the plain text body of this message * * @param msg a valid MuMsg* instance * @param opts options for getting the body * * @return the plain text body or NULL in case of error or if there is no * such body. the returned string should *not* be modified or freed. * The returned data is in UTF8 or NULL. */ const char* mu_msg_get_body_text (MuMsg *msg, MuMsgOptions opts); /** * get the html body of this message * * @param msg a valid MuMsg* instance * @param opts options for getting the body * * @return the html body or NULL in case of error or if there is no * such body. the returned string should *not* be modified or freed. */ const char* mu_msg_get_body_html (MuMsg *msgMu, MuMsgOptions opts); /** * get the sender (From:) of this message * * @param msg a valid MuMsg* instance * * @return the sender of this Message or NULL in case of error or if there * is no sender. the returned string should *not* be modified or freed. */ const char* mu_msg_get_from (MuMsg *msg); /** * get the recipients (To:) of this message * * @param msg a valid MuMsg* instance * * @return the sender of this Message or NULL in case of error or if there * are no recipients. the returned string should *not* be modified or freed. */ const char* mu_msg_get_to (MuMsg *msg); /** * get the carbon-copy recipients (Cc:) of this message * * @param msg a valid MuMsg* instance * * @return the Cc: recipients of this Message or NULL in case of error or if * there are no such recipients. the returned string should *not* be modified * or freed. */ const char* mu_msg_get_cc (MuMsg *msg); /** * get the blind carbon-copy recipients (Bcc:) of this message; this * field usually only appears in outgoing messages * * @param msg a valid MuMsg* instance * * @return the Bcc: recipients of this Message or NULL in case of * error or if there are no such recipients. the returned string * should *not* be modified or freed. */ const char* mu_msg_get_bcc (MuMsg *msg); /** * get the file system path of this message * * @param msg a valid MuMsg* instance * * @return the path of this Message or NULL in case of error. * the returned string should *not* be modified or freed. */ const char* mu_msg_get_path (MuMsg *msg); /** * get the maildir this message lives in; ie, if the path is * ~/Maildir/foo/bar/cur/msg, the maildir would be foo/bar * * @param msg a valid MuMsg* instance * * @return the maildir requested or NULL in case of error. The returned * string should *not* be modified or freed. */ const char* mu_msg_get_maildir (MuMsg *msg); /** * get the subject of this message * * @param msg a valid MuMsg* instance * * @return the subject of this Message or NULL in case of error or if there * is no subject. the returned string should *not* be modified or freed. */ const char* mu_msg_get_subject (MuMsg *msg); /** * get the Message-Id of this message * * @param msg a valid MuMsg* instance * * @return the Message-Id of this message (without the enclosing <>), * or a fake message-id for messages that don't have them, or NULL in * case of error. */ const char* mu_msg_get_msgid (MuMsg *msg); /** * get the mailing list for a message, i.e. the mailing-list * identifier in the List-Id header. * * @param msg a valid MuMsg* instance * * @return the mailing list id for this message (without the enclosing <>) * or NULL in case of error or if there is none. the returned string * should *not* be modified or freed. */ const char* mu_msg_get_mailing_list (MuMsg *msg); /** * get any arbitrary header from this message * * @param msg a valid MuMsg* instance * @header the header requested * * @return the header requested or NULL in case of error or if there * is no such header. the returned string should *not* be modified or freed. */ const char* mu_msg_get_header (MuMsg *msg, const char* header); /** * get the message date/time (the Date: field) as time_t, using UTC * * @param msg a valid MuMsg* instance * * @return message date/time or 0 in case of error or if there * is no such header. */ time_t mu_msg_get_date (MuMsg *msg); /** * get the flags for this message * * @param msg valid MuMsg* instance * * @return the file/content flags as logically OR'd #MuMsgFlags or 0 * if there are none. Non-standard flags are ignored. */ MuFlags mu_msg_get_flags (MuMsg *msg); /** * get the file size in bytes of this message * * @param msg a valid MuMsg* instance * * @return the filesize */ size_t mu_msg_get_size (MuMsg *msg); /** * get some field value as string * * @param msg a valid MuMsg instance * @param field the field to retrieve; it must be a string-typed field * * @return a string that should not be freed */ const char* mu_msg_get_field_string (MuMsg *msg, MuMsgFieldId mfid); /** * get some field value as string-list * * @param msg a valid MuMsg instance * @param field the field to retrieve; it must be a string-list-typed field * * @return a list that should not be freed */ const GSList* mu_msg_get_field_string_list (MuMsg *self, MuMsgFieldId mfid); /** * get some field value as string * * @param msg a valid MuMsg instance * @param field the field to retrieve; it must be a numeric field * * @return a string that should not be freed */ gint64 mu_msg_get_field_numeric (MuMsg *msg, MuMsgFieldId mfid); /** * get the message priority for this message (MU_MSG_PRIO_LOW, * MU_MSG_PRIO_NORMAL or MU_MSG_PRIO_HIGH) the X-Priority, * X-MSMailPriority, Importance and Precedence header are checked, in * that order. if no known or explicit priority is set, * MU_MSG_PRIO_NORMAL is assumed * * @param msg a valid MuMsg* instance * * @return the message priority (!= 0) or 0 in case of error */ MuMsgPrio mu_msg_get_prio (MuMsg *msg); /** * get the timestamp (mtime) for the file containing this message * * @param msg a valid MuMsg* instance * * @return the timestamp or 0 in case of error */ time_t mu_msg_get_timestamp (MuMsg *msg); /** * get a specific header from the message. This value will _not_ be * cached * * @param self a MuMsg instance * @param header a specific header (like 'X-Mailer' or 'Organization') * * @return a header string which is valid as long as this MuMsg is */ const char* mu_msg_get_header (MuMsg *self, const char *header); /** * get the list of references (consisting of both the References and * In-Reply-To fields), with the oldest first and the direct parent as * the last one. Note, any reference (message-id) will appear at most * once, duplicates are filtered out. * * @param msg a valid MuMsg * * @return a list with the references for this msg. Don't modify/free */ const GSList* mu_msg_get_references (MuMsg *msg); /** * get the list of tags (ie., X-Label) * * @param msg a valid MuMsg * * @return a list with the tags for this msg. Don't modify/free */ const GSList* mu_msg_get_tags (MuMsg *self); /** * compare two messages for sorting * * @param m1 a message * @param m2 another message * @param mfid the message to use for the comparison * * @return negative if m1 is smaller, positive if m1 is smaller, 0 if * they are equal */ int mu_msg_cmp (MuMsg *m1, MuMsg *m2, MuMsgFieldId mfid); /** * check whether there there's a readable file behind this message * * @param self a MuMsg* * * @return TRUE if the message file is readable, FALSE otherwise */ gboolean mu_msg_is_readable (MuMsg *self); struct _MuMsgIterThreadInfo; /** * convert the msg to a Lisp symbolic expression (for further processing in * e.g. emacs) * * @param msg a valid message * @param docid the docid for this message, or 0 * @param ti thread info for the current message, or NULL * @param opts, bitwise OR'ed; * - MU_MSG_OPTION_HEADERS_ONLY: only include message fields which can be * obtained from the database (this is much faster if the MuMsg is * database-backed, so no file needs to be opened) * - MU_MSG_OPTION_EXTRACT_IMAGES: extract image attachments as temporary * files and include links to those in the sexp * and for message parts: * MU_MSG_OPTION_CHECK_SIGNATURES: check signatures * MU_MSG_OPTION_AUTO_RETRIEVE_KEY: attempt to retrieve keys online * MU_MSG_OPTION_USE_AGENT: attempt to use GPG-agent * MU_MSG_OPTION_USE_PKCS7: attempt to use PKCS (instead of gpg) * * @return a string with the sexp (free with g_free) or NULL in case of error */ char* mu_msg_to_sexp (MuMsg *msg, unsigned docid, const struct _MuMsgIterThreadInfo *ti, MuMsgOptions ops) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * move a message to another maildir; note that this does _not_ update * the database * * @param msg a message with an existing file system path in an actual * maildir * @param maildir the subdir where the message should go, relative to * rootmaildir. e.g. "/archive" * @param flags to set for the target (influences the filename, path) * @param silently ignore the src=target case (return TRUE) * @param new_name whether to create a new unique name, or keep the * old one * @param err (may be NULL) may contain error information; note if the * function return FALSE, err is not set for all error condition * (ie. not for parameter error * * @return TRUE if it worked, FALSE otherwise */ gboolean mu_msg_move_to_maildir (MuMsg *msg, const char *maildir, MuFlags flags, gboolean ignore_dups, gboolean new_name, GError **err); enum _MuMsgContactType { /* Reply-To:? */ MU_MSG_CONTACT_TYPE_TO = 0, MU_MSG_CONTACT_TYPE_FROM, MU_MSG_CONTACT_TYPE_CC, MU_MSG_CONTACT_TYPE_BCC, MU_MSG_CONTACT_TYPE_REPLY_TO, MU_MSG_CONTACT_TYPE_NUM }; typedef guint MuMsgContactType; /* not a 'real' contact type */ #define MU_MSG_CONTACT_TYPE_ALL (MU_MSG_CONTACT_TYPE_NUM + 1) #define mu_msg_contact_type_is_valid(MCT)\ ((MCT) < MU_MSG_CONTACT_TYPE_NUM) struct _MuMsgContact { const char *name; /* Foo Bar */ const char *address; /* foo@bar.cuux */ MuMsgContactType type; /* MU_MSG_CONTACT_TYPE_{ TO, * CC, BCC, FROM, REPLY_TO} */ }; typedef struct _MuMsgContact MuMsgContact; /** * macro to get the name of a contact * * @param ct a MuMsgContact * * @return the name */ #define mu_msg_contact_name(ct) ((ct)->name) /** * macro to get the address of a contact * * @param ct a MuMsgContact * * @return the address */ #define mu_msg_contact_address(ct) ((ct)->address) /** * macro to get the contact type * * @param ct a MuMsgContact * * @return the contact type */ #define mu_msg_contact_type(ct) ((ct)->type) /** * callback function * * @param contact * @param user_data a user provided data pointer * * @return TRUE if we should continue the foreach, FALSE otherwise */ typedef gboolean (*MuMsgContactForeachFunc) (MuMsgContact* contact, gpointer user_data); /** * call a function for each of the contacts in a message; the order is: * from to cc bcc (of each there are zero or more) * * @param msg a valid MuMsgGMime* instance * @param func a callback function to call for each contact; when * the callback does not return TRUE, it won't be called again * @param user_data a user-provide pointer that will be passed to the callback * */ void mu_msg_contact_foreach (MuMsg *msg, MuMsgContactForeachFunc func, gpointer user_data); G_END_DECLS #endif /*__MU_MSG_H__*/ mu-0.9.18/lib/mu-runtime.h0000644000175000017500000000531212605152236012177 00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_RUNTIME_H__ #define __MU_RUNTIME_H__ #include #include G_BEGIN_DECLS /** * initialize the mu runtime system; initializes logging and other * systems. To uninitialize, use mu_runtime_uninit * * @param muhome path where to find the mu home directory (typicaly, ~/.mu) * @param name of the main program, ie. 'mu', 'mug' or * 'procmule'. this influences the name of the e.g. the logfile * * @return TRUE if succeeded, FALSE in case of error */ gboolean mu_runtime_init (const char *muhome, const char *name); /** * initialize the mu runtime system with comand line argument; this * will parse the command line assuming the parameters of the 'mu' * program. Initializes logging and other systems. To uninitialize, * use mu_runtime_uninit * * @param ptr to the param count (typically, argc) * @param ptr to the params (typically, argv) * @param name of the main program, ie. 'mu', 'mug' or * 'procmule'. this influences the name of the e.g. the logfile * * @return TRUE if succeeded, FALSE in case of error */ gboolean mu_runtime_init_from_cmdline (int *pargc, char ***pargv, const char *name); /** * free all resources * */ void mu_runtime_uninit (void); enum _MuRuntimePath { MU_RUNTIME_PATH_MUHOME, /* mu home path */ MU_RUNTIME_PATH_XAPIANDB, /* mu xapian db path */ MU_RUNTIME_PATH_BOOKMARKS, /* mu bookmarks file path */ MU_RUNTIME_PATH_CACHE, /* mu cache path */ MU_RUNTIME_PATH_LOG, /* mu path for log files */ MU_RUNTIME_PATH_CONTACTS, /* mu path to the contacts cache */ MU_RUNTIME_PATH_NUM }; typedef enum _MuRuntimePath MuRuntimePath; /** * get a file system path to some 'special' file or directory * * @return ma string which should be not be modified/freed, or NULL in * case of error. */ const char* mu_runtime_path (MuRuntimePath path); G_END_DECLS #endif /*__MU_RUNTIME_H__*/ mu-0.9.18/lib/mu-query.h0000644000175000017500000001007212747556356011702 00000000000000/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_QUERY_H__ #define __MU_QUERY_H__ #include #include #include #include G_BEGIN_DECLS struct _MuQuery; typedef struct _MuQuery MuQuery; /** * create a new MuQuery instance. * * @param store a MuStore object * @param err receives error information (if there is any); if * function returns non-NULL, err will _not_be set. err can be NULL * possble errors (err->code) are MU_ERROR_XAPIAN_DIR and * MU_ERROR_XAPIAN_NOT_UPTODATE * * @return a new MuQuery instance, or NULL in case of error. * when the instance is no longer needed, use mu_query_destroy * to free it */ MuQuery *mu_query_new (MuStore *store, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * destroy the MuQuery instance * * @param self a MuQuery instance, or NULL */ void mu_query_destroy (MuQuery *self); /** * get a version string for the database * * @param store a valid MuQuery * * @return the version string (free with g_free), or NULL in case of error */ char* mu_query_version (MuQuery *store) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; enum _MuQueryFlags { MU_QUERY_FLAG_NONE = 0, MU_QUERY_FLAG_DESCENDING = 1 << 0, /**< sort z->a */ MU_QUERY_FLAG_SKIP_UNREADABLE = 1 << 1, /**< skip unreadable msgs */ MU_QUERY_FLAG_SKIP_DUPS = 1 << 2, /**< skip duplicate msgs */ MU_QUERY_FLAG_INCLUDE_RELATED = 1 << 3, /**< include related msgs */ MU_QUERY_FLAG_THREADS = 1 << 4 /**< calculate threading info */ }; typedef int MuQueryFlags; /** * run a Xapian query; for the syntax, please refer to the mu-find * manpage, or http://xapian.org/docs/queryparser.html * * @param self a valid MuQuery instance * @param expr the search expression; use "" to match all messages * @param sortfield the field id to sort by or MU_MSG_FIELD_ID_NONE if * sorting is not desired * @param maxnum maximum number of search results to return, or <= 0 for * unlimited * @param flags bitwise OR'd flags to influence the query (see MuQueryFlags) * @param err receives error information (if there is any); if * function returns non-NULL, err will _not_be set. err can be NULL * possible error (err->code) is MU_ERROR_QUERY, * * @return a MuMsgIter instance you can iterate over, or NULL in * case of error */ MuMsgIter* mu_query_run (MuQuery *self, const char* expr, MuMsgFieldId sortfieldid, int maxnum, MuQueryFlags flags, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * get a string representation of the Xapian search query * * @param self a MuQuery instance * @param searchexpr a xapian search expression * @param err receives error information (if there is any); if * function returns non-NULL, err will _not_be set. err can be NULL * * @return the string representation of the xapian query, or NULL in case of * error; free the returned value with g_free */ char* mu_query_as_string (MuQuery *self, const char* searchexpr, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * pre-process the query; this function is useful mainly for debugging mu * * @param query a query string * * @return a pre-processed query, free it with g_free */ char* mu_query_preprocess (const char *query, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS #endif /*__MU_QUERY_H__*/ mu-0.9.18/lib/mu-log.h0000644000175000017500000000531112605152236011274 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_LOG_H__ #define __MU_LOG_H__ #include /* mu log is the global logging system */ G_BEGIN_DECLS enum _MuLogOptions { MU_LOG_OPTIONS_NONE = 0, /* when size of log file > MU_MAX_LOG_FILE_SIZE, move the log * file to .old and start a new one. The .old file will * overwrite existing files of that name */ MU_LOG_OPTIONS_BACKUP = 1 << 1, /* quiet: don't log non-errors to stdout/stderr */ MU_LOG_OPTIONS_QUIET = 1 << 2, /* should lines be preceded by \n? useful when errors come * during indexing */ MU_LOG_OPTIONS_NEWLINE = 1 << 3, /* color in output (iff output is to a tty) */ MU_LOG_OPTIONS_COLOR = 1 << 4, /* log everything to stderr */ MU_LOG_OPTIONS_STDERR = 1 << 5, /* debug: debug include debug-level information */ MU_LOG_OPTIONS_DEBUG = 1 << 6 }; typedef enum _MuLogOptions MuLogOptions; /** * write logging information to a log file * * @param full path to the log file (does not have to exist yet, but * it's directory must) * @param opts logging options * * @return TRUE if initialization succeeds, FALSE otherwise */ gboolean mu_log_init (const char *logfile, MuLogOptions opts) G_GNUC_WARN_UNUSED_RESULT; /** * be silent except for runtime errors, which will be written to * stderr. * * @return TRUE if initialization succeeds, FALSE otherwise */ gboolean mu_log_init_silence (void) G_GNUC_WARN_UNUSED_RESULT; /** * unitialize the logging system, and free all resources */ void mu_log_uninit (void); /** * set logging options, a logical-OR'd value of MuLogOptions * * @param opts the options (logically OR'd) */ void mu_log_options_set (MuLogOptions opts); /** * get logging options, a logical-OR'd value of MuLogOptions * * @param opts the options (logically OR'd) */ MuLogOptions mu_log_options_get (void); G_END_DECLS #endif /*__MU_LOG_H__*/ mu-0.9.18/lib/mu-store.cc0000644000175000017500000000520213020504332011772 00000000000000/* -*-mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8-*- */ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include #include "mu-store.h" #include "mu-store-priv.hh" /* _MuStore */ #include "mu-msg.h" #include "mu-msg-part.h" #include "mu-store.h" #include "mu-util.h" #include "mu-str.h" #include "mu-date.h" #include "mu-flags.h" #include "mu-contacts.h" MuStore* mu_store_ref (MuStore *store) { g_return_val_if_fail (store, NULL); store->ref(); return store; } MuStore* mu_store_unref (MuStore *store) { g_return_val_if_fail (store, NULL); if (store->unref() == 0) { try { delete store; } MU_XAPIAN_CATCH_BLOCK; } return NULL; } static char* xapian_get_metadata (const gchar *xpath, const gchar *key) { g_return_val_if_fail (xpath, NULL); g_return_val_if_fail (key, NULL); if (access(xpath, F_OK) != 0) { g_warning ("cannot access %s: %s", xpath, strerror(errno)); return NULL; } try { Xapian::Database db (xpath); const std::string val(db.get_metadata (key)); return val.empty() ? NULL : g_strdup (val.c_str()); } MU_XAPIAN_CATCH_BLOCK; return NULL; } char* mu_store_database_version (const gchar *xpath) { g_return_val_if_fail (xpath, NULL); return xapian_get_metadata (xpath, MU_STORE_VERSION_KEY); } gboolean mu_store_database_is_locked (const gchar *xpath) { g_return_val_if_fail (xpath, FALSE); try { Xapian::WritableDatabase db (xpath, Xapian::DB_OPEN); } catch (const Xapian::DatabaseLockError& xer) { return TRUE; } catch (const Xapian::Error &xer) { g_warning ("%s: error: %s", __func__, xer.get_msg().c_str()); } return FALSE; } void mu_store_set_my_addresses (MuStore *store, const char **my_addresses) { g_return_if_fail (store); store->set_my_addresses (my_addresses); } mu-0.9.18/lib/mu-msg-crypto.c0000644000175000017500000002734313020504332012611 00000000000000/* ** Copyright (C) 2012-2016 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include "mu-msg.h" #include "mu-msg-priv.h" #include "mu-msg-part.h" #include "mu-date.h" #include #include #define CALLBACK_DATA "callback-data" struct _CallbackData { MuMsgPartPasswordFunc pw_func; gpointer user_data; }; typedef struct _CallbackData CallbackData; static gboolean password_requester (GMimeCryptoContext *ctx, const char *user_id, const char* prompt_ctx, gboolean reprompt, GMimeStream *response, GError **err) { CallbackData *cbdata; gchar *password; ssize_t written; cbdata = g_object_get_data (G_OBJECT(ctx), CALLBACK_DATA); if (!cbdata || !cbdata->pw_func) return FALSE; password = cbdata->pw_func (user_id, prompt_ctx, reprompt, cbdata->user_data); if (!password) { mu_util_g_set_error (err, MU_ERROR_CRYPTO, "failed to get password"); return FALSE; } written = g_mime_stream_write_string (response, password); if (written != -1) written = g_mime_stream_write_string (response, "\n"); if (written == -1) mu_util_g_set_error (err, MU_ERROR_CRYPTO, "writing password to mime stream failed"); /* it seems that GMime tries to flush the fd; however, this * does not work for pipes/sockets, causing getting a password * to fail. * * I have reported this, and it has been fixed now: * * http://git.gnome.org/browse/gmime/commit/ * ?id=bda4834d3d9a1fbefb6d97edfef2bc1da9357f58 * * however, it may take a while before everybody has this * version of GMime (ie. version > 2.6.10) * */ memset (password, 0, strlen(password)); g_free (password); return written != -1 ? TRUE : FALSE; } static char* dummy_password_func (const char *user_id, const char *prompt_ctx, gboolean reprompt, gpointer user_data) { g_print ("password requested for %s (%s) %s\n", user_id, prompt_ctx, reprompt ? "again" : ""); return NULL; } static char* get_gpg (GError **err) { char *path; const char *envpath; if ((envpath = g_getenv ("MU_GPG_PATH"))) { if (access (envpath, X_OK) != 0) { mu_util_g_set_error ( err, MU_ERROR, "'%s': not a valid gpg path: %s", envpath, strerror (errno)); return NULL; } return g_strdup (envpath); } if (!(path = g_find_program_in_path ("gpg2")) && !(path = g_find_program_in_path ("gpg"))) { mu_util_g_set_error (err, MU_ERROR, "gpg/gpg2 not found"); return NULL; } else return path; } static GMimeCryptoContext* get_gpg_crypto_context (MuMsgOptions opts, GError **err) { GMimeCryptoContext *cctx; char *gpg; cctx = NULL; if (!(gpg = get_gpg (err))) return NULL; cctx = g_mime_gpg_context_new ( (GMimePasswordRequestFunc)password_requester, gpg); g_free (gpg); if (!cctx) { mu_util_g_set_error (err, MU_ERROR, "failed to get GPG crypto context"); return NULL; } /* always try to use the agent */ g_mime_gpg_context_set_use_agent (GMIME_GPG_CONTEXT(cctx), TRUE); g_mime_gpg_context_set_auto_key_retrieve (GMIME_GPG_CONTEXT(cctx), opts & MU_MSG_OPTION_AUTO_RETRIEVE ? TRUE:FALSE); return cctx; } static GMimeCryptoContext* get_crypto_context (MuMsgOptions opts, MuMsgPartPasswordFunc password_func, gpointer user_data, GError **err) { CallbackData *cbdata; GMimeCryptoContext *cctx; cctx = get_gpg_crypto_context (opts, err); if (!cctx) return NULL; /* use gobject to pass data to the callback func */ cbdata = g_new0 (CallbackData, 1); cbdata->pw_func = password_func ? password_func : dummy_password_func; cbdata->user_data = user_data; g_object_set_data_full (G_OBJECT(cctx), CALLBACK_DATA, cbdata, (GDestroyNotify)g_free); return cctx; } static const char* get_pubkey_algo_name (GMimePubKeyAlgo algo) { switch (algo) { case GMIME_PUBKEY_ALGO_DEFAULT: return "default"; case GMIME_PUBKEY_ALGO_RSA: return "RSA"; case GMIME_PUBKEY_ALGO_RSA_E: return "RSA (encryption only)"; case GMIME_PUBKEY_ALGO_RSA_S: return "RSA (signing only)"; case GMIME_PUBKEY_ALGO_ELG_E: return "ElGamal (encryption only)"; case GMIME_PUBKEY_ALGO_DSA: return "DSA"; case GMIME_PUBKEY_ALGO_ELG: return "ElGamal"; default: return "unknown pubkey algorithm"; } } static const gchar* get_digestkey_algo_name (GMimeDigestAlgo algo) { switch (algo) { case GMIME_DIGEST_ALGO_DEFAULT: return "default"; case GMIME_DIGEST_ALGO_MD5: return "MD5"; case GMIME_DIGEST_ALGO_SHA1: return "SHA-1"; case GMIME_DIGEST_ALGO_RIPEMD160: return "RIPEMD160"; case GMIME_DIGEST_ALGO_MD2: return "MD2"; case GMIME_DIGEST_ALGO_TIGER192: return "TIGER-192"; case GMIME_DIGEST_ALGO_HAVAL5160: return "HAVAL-5-160"; case GMIME_DIGEST_ALGO_SHA256: return "SHA-256"; case GMIME_DIGEST_ALGO_SHA384: return "SHA-384"; case GMIME_DIGEST_ALGO_SHA512: return "SHA-512"; case GMIME_DIGEST_ALGO_SHA224: return "SHA-224"; case GMIME_DIGEST_ALGO_MD4: return "MD4"; default: return "unknown digest algorithm"; } } /* get data from the 'certificate' */ static char* get_cert_data (GMimeCertificate *cert) { const char /**email,*/ *name, *digest_algo, *pubkey_algo, *keyid, *trust; /* email = g_mime_certificate_get_email (cert); */ name = g_mime_certificate_get_name (cert); keyid = g_mime_certificate_get_key_id (cert); digest_algo = get_digestkey_algo_name (g_mime_certificate_get_digest_algo (cert)); pubkey_algo = get_pubkey_algo_name (g_mime_certificate_get_pubkey_algo (cert)); switch (g_mime_certificate_get_trust (cert)) { case GMIME_CERTIFICATE_TRUST_NONE: trust = "none"; break; case GMIME_CERTIFICATE_TRUST_NEVER: trust = "never"; break; case GMIME_CERTIFICATE_TRUST_UNDEFINED: trust = "undefined"; break; case GMIME_CERTIFICATE_TRUST_MARGINAL: trust = "marginal"; break; case GMIME_CERTIFICATE_TRUST_FULLY: trust = "full"; break; case GMIME_CERTIFICATE_TRUST_ULTIMATE: trust = "ultimate"; break; default: g_return_val_if_reached (NULL); } return g_strdup_printf ( "signer:%s, key:%s (%s,%s), trust:%s", name ? name : "?", /* email ? email : "?", */ keyid, pubkey_algo, digest_algo, trust); } /* get a human-readable report about the signature */ static char* get_verdict_report (GMimeSignature *msig) { time_t t; const char *status, *created, *expires; gchar *certdata, *report; switch (g_mime_signature_get_status (msig)) { case GMIME_SIGNATURE_STATUS_GOOD: status = "good"; break; case GMIME_SIGNATURE_STATUS_ERROR: status = "error"; break; case GMIME_SIGNATURE_STATUS_BAD: status = "bad"; break; default: g_return_val_if_reached (NULL); } t = g_mime_signature_get_created (msig); created = (t == 0 || t == (time_t)-1) ? "?" : mu_date_str_s ("%x", t); t = g_mime_signature_get_expires (msig); expires = (t == 0 || t == (time_t)-1) ? "?" : mu_date_str_s ("%x", t); certdata = get_cert_data (g_mime_signature_get_certificate (msig)); report = g_strdup_printf ("%s; created:%s, expires:%s, %s", status, created, expires, certdata ? certdata : "?"); g_free (certdata); return report; } static MuMsgPartSigStatusReport* get_status_report (GMimeSignatureList *sigs) { int i; MuMsgPartSigStatus status; MuMsgPartSigStatusReport *status_report; char *report; status = MU_MSG_PART_SIG_STATUS_GOOD; /* let's start positive! */ for (i = 0, report = NULL; i != g_mime_signature_list_length (sigs); ++i) { GMimeSignature *msig; GMimeSignatureStatus sigstat; gchar *rep; msig = g_mime_signature_list_get_signature (sigs, i); sigstat = g_mime_signature_get_status (msig); switch (sigstat) { case GMIME_SIGNATURE_STATUS_GOOD: break; case GMIME_SIGNATURE_STATUS_ERROR: status = MU_MSG_PART_SIG_STATUS_ERROR; break; case GMIME_SIGNATURE_STATUS_BAD: status = MU_MSG_PART_SIG_STATUS_BAD; break; default: g_return_val_if_reached (NULL); } rep = get_verdict_report (msig); report = g_strdup_printf ("%s%s%d: %s", report ? report : "", report ? "; " : "", i + 1, rep); g_free (rep); } status_report = g_slice_new (MuMsgPartSigStatusReport); status_report->verdict = status; status_report->report = report; return status_report; } void mu_msg_part_sig_status_report_destroy (MuMsgPartSigStatusReport *report) { if (!report) return; g_free ((char*)report->report); g_slice_free (MuMsgPartSigStatusReport, report); } static inline void tag_with_sig_status(GObject *part, MuMsgPartSigStatusReport *report) { g_object_set_data_full (part, SIG_STATUS_REPORT, report, (GDestroyNotify)mu_msg_part_sig_status_report_destroy); } void mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts, GError **err) { /* the signature status */ MuMsgPartSigStatusReport *report; GMimeCryptoContext *ctx; GMimeSignatureList *sigs; g_return_if_fail (GMIME_IS_MULTIPART_SIGNED(sig)); ctx = get_crypto_context (opts, NULL, NULL, err); if (!ctx) { mu_util_g_set_error (err, MU_ERROR_CRYPTO, "failed to get crypto context"); return; } sigs = g_mime_multipart_signed_verify (sig, ctx, err); g_object_unref (ctx); if (!sigs) { if (err && !*err) mu_util_g_set_error (err, MU_ERROR_CRYPTO, "verification failed"); return; } report = get_status_report (sigs); g_mime_signature_list_clear (sigs); /* tag this part with the signature status check */ tag_with_sig_status(G_OBJECT(sig), report); } static inline void check_decrypt_result(GMimeMultipartEncrypted *part, GMimeDecryptResult *res, GError **err) { GMimeSignatureList *sigs; MuMsgPartSigStatusReport *report; if (res) { /* Check if the decrypted part had any embed signatures */ sigs = res->signatures; if (sigs) { report = get_status_report (sigs); g_mime_signature_list_clear (sigs); /* tag this part with the signature status check */ tag_with_sig_status(G_OBJECT(part), report); } else { if (err && !*err) mu_util_g_set_error (err, MU_ERROR_CRYPTO, "verification failed"); } g_object_unref (res); } } GMimeObject* /* this is declared in mu-msg-priv.h */ mu_msg_crypto_decrypt_part (GMimeMultipartEncrypted *enc, MuMsgOptions opts, MuMsgPartPasswordFunc func, gpointer user_data, GError **err) { GMimeObject *dec; GMimeCryptoContext *ctx; GMimeDecryptResult *res; g_return_val_if_fail (GMIME_IS_MULTIPART_ENCRYPTED(enc), NULL); ctx = get_crypto_context (opts, func, user_data, err); if (!ctx) { mu_util_g_set_error (err, MU_ERROR_CRYPTO, "failed to get crypto context"); return NULL; } /* at the time of writing, there is a small leak in * g_mime_multipart_encrypted_decrypt; I've notified its * author and it has been fixed 2012-09-12: * http://git.gnome.org/browse/gmime/commit/ * ?id=1bacd43b50d91bd03a4ae1dc9f46f5783dee61b1 * (or GMime > 2.6.10) * */ res = NULL; dec = g_mime_multipart_encrypted_decrypt (enc, ctx, &res, err); g_object_unref (ctx); check_decrypt_result(enc, res, err); if (!dec) { if (err && !*err) mu_util_g_set_error (err, MU_ERROR_CRYPTO, "decryption failed"); return NULL; } return dec; } mu-0.9.18/lib/mu-msg-doc.h0000644000175000017500000000550212605152236012046 00000000000000/* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_MSG_DOC_H__ #define __MU_MSG_DOC_H__ #include #include /* for XapianDocument */ G_BEGIN_DECLS struct _MuMsgDoc; typedef struct _MuMsgDoc MuMsgDoc; /** * create a new MuMsgDoc instance * * @param doc a Xapian::Document* (you'll need to cast the * Xapian::Document* to XapianDocument*, because only C (not C++) is * allowed in this header file. MuMsgDoc takes _ownership_ of this pointer; * don't touch it afterwards * @param err receives error info, or NULL * * @return a new MuMsgDoc instance (free with mu_msg_doc_destroy), or * NULL in case of error. */ MuMsgDoc* mu_msg_doc_new (XapianDocument *doc, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * destroy a MuMsgDoc instance -- free all the resources. Note, after * destroying, any strings returned from mu_msg_doc_get_str_field with * do_free==FALSE are no longer valid * * @param self a MuMsgDoc instance */ void mu_msg_doc_destroy (MuMsgDoc *self); /** * get a string parameter from the msgdoc * * @param self a MuMsgDoc instance * @param mfid a MuMsgFieldId for a string field * * @return a string for the given field (see do_free), or NULL in case of error. * free with g_free */ gchar* mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid) G_GNUC_WARN_UNUSED_RESULT; /** * get a string-list parameter from the msgdoc * * @param self a MuMsgDoc instance * @param mfid a MuMsgFieldId for a string-list field * * @return a list for the given field (see do_free), or NULL in case * of error. free with mu_str_free_list */ GSList* mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid) G_GNUC_WARN_UNUSED_RESULT; /** * * get a numeric parameter from the msgdoc * * @param self a MuMsgDoc instance * @param mfid a MuMsgFieldId for a numeric field * * @return the numerical value, or -1 in case of error. You'll need to * cast this value to the actual type (e.g. time_t for MU_MSG_FIELD_ID_DATE) */ gint64 mu_msg_doc_get_num_field (MuMsgDoc *self, MuMsgFieldId mfid); G_END_DECLS #endif /*__MU_MSG_DOC_H__*/ mu-0.9.18/lib/mu-msg-fields.h0000644000175000017500000002017113020504332012534 00000000000000/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_FIELDS_H__ #define __MU_MSG_FIELDS_H__ #include G_BEGIN_DECLS /* don't change the order, add new types at the end, as these numbers * are used in the database */ enum _MuMsgFieldId { /* first all the string-based ones */ MU_MSG_FIELD_ID_BCC = 0, MU_MSG_FIELD_ID_BODY_HTML, MU_MSG_FIELD_ID_BODY_TEXT, MU_MSG_FIELD_ID_CC, MU_MSG_FIELD_ID_EMBEDDED_TEXT, MU_MSG_FIELD_ID_FILE, MU_MSG_FIELD_ID_FROM, MU_MSG_FIELD_ID_MAILDIR, MU_MSG_FIELD_ID_MIME, /* mime-type */ MU_MSG_FIELD_ID_MSGID, MU_MSG_FIELD_ID_PATH, MU_MSG_FIELD_ID_SUBJECT, MU_MSG_FIELD_ID_TO, MU_MSG_FIELD_ID_UID, /* special, generated from path */ /* string list items... */ MU_MSG_FIELD_ID_REFS, MU_MSG_FIELD_ID_TAGS, /* then the numerical ones */ MU_MSG_FIELD_ID_DATE, MU_MSG_FIELD_ID_FLAGS, MU_MSG_FIELD_ID_PRIO, MU_MSG_FIELD_ID_SIZE, /* add new ones here... */ MU_MSG_FIELD_ID_MAILING_LIST, /* mailing list */ MU_MSG_FIELD_ID_THREAD_ID, MU_MSG_FIELD_ID_NUM }; typedef guint8 MuMsgFieldId; /* some specials... */ static const MuMsgFieldId MU_MSG_FIELD_ID_NONE = (MuMsgFieldId)-1; #define MU_MSG_STRING_FIELD_ID_NUM (MU_MSG_FIELD_ID_UID + 1) /* this is a shortcut for To/From/Cc/Bcc in queries; handled specially * in mu-query.cc and mu-str.c */ #define MU_MSG_FIELD_PSEUDO_CONTACT "contact" /* this is a shortcut for To/Cc/Bcc in queries; handled specially in * mu-query.cc and mu-str.c */ #define MU_MSG_FIELD_PSEUDO_RECIP "recip" #define mu_msg_field_id_is_valid(MFID) \ ((MFID) < MU_MSG_FIELD_ID_NUM) /* don't change the order, add new types at the end (before _NUM)*/ enum _MuMsgFieldType { MU_MSG_FIELD_TYPE_STRING, MU_MSG_FIELD_TYPE_STRING_LIST, MU_MSG_FIELD_TYPE_BYTESIZE, MU_MSG_FIELD_TYPE_TIME_T, MU_MSG_FIELD_TYPE_INT, MU_MSG_FIELD_TYPE_NUM }; typedef guint8 MuMsgFieldType; static const MuMsgFieldType MU_MSG_FIELD_TYPE_NONE = (MuMsgFieldType)-1; typedef void (*MuMsgFieldForeachFunc) (MuMsgFieldId id, gconstpointer data); /** * iterator over all possible message fields * * @param func a function called for each field * @param data a user data pointer passed the callback function */ void mu_msg_field_foreach (MuMsgFieldForeachFunc func, gconstpointer data); /** * get the name of the field -- this a name that can be use in queries, * ie. 'subject:foo', with 'subject' being the name * * @param id a MuMsgFieldId * * @return the name of the field as a constant string, or * NULL if the field is unknown */ const char* mu_msg_field_name (MuMsgFieldId id) G_GNUC_PURE; /** * get the shortcut of the field -- this a shortcut that can be use in * queries, ie. 's:foo', with 's' meaning 'subject' being the name * * @param id a MuMsgFieldId * * @return the shortcut character, or 0 if the field is unknown */ char mu_msg_field_shortcut (MuMsgFieldId id) G_GNUC_PURE; /** * get the xapian prefix of the field -- that is, the prefix used in * the Xapian database to identify the field * * @param id a MuMsgFieldId * * @return the xapian prefix char or 0 if the field is unknown */ char mu_msg_field_xapian_prefix (MuMsgFieldId id) G_GNUC_PURE; /** * get the type of the field (string, size, time etc.) * * @param field a MuMsgField * * @return the type of the field (a #MuMsgFieldType), or * MU_MSG_FIELD_TYPE_NONE if it is not found */ MuMsgFieldType mu_msg_field_type (MuMsgFieldId id) G_GNUC_PURE; /** * is the field a string? * * @param id a MuMsgFieldId * * @return TRUE if the field a string, FALSE otherwise */ #define mu_msg_field_is_string(MFID)\ (mu_msg_field_type((MFID))==MU_MSG_FIELD_TYPE_STRING?TRUE:FALSE) /** * is the field a string-list? * * @param id a MuMsgFieldId * * @return TRUE if the field a string-list, FALSE otherwise */ #define mu_msg_field_is_string_list(MFID)\ (mu_msg_field_type((MFID))==MU_MSG_FIELD_TYPE_STRING_LIST?TRUE:FALSE) /** * is the field numeric (has type MU_MSG_FIELD_TYPE_(BYTESIZE|TIME_T|INT))? * * @param id a MuMsgFieldId * * @return TRUE if the field is numeric, FALSE otherwise */ gboolean mu_msg_field_is_numeric (MuMsgFieldId id) G_GNUC_PURE; /** * whether the field value should be cached (in MuMsg) -- we cache * values so we can use the MuMsg without needing to keep the * underlying data source (the GMimeMessage or the database ptr) alive * in practice, the fields we *don't* cache are the message body * (html, txt), because they take too much memory */ gboolean mu_msg_field_is_cacheable (MuMsgFieldId id) G_GNUC_PURE; /** * is the field Xapian-indexable? That is, should this field be * indexed in the in the Xapian database, so we can use the all the * phrasing, stemming etc. magic * * @param id a MuMsgFieldId * * @return TRUE if the field is Xapian-enabled, FALSE otherwise */ gboolean mu_msg_field_xapian_index (MuMsgFieldId id) G_GNUC_PURE; /** * should this field be stored as a xapian term? * * @param id a MuMsgFieldId * * @return TRUE if the field is Xapian-enabled, FALSE otherwise */ gboolean mu_msg_field_xapian_term (MuMsgFieldId id) G_GNUC_PURE; /** * should this field be stored as a xapian value? * * @param field a MuMsgField * * @return TRUE if the field is Xapian-enabled, FALSE otherwise */ gboolean mu_msg_field_xapian_value (MuMsgFieldId id) G_GNUC_PURE; /** * whether we should use add_boolean_prefix (see Xapian documentation) * for this field in queries. Used in mu-query.cc * * @param id a MuMsgFieldId * * @return TRUE if this field wants add_boolean_prefix, FALSE * otherwise */ gboolean mu_msg_field_uses_boolean_prefix (MuMsgFieldId id) G_GNUC_PURE; /** * should this field be escaped for xapian? in practice, should * word-breaking chars be replaced with '_'? Also, flatten accents, * downcase? * * @param field a MuMsgField * * @return TRUE if the field is to be preprocessed, FALSE otherwise */ gboolean mu_msg_field_preprocess (MuMsgFieldId id) G_GNUC_PURE; /** * is this a range-field? ie. date, or size * * @param id a MuMsgField * * @return TRUE if this field is a range field, FALSE otherwise */ gboolean mu_msg_field_is_range_field (MuMsgFieldId id) G_GNUC_PURE; /** * should this field be stored as contact information? This means that * e-mail address will be stored as terms, and names will be indexed * * @param id a MuMsgFieldId * * @return TRUE if the field should be stored as contact information, * FALSE otherwise */ gboolean mu_msg_field_xapian_contact (MuMsgFieldId id) G_GNUC_PURE; /** * is the field gmime-enabled? That is, can be field be retrieved * using GMime? * * @param id a MuMsgFieldId * * @return TRUE if the field is Gmime-enabled, FALSE otherwise */ gboolean mu_msg_field_gmime (MuMsgFieldId id) G_GNUC_PURE; /** * get the corresponding MuMsgField for a name (as in mu_msg_field_name) * * @param str a name * @param err, if TRUE, when the shortcut is not found, will issue a * g_critical warning * * @return a MuMsgField, or NULL if it could not be found */ MuMsgFieldId mu_msg_field_id_from_name (const char* str, gboolean err) G_GNUC_PURE; /** * get the corresponding MuMsgField for a shortcut (as in mu_msg_field_shortcut) * * @param kar a shortcut character * @param err, if TRUE, when the shortcut is not found, will issue a * g_critical warning * * @return a MuMsgField, or NULL if it could not be found */ MuMsgFieldId mu_msg_field_id_from_shortcut (char kar, gboolean err) G_GNUC_PURE; G_END_DECLS #endif /*__MU_MSG_FIELDS_H__*/ mu-0.9.18/lib/mu-msg-priv.h0000644000175000017500000001031013020504332012240 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_PRIV_H__ #define __MU_MSG_PRIV_H__ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include "mu-msg-part.h" G_BEGIN_DECLS struct _MuMsgFile { GMimeMessage *_mime_msg; time_t _timestamp; size_t _size; char _path [PATH_MAX + 1]; char _maildir [PATH_MAX + 1]; }; /* we put the the MuMsg definition in this separate -priv file, so we * can split the mu_msg implementations over separate files */ struct _MuMsg { guint _refcount; /* our two backend */ MuMsgFile *_file; /* based on GMime, ie. a file on disc */ MuMsgDoc *_doc; /* based on Xapian::Document */ /* lists where we push allocated strings / GSLists of string * so we can free them when the struct gets destroyed (and we * can return them as 'const to callers) */ GSList *_free_later_str; GSList *_free_later_lst; }; /** * convert a GMimePart to a string * * @param part a GMimePart * @param err will receive TRUE if there was an error, FALSE * otherwise. Must NOT be NULL. * * @return utf8 string for this MIME part, to be freed by caller */ gchar* mu_msg_mime_part_to_string (GMimePart *part, gboolean *err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Like g_mime_message_foreach, but will recurse into encrypted parts * if @param decrypt is TRUE and mu was built with crypto support * * @param msg a GMimeMessage * @param decrypt whether to try to automatically decrypt * @param func user callback function for each part * @param user_data user point passed to callback function * @param err receives error information * */ void mu_mime_message_foreach (GMimeMessage *msg, gboolean decrypt, GMimeObjectForeachFunc func, gpointer user_data); /** * callback function to retrieve a password from the user * * @param user_id the user name / id to get the password for * @param prompt_ctx a string containing some helpful context for the prompt * @param reprompt whether this is a reprompt after an earlier, incorrect password * @param user_data the user_data pointer passed to mu_msg_part_decrypt_foreach * * @return a newly allocated (g_free'able) string */ typedef char* (*MuMsgPartPasswordFunc) (const char *user_id, const char *prompt_ctx, gboolean reprompt, gpointer user_data); /** * verify the signature of a signed message part * * @param sig a signed message part * @param opts message options * @param err receive error information * * @return a status report object, free with mu_msg_part_sig_status_report_destroy */ void mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts, GError **err); /** * decrypt the given encrypted mime multipart * * @param enc encrypted part * @param opts options * @param password_func callback function to retrieve as password (or NULL) * @param user_data pointer passed to the password func * @param err receives error data * * @return the decrypted part, or NULL in case of error */ GMimeObject* mu_msg_crypto_decrypt_part (GMimeMultipartEncrypted *enc, MuMsgOptions opts, MuMsgPartPasswordFunc func, gpointer user_data, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS #endif /*__MU_MSG_PRIV_H__*/ mu-0.9.18/lib/mu-msg-prio.c0000644000175000017500000000320112605152236012237 00000000000000/* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #include "mu-msg-prio.h" const char* mu_msg_prio_name (MuMsgPrio prio) { switch (prio) { case MU_MSG_PRIO_LOW : return "low"; case MU_MSG_PRIO_NORMAL : return "normal"; case MU_MSG_PRIO_HIGH : return "high"; default : g_return_val_if_reached (NULL); } } MuMsgPrio mu_msg_prio_from_char (char k) { g_return_val_if_fail (k == 'l' || k == 'n' || k == 'h', MU_MSG_PRIO_NONE); return (MuMsgPrio)k; } char mu_msg_prio_char (MuMsgPrio prio) { if (!(prio == 'l' || prio == 'n' || prio == 'h')) { g_warning ("prio: %c", (char)prio); } g_return_val_if_fail (prio == 'l' || prio == 'n' || prio == 'h', 0); return (char)prio; } void mu_msg_prio_foreach (MuMsgPrioForeachFunc func, gpointer user_data) { g_return_if_fail (func); func (MU_MSG_PRIO_LOW, user_data); func (MU_MSG_PRIO_NORMAL, user_data); func (MU_MSG_PRIO_HIGH, user_data); } mu-0.9.18/lib/mu-store.h0000644000175000017500000002703713020504332011646 00000000000000/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_STORE_H__ #define __MU_STORE_H__ #include #include #include #include /* for MuError, MuError */ G_BEGIN_DECLS struct _MuStore; typedef struct _MuStore MuStore; /** * create a new writable Xapian store, a place to store documents * * @param path the path to the database * @param ccachepath path where to cache the contacts information, or NULL * @param err to receive error info or NULL. err->code is MuError value * * @return a new MuStore object with ref count == 1, or NULL in case * of error; free with mu_store_unref */ MuStore* mu_store_new_writable (const char *xpath, const char *ccachepath, gboolean rebuild, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * create a new read-only Xapian store, for querying documents * * @param path the path to the database * @param err to receive error info or NULL. err->code is MuError value * * @return a new MuStore object with ref count == 1, or NULL in case * of error; free with mu_store_unref */ MuStore* mu_store_new_read_only (const char* xpath, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * increase the reference count for this store with 1 * * @param store a valid store object * * @return the same store with increased ref count, or NULL in case of * error */ MuStore* mu_store_ref (MuStore *store); /** * decrease the reference count for this store with 1 * * @param store a valid store object * * @return NULL */ MuStore* mu_store_unref (MuStore *store); /** * we need this when using Xapian::WritableDatabase* from C */ typedef gpointer XapianWritableDatabase; /** * get the underlying writable database object for this store; not * that this pointer becomes in valid after mu_store_destroy * * @param store a valid store * * @return a Xapian::WritableDatabase (you'll need to cast in C++), or * NULL in case of error. */ XapianWritableDatabase* mu_store_get_writable_database (MuStore *store); /** * we need this when using Xapian::WritableDatabase* from C */ typedef gpointer XapianDatabase; /** * get the underlying read-only database object for this store; not that this * pointer becomes in valid after mu_store_destroy * * @param store a valid store * * @return a Xapian::Database (you'll need to cast in C++), or * NULL in case of error. */ XapianDatabase* mu_store_get_read_only_database (MuStore *store); /** * set the Xapian batch size for this store. Normally, there's no need * to use this function as the default is good enough; however, if you * use mu in a very memory-constrained environment, you can set the * batchsize to e.g. 1000 at the cost of significant slow-down. * * @param store a valid store object * @param batchsize the new batch size; or 0 to reset to * the default batch size */ void mu_store_set_batch_size (MuStore *store, guint batchsize); /** * register a char** of email addresses as 'my' addresses, ie. mark * message that have these addresses in one of the address fields as * 'personal' (e.g., in mu-contacts). calling this function overrides * any 'my addresses' that were set before, using this function or * through mu_store_new_writable * * @param store a valid store object * @param my_addresses a char** of email addresses */ void mu_store_set_my_addresses (MuStore *store, const char **my_addresses); /** * get the numbers of documents in the database * * @param index a valid MuStore instance * @param err to receive error info or NULL. err->code is MuError value * * @return the number of documents in the database; (unsigned)-1 in * case of error */ unsigned mu_store_count (const MuStore *store, GError **err); /** * get a version string for the database; it's a const string, which * is valid as long MuStore exists and mu_store_version is not called * again. * * @param store a valid MuStore * * @return the version string or NULL in case of error */ const char* mu_store_version (const MuStore *store); /** * try to flush/commit all outstanding work to the database and the contacts * cache. * * @param store a valid xapian store */ void mu_store_flush (MuStore *store); #define MU_STORE_INVALID_DOCID 0 /** * store an email message in the XapianStore * * @param store a valid store * @param msg a valid message * @param err receives error information, if any, or NULL * * @return the docid of the stored message, or 0 * (MU_STORE_INVALID_DOCID) in case of error */ unsigned mu_store_add_msg (MuStore *store, MuMsg *msg, GError **err); /** * update an email message in the XapianStore * * @param store a valid store * @param the docid for the message * @param msg a valid message * @param err receives error information, if any, or NULL * * @return the docid of the stored message, or 0 * (MU_STORE_INVALID_DOCID) in case of error */ unsigned mu_store_update_msg (MuStore *store, unsigned docid, MuMsg *msg, GError **err); /** * store an email message in the XapianStore; similar to * mu_store_store, but instead takes a path as parameter instead of a * MuMsg* * * @param store a valid store * @param path full filesystem path to a valid message * @param maildir set the maildir (e.g. "/drafts") for this message, or NULL * note that you cannot mu_msg_move_msg_to_maildir unless maildir is set. * @param err receives error information, if any, or NULL * * @return the docid of the stored message, or 0 * (MU_STORE_INVALID_DOCID) in case of error */ unsigned mu_store_add_path (MuStore *store, const char *path, const char* maildir, GError **err); /** * remove a message from the database based on its path * * @param store a valid store * @param msgpath path of the message (note, this is only used to * *identify* the message; a common use of this function is to remove * a message from the database, for which there is no message anymore * in the filesystem. * * @return TRUE if it succeeded, FALSE otherwise */ gboolean mu_store_remove_path (MuStore *store, const char* msgpath); /** * does a certain message exist in the database already? * * @param store a store * @param path the message path * @param err to receive error info or NULL. err->code is MuError value * * @return TRUE if the message exists, FALSE otherwise */ gboolean mu_store_contains_message (const MuStore *store, const char* path, GError **err); /** * get the docid for message at path * * @param store a store * @param path the message path * @param err to receive error info or NULL. err->code is MuError value * * @return the docid if the message was found, MU_STORE_INVALID_DOCID (0) otherwise * */ unsigned mu_store_get_docid_for_path (const MuStore *store, const char* path, GError **err); /** * store a timestamp for a directory * * @param store a valid store * @param msgpath path to a maildir * @param stamp a timestamp * @param err to receive error info or NULL. err->code is MuError value * * @return TRUE if setting the timestamp succeeded, FALSE otherwise */ gboolean mu_store_set_timestamp (MuStore *store, const char* msgpath, time_t stamp, GError **err); /** * get the timestamp for a directory * * @param store a valid store * @param msgpath path to a maildir * @param err to receive error info or NULL. err->code is MuError value * * @return the timestamp, or 0 in case of error */ time_t mu_store_get_timestamp (const MuStore *store, const char* msgpath, GError **err); /** * check whether this store is read-only * * @param store a store * * @return TRUE if the store is read-only, FALSE otherwise (and in * case of error) */ gboolean mu_store_is_read_only (const MuStore *store); /** * call a function for each document in the database * * @param self a valid store * @param func a callback function to to call for each document * @param user_data a user pointer passed to the callback function * @param err to receive error info or NULL. err->code is MuError value * * @return MU_OK if all went well, MU_STOP if the foreach was interrupted, * MU_ERROR in case of error */ typedef MuError (*MuStoreForeachFunc) (const char* path, gpointer user_data); MuError mu_store_foreach (MuStore *self, MuStoreForeachFunc func, void *user_data, GError **err); /** * set metadata for this MuStore * * @param store a store * @param key metadata key * @param val metadata value * @param err to receive error info or NULL. err->code is the MuError value * * @return TRUE if succeeded, FALSE otherwise */ gboolean mu_store_set_metadata (MuStore *store, const char *key, const char *val, GError **err); /** * get metadata for this MuStore * * @param store a store * @param key the metadata key * @param err to receive error info or NULL. err->code is MuError value * * @return the value of the metadata (gfree when done with it), or * NULL in case of error */ char* mu_store_get_metadata (const MuStore *store, const char *key, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** " * get the version of the xapian database (ie., the version of the * 'schema' we are using). If this version != MU_STORE_SCHEMA_VERSION, * it's means we need to a full reindex. * * @param xpath path to the xapian database * * @return the version of the database as a newly allocated string * (free with g_free); if there is no version yet, it will return NULL */ gchar* mu_store_database_version (const gchar *xpath) G_GNUC_WARN_UNUSED_RESULT; /** * check whether the database schema's version is the same as the one * that the current mu uses. If they are not the same, we'll need a * database rebuild. * * @param store a MuStore instance * * @return TRUE if the versions are the same, FALSE otherwise. */ gboolean mu_store_versions_match (const MuStore *store); /** * clear the database, ie., remove all of the contents. This is a * destructive operation, but the database can be restored be doing a * full scan of the maildirs. Also, clear the contacts cache file * * @param store a MuStore object * @param err to receive error info or NULL. err->code is MuError value * * @return TRUE if the clearing succeeded, FALSE otherwise. */ gboolean mu_store_clear (MuStore *store, GError **err); /** * check if the database is locked for writing * * @param xpath path to a xapian database * * @return TRUE if it is locked, FALSE otherwise (or in case of error) */ gboolean mu_store_database_is_locked (const gchar *xpath); /** * get a specific message, based on its Xapian docid * * @param self a valid MuQuery instance * @param docid the Xapian docid for the wanted message * @param err receives error information, or NULL * * @return a MuMsg instance (use mu_msg_unref when done with it), or * NULL in case of error */ MuMsg* mu_store_get_msg (const MuStore *self, unsigned docid, GError **err) G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS #endif /*__MU_STORE_H__*/ mu-0.9.18/lib/mu-container.h0000644000175000017500000001470413020504331012470 00000000000000/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_CONTAINER_H__ #define __MU_CONTAINER_H__ #include #include enum _MuContainerFlag { MU_CONTAINER_FLAG_NONE = 0, MU_CONTAINER_FLAG_DELETE = 1 << 0, MU_CONTAINER_FLAG_SPLICE = 1 << 1, MU_CONTAINER_FLAG_DUP = 1 << 2 }; typedef enum _MuContainerFlag MuContainerFlag; /* * MuContainer data structure, as seen in JWZs document: * http://www.jwz.org/doc/threading.html */ struct _MuContainer { struct _MuContainer *parent, *child, *next; /* note: we cache the last of the string of next->next->... * `mu_container_append_siblings' shows up high in the * profiles since it needs to walk to the end, and this give * O(n*n) behavior. * */ struct _MuContainer *last; /* Node in the subtree rooted at this node which comes first * in the descending sort order, e.g. the latest message if * sorting by date. We compare the leaders when ordering * subtrees. */ struct _MuContainer *leader; MuMsg *msg; const char *msgid; unsigned docid; MuContainerFlag flags; }; typedef struct _MuContainer MuContainer; /** * create a new Container object * * @param msg a MuMsg, or NULL; when it's NULL, docid should be 0 * @param docid a Xapian docid, or 0 * @param msgid a message id, or NULL * * @return a new Container instance, or NULL in case of error; free * with mu_container_destroy */ MuContainer* mu_container_new (MuMsg *msg, guint docid, const char* msgid); /** * free a Container object * * @param c a Container object, or NULL */ void mu_container_destroy (MuContainer *c); /** * append new child(ren) to this container; the child(ren) container's * parent pointer will point to this one * * @param c a Container instance * @param child a child * * @return the Container instance with a child added */ MuContainer* mu_container_append_children (MuContainer *c, MuContainer *child); /** * append a new sibling to this (list of) containers; all the siblings * will get the same parent that @c has * * @param c a container instance * @param sibling a sibling * * @return the container (list) with the sibling(s) appended */ MuContainer* mu_container_append_siblings (MuContainer *c, MuContainer *sibling); /** * remove a _single_ child container from a container * * @param c a container instance * @param child the child container to remove * * @return the container with the child removed; if the container did * have this child, nothing changes */ MuContainer* mu_container_remove_child (MuContainer *c, MuContainer *child); /** * remove a _single_ sibling container from a container * * @param c a container instance * @param sibling the sibling container to remove * * @return the container with the sibling removed; if the container did * have this sibling, nothing changes */ MuContainer* mu_container_remove_sibling (MuContainer *c, MuContainer *sibling); /** * promote sibling's children to be this container's siblings * * @param c a container instance * @param sibling a sibling of this container * * @return the container with the sibling's children promoted */ MuContainer* mu_container_splice_children (MuContainer *c, MuContainer *sibling); /** * promote child's children to be parent's children * * @param parent a container instance * @param child a child of this container * * @return the new container with it's children's children promoted */ MuContainer* mu_container_splice_grandchildren (MuContainer *parent, MuContainer *child); typedef gboolean (*MuContainerForeachFunc) (MuContainer*, gpointer); /** * execute some function on all siblings an children of some container * (recursively) until all children have been visited or the callback * function returns FALSE * * @param c a container * @param func a function to call for each container * @param user_data a pointer to pass to the callback function * * @return */ gboolean mu_container_foreach (MuContainer *c, MuContainerForeachFunc func, gpointer user_data); /** * check wither container needle is a child or sibling (recursively) * of container haystack * * @param haystack a container * @param needle a container * * @return TRUE if needle is reachable from haystack, FALSE otherwise */ gboolean mu_container_reachable (MuContainer *haystack, MuContainer *needle); /** * dump the container to stdout (for debugging) * * @param c a container * @param recursive whether to include siblings, children */ void mu_container_dump (MuContainer *c, gboolean recursive); typedef int (*MuContainerCmpFunc) (MuContainer *c1, MuContainer *c2, gpointer user_data); /** * sort the tree of MuContainers, recursively; ie. each of the list of * siblings (children) will be sorted according to @func; if the * container is empty, the first non-empty 'leftmost' child is used. * * @param c a container * @param mfid the field to sort by * @param revert if TRUE, revert the sorting order * * @param user_data a user pointer to pass to the sorting function * * @return a sorted container */ MuContainer* mu_container_sort (MuContainer *c, MuMsgFieldId mfid, gboolean revert, gpointer user_data); /** * create a hashtable with maps document-ids to information about them, * ie. Xapian docid => MuMsgIterThreadInfo * * @param root_set the containers @param matchnum the number of * matches in the list (this is needed to determine the shortest * possible collation keys ('threadpaths') for the messages * * @return a hash; free with g_hash_table_destroy */ GHashTable* mu_container_thread_info_hash_new (MuContainer *root_set, size_t matchnum); #endif /*__MU_CONTAINER_H__*/ mu-0.9.18/lib/mu-store-priv.hh0000644000175000017500000001542513020504332012772 00000000000000/* -*-mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8-*- */ /* ** Copyright (C) 2011-2016 ** ** 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, 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. ** */ #ifndef __MU_STORE_PRIV_HH__ #define __MU_STORE_PRIV_HH__ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include "mu-store.h" #include "mu-contacts.h" #include "mu-str.h" class MuStoreError { public: MuStoreError (MuError err, const std::string& what): _err (err), _what(what) {} MuError mu_error () const { return _err; } const std::string& what() const { return _what; } private: MuError _err; const std::string _what; }; struct _MuStore { public: /* create a read-write MuStore */ _MuStore (const char *path, const char *contacts_path, bool rebuild) { init (path, contacts_path, rebuild, false); if (rebuild) _db = new Xapian::WritableDatabase (path, Xapian::DB_CREATE_OR_OVERWRITE); else _db = new Xapian::WritableDatabase (path, Xapian::DB_CREATE_OR_OPEN); check_set_version (); if (contacts_path) { /* when rebuilding, attempt to clear the * contacts path */ if (rebuild && access (contacts_path, W_OK) == 0) (void)unlink (contacts_path); _contacts = mu_contacts_new (contacts_path); if (!_contacts) throw MuStoreError (MU_ERROR_FILE, ("failed to init contacts cache")); } MU_WRITE_LOG ("%s: opened %s (batch size: %u) for read-write", __func__, this->path(), (unsigned)batch_size()); } /* create a read-only MuStore */ _MuStore (const char *path) { init (path, NULL, false, false); _db = new Xapian::Database (path); if (!mu_store_versions_match(this)) { char *errstr = g_strdup_printf ("db version: %s, but we need %s; " "database rebuild is required", mu_store_version (this), MU_STORE_SCHEMA_VERSION); MuStoreError exc (MU_ERROR_XAPIAN_VERSION_MISMATCH, errstr); g_free (errstr); throw exc; } MU_WRITE_LOG ("%s: opened %s read-only", __func__, this->path()); } void init (const char *path, const char *contacts_path, bool rebuild, bool read_only) { _my_addresses = NULL; _batch_size = DEFAULT_BATCH_SIZE; _contacts = 0; _in_transaction = false; _path = path; _processed = 0; _read_only = read_only; _ref_count = 1; _version = NULL; } void set_my_addresses (const char **my_addresses) { if (_my_addresses) { mu_str_free_list (_my_addresses); _my_addresses = NULL; } while (my_addresses && *my_addresses) { _my_addresses = g_slist_prepend (_my_addresses, g_strdup (*my_addresses)); ++my_addresses; } } void check_set_version () { if (_version) return; _version = mu_store_get_metadata (this, MU_STORE_VERSION_KEY, NULL); if (!_version) { mu_store_set_metadata (this, MU_STORE_VERSION_KEY, MU_STORE_SCHEMA_VERSION, NULL); _version = mu_store_get_metadata (this, MU_STORE_VERSION_KEY, NULL); } else if (g_strcmp0 (_version, MU_STORE_SCHEMA_VERSION) != 0) throw MuStoreError (MU_ERROR_XAPIAN_VERSION_MISMATCH, "the database needs a rebuild"); } ~_MuStore () { try { if (_ref_count != 0) g_warning ("ref count != 0"); mu_contacts_destroy (_contacts); _contacts = NULL; if (!_read_only) mu_store_flush (this); g_free (_version); mu_str_free_list (_my_addresses); MU_WRITE_LOG ("closing xapian database with %d document(s)", (int)db_read_only()->get_doccount()); delete _db; } MU_XAPIAN_CATCH_BLOCK; } /* close the old database, and write an empty one on top of it */ void clear () { if (is_read_only()) throw std::runtime_error ("database is read-only"); // clear the database db_writable()->close (); delete _db; _db = new Xapian::WritableDatabase (path(), Xapian::DB_CREATE_OR_OVERWRITE); // clear the contacts cache if (_contacts) mu_contacts_clear (_contacts); } // not re-entrant; stays valid until called again const char *get_uid_term (const char *path) const; MuContacts* contacts() { return _contacts; } const char *version () const { if (!_version) _version = mu_store_get_metadata (this, MU_STORE_VERSION_KEY, NULL); return _version; } void set_version (const char *version) { g_free (_version); _version = NULL; mu_store_set_metadata (this, MU_STORE_VERSION_KEY, version, NULL); } static unsigned max_term_length() { return MAX_TERM_LENGTH; } void begin_transaction (); void commit_transaction (); void rollback_transaction (); Xapian::WritableDatabase* db_writable() { if (G_UNLIKELY(is_read_only())) throw std::runtime_error ("database is read-only"); return (Xapian::WritableDatabase*)_db; } Xapian::Database* db_read_only() const { return _db; } const char* path () const { return _path.c_str(); } bool is_read_only () const { return _read_only; } size_t batch_size () const { return _batch_size;} size_t set_batch_size (size_t n) { return _batch_size = ( n == 0) ? DEFAULT_BATCH_SIZE : n; } bool in_transaction () const { return _in_transaction; } bool in_transaction (bool in_tx) { return _in_transaction = in_tx; } int processed () const { return _processed; } int set_processed (int n) { return _processed = n;} int inc_processed () { return ++_processed; } /* MuStore is ref-counted */ guint ref () { return ++_ref_count; } guint unref () { if (_ref_count < 1) g_critical ("ref count error"); return --_ref_count; } GSList *my_addresses () { return _my_addresses; } /* by default, use transactions of 30000 messages */ static const unsigned DEFAULT_BATCH_SIZE = 30000; /* http://article.gmane.org/gmane.comp.search.xapian.general/3656 */ static const unsigned MAX_TERM_LENGTH = 240; private: /* transaction handling */ bool _in_transaction; int _processed; size_t _batch_size; /* batch size of a xapian transaction */ /* contacts object to cache all the contact information */ MuContacts *_contacts; std::string _path; mutable char *_version; Xapian::Database *_db; bool _read_only; guint _ref_count; GSList *_my_addresses; }; #endif /*__MU_STORE_PRIV_HH__*/ mu-0.9.18/lib/mu-msg.c0000644000175000017500000004515213020504332011271 00000000000000/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include #include #include #include #include #include #include #include #include "mu-msg-priv.h" /* include before mu-msg.h */ #include "mu-msg.h" #include "mu-util.h" #include "mu-str.h" #include "mu-maildir.h" /* note, we do the gmime initialization here rather than in * mu-runtime, because this way we don't need mu-runtime for simple * cases -- such as our unit tests. Also note that we need gmime init * even for the doc backend, as we use the address parsing functions * also there. */ static gboolean _gmime_initialized = FALSE; static void gmime_init (void) { g_return_if_fail (!_gmime_initialized); #ifdef GMIME_ENABLE_RFC2047_WORKAROUNDS g_mime_init(GMIME_ENABLE_RFC2047_WORKAROUNDS); #else g_mime_init(0); #endif /* GMIME_ENABLE_RFC2047_WORKAROUNDS */ _gmime_initialized = TRUE; } static void gmime_uninit (void) { g_return_if_fail (_gmime_initialized); g_mime_shutdown(); _gmime_initialized = FALSE; } static MuMsg* msg_new (void) { MuMsg *self; self = g_slice_new0 (MuMsg); self->_refcount = 1; return self; } MuMsg* mu_msg_new_from_file (const char *path, const char *mdir, GError **err) { MuMsg *self; MuMsgFile *msgfile; g_return_val_if_fail (path, NULL); if (G_UNLIKELY(!_gmime_initialized)) { gmime_init (); atexit (gmime_uninit); } msgfile = mu_msg_file_new (path, mdir, err); if (!msgfile) return NULL; self = msg_new (); self->_file = msgfile; return self; } MuMsg* mu_msg_new_from_doc (XapianDocument *doc, GError **err) { MuMsg *self; MuMsgDoc *msgdoc; g_return_val_if_fail (doc, NULL); if (G_UNLIKELY(!_gmime_initialized)) { gmime_init (); atexit (gmime_uninit); } msgdoc = mu_msg_doc_new (doc, err); if (!msgdoc) return NULL; self = msg_new (); self->_doc = msgdoc; return self; } static void mu_msg_destroy (MuMsg *self) { if (!self) return; mu_msg_file_destroy (self->_file); mu_msg_doc_destroy (self->_doc); { /* cleanup the strings / lists we stored */ mu_str_free_list (self->_free_later_str); g_slist_foreach (self->_free_later_lst, (GFunc)mu_str_free_list, NULL); g_slist_free (self->_free_later_lst); } g_slice_free (MuMsg, self); } MuMsg* mu_msg_ref (MuMsg *self) { g_return_val_if_fail (self, NULL); ++self->_refcount; return self; } void mu_msg_unref (MuMsg *self) { g_return_if_fail (self); g_return_if_fail (self->_refcount >= 1); if (--self->_refcount == 0) mu_msg_destroy (self); } static const gchar* free_later_str (MuMsg *self, gchar *str) { if (str) self->_free_later_str = g_slist_prepend (self->_free_later_str, str); return str; } static const GSList* free_later_lst (MuMsg *self, GSList *lst) { if (lst) self->_free_later_lst = g_slist_prepend (self->_free_later_lst, lst); return lst; } /* use this instead of mu_msg_get_path so we don't get into infinite * regress...*/ static const char* get_path (MuMsg *self) { char *val; gboolean do_free; do_free = TRUE; val = NULL; if (self->_doc) val = mu_msg_doc_get_str_field (self->_doc, MU_MSG_FIELD_ID_PATH); /* not in the cache yet? try to get it from the file backend, * in case we are using that */ if (!val && self->_file) val = mu_msg_file_get_str_field (self->_file, MU_MSG_FIELD_ID_PATH, &do_free); /* shouldn't happen */ if (!val) g_warning ("%s: cannot find path", __func__); return free_later_str (self, val); } /* for some data, we need to read the message file from disk */ gboolean mu_msg_load_msg_file (MuMsg *self, GError **err) { const char *path; g_return_val_if_fail (self, FALSE); if (self->_file) return TRUE; /* nothing to do */ if (!(path = get_path (self))) { mu_util_g_set_error (err, MU_ERROR_INTERNAL, "cannot get path for message"); return FALSE; } self->_file = mu_msg_file_new (path, NULL, err); return (self->_file != NULL); } void mu_msg_unload_msg_file (MuMsg *msg) { g_return_if_fail (msg); mu_msg_file_destroy (msg->_file); msg->_file = NULL; } static const GSList* get_str_list_field (MuMsg *self, MuMsgFieldId mfid) { GSList *val; val = NULL; if (self->_doc && mu_msg_field_xapian_value (mfid)) val = mu_msg_doc_get_str_list_field (self->_doc, mfid); else if (mu_msg_field_gmime (mfid)) { /* if we don't have a file object yet, we need to * create it from the file on disk */ if (!mu_msg_load_msg_file (self, NULL)) return NULL; val = mu_msg_file_get_str_list_field (self->_file, mfid); } return free_later_lst (self, val); } static const char* get_str_field (MuMsg *self, MuMsgFieldId mfid) { char *val; gboolean do_free; do_free = TRUE; val = NULL; if (self->_doc && mu_msg_field_xapian_value (mfid)) val = mu_msg_doc_get_str_field (self->_doc, mfid); else if (mu_msg_field_gmime (mfid)) { /* if we don't have a file object yet, we need to * create it from the file on disk */ if (!mu_msg_load_msg_file (self, NULL)) return NULL; val = mu_msg_file_get_str_field (self->_file, mfid, &do_free); } else val = NULL; return do_free ? free_later_str (self, val) : val; } static gint64 get_num_field (MuMsg *self, MuMsgFieldId mfid) { guint64 val; val = -1; if (self->_doc && mu_msg_field_xapian_value (mfid)) val = mu_msg_doc_get_num_field (self->_doc, mfid); else { /* if we don't have a file object yet, we need to * create it from the file on disk */ if (!mu_msg_load_msg_file (self, NULL)) return -1; val = mu_msg_file_get_num_field (self->_file, mfid); } return val; } const char* mu_msg_get_header (MuMsg *self, const char *header) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (header, NULL); /* if we don't have a file object yet, we need to * create it from the file on disk */ if (!mu_msg_load_msg_file (self, NULL)) return NULL; return free_later_str (self, mu_msg_file_get_header (self->_file, header)); } time_t mu_msg_get_timestamp (MuMsg *self) { const char *path; struct stat statbuf; g_return_val_if_fail (self, 0); if (self->_file) return self->_file->_timestamp; path = mu_msg_get_path (self); if (!path || stat (path, &statbuf) < 0) return 0; return statbuf.st_mtime; } const char* mu_msg_get_path (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_PATH); } const char* mu_msg_get_subject (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_SUBJECT); } const char* mu_msg_get_msgid (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_MSGID); } const char* mu_msg_get_mailing_list (MuMsg *self) { const char *ml; char *decml; g_return_val_if_fail (self, NULL); ml = get_str_field (self, MU_MSG_FIELD_ID_MAILING_LIST); if (!ml) return NULL; decml = g_mime_utils_header_decode_text (ml); if (!decml) return NULL; return free_later_str (self, decml); } const char* mu_msg_get_maildir (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_MAILDIR); } const char* mu_msg_get_from (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_FROM); } const char* mu_msg_get_to (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_TO); } const char* mu_msg_get_cc (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_CC); } const char* mu_msg_get_bcc (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_field (self, MU_MSG_FIELD_ID_BCC); } time_t mu_msg_get_date (MuMsg *self) { g_return_val_if_fail (self, (time_t)-1); return (time_t)get_num_field (self, MU_MSG_FIELD_ID_DATE); } MuFlags mu_msg_get_flags (MuMsg *self) { g_return_val_if_fail (self, MU_FLAG_NONE); return (MuFlags)get_num_field (self, MU_MSG_FIELD_ID_FLAGS); } size_t mu_msg_get_size (MuMsg *self) { g_return_val_if_fail (self, (size_t)-1); return (size_t)get_num_field (self, MU_MSG_FIELD_ID_SIZE); } MuMsgPrio mu_msg_get_prio (MuMsg *self) { g_return_val_if_fail (self, MU_MSG_PRIO_NORMAL); return (MuMsgPrio)get_num_field (self, MU_MSG_FIELD_ID_PRIO); } struct _BodyData { GString *gstr; gboolean want_html; }; typedef struct _BodyData BodyData; static void accumulate_body (MuMsg *msg, MuMsgPart *mpart, BodyData *bdata) { char *txt; gboolean err; if (!GMIME_IS_PART(mpart->data)) return; txt = NULL; err = TRUE; /* text-like attachments are included when in text-mode */ if (!bdata->want_html && (mpart->part_type & MU_MSG_PART_TYPE_TEXT_PLAIN)) txt = mu_msg_mime_part_to_string ( (GMimePart*)mpart->data, &err); else if (!(mpart->part_type & MU_MSG_PART_TYPE_ATTACHMENT) && bdata->want_html && (mpart->part_type & MU_MSG_PART_TYPE_TEXT_HTML)) txt = mu_msg_mime_part_to_string ( (GMimePart*)mpart->data, &err); if (!err && txt) bdata->gstr = g_string_append (bdata->gstr, txt); g_free (txt); } static char* get_body (MuMsg *self, MuMsgOptions opts, gboolean want_html) { BodyData bdata; bdata.want_html = want_html; bdata.gstr = g_string_sized_new (4096); mu_msg_part_foreach (self, opts, (MuMsgPartForeachFunc)accumulate_body, &bdata); if (bdata.gstr->len == 0) { g_string_free (bdata.gstr, TRUE); return NULL; } else return g_string_free (bdata.gstr, FALSE); } const char* mu_msg_get_body_html (MuMsg *self, MuMsgOptions opts) { g_return_val_if_fail (self, NULL); return free_later_str (self, get_body (self, opts, TRUE)); } const char* mu_msg_get_body_text (MuMsg *self, MuMsgOptions opts) { g_return_val_if_fail (self, NULL); return free_later_str (self, get_body (self, opts, FALSE)); } const GSList* mu_msg_get_references (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_list_field (self, MU_MSG_FIELD_ID_REFS); } const GSList* mu_msg_get_tags (MuMsg *self) { g_return_val_if_fail (self, NULL); return get_str_list_field (self, MU_MSG_FIELD_ID_TAGS); } const char* mu_msg_get_field_string (MuMsg *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, NULL); return get_str_field (self, mfid); } const GSList* mu_msg_get_field_string_list (MuMsg *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, NULL); return get_str_list_field (self, mfid); } gint64 mu_msg_get_field_numeric (MuMsg *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, -1); return get_num_field (self, mfid); } static gboolean fill_contact (MuMsgContact *self, InternetAddress *addr, MuMsgContactType ctype) { if (!addr) return FALSE; self->name = internet_address_get_name (addr); self->type = ctype; /* we only support internet mailbox addresses; if we don't * check, g_mime hits an assert */ if (INTERNET_ADDRESS_IS_MAILBOX(addr)) self->address = internet_address_mailbox_get_addr (INTERNET_ADDRESS_MAILBOX(addr)); else self->address = NULL; /* if there's no address, just a name, it's probably a local * address (without @) */ if (self->name && !self->address) self->address = self->name; /* note, the address could NULL e.g. when the recipient is something like * 'Undisclosed recipients' */ return self->address != NULL; } static void address_list_foreach (InternetAddressList *addrlist, MuMsgContactType ctype, MuMsgContactForeachFunc func, gpointer user_data) { int i, len; if (!addrlist) return; len = internet_address_list_length(addrlist); for (i = 0; i != len; ++i) { MuMsgContact contact; if (!fill_contact(&contact, internet_address_list_get_address (addrlist, i), ctype)) continue; if (!(func)(&contact, user_data)) break; } } static void addresses_foreach (const char* addrs, MuMsgContactType ctype, MuMsgContactForeachFunc func, gpointer user_data) { InternetAddressList *addrlist; if (!addrs) return; addrlist = internet_address_list_parse_string (addrs); if (addrlist) { address_list_foreach (addrlist, ctype, func, user_data); g_object_unref (addrlist); } } static void msg_contact_foreach_file (MuMsg *msg, MuMsgContactForeachFunc func, gpointer user_data) { int i; struct { GMimeRecipientType _gmime_type; MuMsgContactType _type; } ctypes[] = { {GMIME_RECIPIENT_TYPE_TO, MU_MSG_CONTACT_TYPE_TO}, {GMIME_RECIPIENT_TYPE_CC, MU_MSG_CONTACT_TYPE_CC}, {GMIME_RECIPIENT_TYPE_BCC, MU_MSG_CONTACT_TYPE_BCC}, }; /* sender */ addresses_foreach (g_mime_message_get_sender (msg->_file->_mime_msg), MU_MSG_CONTACT_TYPE_FROM, func, user_data); /* reply_to */ addresses_foreach (g_mime_message_get_reply_to (msg->_file->_mime_msg), MU_MSG_CONTACT_TYPE_REPLY_TO, func, user_data); /* get to, cc, bcc */ for (i = 0; i != G_N_ELEMENTS(ctypes); ++i) { InternetAddressList *addrlist; addrlist = g_mime_message_get_recipients (msg->_file->_mime_msg, ctypes[i]._gmime_type); address_list_foreach (addrlist, ctypes[i]._type, func, user_data); } } static void msg_contact_foreach_doc (MuMsg *msg, MuMsgContactForeachFunc func, gpointer user_data) { addresses_foreach (mu_msg_get_from (msg), MU_MSG_CONTACT_TYPE_FROM, func, user_data); addresses_foreach (mu_msg_get_to (msg), MU_MSG_CONTACT_TYPE_TO, func, user_data); addresses_foreach (mu_msg_get_cc (msg), MU_MSG_CONTACT_TYPE_CC, func, user_data); addresses_foreach (mu_msg_get_bcc (msg), MU_MSG_CONTACT_TYPE_BCC, func, user_data); } void mu_msg_contact_foreach (MuMsg *msg, MuMsgContactForeachFunc func, gpointer user_data) { g_return_if_fail (msg); g_return_if_fail (func); if (msg->_file) msg_contact_foreach_file (msg, func, user_data); else if (msg->_doc) msg_contact_foreach_doc (msg, func, user_data); else g_return_if_reached (); } static int cmp_str (const char *s1, const char *s2) { if (s1 == s2) return 0; else if (!s1) return -1; else if (!s2) return 1; /* optimization 1: ascii */ if (isascii(s1[0]) && isascii(s2[0])) { int diff; diff = tolower(s1[0]) - tolower(s2[0]); if (diff != 0) return diff; } /* utf 8 */ { char *u1, *u2; int diff; u1 = g_utf8_strdown (s1, -1); u2 = g_utf8_strdown (s2, -1); diff = g_utf8_collate (u1, u2); g_free (u1); g_free (u2); return diff; } } static int cmp_subject (const char* s1, const char *s2) { if (s1 == s2) return 0; else if (!s1) return -1; else if (!s2) return 1; return cmp_str ( mu_str_subject_normalize (s1), mu_str_subject_normalize (s2)); } int mu_msg_cmp (MuMsg *m1, MuMsg *m2, MuMsgFieldId mfid) { g_return_val_if_fail (m1, 0); g_return_val_if_fail (m2, 0); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), 0); /* even though date is a numeric field, we can sort it by its * string repr. in the database, which is much faster */ if (mfid == MU_MSG_FIELD_ID_DATE || mu_msg_field_is_string (mfid)) return cmp_str (get_str_field (m1, mfid), get_str_field (m2, mfid)); if (mfid == MU_MSG_FIELD_ID_SUBJECT) return cmp_subject (get_str_field (m1, mfid), get_str_field (m2, mfid)); /* TODO: note, we cast (potentially > MAXINT to int) */ if (mu_msg_field_is_numeric (mfid)) return get_num_field(m1, mfid) - get_num_field(m2, mfid); return 0; /* TODO: handle lists */ } gboolean mu_msg_is_readable (MuMsg *self) { g_return_val_if_fail (self, FALSE); return access (mu_msg_get_path (self), R_OK) == 0 ? TRUE : FALSE; } /* we need do to determine the * /home/foo/Maildir/bar * from the /bar * that we got */ char* get_target_mdir (MuMsg *msg, const char *target_maildir, GError **err) { char *rootmaildir, *rv; const char *maildir; gboolean not_top_level; /* maildir is the maildir stored in the message, e.g. '/foo' */ maildir = mu_msg_get_maildir(msg); if (!maildir) { mu_util_g_set_error (err, MU_ERROR_GMIME, "message without maildir"); return NULL; } /* the 'rootmaildir' is the filesystem path from root to * maildir, ie. /home/user/Maildir/foo */ rootmaildir = mu_maildir_get_maildir_from_path (mu_msg_get_path(msg)); if (!rootmaildir) { mu_util_g_set_error (err, MU_ERROR_GMIME, "cannot determine maildir"); return NULL; } /* we do a sanity check: verify that that maildir is a suffix of * rootmaildir;*/ not_top_level = TRUE; if (!g_str_has_suffix (rootmaildir, maildir) && /* special case for the top-level '/' maildir, and * remember not_top_level */ (not_top_level = (g_strcmp0 (maildir, "/") != 0))) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE, "path is '%s', but maildir is '%s' ('%s')", rootmaildir, mu_msg_get_maildir(msg), mu_msg_get_path (msg)); g_free (rootmaildir); return NULL; } /* if we're not at the top-level, remove the final '/' from * the rootmaildir */ if (not_top_level) rootmaildir[strlen(rootmaildir) - strlen (mu_msg_get_maildir(msg))] = '\0'; rv = g_strconcat (rootmaildir, target_maildir, NULL); g_free (rootmaildir); return rv; } /* * move a msg to another maildir, trying to maintain 'integrity', * ie. msg in 'new/' will go to new/, one in cur/ goes to cur/. be * super-paranoid here... */ gboolean mu_msg_move_to_maildir (MuMsg *self, const char *maildir, MuFlags flags, gboolean ignore_dups, gboolean new_name, GError **err) { char *newfullpath; char *targetmdir; g_return_val_if_fail (self, FALSE); g_return_val_if_fail (maildir, FALSE); /* i.e. "/inbox" */ /* targetmdir is the full path to maildir, i.e., * /home/foo/Maildir/inbox */ targetmdir = get_target_mdir (self, maildir, err); if (!targetmdir) return FALSE; newfullpath = mu_maildir_move_message (mu_msg_get_path (self), targetmdir, flags, ignore_dups, new_name, err); if (!newfullpath) { g_free (targetmdir); return FALSE; } /* clear the old backends */ mu_msg_doc_destroy (self->_doc); self->_doc = NULL; mu_msg_file_destroy (self->_file); /* and create a new one */ self->_file = mu_msg_file_new (newfullpath, maildir, err); g_free (targetmdir); return self->_file ? TRUE : FALSE; } mu-0.9.18/lib/mu-bookmarks.h0000644000175000017500000000444512605152236012512 00000000000000/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_BOOKMARKS_H__ #define __MU_BOOKMARKS_H__ #include G_BEGIN_DECLS /** * @addtogroup MuBookmarks * Functions for dealing with bookmarks * @{ */ struct _MuBookmarks; /*! \struct MuBookmarks * \brief Opaque structure representing a sequence of bookmarks */ typedef struct _MuBookmarks MuBookmarks; /** * create a new bookmarks object. when it's no longer needed, use * mu_bookmarks_destroy * * @param bmpath path to the bookmarks file * * @return a new BookMarks object, or NULL in case of error */ MuBookmarks *mu_bookmarks_new (const gchar *bmpath) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * destroy a bookmarks object * * @param bm a bookmarks object, or NULL */ void mu_bookmarks_destroy (MuBookmarks *bm); /** * get the value for some bookmark * * @param bm a valid bookmarks object * @param name name of the bookmark to retrieve * * @return the value of the bookmark or NULL in case in error, e.g. if * the bookmark was not found */ const gchar* mu_bookmarks_lookup (MuBookmarks *bm, const gchar *name); typedef void (*MuBookmarksForeachFunc) (const gchar *key, const gchar *val, gpointer user_data); /** * call a function for each bookmark * * @param bm a valid bookmarks object * @param func a callback function to be called for each bookmarks * @param user_data a user pointer passed to the callback */ void mu_bookmarks_foreach (MuBookmarks *bm, MuBookmarksForeachFunc func, gpointer user_data); /** @} */ G_END_DECLS #endif /*__MU_BOOKMARKS_H__*/ mu-0.9.18/lib/mu-msg-prio.h0000644000175000017500000000432012605152236012247 00000000000000/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_PRIO_H__ #define __MU_MSG_PRIO_H__ #include G_BEGIN_DECLS enum _MuMsgPrio { MU_MSG_PRIO_LOW = 'l', MU_MSG_PRIO_NORMAL = 'n', MU_MSG_PRIO_HIGH = 'h' }; typedef enum _MuMsgPrio MuMsgPrio; static const MuMsgPrio MU_MSG_PRIO_NONE = (MuMsgPrio)0; /** * get a printable name for the message priority * (ie., MU_MSG_PRIO_LOW=>"low" etc.) * * @param prio a message priority * * @return a printable name for this priority */ const char* mu_msg_prio_name (MuMsgPrio prio) G_GNUC_CONST; /** * get the MuMsgPriority corresponding to a one-character shortcut * ('l'=>MU_MSG_PRIO_, 'n'=>MU_MSG_PRIO_NORMAL or * 'h'=>MU_MSG_PRIO_HIGH) * * @param k a character * * @return a message priority */ MuMsgPrio mu_msg_prio_from_char (char k) G_GNUC_CONST; /** * get the one-character shortcut corresponding to a message priority * ('l'=>MU_MSG_PRIO_, 'n'=>MU_MSG_PRIO_NORMAL or * 'h'=>MU_MSG_PRIO_HIGH) * * @param prio a mesage priority * * @return a shortcut character or 0 in case of error */ char mu_msg_prio_char (MuMsgPrio prio) G_GNUC_CONST; typedef void (*MuMsgPrioForeachFunc) (MuMsgPrio prio, gpointer user_data); /** * call a function for each message priority * * @param func a callback function * @param user_data a user pointer to pass to the callback */ void mu_msg_prio_foreach (MuMsgPrioForeachFunc func, gpointer user_data); G_END_DECLS #endif /*__MU_MSG_PRIO_H__*/ mu-0.9.18/lib/mu-msg-sexp.c0000644000175000017500000003123613020504332012244 00000000000000/* ** Copyright (C) 2011-2016 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #include #include #include "mu-str.h" #include "mu-msg.h" #include "mu-msg-iter.h" #include "mu-msg-part.h" #include "mu-maildir.h" static void append_sexp_attr_list (GString *gstr, const char* elm, const GSList *lst) { const GSList *cur; if (!lst) return; /* empty list, don't include */ g_string_append_printf (gstr, "\t:%s ( ", elm); for (cur = lst; cur; cur = g_slist_next(cur)) { char *str; str = mu_str_escape_c_literal ((const gchar*)cur->data, TRUE); g_string_append_printf (gstr, "%s ", str); g_free (str); } g_string_append (gstr, ")\n"); } static void append_sexp_attr (GString *gstr, const char* elm, const char *str) { gchar *esc, *utf8, *cur; if (!str || strlen(str) == 0) return; /* empty: don't include */ utf8 = mu_str_utf8ify (str); for (cur = utf8; *cur; ++cur) if (iscntrl(*cur)) *cur = ' '; esc = mu_str_escape_c_literal (utf8, TRUE); g_free (utf8); g_string_append_printf (gstr, "\t:%s %s\n", elm, esc); g_free (esc); } static void append_sexp_body_attr (GString *gstr, const char* elm, const char *str) { gchar *esc; if (!str || strlen(str) == 0) return; /* empty: don't include */ esc = mu_str_escape_c_literal (str, TRUE); g_string_append_printf (gstr, "\t:%s %s\n", elm, esc); g_free (esc); } struct _ContactData { gboolean from, to, cc, bcc, reply_to; GString *gstr; MuMsgContactType prev_ctype; }; typedef struct _ContactData ContactData; static gchar* get_name_addr_pair (MuMsgContact *c) { gchar *name, *addr, *pair; name = (char*)mu_msg_contact_name(c); addr = (char*)mu_msg_contact_address(c); name = name ? mu_str_escape_c_literal (name, TRUE) : NULL; addr = addr ? mu_str_escape_c_literal (addr, TRUE) : NULL; pair = g_strdup_printf ("(%s . %s)", name ? name : "nil", addr ? addr : "nil"); g_free (name); g_free (addr); return pair; } static void add_prefix_maybe (GString *gstr, gboolean *field, const char *prefix) { /* if there's nothing in the field yet, add the prefix */ if (!*field) g_string_append (gstr, prefix); *field = TRUE; } static gboolean each_contact (MuMsgContact *c, ContactData *cdata) { char *pair; MuMsgContactType ctype; ctype = mu_msg_contact_type (c); /* if the current type is not the previous type, close the * previous first */ if (cdata->prev_ctype != ctype && cdata->prev_ctype != (unsigned)-1) g_string_append (cdata->gstr, ")\n"); switch (ctype) { case MU_MSG_CONTACT_TYPE_FROM: add_prefix_maybe (cdata->gstr, &cdata->from, "\t:from ("); break; case MU_MSG_CONTACT_TYPE_TO: add_prefix_maybe (cdata->gstr, &cdata->to, "\t:to ("); break; case MU_MSG_CONTACT_TYPE_CC: add_prefix_maybe (cdata->gstr, &cdata->cc, "\t:cc ("); break; case MU_MSG_CONTACT_TYPE_BCC: add_prefix_maybe (cdata->gstr, &cdata->bcc, "\t:bcc ("); break; case MU_MSG_CONTACT_TYPE_REPLY_TO: add_prefix_maybe (cdata->gstr, &cdata->reply_to, "\t:reply-to ("); break; default: g_return_val_if_reached (FALSE); } cdata->prev_ctype = ctype; pair = get_name_addr_pair (c); g_string_append (cdata->gstr, pair); g_free (pair); return TRUE; } static void append_sexp_contacts (GString *gstr, MuMsg *msg) { ContactData cdata; cdata.from = cdata.to = cdata.cc = cdata.bcc = cdata.reply_to = FALSE; cdata.gstr = gstr; cdata.prev_ctype = (unsigned)-1; mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)each_contact, &cdata); if (cdata.from || cdata.to || cdata.cc || cdata.bcc || cdata.reply_to) gstr = g_string_append (gstr, ")\n"); } struct _FlagData { char *flagstr; MuFlags msgflags; }; typedef struct _FlagData FlagData; static void each_flag (MuFlags flag, FlagData *fdata) { if (!(flag & fdata->msgflags)) return; if (!fdata->flagstr) fdata->flagstr = g_strdup (mu_flag_name(flag)); else { gchar *tmp; tmp = g_strconcat (fdata->flagstr, " ", mu_flag_name(flag), NULL); g_free (fdata->flagstr); fdata->flagstr = tmp; } } static void append_sexp_flags (GString *gstr, MuMsg *msg) { FlagData fdata; fdata.msgflags = mu_msg_get_flags (msg); fdata.flagstr = NULL; mu_flags_foreach ((MuFlagsForeachFunc)each_flag, &fdata); if (fdata.flagstr) g_string_append_printf (gstr, "\t:flags (%s)\n", fdata.flagstr); g_free (fdata.flagstr); } static char* get_temp_file (MuMsg *msg, MuMsgOptions opts, unsigned index) { char *path; GError *err; err = NULL; path = mu_msg_part_get_cache_path (msg, opts, index, &err); if (!path) goto errexit; if (!mu_msg_part_save (msg, opts, path, index, &err)) goto errexit; return path; errexit: g_warning ("failed to save mime part: %s", err->message ? err->message : "something went wrong"); g_clear_error (&err); g_free (path); return NULL; } static gchar* get_temp_file_maybe (MuMsg *msg, MuMsgPart *part, MuMsgOptions opts) { char *tmp, *tmpfile; opts |= MU_MSG_OPTION_USE_EXISTING; if (!(opts & MU_MSG_OPTION_EXTRACT_IMAGES) || g_ascii_strcasecmp (part->type, "image") != 0) return NULL; tmp = get_temp_file (msg, opts, part->index); if (!tmp) return NULL; tmpfile = mu_str_escape_c_literal (tmp, TRUE); g_free (tmp); return tmpfile; } struct _PartInfo { char *parts; MuMsgOptions opts; }; typedef struct _PartInfo PartInfo; static const char* sig_verdict (MuMsgPart *mpart) { MuMsgPartSigStatusReport *report; report = mpart->sig_status_report; if (!report) return ""; switch (report->verdict) { case MU_MSG_PART_SIG_STATUS_GOOD: return ":signature verified"; case MU_MSG_PART_SIG_STATUS_BAD: return ":signature bad"; case MU_MSG_PART_SIG_STATUS_ERROR: return ":signature unverified"; default: return ""; } } static const char* dec_verdict (MuMsgPart *mpart) { MuMsgPartType ptype; ptype = mpart->part_type; if (ptype & MU_MSG_PART_TYPE_DECRYPTED) return ":decryption succeeded"; else if (ptype & MU_MSG_PART_TYPE_ENCRYPTED) return ":decryption failed"; else return ""; } static gchar * get_part_type_string (MuMsgPartType ptype) { GString *gstr; unsigned u; struct PartTypes { MuMsgPartType ptype; const char* name; } ptypes[] = { { MU_MSG_PART_TYPE_LEAF, "leaf" }, { MU_MSG_PART_TYPE_MESSAGE, "message" }, { MU_MSG_PART_TYPE_INLINE, "inline" }, { MU_MSG_PART_TYPE_ATTACHMENT, "attachment" }, { MU_MSG_PART_TYPE_SIGNED, "signed" }, { MU_MSG_PART_TYPE_ENCRYPTED, "encrypted" } }; gstr = g_string_sized_new (100); /* more than enough */ gstr = g_string_append_c (gstr, '('); for (u = 0; u!= G_N_ELEMENTS(ptypes); ++u) { if (ptype & ptypes[u].ptype) { if (gstr->len > 1) gstr = g_string_append_c (gstr, ' '); gstr = g_string_append (gstr, ptypes[u].name); } } gstr = g_string_append_c (gstr, ')'); return g_string_free (gstr, FALSE); } static void each_part (MuMsg *msg, MuMsgPart *part, PartInfo *pinfo) { char *name, *tmp, *parttype; char *tmpfile, *cid; name = mu_msg_part_get_filename (part, TRUE); tmpfile = get_temp_file_maybe (msg, part, pinfo->opts); parttype = get_part_type_string (part->part_type); cid = mu_str_escape_c_literal(mu_msg_part_get_content_id(part), TRUE); tmp = g_strdup_printf ("%s(:index %d :name \"%s\" :mime-type \"%s/%s\"%s%s " ":type %s " ":attachment %s %s%s :size %i %s %s)", pinfo->parts ? pinfo->parts: "", part->index, name ? mu_str_escape_c_literal(name, FALSE) : "noname", part->type ? part->type : "application", part->subtype ? part->subtype : "octet-stream", tmpfile ? " :temp" : "", tmpfile ? tmpfile : "", parttype, mu_msg_part_maybe_attachment (part) ? "t" : "nil", cid ? " :cid" : "", cid ? cid : "", (int)part->size, sig_verdict (part), dec_verdict (part)); g_free (name); g_free (tmpfile); g_free (parttype); g_free (cid); g_free (pinfo->parts); pinfo->parts = tmp; } static void append_sexp_parts (GString *gstr, MuMsg *msg, MuMsgOptions opts) { PartInfo pinfo; pinfo.parts = NULL; pinfo.opts = opts; if (!mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)each_part, &pinfo)) { /* do nothing */ } else if (pinfo.parts) { g_string_append_printf (gstr, "\t:parts (%s)\n", pinfo.parts); g_free (pinfo.parts); } } static void append_sexp_thread_info (GString *gstr, const MuMsgIterThreadInfo *ti) { g_string_append_printf (gstr, "\t:thread (:path \"%s\" :level %u%s%s%s%s)\n", ti->threadpath, ti->level, ti->prop & MU_MSG_ITER_THREAD_PROP_FIRST_CHILD ? " :first-child t" : "", ti->prop & MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT ? " :empty-parent t" : "", ti->prop & MU_MSG_ITER_THREAD_PROP_DUP ? " :duplicate t" : "", ti->prop & MU_MSG_ITER_THREAD_PROP_HAS_CHILD ? " :has-child t" : ""); } static void append_message_file_parts (GString *gstr, MuMsg *msg, MuMsgOptions opts) { const char *str; GError *err; err = NULL; if (!mu_msg_load_msg_file (msg, &err)) { g_warning ("failed to load message file: %s", err ? err->message : "some error occured"); g_clear_error (&err); return; } append_sexp_parts (gstr, msg, opts); append_sexp_contacts (gstr, msg); /* add the user-agent / x-mailer */ str = mu_msg_get_header (msg, "User-Agent"); if (str || (str = mu_msg_get_header (msg, "X-Mailer"))) append_sexp_attr (gstr, "user-agent", str); append_sexp_body_attr (gstr, "body-txt", mu_msg_get_body_text(msg, opts)); append_sexp_body_attr (gstr, "body-html", mu_msg_get_body_html(msg, opts)); } static void append_sexp_date_and_size (GString *gstr, MuMsg *msg) { time_t t; size_t s; t = mu_msg_get_date (msg); if (t == (time_t)-1) /* invalid date? */ t = 0; s = mu_msg_get_size (msg); if (s == (size_t)-1) /* invalid size? */ s = 0; g_string_append_printf (gstr, "\t:date (%u %u 0)\n\t:size %u\n", (unsigned)(t >> 16), (unsigned)(t & 0xffff), (unsigned)s); } static void append_sexp_tags (GString *gstr, MuMsg *msg) { const GSList *tags, *t; gchar *tagesc; GString *tagstr = g_string_new(""); tags = mu_msg_get_tags (msg); for(t = tags; t; t = t->next) { if (t != tags) g_string_append(tagstr, " "); tagesc = mu_str_escape_c_literal((const gchar *)t->data, TRUE); g_string_append(tagstr, tagesc); g_free(tagesc); } if (tagstr->len > 0) g_string_append_printf (gstr, "\t:tags (%s)\n", tagstr->str); g_string_free (tagstr, TRUE); } char* mu_msg_to_sexp (MuMsg *msg, unsigned docid, const MuMsgIterThreadInfo *ti, MuMsgOptions opts) { GString *gstr; g_return_val_if_fail (msg, NULL); g_return_val_if_fail (!((opts & MU_MSG_OPTION_HEADERS_ONLY) && (opts & MU_MSG_OPTION_EXTRACT_IMAGES)),NULL); gstr = g_string_sized_new ((opts & MU_MSG_OPTION_HEADERS_ONLY) ? 1024 : 8192); if (docid == 0) g_string_append (gstr, "(\n"); else g_string_append_printf (gstr, "(\n\t:docid %u\n", docid); if (ti) append_sexp_thread_info (gstr, ti); append_sexp_attr (gstr, "subject", mu_msg_get_subject (msg)); /* in the no-headers-only case (see below) we get a more * complete list of contacts, so no need to get them here if * that's the case */ if (opts & MU_MSG_OPTION_HEADERS_ONLY) append_sexp_contacts (gstr, msg); append_sexp_date_and_size (gstr, msg); append_sexp_attr (gstr, "message-id", mu_msg_get_msgid (msg)); append_sexp_attr (gstr, "mailing-list", mu_msg_get_mailing_list (msg)); append_sexp_attr (gstr, "path", mu_msg_get_path (msg)); append_sexp_attr (gstr, "maildir", mu_msg_get_maildir (msg)); g_string_append_printf (gstr, "\t:priority %s\n", mu_msg_prio_name(mu_msg_get_prio(msg))); append_sexp_flags (gstr, msg); append_sexp_tags (gstr, msg); append_sexp_attr_list (gstr, "references", mu_msg_get_references (msg)); append_sexp_attr (gstr, "in-reply-to", mu_msg_get_header (msg, "In-Reply-To")); /* headers are retrieved from the database, views from the * message file file attr things can only be gotten from the * file (ie., mu view), not from the database (mu find). */ if (!(opts & MU_MSG_OPTION_HEADERS_ONLY)) append_message_file_parts (gstr, msg, opts); g_string_append (gstr, ")\n"); return g_string_free (gstr, FALSE); } mu-0.9.18/lib/mu-msg-part.h0000644000175000017500000001670713020504332012246 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_MSG_PART_H__ #define __MU_MSG_PART_H__ #include #include /* for ssize_t */ #define SIG_STATUS_REPORT "sig-status-report" G_BEGIN_DECLS enum _MuMsgPartType { MU_MSG_PART_TYPE_NONE = 0, /* MIME part without children */ MU_MSG_PART_TYPE_LEAF = 1 << 1, /* an RFC822 message part? */ MU_MSG_PART_TYPE_MESSAGE = 1 << 2, /* disposition inline? */ MU_MSG_PART_TYPE_INLINE = 1 << 3, /* disposition attachment? */ MU_MSG_PART_TYPE_ATTACHMENT = 1 << 4, /* a signed part? */ MU_MSG_PART_TYPE_SIGNED = 1 << 5, /* an encrypted part? */ MU_MSG_PART_TYPE_ENCRYPTED = 1 << 6, /* a decrypted part? */ MU_MSG_PART_TYPE_DECRYPTED = 1 << 7, /* a text/plain part? */ MU_MSG_PART_TYPE_TEXT_PLAIN = 1 << 8, /* a text/html part? */ MU_MSG_PART_TYPE_TEXT_HTML = 1 << 9 }; typedef enum _MuMsgPartType MuMsgPartType; /* the signature status */ enum _MuMsgPartSigStatus { MU_MSG_PART_SIG_STATUS_UNSIGNED = 0, MU_MSG_PART_SIG_STATUS_GOOD, MU_MSG_PART_SIG_STATUS_BAD, MU_MSG_PART_SIG_STATUS_ERROR, MU_MSG_PART_SIG_STATUS_FAIL }; typedef enum _MuMsgPartSigStatus MuMsgPartSigStatus; struct _MuMsgPartSigStatusReport { MuMsgPartSigStatus verdict; const char *report; }; typedef struct _MuMsgPartSigStatusReport MuMsgPartSigStatusReport; /** * destroy a MuMsgPartSignatureStatusReport object * * @param report a MuMsgPartSignatureStatusReport object */ void mu_msg_part_sig_status_report_destroy (MuMsgPartSigStatusReport *report); struct _MuMsgPart { /* index of this message part */ unsigned index; /* cid */ /* const char *content_id; */ /* content-type: type/subtype, ie. text/plain */ const char *type; const char *subtype; /* size of the part; or < 0 if unknown */ ssize_t size; gpointer data; /* opaque data */ MuMsgPartType part_type; MuMsgPartSigStatusReport *sig_status_report; }; typedef struct _MuMsgPart MuMsgPart; /** * get some appropriate file name for the mime-part * * @param mpart a MuMsgPart * @param construct_if_needed if there is no * real filename, construct one. * * @return the file name (free with g_free) */ char *mu_msg_part_get_filename (MuMsgPart *mpart, gboolean construct_if_needed) G_GNUC_WARN_UNUSED_RESULT; /** * get appropriate content id for the mime-part * * @param mpart a MuMsgPart * * @return const content id */ const gchar* mu_msg_part_get_content_id (MuMsgPart *mpart) G_GNUC_WARN_UNUSED_RESULT; /** * get the text in the MuMsgPart (ie. in its GMimePart) * * @param msg a MuMsg * @param part a MuMsgPart * @param opts MuMsgOptions * * @return utf8 string for this MIME part, to be freed by caller */ char* mu_msg_part_get_text (MuMsg *msg, MuMsgPart *part, MuMsgOptions opts) G_GNUC_WARN_UNUSED_RESULT; /** * does this msg part look like an attachment? * * @param part a message part * * @return TRUE if it looks like an attachment, FALSE otherwise */ gboolean mu_msg_part_maybe_attachment (MuMsgPart *part); /** * save a specific attachment to some targetdir * * @param msg a valid MuMsg instance * @param opts mu-message options (OVERWRITE/USE_EXISTING) * @gchar filepath the filepath to save * @param partidx index of the attachment you want to save * @param err receives error information (when function returns NULL) * * @return full path to the message part saved or NULL in case or * error; free with g_free */ gboolean mu_msg_part_save (MuMsg *msg, MuMsgOptions opts, const char *filepath, guint partidx, GError **err); /** * save a message part to a temporary file and return the full path to * this file * * @param msg a MuMsg message * @param opts mu-message options (OVERWRITE/USE_EXISTING) * @param partidx index of the part to save * @param err receives error information if any * * @return the full path to the temp file, or NULL in case of error */ gchar* mu_msg_part_save_temp (MuMsg *msg, MuMsgOptions opts, guint partidx, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * get a filename for the saving the message part; try the filename * specified for the message part if any, otherwise determine a unique * name based on the partidx and the message path * * @param msg a msg * @param opts mu-message options * @param targetdir where to store the part * @param partidx the part for which to determine a filename * @param err receives error information (when function returns NULL) * * @return a filepath (g_free when done with it) or NULL in case of error */ gchar* mu_msg_part_get_path (MuMsg *msg, MuMsgOptions opts, const char* targetdir, guint partidx, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * get a full path name for a file for saving the message part INDEX; * this path is unique (1:1) for this particular message and part for * this user. Thus, it can be used as a cache. * * Will create the directory if needed. * * @param msg a msg * @param opts mu-message options * @param partidx the part for which to determine a filename * @param err receives error information (when function returns NULL) * * @return a filepath (g_free when done with it) or NULL in case of error */ gchar* mu_msg_part_get_cache_path (MuMsg *msg, MuMsgOptions opts, guint partidx, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * get the part index for the message part with a certain content-id * * @param msg a message * @param content_id a content-id to search * * @return the part index number of the found part, or -1 if it was not found */ int mu_msg_find_index_for_cid (MuMsg *msg, MuMsgOptions opts, const char* content_id); /** * retrieve a list of indices for mime-parts with filenames matching a regex * * @param msg a message * @param opts * @param a regular expression to match the filename with * * @return a list with indices for the files matching the pattern; the * indices are the GPOINTER_TO_UINT(lst->data) of the list. They must * be freed with g_slist_free */ GSList* mu_msg_find_files (MuMsg *msg, MuMsgOptions opts, const GRegex *pattern); typedef void (*MuMsgPartForeachFunc) (MuMsg *msg, MuMsgPart*, gpointer); /** * call a function for each of the mime part in a message * * @param msg a valid MuMsg* instance * @param func a callback function to call for each contact; when * the callback does not return TRUE, it won't be called again * @param user_data a user-provide pointer that will be passed to the callback * @param options, bit-wise OR'ed * * @return FALSE in case of error, TRUE otherwise */ gboolean mu_msg_part_foreach (MuMsg *msg, MuMsgOptions opts, MuMsgPartForeachFunc func, gpointer user_data); G_END_DECLS #endif /*__MU_MSG_PART_H__*/ mu-0.9.18/lib/mu-msg-doc.cc0000644000175000017500000000630412605152236012205 00000000000000/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include #include #include #include #include #include "mu-util.h" #include "mu-msg-fields.h" #include "mu-msg-doc.h" #include "mu-str.h" #include "mu-date.h" struct _MuMsgDoc { _MuMsgDoc (Xapian::Document *doc): _doc (doc) { } ~_MuMsgDoc () { delete _doc; } const Xapian::Document doc() const { return *_doc; } private: Xapian::Document *_doc; }; MuMsgDoc* mu_msg_doc_new (XapianDocument *doc, GError **err) { g_return_val_if_fail (doc, NULL); try { return new MuMsgDoc ((Xapian::Document*)doc); } MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(err, MU_ERROR_XAPIAN, NULL); return FALSE; } void mu_msg_doc_destroy (MuMsgDoc *self) { try { delete self; } MU_XAPIAN_CATCH_BLOCK; } gchar* mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), NULL); // disable this check: // g_return_val_if_fail (mu_msg_field_is_string(mfid), NULL); // because it's useful to get numerical field as strings, // for example when sorting (which is much faster if don't // have to convert to numbers first, esp. when it's a date // time_t) try { const std::string s (self->doc().get_value(mfid)); return s.empty() ? NULL : g_strdup (s.c_str()); } MU_XAPIAN_CATCH_BLOCK_RETURN(NULL); } GSList* mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, NULL); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), NULL); g_return_val_if_fail (mu_msg_field_is_string_list(mfid), NULL); try { /* return a comma-separated string as a GSList */ const std::string s (self->doc().get_value(mfid)); return s.empty() ? NULL : mu_str_to_list(s.c_str(),',',TRUE); } MU_XAPIAN_CATCH_BLOCK_RETURN(NULL); } gint64 mu_msg_doc_get_num_field (MuMsgDoc *self, MuMsgFieldId mfid) { g_return_val_if_fail (self, -1); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), -1); g_return_val_if_fail (mu_msg_field_is_numeric(mfid), -1); /* date is a special case, because we store dates as * strings */ try { const std::string s (self->doc().get_value(mfid)); if (s.empty()) return 0; else if (mfid == MU_MSG_FIELD_ID_DATE) { time_t t; t = mu_date_str_to_time_t (s.c_str(), FALSE/*utc*/); return static_cast(t); } else { return static_cast (Xapian::sortable_unserialise(s)); } } MU_XAPIAN_CATCH_BLOCK_RETURN(-1); } mu-0.9.18/lib/mu-msg-iter.h0000644000175000017500000001420013020504332012225 00000000000000/* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_MSG_ITER_H__ #define __MU_MSG_ITER_H__ #include #include G_BEGIN_DECLS /** * MuMsgIter is a structure to iterate over the results of a * query. You can iterate only in one-direction, and you can do it * only once. * */ struct _MuMsgIter; typedef struct _MuMsgIter MuMsgIter; enum _MuMsgIterFlags { MU_MSG_ITER_FLAG_NONE = 0, /* sort Z->A (only for threads) */ MU_MSG_ITER_FLAG_DESCENDING = 1 << 0, /* ignore results for which there is no existing * readable message-file? */ MU_MSG_ITER_FLAG_SKIP_UNREADABLE = 1 << 1, /* ignore duplicate messages? */ MU_MSG_ITER_FLAG_SKIP_DUPS = 1 << 2, /* calculate threads? */ MU_MSG_ITER_FLAG_THREADS = 1 << 3 }; typedef unsigned MuMsgIterFlags; /** * create a new MuMsgIter -- basically, an iterator over the search * results * * @param enq a Xapian::Enquire* cast to XapianEnquire* (because this * is C, not C++),providing access to search results * @param maxnum the maximum number of results * @param sortfield field to sort by * @param flags flags for this iterator (see MsgIterFlags) * @param err receives error information. if the error is * MU_ERROR_XAPIAN_MODIFIED, the database should be reloaded. * * @return a new MuMsgIter, or NULL in case of error */ MuMsgIter *mu_msg_iter_new (XapianEnquire *enq, size_t maxnum, MuMsgFieldId sortfield, MuMsgIterFlags flags, GError **err) G_GNUC_WARN_UNUSED_RESULT; /** * get the next message (which you got from * e.g. mu_query_run) * * @param iter a valid MuMsgIter iterator * * @return TRUE if it succeeded, FALSE otherwise (e.g., because there * are no more messages in the query result) */ gboolean mu_msg_iter_next (MuMsgIter *iter); /** * reset the iterator to the beginning * * @param iter a valid MuMsgIter iterator * * @return TRUE if it succeeded, FALSE otherwise */ gboolean mu_msg_iter_reset (MuMsgIter *iter); /** * does this iterator point past the end of the list? * * @param iter a valid MuMsgIter iterator * * @return TRUE if the iter points past end of the list, FALSE * otherwise */ gboolean mu_msg_iter_is_done (MuMsgIter *iter); /** * destroy the sequence of messages; ie. /all/ of them * * @param msg a valid MuMsgIter message or NULL */ void mu_msg_iter_destroy (MuMsgIter *iter); /** * get the corresponding MuMsg for this iter; this instance is owned * by MuMsgIter, and becomes invalid after either mu_msg_iter_destroy * or mu_msg_iter_next. _do not_ unref it; it's a floating reference. * * @param iter a valid MuMsgIter instance* * * @return a MuMsg instance, or NULL in case of error */ MuMsg* mu_msg_iter_get_msg_floating (MuMsgIter *iter) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Provide a preferred_hash, which is a hashtable msgid->docid to * indicate the messages which should /not/ be seen as duplicates. * * @param iter a valid MuMsgIter iterator * @param preferred_hash a hashtable msgid->docid of message /not/ to * mark as duplicates, or NULL */ void mu_msg_iter_set_preferred (MuMsgIter *iter, GHashTable *preferred_hash); /** * get the document id for the current message * * @param iter a valid MuMsgIter iterator * * @return the docid or (unsigned int)-1 in case of error */ guint mu_msg_iter_get_docid (MuMsgIter *iter); /** * calculate the message threads * * @param iter a valid MuMsgIter iterator * * @return TRUE if it worked, FALSE otherwsie. */ gboolean mu_msg_iter_calculate_threads (MuMsgIter *iter); enum _MuMsgIterThreadProp { MU_MSG_ITER_THREAD_PROP_NONE = 0 << 0, MU_MSG_ITER_THREAD_PROP_ROOT = 1 << 0, MU_MSG_ITER_THREAD_PROP_FIRST_CHILD = 1 << 1, MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT = 1 << 2, MU_MSG_ITER_THREAD_PROP_DUP = 1 << 3, MU_MSG_ITER_THREAD_PROP_HAS_CHILD = 1 << 4 }; typedef guint8 MuMsgIterThreadProp; struct _MuMsgIterThreadInfo { gchar *threadpath; /* a string decribing the thread-path in * such a way that we can sort by this * string to get the right order. */ guint level; /* thread-depth -- [0...] */ MuMsgIterThreadProp prop; }; typedef struct _MuMsgIterThreadInfo MuMsgIterThreadInfo; /** * get a the MuMsgThreaderInfo struct for this message; this only * works when you created the mu-msg-iter with threading enabled * (otherwise, return NULL) * * @param iter a valid MuMsgIter iterator * * @return an info struct */ const MuMsgIterThreadInfo* mu_msg_iter_get_thread_info (MuMsgIter *iter); /** * get the message-id for this message * * @param iter a valid MuMsgIter iterator * * @return the message-id; free with g_free(). */ char* mu_msg_iter_get_msgid (MuMsgIter *iter) G_GNUC_WARN_UNUSED_RESULT; /** * get the list of references for this messages as a NULL-terminated * string array * * @param iter a valid MuMsgIter iterator * * @return a NULL-terminated string array. free with g_strfreev when * it's no longer needed. */ char** mu_msg_iter_get_refs (MuMsgIter *iter) G_GNUC_WARN_UNUSED_RESULT; /** * get the thread-id for this message * * @param iter a valid MuMsgIter iterator * * @return the thread-id; free with g_free(). */ char* mu_msg_iter_get_thread_id (MuMsgIter *iter) G_GNUC_WARN_UNUSED_RESULT; /* FIXME */ const char* mu_msg_iter_get_path (MuMsgIter *iter); G_END_DECLS #endif /*__MU_MSG_ITER_H__*/ mu-0.9.18/lib/mu-msg-fields.c0000644000175000017500000002443313020504332012534 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include #include "mu-msg-fields.h" /* * note: the differences for our purposes between a xapian field and a * term: - there is only a single value for some item in per document * (msg), ie. one value containing the list of To: addresses - there * can be multiple terms, each containing e.g. one of the To: * addresses - searching uses terms, but to display some field, it * must be in the value (at least when using MuMsgIter) */ enum _FieldFlags { FLAG_GMIME = 1 << 0, /* field retrieved through * gmime */ FLAG_XAPIAN_INDEX = 1 << 1, /* field is indexed in * xapian (i.e., the text * is processed */ FLAG_XAPIAN_TERM = 1 << 2, /* field stored as term in * xapian (so it can be searched) */ FLAG_XAPIAN_VALUE = 1 << 3, /* field stored as value in * xapian (so the literal * value can be * retrieved) */ FLAG_XAPIAN_CONTACT = 1 << 4, /* field contains one or more * e-mail-addresses */ FLAG_XAPIAN_BOOLEAN = 1 << 5, /* use 'add_boolean_prefix' * for Xapian queries; * wildcards do NOT WORK * for such fields */ FLAG_PREPROCESS = 1 << 6, /* field needs flattening for * case/accents */ FLAG_DONT_CACHE = 1 << 7, /* don't cache this field in * the MuMsg cache */ FLAG_RANGE_FIELD = 1 << 8 /* whether this is a range field */ }; typedef enum _FieldFlags FieldFlags; /* * this struct describes the fields of an e-mail /*/ struct _MuMsgField { MuMsgFieldId _id; /* the id of the field */ MuMsgFieldType _type; /* the type of the field */ const char *_name; /* the name of the field */ const char _shortcut; /* the shortcut for use in * --fields and sorting */ const char _xprefix; /* the Xapian-prefix */ FieldFlags _flags; /* the flags that tells us * what to do */ }; typedef struct _MuMsgField MuMsgField; /* the name and shortcut fields must be lower case, or they might be * misinterpreted by the query-preprocesser which turns queries into * lowercase */ static const MuMsgField FIELD_DATA[] = { { MU_MSG_FIELD_ID_BCC, MU_MSG_FIELD_TYPE_STRING, "bcc" , 'h', 'H', /* 'hidden */ FLAG_GMIME | FLAG_XAPIAN_CONTACT | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_BODY_TEXT, MU_MSG_FIELD_TYPE_STRING, "body", 'b', 'B', FLAG_GMIME | FLAG_XAPIAN_INDEX | FLAG_PREPROCESS | FLAG_DONT_CACHE }, { MU_MSG_FIELD_ID_BODY_HTML, MU_MSG_FIELD_TYPE_STRING, "bodyhtml", 0, 0, FLAG_GMIME | FLAG_DONT_CACHE }, { MU_MSG_FIELD_ID_CC, MU_MSG_FIELD_TYPE_STRING, "cc", 'c', 'C', FLAG_GMIME | FLAG_XAPIAN_CONTACT | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_DATE, MU_MSG_FIELD_TYPE_TIME_T, "date", 'd', 'D', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | FLAG_XAPIAN_BOOLEAN | FLAG_RANGE_FIELD }, { MU_MSG_FIELD_ID_EMBEDDED_TEXT, MU_MSG_FIELD_TYPE_STRING, "embed", 'e', 'E', FLAG_GMIME | FLAG_XAPIAN_INDEX | FLAG_PREPROCESS | FLAG_DONT_CACHE }, { MU_MSG_FIELD_ID_FILE, MU_MSG_FIELD_TYPE_STRING, "file" , 'j', 'J', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_PREPROCESS | FLAG_DONT_CACHE }, { MU_MSG_FIELD_ID_FLAGS, MU_MSG_FIELD_TYPE_INT, "flag", 'g', 'G', /* flaGs */ FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE }, { MU_MSG_FIELD_ID_FROM, MU_MSG_FIELD_TYPE_STRING, "from", 'f', 'F', FLAG_GMIME | FLAG_XAPIAN_CONTACT | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_MAILDIR, MU_MSG_FIELD_TYPE_STRING, "maildir", 'm', 'M', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_MAILING_LIST, MU_MSG_FIELD_TYPE_STRING, "list", 'v', 'V', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_MIME, MU_MSG_FIELD_TYPE_STRING, "mime" , 'y', 'Y', FLAG_XAPIAN_TERM | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_MSGID, MU_MSG_FIELD_TYPE_STRING, "msgid", 'i', 'I', /* 'i' for Id */ FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_PATH, MU_MSG_FIELD_TYPE_STRING, "path", 'l', 'L', /* 'l' for location */ FLAG_GMIME | FLAG_XAPIAN_VALUE | FLAG_XAPIAN_BOOLEAN | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_PRIO, MU_MSG_FIELD_TYPE_INT, "prio", 'p', 'P', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE }, { MU_MSG_FIELD_ID_REFS, MU_MSG_FIELD_TYPE_STRING_LIST, "refs", 'r', 'R', FLAG_GMIME | FLAG_XAPIAN_VALUE }, { MU_MSG_FIELD_ID_SIZE, MU_MSG_FIELD_TYPE_BYTESIZE, "size", 'z', 'Z', /* siZe */ FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | FLAG_RANGE_FIELD }, { MU_MSG_FIELD_ID_SUBJECT, MU_MSG_FIELD_TYPE_STRING, "subject", 's', 'S', FLAG_GMIME | FLAG_XAPIAN_INDEX | FLAG_XAPIAN_VALUE | FLAG_XAPIAN_TERM | FLAG_PREPROCESS }, { MU_MSG_FIELD_ID_TAGS, MU_MSG_FIELD_TYPE_STRING_LIST, "tag", 'x', 'X', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_PREPROCESS | FLAG_XAPIAN_VALUE }, { /* remember which thread this message is in */ MU_MSG_FIELD_ID_THREAD_ID, MU_MSG_FIELD_TYPE_STRING, "thread", 0, 'W', FLAG_XAPIAN_TERM }, { MU_MSG_FIELD_ID_TO, MU_MSG_FIELD_TYPE_STRING, "to", 't', 'T', FLAG_GMIME | FLAG_XAPIAN_CONTACT | FLAG_XAPIAN_VALUE | FLAG_PREPROCESS }, { /* special, internal field, to get a unique key */ MU_MSG_FIELD_ID_UID, MU_MSG_FIELD_TYPE_STRING, "uid", 0, 'U', FLAG_XAPIAN_TERM } /* note, mu-store also use the 'Q' internal prefix for its uids */ }; /* the MsgField data in an array, indexed by the MsgFieldId; * this allows for O(1) access */ static MuMsgField* _msg_field_data[MU_MSG_FIELD_ID_NUM]; static const MuMsgField* mu_msg_field (MuMsgFieldId id) { static gboolean _initialized; _initialized = FALSE; /* initialize the array, but only once... */ if (G_UNLIKELY(!_initialized)) { int i; for (i = 0; i != G_N_ELEMENTS(FIELD_DATA); ++i) _msg_field_data[FIELD_DATA[i]._id] = (MuMsgField*)&FIELD_DATA[i]; _initialized = TRUE; } return _msg_field_data[id]; } void mu_msg_field_foreach (MuMsgFieldForeachFunc func, gconstpointer data) { int i; for (i = 0; i != MU_MSG_FIELD_ID_NUM; ++i) func (i, data); } MuMsgFieldId mu_msg_field_id_from_name (const char* str, gboolean err) { int i; g_return_val_if_fail (str, MU_MSG_FIELD_ID_NONE); for (i = 0; i != G_N_ELEMENTS(FIELD_DATA); ++i) if (g_strcmp0(str, FIELD_DATA[i]._name) == 0) return FIELD_DATA[i]._id; if (err) g_return_val_if_reached (MU_MSG_FIELD_ID_NONE); return MU_MSG_FIELD_ID_NONE; } MuMsgFieldId mu_msg_field_id_from_shortcut (char kar, gboolean err) { int i; for (i = 0; i != G_N_ELEMENTS(FIELD_DATA); ++i) if (kar == FIELD_DATA[i]._shortcut) return FIELD_DATA[i]._id; if (err) g_return_val_if_reached (MU_MSG_FIELD_ID_NONE); return MU_MSG_FIELD_ID_NONE; } gboolean mu_msg_field_gmime (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_GMIME ? TRUE: FALSE; } gboolean mu_msg_field_xapian_index (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_XAPIAN_INDEX ? TRUE: FALSE; } gboolean mu_msg_field_xapian_value (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_XAPIAN_VALUE ? TRUE: FALSE; } gboolean mu_msg_field_xapian_term (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_XAPIAN_TERM ? TRUE: FALSE; } gboolean mu_msg_field_is_range_field (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_RANGE_FIELD ? TRUE: FALSE; } gboolean mu_msg_field_uses_boolean_prefix (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_XAPIAN_BOOLEAN ? TRUE:FALSE; } gboolean mu_msg_field_is_cacheable (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); /* note the FALSE: TRUE */ return mu_msg_field(id)->_flags & FLAG_DONT_CACHE ? FALSE : TRUE; } gboolean mu_msg_field_xapian_contact (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_XAPIAN_CONTACT ? TRUE: FALSE; } gboolean mu_msg_field_preprocess (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),FALSE); return mu_msg_field(id)->_flags & FLAG_PREPROCESS ? TRUE: FALSE; } gboolean mu_msg_field_is_numeric (MuMsgFieldId mfid) { MuMsgFieldType type; g_return_val_if_fail (mu_msg_field_id_is_valid(mfid),FALSE); type = mu_msg_field_type (mfid); return type == MU_MSG_FIELD_TYPE_BYTESIZE || type == MU_MSG_FIELD_TYPE_TIME_T || type == MU_MSG_FIELD_TYPE_INT; } const char* mu_msg_field_name (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),NULL); return mu_msg_field(id)->_name; } char mu_msg_field_shortcut (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),0); return mu_msg_field(id)->_shortcut; } char mu_msg_field_xapian_prefix (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id),0); return mu_msg_field(id)->_xprefix; } MuMsgFieldType mu_msg_field_type (MuMsgFieldId id) { g_return_val_if_fail (mu_msg_field_id_is_valid(id), MU_MSG_FIELD_TYPE_NONE); return mu_msg_field(id)->_type; } mu-0.9.18/lib/mu-bookmarks.c0000644000175000017500000000603012605152236012475 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2010-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #include #include "mu-bookmarks.h" #define MU_BOOKMARK_GROUP "mu" struct _MuBookmarks { char *_bmpath; GHashTable *_hash; }; static void fill_hash (GHashTable *hash, GKeyFile *kfile) { gchar **keys, **cur; keys = g_key_file_get_keys (kfile, MU_BOOKMARK_GROUP, NULL, NULL); if (!keys) return; for (cur = keys; *cur; ++cur) { gchar *val; val = g_key_file_get_string (kfile, MU_BOOKMARK_GROUP, *cur, NULL); if (val) g_hash_table_insert (hash, *cur, val); } /* don't use g_strfreev, because we put them in the hash table; * only free the gchar** itself */ g_free (keys); } static GHashTable* create_hash_from_key_file (const gchar *bmpath) { GKeyFile *kfile; GHashTable *hash; kfile = g_key_file_new (); if (!g_key_file_load_from_file (kfile, bmpath, G_KEY_FILE_NONE, NULL)) { g_key_file_free (kfile); return NULL; } hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); fill_hash (hash, kfile); g_key_file_free (kfile); return hash; } MuBookmarks* mu_bookmarks_new (const gchar *bmpath) { MuBookmarks *bookmarks; GHashTable *hash; g_return_val_if_fail (bmpath, NULL); hash = create_hash_from_key_file (bmpath); if (!hash) return NULL; bookmarks = g_new (MuBookmarks, 1); bookmarks->_bmpath = g_strdup (bmpath); bookmarks->_hash = hash; return bookmarks; } void mu_bookmarks_destroy (MuBookmarks *bm) { if (!bm) return; g_free (bm->_bmpath); g_hash_table_destroy (bm->_hash); g_free (bm); } const gchar* mu_bookmarks_lookup (MuBookmarks *bm, const gchar *name) { g_return_val_if_fail (bm, NULL); g_return_val_if_fail (name, NULL); return g_hash_table_lookup (bm->_hash, name); } struct _BMData { MuBookmarksForeachFunc _func; gpointer _user_data; }; typedef struct _BMData BMData; static void each_bookmark (const gchar* key, const gchar *val, BMData *bmdata) { bmdata->_func (key, val, bmdata->_user_data); } void mu_bookmarks_foreach (MuBookmarks *bm, MuBookmarksForeachFunc func, gpointer user_data) { BMData bmdata; g_return_if_fail (bm); g_return_if_fail (func); bmdata._func = func; bmdata._user_data = user_data; g_hash_table_foreach (bm->_hash, (GHFunc)each_bookmark, &bmdata); } mu-0.9.18/lib/mu-threader.h0000644000175000017500000000365112605152237012317 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_THREADER_H__ #define __MU_THREADER_H__ #include #include G_BEGIN_DECLS /** * takes an iter and the total number of matches, and from this * generates a hash-table with information about the thread structure * of these matches. * * the algorithm to find this structure is based on JWZ's * message-threading algorithm, as descrbed in: * http://www.jwz.org/doc/threading.html * * the returned hashtable maps the Xapian docid of iter (msg) to a ptr * to a MuMsgIterThreadInfo structure (see mu-msg-iter.h) * * @param iter an iter; note this function will mu_msgi_iter_reset this iterator * @param matches the number of matches in the set * * @param sortfield the field to sort results by, or * MU_MSG_FIELD_ID_NONE if no sorting should be performed * @param revert if TRUE, if revert the sorting order * * @return a hashtable; free with g_hash_table_destroy when done with it */ GHashTable *mu_threader_calculate (MuMsgIter *iter, size_t matches, MuMsgFieldId sortfield, gboolean revert); G_END_DECLS #endif /*__MU_THREADER_H__*/ mu-0.9.18/lib/mu-msg-file.h0000644000175000017500000000613412605152236012222 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_MSG_FILE_H__ #define __MU_MSG_FILE_H__ struct _MuMsgFile; typedef struct _MuMsgFile MuMsgFile; /** * create a new message from a file * * @param path full path to the message * @param mdir * @param err error to receive (when function returns NULL), or NULL * * @return a new MuMsg, or NULL in case of error */ MuMsgFile *mu_msg_file_new (const char *path, const char* mdir, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * destroy a MuMsgFile object * * @param self object to destroy, or NULL */ void mu_msg_file_destroy (MuMsgFile *self); /** * get a specific header * * @param self a MuMsgFile instance * @param header a header (e.g. 'X-Mailer' or 'List-Id') * * @return the value of the header or NULL if not found; free with g_free */ char* mu_msg_file_get_header (MuMsgFile *self, const char *header); /** * get a string value for this message * * @param self a valid MuMsgFile * @param msfid the message field id to get (must be of type string) * @param do_free receives TRUE or FALSE, conveying if this string * should be owned & freed (TRUE) or not by caller. In case 'FALSE', * this function should be treated as if it were returning a const * char*, and note that in that case the string is only valid as long * as the MuMsgFile is alive, ie. before mu_msg_file_destroy * * @return a string, or NULL */ char* mu_msg_file_get_str_field (MuMsgFile *self, MuMsgFieldId msfid, gboolean *do_free) G_GNUC_WARN_UNUSED_RESULT; /** * get a string-list value for this message * * @param self a valid MuMsgFile * @param msfid the message field id to get (must be of type string-list) * * @return a GSList*, or NULL; free with mu_str_free_list */ GSList* mu_msg_file_get_str_list_field (MuMsgFile *self, MuMsgFieldId msfid) G_GNUC_WARN_UNUSED_RESULT; /** * get a numeric value for this message -- the return value should be * cast into the actual type, e.g., time_t, MuMsgPrio etc. * * @param self a valid MuMsgFile * @param msfid the message field id to get (must be string-based one) * * @return the numeric value, or -1 in case of error */ gint64 mu_msg_file_get_num_field (MuMsgFile *self, MuMsgFieldId mfid); #endif /*__MU_MSG_FILE_H__*/ mu-0.9.18/lib/mu-msg-part.c0000644000175000017500000005703313020504332012236 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2014 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include "mu-util.h" #include "mu-str.h" #include "mu-msg-priv.h" #include "mu-msg-part.h" struct _DoData { GMimeObject *mime_obj; unsigned index; }; typedef struct _DoData DoData; static void do_it_with_index (MuMsg *msg, MuMsgPart *part, DoData *ddata) { if (ddata->mime_obj) return; if (part->index == ddata->index) { /* Add a reference to this object, this way if it is * encrypted it will not be garbage collected before * we are done with it. */ g_object_ref (part->data); ddata->mime_obj = (GMimeObject*)part->data; } } static GMimeObject* get_mime_object_at_index (MuMsg *msg, MuMsgOptions opts, unsigned index) { DoData ddata; ddata.mime_obj = NULL; ddata.index = index; mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)do_it_with_index, &ddata); return ddata.mime_obj; } typedef gboolean (*MuMsgPartMatchFunc) (MuMsgPart *, gpointer); struct _MatchData { MuMsgPartMatchFunc match_func; gpointer user_data; int index; }; typedef struct _MatchData MatchData; static void check_match (MuMsg *msg, MuMsgPart *part, MatchData *mdata) { if (mdata->index != -1) return; if (mdata->match_func (part, mdata->user_data)) mdata->index = part->index; } static int get_matching_part_index (MuMsg *msg, MuMsgOptions opts, MuMsgPartMatchFunc func, gpointer user_data) { MatchData mdata; mdata.match_func = func; mdata.user_data = user_data; mdata.index = -1; mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)check_match, &mdata); return mdata.index; } static void accumulate_text_message (MuMsg *msg, MuMsgPart *part, GString **gstrp) { const gchar *str; char *adrs; GMimeMessage *mimemsg; InternetAddressList *addresses; /* put sender, recipients and subject in the string, so they * can be indexed as well */ mimemsg = GMIME_MESSAGE (part->data); str = g_mime_message_get_sender (mimemsg); g_string_append_printf (*gstrp, "%s%s", str ? str : "", str ? "\n" : ""); str = g_mime_message_get_subject (mimemsg); g_string_append_printf (*gstrp, "%s%s", str ? str : "", str ? "\n" : ""); addresses = g_mime_message_get_all_recipients (mimemsg); adrs = internet_address_list_to_string (addresses, FALSE); g_object_unref (addresses); g_string_append_printf (*gstrp, "%s%s", adrs ? adrs : "", adrs ? "\n" : ""); g_free (adrs); } static void accumulate_text_part (MuMsg *msg, MuMsgPart *part, GString **gstrp) { GMimeContentType *ctype; gboolean err; char *txt; ctype = g_mime_object_get_content_type ((GMimeObject*)part->data); if (!g_mime_content_type_is_type (ctype, "text", "plain")) return; /* not plain text */ txt = mu_msg_mime_part_to_string ((GMimePart*)part->data, &err); if (txt) g_string_append (*gstrp, txt); g_free (txt); } static void accumulate_text (MuMsg *msg, MuMsgPart *part, GString **gstrp) { if (GMIME_IS_MESSAGE(part->data)) accumulate_text_message (msg, part, gstrp); else if (GMIME_IS_PART (part->data)) accumulate_text_part (msg, part, gstrp); } /* declaration, so we can use it earlier */ static gboolean handle_mime_object (MuMsg *msg, GMimeObject *mobj, GMimeObject *parent, MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data); static char* get_text_from_mime_msg (MuMsg *msg, GMimeMessage *mmsg, MuMsgOptions opts) { GString *gstr; unsigned index; index = 1; gstr = g_string_sized_new (4096); handle_mime_object (msg, mmsg->mime_part, (GMimeObject *) mmsg, opts, &index, FALSE, (MuMsgPartForeachFunc)accumulate_text, &gstr); return g_string_free (gstr, FALSE); } char* mu_msg_part_get_text (MuMsg *msg, MuMsgPart *self, MuMsgOptions opts) { GMimeObject *mobj; GMimeMessage *mime_msg; gboolean err; g_return_val_if_fail (msg, NULL); g_return_val_if_fail (self && GMIME_IS_OBJECT(self->data), NULL); mobj = (GMimeObject*)self->data; err = FALSE; if (GMIME_IS_PART (mobj)) { if (self->part_type & MU_MSG_PART_TYPE_TEXT_PLAIN) return mu_msg_mime_part_to_string ((GMimePart*)mobj, &err); else return NULL; /* non-text MimePart */ } mime_msg = NULL; if (GMIME_IS_MESSAGE_PART (mobj)) mime_msg = g_mime_message_part_get_message ((GMimeMessagePart*)mobj); else if (GMIME_IS_MESSAGE (mobj)) mime_msg = (GMimeMessage*)mobj; /* apparently, g_mime_message_part_get_message may still * return NULL */ if (mime_msg) return get_text_from_mime_msg (msg, mime_msg, opts); return NULL; } /* note: this will return -1 in case of error or if the size is * unknown */ static ssize_t get_part_size (GMimePart *part) { GMimeDataWrapper *wrapper; GMimeStream *stream; wrapper = g_mime_part_get_content_object (part); if (!GMIME_IS_DATA_WRAPPER(wrapper)) return -1; stream = g_mime_data_wrapper_get_stream (wrapper); if (!stream) return -1; /* no stream -> size is 0 */ else return g_mime_stream_length (stream); /* NOTE: stream/wrapper are owned by gmime, no unreffing */ } static char* cleanup_filename (char *fname) { GString *gstr; gchar *cur; gunichar uc; gstr = g_string_sized_new (strlen (fname)); /* replace control characters, slashes, and colons by '-' */ for (cur = fname; cur && *cur; cur = g_utf8_next_char (cur)) { uc = g_utf8_get_char (cur); if (g_unichar_iscntrl (uc) || uc == '/' || uc == ':') g_string_append_unichar (gstr, '-'); else g_string_append_unichar (gstr, uc); } g_free (fname); return g_string_free (gstr, FALSE); } /* * when a part doesn't have a filename, it can be useful to 'guess' one based on * its mime-type, which allows other tools to handle them correctly, e.g. from * mu4e. * * For now, we only handle calendar invitations in that way, but others may * follow. */ static char* guess_file_name (GMimeObject *mobj, unsigned index) { GMimeContentType *ctype; ctype = g_mime_object_get_content_type (mobj); /* special case for calendars; map to '.vcs' */ if (g_mime_content_type_is_type (ctype, "text", "calendar")) return g_strdup_printf ("vcal-%u.vcs", index); /* fallback */ return g_strdup_printf ("%u.msgpart", index); } static char* mime_part_get_filename (GMimeObject *mobj, unsigned index, gboolean construct_if_needed) { gchar *fname; fname = NULL; if (GMIME_IS_PART (mobj)) { /* the easy case: the part has a filename */ fname = (gchar*)g_mime_part_get_filename (GMIME_PART(mobj)); if (fname) /* don't include directory components */ fname = g_path_get_basename (fname); } if (!fname && !construct_if_needed) return NULL; if (GMIME_IS_MESSAGE_PART(mobj)) { GMimeMessage *msg; const char *subj; msg = g_mime_message_part_get_message (GMIME_MESSAGE_PART(mobj)); subj = g_mime_message_get_subject (msg); fname = g_strdup_printf ("%s.eml", subj ? subj : "message"); } if (!fname) fname = guess_file_name (mobj, index); /* replace control characters, slashes, and colons */ fname = cleanup_filename (fname); return fname; } char* mu_msg_part_get_filename (MuMsgPart *mpart, gboolean construct_if_needed) { g_return_val_if_fail (mpart, NULL); g_return_val_if_fail (GMIME_IS_OBJECT(mpart->data), NULL); return mime_part_get_filename ((GMimeObject*)mpart->data, mpart->index, construct_if_needed); } const gchar* mu_msg_part_get_content_id (MuMsgPart *mpart) { g_return_val_if_fail (mpart, NULL); g_return_val_if_fail (GMIME_IS_OBJECT(mpart->data), NULL); return g_mime_object_get_content_id((GMimeObject*)mpart->data); } static MuMsgPartType get_disposition (GMimeObject *mobj) { const char *disp; disp = g_mime_object_get_disposition (mobj); if (!disp) return MU_MSG_PART_TYPE_NONE; if (strcasecmp (disp, GMIME_DISPOSITION_ATTACHMENT) == 0) return MU_MSG_PART_TYPE_ATTACHMENT; if (strcasecmp (disp, GMIME_DISPOSITION_INLINE) == 0) return MU_MSG_PART_TYPE_INLINE; return MU_MSG_PART_TYPE_NONE; } /* call 'func' with information about this MIME-part */ static inline void check_signature (MuMsg *msg, GMimeMultipartSigned *part, MuMsgOptions opts) { GError *err; err = NULL; mu_msg_crypto_verify_part (part, opts, &err); if (err) { g_warning ("error verifying signature: %s", err->message); g_clear_error (&err); } } /* Note: this is function will be called by GMime when it needs a * password. However, GMime <= 2.6.10 does not handle * getting passwords correctly, so this might fail. see: * password_requester in mu-msg-crypto.c */ static gchar* get_console_pw (const char* user_id, const char *prompt_ctx, gboolean reprompt, gpointer user_data) { char *prompt, *pass; if (!g_mime_check_version(2,6,11)) g_printerr ( "*** the gmime library you are using has version " "%u.%u.%u (<= 2.6.10)\n" "*** this version has a bug in its password " "retrieval routine, and probably won't work.\n", gmime_major_version, gmime_minor_version, gmime_micro_version); if (reprompt) g_print ("Authentication failed. Please try again\n"); prompt = g_strdup_printf ("Password for %s: ", user_id); pass = mu_util_read_password (prompt); g_free (prompt); return pass; } static gboolean handle_encrypted_part (MuMsg *msg, GMimeMultipartEncrypted *part, MuMsgOptions opts, unsigned *index, MuMsgPartForeachFunc func, gpointer user_data) { GError *err; gboolean rv; GMimeObject *dec; MuMsgPartPasswordFunc pw_func; if (opts & MU_MSG_OPTION_CONSOLE_PASSWORD) pw_func = (MuMsgPartPasswordFunc)get_console_pw; else pw_func = NULL; err = NULL; dec = mu_msg_crypto_decrypt_part (part, opts, pw_func, NULL, &err); if (err) { g_warning ("error decrypting part: %s", err->message); g_clear_error (&err); } if (dec) { rv = handle_mime_object (msg, dec, (GMimeObject *) part, opts, index, TRUE, func, user_data); g_object_unref (dec); } else { /* On failure to decrypt, list the encrypted part as * an attachment */ GMimeObject *encrypted; encrypted = g_mime_multipart_get_part ( GMIME_MULTIPART (part), 1); g_return_val_if_fail (GMIME_IS_PART(encrypted), FALSE); rv = handle_mime_object (msg, encrypted, (GMimeObject *) part, opts, index, FALSE, func, user_data); } return rv; } static gboolean looks_like_text_body_part (GMimeContentType *ctype) { unsigned u; static struct { const char *type; const char *subtype; } types[] = { { "text", "plain" }, { "text", "x-diff" }, { "text", "x-patch" }, { "application", "x-patch"} /* possible other types */ }; for (u = 0; u != G_N_ELEMENTS(types); ++u) if (g_mime_content_type_is_type ( ctype, types[u].type, types[u].subtype)) return TRUE; return FALSE; } /* call 'func' with information about this MIME-part */ static gboolean handle_part (MuMsg *msg, GMimePart *part, GMimeObject *parent, MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data) { GMimeContentType *ct; MuMsgPart msgpart; memset (&msgpart, 0, sizeof(MuMsgPart)); msgpart.size = get_part_size (part); msgpart.part_type = MU_MSG_PART_TYPE_LEAF; msgpart.part_type |= get_disposition ((GMimeObject*)part); if (decrypted) msgpart.part_type |= MU_MSG_PART_TYPE_DECRYPTED; else if ((opts & MU_MSG_OPTION_DECRYPT) && GMIME_IS_MULTIPART_ENCRYPTED (parent)) msgpart.part_type |= MU_MSG_PART_TYPE_ENCRYPTED; ct = g_mime_object_get_content_type ((GMimeObject*)part); if (GMIME_IS_CONTENT_TYPE(ct)) { msgpart.type = g_mime_content_type_get_media_type (ct); msgpart.subtype = g_mime_content_type_get_media_subtype (ct); /* store in the part_type as well, for quick checking */ if (looks_like_text_body_part (ct)) msgpart.part_type |= MU_MSG_PART_TYPE_TEXT_PLAIN; else if (g_mime_content_type_is_type (ct, "text", "html")) msgpart.part_type |= MU_MSG_PART_TYPE_TEXT_HTML; } /* put the verification info in the pgp-signature and every * descendent of a pgp-encrypted part */ msgpart.sig_status_report = NULL; if (g_ascii_strcasecmp (msgpart.subtype, "pgp-signature") == 0 || decrypted) { msgpart.sig_status_report = (MuMsgPartSigStatusReport*) g_object_get_data (G_OBJECT(parent), SIG_STATUS_REPORT); if (msgpart.sig_status_report) msgpart.part_type |= MU_MSG_PART_TYPE_SIGNED; } msgpart.data = (gpointer)part; msgpart.index = (*index)++; func (msg, &msgpart, user_data); return TRUE; } /* call 'func' with information about this MIME-part */ static gboolean handle_message_part (MuMsg *msg, GMimeMessagePart *mimemsgpart, GMimeObject *parent, MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data) { MuMsgPart msgpart; memset (&msgpart, 0, sizeof(MuMsgPart)); msgpart.type = "message"; msgpart.subtype = "rfc822"; msgpart.index = (*index)++; /* msgpart.size = 0; /\* maybe calculate this? *\/ */ msgpart.part_type = MU_MSG_PART_TYPE_MESSAGE; msgpart.part_type |= get_disposition ((GMimeObject*)mimemsgpart); msgpart.data = (gpointer)mimemsgpart; func (msg, &msgpart, user_data); if (opts & MU_MSG_OPTION_RECURSE_RFC822) { GMimeMessage *mmsg; /* may return NULL for some * messages */ mmsg = g_mime_message_part_get_message (mimemsgpart); if (mmsg) return handle_mime_object (msg, mmsg->mime_part, parent, opts, index, decrypted, func, user_data); } return TRUE; } static gboolean handle_multipart (MuMsg *msg, GMimeMultipart *mpart, GMimeObject *parent, MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data) { gboolean res; GMimeObject *part; guint i; res = TRUE; for (i = 0; i < mpart->children->len; i++) { part = (GMimeObject *) mpart->children->pdata[i]; res &= handle_mime_object (msg, part, parent, opts, index, decrypted, func, user_data); } return res; } static gboolean handle_mime_object (MuMsg *msg, GMimeObject *mobj, GMimeObject *parent, MuMsgOptions opts, unsigned *index, gboolean decrypted, MuMsgPartForeachFunc func, gpointer user_data) { if (GMIME_IS_PART (mobj)) return handle_part (msg, GMIME_PART(mobj), parent, opts, index, decrypted, func, user_data); else if (GMIME_IS_MESSAGE_PART (mobj)) return handle_message_part (msg, GMIME_MESSAGE_PART(mobj), parent, opts, index, decrypted, func, user_data); else if ((opts & MU_MSG_OPTION_VERIFY) && GMIME_IS_MULTIPART_SIGNED (mobj)) { check_signature (msg, GMIME_MULTIPART_SIGNED (mobj), opts); return handle_multipart (msg, GMIME_MULTIPART (mobj), mobj, opts, index, decrypted, func, user_data); } else if ((opts & MU_MSG_OPTION_DECRYPT) && GMIME_IS_MULTIPART_ENCRYPTED (mobj)) return handle_encrypted_part (msg, GMIME_MULTIPART_ENCRYPTED (mobj), opts, index, func, user_data); else if (GMIME_IS_MULTIPART (mobj)) return handle_multipart (msg, GMIME_MULTIPART (mobj), parent, opts, index, decrypted, func, user_data); return TRUE; } gboolean mu_msg_part_foreach (MuMsg *msg, MuMsgOptions opts, MuMsgPartForeachFunc func, gpointer user_data) { unsigned index; index = 1; g_return_val_if_fail (msg, FALSE); if (!mu_msg_load_msg_file (msg, NULL)) return FALSE; return handle_mime_object (msg, msg->_file->_mime_msg->mime_part, (GMimeObject *) msg->_file->_mime_msg, opts, &index, FALSE, func, user_data); } gboolean write_part_to_fd (GMimePart *part, int fd, GError **err) { GMimeStream *stream; GMimeDataWrapper *wrapper; gboolean rv; stream = g_mime_stream_fs_new (fd); if (!GMIME_IS_STREAM(stream)) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "failed to create stream"); return FALSE; } g_mime_stream_fs_set_owner (GMIME_STREAM_FS(stream), FALSE); wrapper = g_mime_part_get_content_object (part); if (!GMIME_IS_DATA_WRAPPER(wrapper)) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "failed to create wrapper"); g_object_unref (stream); return FALSE; } g_object_ref (part); /* FIXME: otherwise, the unrefs below * give errors...*/ if (g_mime_data_wrapper_write_to_stream (wrapper, stream) == -1) { rv = FALSE; g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "failed to write to stream"); } else rv = TRUE; /* g_object_unref (wrapper); we don't own it */ g_object_unref (stream); return rv; } static gboolean write_object_to_fd (GMimeObject *obj, int fd, GError **err) { gchar *str; str = g_mime_object_to_string (obj); if (!str) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "could not get string from object"); return FALSE; } if (write (fd, str, strlen(str)) == -1) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "failed to write object: %s", strerror(errno)); return FALSE; } return TRUE; } static gboolean save_object (GMimeObject *obj, MuMsgOptions opts, const char *fullpath, GError **err) { int fd; gboolean rv; gboolean use_existing, overwrite; use_existing = opts & MU_MSG_OPTION_USE_EXISTING; overwrite = opts & MU_MSG_OPTION_OVERWRITE; /* don't try to overwrite when we already have it; useful when * you're sure it's not a different file with the same name */ if (use_existing && access (fullpath, F_OK) == 0) return TRUE; /* ok, try to create the file */ fd = mu_util_create_writeable_fd (fullpath, 0600, overwrite); if (fd == -1) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE, "could not open '%s' for writing: %s", fullpath, errno ? strerror(errno) : "error"); return FALSE; } if (GMIME_IS_PART (obj)) rv = write_part_to_fd ((GMimePart*)obj, fd, err); else rv = write_object_to_fd (obj, fd, err); /* Unref it since it was referenced earlier by * get_mime_object_at_index */ g_object_unref (obj); if (close (fd) != 0 && !err) { /* don't write on top of old err */ g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE, "could not close '%s': %s", fullpath, errno ? strerror(errno) : "error"); return FALSE; } return rv; } gchar* mu_msg_part_get_path (MuMsg *msg, MuMsgOptions opts, const char* targetdir, unsigned index, GError **err) { char *fname, *filepath; GMimeObject* mobj; g_return_val_if_fail (msg, NULL); if (!mu_msg_load_msg_file (msg, NULL)) return NULL; mobj = get_mime_object_at_index (msg, opts, index); if (!mobj){ mu_util_g_set_error (err, MU_ERROR_GMIME, "cannot find part %u", index); return NULL; } fname = mime_part_get_filename (mobj, index, TRUE); filepath = g_build_path (G_DIR_SEPARATOR_S, targetdir ? targetdir : "", fname, NULL); /* Unref it since it was referenced earlier by * get_mime_object_at_index */ g_object_unref (mobj); g_free (fname); return filepath; } gchar* mu_msg_part_get_cache_path (MuMsg *msg, MuMsgOptions opts, guint partid, GError **err) { char *dirname, *filepath; const char* path; g_return_val_if_fail (msg, NULL); if (!mu_msg_load_msg_file (msg, NULL)) return NULL; path = mu_msg_get_path (msg); /* g_compute_checksum_for_string may be better, but requires * rel. new glib (2.16) */ dirname = g_strdup_printf ("%s%c%x%c%u", mu_util_cache_dir(), G_DIR_SEPARATOR, g_str_hash (path), G_DIR_SEPARATOR, partid); if (!mu_util_create_dir_maybe (dirname, 0700, FALSE)) { mu_util_g_set_error (err, MU_ERROR_FILE, "failed to create dir %s", dirname); g_free (dirname); return NULL; } filepath = mu_msg_part_get_path (msg, opts, dirname, partid, err); g_free (dirname); return filepath; } gboolean mu_msg_part_save (MuMsg *msg, MuMsgOptions opts, const char *fullpath, guint partidx, GError **err) { GMimeObject *part; g_return_val_if_fail (msg, FALSE); g_return_val_if_fail (fullpath, FALSE); g_return_val_if_fail (!((opts & MU_MSG_OPTION_OVERWRITE) && (opts & MU_MSG_OPTION_USE_EXISTING)), FALSE); if (!mu_msg_load_msg_file (msg, err)) return FALSE; part = get_mime_object_at_index (msg, opts, partidx); /* special case: convert a message-part into a message */ if (GMIME_IS_MESSAGE_PART (part)) part = (GMimeObject*)g_mime_message_part_get_message (GMIME_MESSAGE_PART (part)); if (!part) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "part %u does not exist", partidx); return FALSE; } if (!GMIME_IS_PART(part) && !GMIME_IS_MESSAGE(part)) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME, "unexpected type %s for part %u", G_OBJECT_TYPE_NAME((GObject*)part), partidx); return FALSE; } return save_object (part, opts, fullpath, err); } gchar* mu_msg_part_save_temp (MuMsg *msg, MuMsgOptions opts, guint partidx, GError **err) { gchar *filepath; filepath = mu_msg_part_get_cache_path (msg, opts, partidx, err); if (!filepath) return NULL; if (!mu_msg_part_save (msg, opts, filepath, partidx, err)) { g_free (filepath); return NULL; } return filepath; } static gboolean match_cid (MuMsgPart *mpart, const char *cid) { const char *this_cid; this_cid = g_mime_object_get_content_id ((GMimeObject*)mpart->data); return g_strcmp0 (this_cid, cid) ? TRUE : FALSE; } int mu_msg_find_index_for_cid (MuMsg *msg, MuMsgOptions opts, const char *sought_cid) { const char* cid; g_return_val_if_fail (msg, -1); g_return_val_if_fail (sought_cid, -1); if (!mu_msg_load_msg_file (msg, NULL)) return -1; cid = g_str_has_prefix (sought_cid, "cid:") ? sought_cid + 4 : sought_cid; return get_matching_part_index (msg, opts, (MuMsgPartMatchFunc)match_cid, (gpointer)cid); } struct _RxMatchData { GSList *_lst; const GRegex *_rx; guint _idx; }; typedef struct _RxMatchData RxMatchData; static void match_filename_rx (MuMsg *msg, MuMsgPart *mpart, RxMatchData *mdata) { char *fname; fname = mu_msg_part_get_filename (mpart, FALSE); if (!fname) return; if (g_regex_match (mdata->_rx, fname, 0, NULL)) mdata->_lst = g_slist_prepend (mdata->_lst, GUINT_TO_POINTER(mpart->index)); g_free (fname); } GSList* mu_msg_find_files (MuMsg *msg, MuMsgOptions opts, const GRegex *pattern) { RxMatchData mdata; g_return_val_if_fail (msg, NULL); g_return_val_if_fail (pattern, NULL); if (!mu_msg_load_msg_file (msg, NULL)) return NULL; mdata._lst = NULL; mdata._rx = pattern; mdata._idx = 0; mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)match_filename_rx, &mdata); return mdata._lst; } gboolean mu_msg_part_maybe_attachment (MuMsgPart *part) { g_return_val_if_fail (part, FALSE); /* attachments must be leaf parts */ if (!(part->part_type & MU_MSG_PART_TYPE_LEAF)) return FALSE; /* parts other than text/plain, text/html are considered * attachments as well */ if (!(part->part_type & MU_MSG_PART_TYPE_TEXT_PLAIN) && !(part->part_type & MU_MSG_PART_TYPE_TEXT_HTML)) return TRUE; return part->part_type & MU_MSG_PART_TYPE_ATTACHMENT ? TRUE : FALSE; } mu-0.9.18/lib/mu-script.h0000644000175000017500000000710012605152236012015 00000000000000/* ** Copyright (C) 2012-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_SCRIPT_H__ #define __MU_SCRIPT_H__ #include G_BEGIN_DECLS /* Opaque structure with information about a script */ struct _MuScriptInfo; typedef struct _MuScriptInfo MuScriptInfo; /** * get the name of the script (sans-extension, if some extension was * provided to mu_script_get_scripts) * * @param msi a MuScriptInfo structure * * @return the name */ const char* mu_script_info_name (MuScriptInfo *msi); /** * get the full filesystem path of the script * * @param msi a MuScriptInfo structure * * @return the path */ const char* mu_script_info_path (MuScriptInfo *msi); /** * get a one-line description for the script * * @param msi a MuScriptInfo structure * * @return the description, or NULL if there was none */ const char* mu_script_info_one_line (MuScriptInfo *msi); /** * get a full description for the script * * @param msi a MuScriptInfo structure * * @return the description, or NULL if there was none */ const char* mu_script_info_description (MuScriptInfo *msi); /** * check whether either the name or one-line description of a * MuScriptInfo matches regular expression rxstr * * @param msi a MuScriptInfo * @param rxstr a regular expression string * @param err receives error information * * @return TRUE if it matches, FALSE if not or in case of error */ gboolean mu_script_info_matches_regex (MuScriptInfo *msi, const char *rxstr, GError **err); /** * Get the list of all scripts in path with extension ext * * @param path a file system path * @param ext an extension (e.g., ".scm"), or NULL * @param prefix for the one-line description * (e.g., ";; DESCRIPTION: "), or NULL * @param err receives error information, if any * * @return a list of Mu */ GSList *mu_script_get_script_info_list (const char *path, const char *ext, const char *descprefix, GError **err); /** * destroy a list of MuScriptInfo* objects * * @param scriptslst a list of MuScriptInfo* objects */ void mu_script_info_list_destroy (GSList *lst); /** * find the MuScriptInfo object for the first script with a certain * name, or return NULL if not found. * * @param lst a list of MuScriptInfo* objects * @param name the name to search for * * @return a MuScriptInfo* object, or NULL if not found. */ MuScriptInfo* mu_script_find_script_with_name (GSList *lst, const char *name); /** * run the guile script at path * * @param msi MuScriptInfo object for the script * @param muhome path to the mu home dir * @param args NULL-terminated array of strings (argv for the script) * @param err receives error information * * @return FALSE in case of error -- otherwise, this function will * _not return_ */ gboolean mu_script_guile_run (MuScriptInfo *msi, const char *muhome, const char **args, GError **err); G_END_DECLS #endif /*__MU_SCRIPT_H__*/ mu-0.9.18/lib/mu-util.c0000644000175000017500000002517613020504332011464 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include "mu-util.h" #define _XOPEN_SOURCE 500 #ifdef HAVE_WORDEXP_H #include /* for shell-style globbing */ #endif /*HAVE_WORDEXP_H*/ #include #include #include /* for setlocale() */ #include #include #include #include #include #include #include #include static char* do_wordexp (const char *path) { #ifdef HAVE_WORDEXP_H wordexp_t wexp; char *dir; if (!path) { /* g_debug ("%s: path is empty", __func__); */ return NULL; } if (wordexp (path, &wexp, 0) != 0) { /* g_debug ("%s: expansion failed for %s", __func__, path); */ return NULL; } /* we just pick the first one */ dir = g_strdup (wexp.we_wordv[0]); /* strangely, below seems to lead to a crash on MacOS (BSD); so we have to allow for a tiny leak here on that platform... maybe instead of __APPLE__ it should be __BSD__? Hmmm., cannot reproduce that crash anymore, so commenting it out for now... */ /* #ifndef __APPLE__ */ wordfree (&wexp); /* #endif /\*__APPLE__*\/ */ return dir; # else /*!HAVE_WORDEXP_H*/ /* E.g. OpenBSD does not have wordexp.h, so we ignore it */ return path ? g_strdup (path) : NULL; #endif /*HAVE_WORDEXP_H*/ } /* note, the g_debugs are commented out because this function may be * called before the log handler is installed. */ char* mu_util_dir_expand (const char *path) { char *dir; char resolved[PATH_MAX + 1]; g_return_val_if_fail (path, NULL); dir = do_wordexp (path); if (!dir) return NULL; /* error */ /* don't try realpath if the dir does not exist */ if (access (dir, F_OK) != 0) return dir; /* now resolve any symlinks, .. etc. */ if (realpath (dir, resolved) == NULL) { /* g_debug ("%s: could not get realpath for '%s': %s", */ /* __func__, dir, strerror(errno)); */ g_free (dir); return NULL; } else g_free (dir); return g_strdup (resolved); } char* mu_util_create_tmpdir (void) { gchar *dirname; dirname = g_strdup_printf ("%s%cmu-%d%c%x", g_get_tmp_dir(), G_DIR_SEPARATOR, getuid(), G_DIR_SEPARATOR, (int)random()*getpid()*(int)time(NULL)); if (!mu_util_create_dir_maybe (dirname, 0700, FALSE)) { g_free (dirname); return NULL; } return dirname; } GQuark mu_util_error_quark (void) { static GQuark error_domain = 0; if (G_UNLIKELY(error_domain == 0)) error_domain = g_quark_from_static_string ("mu-error-quark"); return error_domain; } const char* mu_util_cache_dir (void) { static char cachedir [PATH_MAX]; snprintf (cachedir, sizeof(cachedir), "%s%cmu-%u", g_get_tmp_dir(), G_DIR_SEPARATOR, getuid()); return cachedir; } gboolean mu_util_check_dir (const gchar* path, gboolean readable, gboolean writeable) { int mode; struct stat statbuf; if (!path) return FALSE; mode = F_OK | (readable ? R_OK : 0) | (writeable ? W_OK : 0); if (access (path, mode) != 0) { /* g_debug ("Cannot access %s: %s", path, strerror (errno)); */ return FALSE; } if (stat (path, &statbuf) != 0) { /* g_debug ("Cannot stat %s: %s", path, strerror (errno)); */ return FALSE; } return S_ISDIR(statbuf.st_mode) ? TRUE: FALSE; } gchar* mu_util_guess_maildir (void) { const gchar *mdir1, *home; /* first, try MAILDIR */ mdir1 = g_getenv ("MAILDIR"); if (mdir1 && mu_util_check_dir (mdir1, TRUE, FALSE)) return g_strdup (mdir1); /* then, try /Maildir */ home = g_get_home_dir(); if (home) { char *mdir2; mdir2 = g_strdup_printf ("%s%cMaildir", home, G_DIR_SEPARATOR); if (mu_util_check_dir (mdir2, TRUE, FALSE)) return mdir2; g_free (mdir2); } /* nope; nothing found */ return NULL; } gchar* mu_util_guess_mu_homedir (void) { const char* home; /* g_get_home_dir use /etc/passwd, not $HOME; this is better, * as HOME may be wrong when using 'sudo' etc.*/ home = g_get_home_dir (); if (!home) { MU_WRITE_LOG ("failed to determine homedir"); return NULL; } return g_strdup_printf ("%s%c%s", home ? home : ".", G_DIR_SEPARATOR, ".mu"); } gboolean mu_util_create_dir_maybe (const gchar *path, mode_t mode, gboolean nowarn) { struct stat statbuf; g_return_val_if_fail (path, FALSE); /* if it exists, it must be a readable dir */ if (stat (path, &statbuf) == 0) { if ((!S_ISDIR(statbuf.st_mode)) || (access (path, W_OK|R_OK) != 0)) { if (!nowarn) g_warning ("not a read-writable" "directory: %s", path); return FALSE; } } if (g_mkdir_with_parents (path, mode) != 0) { if (!nowarn) g_warning ("failed to create %s: %s", path, strerror(errno)); return FALSE; } return TRUE; } gchar* mu_util_str_from_strv (const gchar **params) { GString *str; int i; g_return_val_if_fail (params, NULL); if (!params[0]) return g_strdup (""); str = g_string_sized_new (64); /* just a guess */ for (i = 0; params[i]; ++i) { if (i > 0) g_string_append_c (str, ' '); g_string_append (str, params[i]); } return g_string_free (str, FALSE); } int mu_util_create_writeable_fd (const char* path, mode_t mode, gboolean overwrite) { errno = 0; /* clear! */ g_return_val_if_fail (path, -1); if (overwrite) return open (path, O_WRONLY|O_CREAT|O_TRUNC, mode); else return open (path, O_WRONLY|O_CREAT|O_EXCL, mode); } gboolean mu_util_is_local_file (const char* path) { /* if it starts with file:// it's a local file (for the * purposes of this function -- if it's on a remote FS it's * still considered local) */ if (g_ascii_strncasecmp ("file://", path, strlen("file://")) == 0) return TRUE; if (access (path, R_OK) == 0) return TRUE; return FALSE; } gboolean mu_util_supports (MuFeature feature) { /* check for Guile support */ #ifndef BUILD_GUILE if (feature & MU_FEATURE_GUILE) return FALSE; #endif /*BUILD_GUILE*/ /* check for Gnuplot */ if (feature & MU_FEATURE_GNUPLOT) if (!mu_util_program_in_path ("gnuplot")) return FALSE; return TRUE; } gboolean mu_util_program_in_path (const char *prog) { gchar *path; g_return_val_if_fail (prog, FALSE); path = g_find_program_in_path (prog); g_free (path); return (path != NULL) ? TRUE : FALSE; } gboolean mu_util_play (const char *path, gboolean allow_local, gboolean allow_remote, GError **err) { gboolean rv; const gchar *argv[3]; const char *prog; g_return_val_if_fail (path, FALSE); g_return_val_if_fail (mu_util_is_local_file (path) || allow_remote, FALSE); g_return_val_if_fail (!mu_util_is_local_file (path) || allow_local, FALSE); prog = g_getenv ("MU_PLAY_PROGRAM"); if (!prog) { #ifdef __APPLE__ prog = "open"; #else prog = "xdg-open"; #endif /*!__APPLE__*/ } if (!mu_util_program_in_path (prog)) { mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_EXECUTE, "cannot find '%s' in path", prog); return FALSE; } argv[0] = prog; argv[1] = path; argv[2] = NULL; err = NULL; rv = g_spawn_async (NULL, (gchar**)&argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, err); return rv; } unsigned char mu_util_get_dtype_with_lstat (const char *path) { struct stat statbuf; g_return_val_if_fail (path, DT_UNKNOWN); if (lstat (path, &statbuf) != 0) { g_warning ("stat failed on %s: %s", path, strerror(errno)); return DT_UNKNOWN; } /* we only care about dirs, regular files and links */ if (S_ISREG (statbuf.st_mode)) return DT_REG; else if (S_ISDIR (statbuf.st_mode)) return DT_DIR; else if (S_ISLNK (statbuf.st_mode)) return DT_LNK; return DT_UNKNOWN; } gboolean mu_util_locale_is_utf8 (void) { const gchar *dummy; static int is_utf8 = -1; if (G_UNLIKELY(is_utf8 == -1)) is_utf8 = g_get_charset(&dummy) ? 1 : 0; return is_utf8 ? TRUE : FALSE; } gboolean mu_util_fputs_encoded (const char *str, FILE *stream) { int rv; char *conv; g_return_val_if_fail (stream, FALSE); /* g_get_charset return TRUE when the locale is UTF8 */ if (mu_util_locale_is_utf8()) return fputs (str, stream) == EOF ? FALSE : TRUE; /* charset is _not_ utf8, so we need to convert it */ conv = NULL; if (g_utf8_validate (str, -1, NULL)) conv = g_locale_from_utf8 (str, -1, NULL, NULL, NULL); /* conversion failed; this happens because is some cases GMime may gives * us non-UTF-8 strings from e.g. wrongly encoded message-subjects; if * so, we escape the string */ conv = conv ? conv : g_strescape (str, "\n\t"); rv = conv ? fputs (conv, stream) : EOF; g_free (conv); return (rv == EOF) ? FALSE : TRUE; } gboolean mu_util_g_set_error (GError **err, MuError errcode, const char *frm, ...) { va_list ap; char *msg; /* don't bother with NULL errors, or errors already set */ if (!err || *err) return FALSE; msg = NULL; va_start (ap, frm); g_vasprintf (&msg, frm, ap); va_end (ap); g_set_error (err, MU_ERROR_DOMAIN, errcode, "%s", msg); g_free (msg); return FALSE; } static gboolean print_args (FILE *stream, const char *frm, va_list args) { gchar *str; gboolean rv; str = g_strdup_vprintf (frm, args); rv = mu_util_fputs_encoded (str, stream); g_free (str); return rv; } gboolean mu_util_print_encoded (const char *frm, ...) { va_list args; gboolean rv; g_return_val_if_fail (frm, FALSE); va_start (args, frm); rv = print_args (stdout, frm, args); va_end (args); return rv; } gboolean mu_util_printerr_encoded (const char *frm, ...) { va_list args; gboolean rv; g_return_val_if_fail (frm, FALSE); va_start (args, frm); rv = print_args (stderr, frm, args); va_end (args); return rv; } char* mu_util_read_password (const char *prompt) { char *pass; g_return_val_if_fail (prompt, NULL); /* note: getpass is obsolete; replace with something better */ pass = getpass (prompt); /* returns static mem, don't free */ if (!pass) { if (errno) g_warning ("error: %s", strerror(errno)); return NULL; } return g_strdup (pass); } mu-0.9.18/lib/mu-flags.h0000644000175000017500000001125213020504331011575 00000000000000/* ** Copyright (C) 2011-2013 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #ifndef __MU_FLAGS_H__ #define __MU_FLAGS_H__ #include G_BEGIN_DECLS enum _MuFlags { MU_FLAG_NONE = 0, /* next 6 are seen in the file-info part of maildir message * file names, ie., in a name like "1234345346:2,", * consists of zero or more of the following * characters (in ascii order) */ MU_FLAG_DRAFT = 1 << 0, MU_FLAG_FLAGGED = 1 << 1, MU_FLAG_PASSED = 1 << 2, MU_FLAG_REPLIED = 1 << 3, MU_FLAG_SEEN = 1 << 4, MU_FLAG_TRASHED = 1 << 5, /* decides on cur/ or new/ in the maildir */ MU_FLAG_NEW = 1 << 6, /* content flags -- not visible in the filename, but used for * searching */ MU_FLAG_SIGNED = 1 << 7, MU_FLAG_ENCRYPTED = 1 << 8, MU_FLAG_HAS_ATTACH = 1 << 9, /* pseudo-flag, only for queries, so we can search for * flag:unread, which is equivalent to 'flag:new OR NOT * flag:seen' */ MU_FLAG_UNREAD = 1 << 10, /* other content flags */ MU_FLAG_LIST = 1 << 11 }; typedef enum _MuFlags MuFlags; #define MU_FLAG_INVALID ((MuFlags)-1) enum _MuFlagType { MU_FLAG_TYPE_MAILFILE = 1 << 0, MU_FLAG_TYPE_MAILDIR = 1 << 1, MU_FLAG_TYPE_CONTENT = 1 << 2, MU_FLAG_TYPE_PSEUDO = 1 << 3 }; typedef enum _MuFlagType MuFlagType; #define MU_FLAG_TYPE_ANY ((MuFlagType)-1) #define MU_FLAG_TYPE_INVALID ((MuFlagType)-1) /** * Get the type of flag (mailfile, maildir, pseudo or content) * * @param flag a MuFlag * * @return the flag type or MU_FLAG_TYPE_INVALID in case of error */ MuFlagType mu_flag_type (MuFlags flag) G_GNUC_CONST; /** * Get the flag character * * @param flag a MuFlag (single) * * @return the character, or 0 if it's not a valid flag */ char mu_flag_char (MuFlags flag) G_GNUC_CONST; /** * Get the flag name * * @param flag a single MuFlag * * @return the name (don't free) as string or NULL in case of error */ const char* mu_flag_name (MuFlags flag) G_GNUC_CONST; /** * Get the string representation of an OR'ed set of flags * * @param flags MuFlag (OR'ed) * @param types allowable types (OR'ed) for the result; the rest is ignored * * @return The string representation (static, don't free), or NULL in * case of error */ const char* mu_flags_to_str_s (MuFlags flags, MuFlagType types); /** * Get the (OR'ed) flags corresponding to a string representation * * @param str the file info string * @param types the flag types to accept (other will be ignored) * @param ignore invalid if TRUE, ignore invalid flags, otherwise return * MU_FLAG_INVALID if an invalid flag is encountered * * @return the (OR'ed) flags */ MuFlags mu_flags_from_str (const char *str, MuFlagType types, gboolean ignore_invalid); /** * return the concatenation of all non-standard file flags in str * (ie., characters other than DFPRST) as a newly allocated string. * * @param str the file info string * * @return concatenation of all non-standard flags, as a string; free * with g_free when done. If there are no such flags, return NULL. */ char* mu_flags_custom_from_str (const char *str) G_GNUC_WARN_UNUSED_RESULT; /** * Update #oldflags with the flags in #str, where #str consists of the * the normal flag characters, but prefixed with either '+' or '-', * which means resp. "add this flag" or "remove this flag" from * oldflags. So, e.g. "-N+S" would unset the NEW flag and set the * SEEN flag, without affecting other flags. * * @param str the string representation * @param old flags to update * @param types the flag types to accept (other will be ignored) * * @return */ MuFlags mu_flags_from_str_delta (const char *str, MuFlags oldflags, MuFlagType types); typedef void (*MuFlagsForeachFunc) (MuFlags flag, gpointer user_data); /** * call a function for each available flag * * @param func a function to call * @param user_data a user pointer to pass to the function */ void mu_flags_foreach (MuFlagsForeachFunc func, gpointer user_data); G_END_DECLS #endif /*__MU_FLAGS_H__*/ mu-0.9.18/lib/mu-maildir.c0000644000175000017500000005426213020504331012125 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2016 Dirk-Jan C. Binnema ** ** 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, 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 59the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include #include #include #include "mu-util.h" #include "mu-maildir.h" #include "mu-str.h" #define MU_MAILDIR_NOINDEX_FILE ".noindex" #define MU_MAILDIR_NOUPDATE_FILE ".noupdate" /* On Linux (and some BSD), we have entry->d_type, but some file * systems (XFS, ReiserFS) do not support it, and set it DT_UNKNOWN. * On other OSs, notably Solaris, entry->d_type is not present at all. * For these cases, we use lstat (in get_dtype) as a slower fallback, * and return it in the d_type parameter */ #ifdef HAVE_STRUCT_DIRENT_D_TYPE #define GET_DTYPE(DE,FP) \ ((DE)->d_type == DT_UNKNOWN ? mu_util_get_dtype_with_lstat((FP)) : \ (DE)->d_type) #else #define GET_DTYPE(DE,FP) \ mu_util_get_dtype_with_lstat((FP)) #endif /*HAVE_STRUCT_DIRENT_D_TYPE*/ static gboolean create_maildir (const char *path, mode_t mode, GError **err) { int i; const gchar* subdirs[] = {"new", "cur", "tmp"}; for (i = 0; i != G_N_ELEMENTS(subdirs); ++i) { const char *fullpath; int rv; /* static buffer */ fullpath = mu_str_fullpath_s (path, subdirs[i]); /* if subdir already exists, don't try to re-create * it */ if (mu_util_check_dir (fullpath, TRUE, TRUE)) continue; rv = g_mkdir_with_parents (fullpath, (int)mode); /* note, g_mkdir_with_parents won't detect an error if * there's already such a dir, but with the wrong * permissions; so we need to check */ if (rv != 0 || !mu_util_check_dir(fullpath, TRUE, TRUE)) return mu_util_g_set_error (err,MU_ERROR_FILE_CANNOT_MKDIR, "creating dir failed for %s: %s", fullpath, strerror (errno)); } return TRUE; } static gboolean create_noindex (const char *path, GError **err) { /* create a noindex file if requested */ int fd; const char *noindexpath; /* static buffer */ noindexpath = mu_str_fullpath_s (path, MU_MAILDIR_NOINDEX_FILE); fd = creat (noindexpath, 0644); /* note, if the 'close' failed, creation may still have * succeeded...*/ if (fd < 0 || close (fd) != 0) return mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_CREATE, "error in create_noindex: %s", strerror (errno)); return TRUE; } gboolean mu_maildir_mkdir (const char* path, mode_t mode, gboolean noindex, GError **err) { g_return_val_if_fail (path, FALSE); MU_WRITE_LOG ("%s (%s, %o, %s)", __func__, path, mode, noindex ? "TRUE" : "FALSE"); if (!create_maildir (path, mode, err)) return FALSE; if (noindex && !create_noindex (path, err)) return FALSE; return TRUE; } /* determine whether the source message is in 'new' or in 'cur'; * we ignore messages in 'tmp' for obvious reasons */ static gboolean check_subdir (const char *src, gboolean *in_cur, GError **err) { gboolean rv; gchar *srcpath; srcpath = g_path_get_dirname (src); *in_cur = FALSE; rv = TRUE; if (g_str_has_suffix (srcpath, "cur")) *in_cur = TRUE; else if (!g_str_has_suffix (srcpath, "new")) rv = mu_util_g_set_error (err, MU_ERROR_FILE_INVALID_SOURCE, "invalid source message '%s'", src); g_free (srcpath); return rv; } static gchar* get_target_fullpath (const char* src, const gchar *targetpath, GError **err) { gchar *targetfullpath, *srcfile; gboolean in_cur; if (!check_subdir (src, &in_cur, err)) return NULL; srcfile = g_path_get_basename (src); /* create targetpath; note: make the filename *cough* unique * by including a hash of the srcname in the targetname. This * helps if there are copies of a message (which all have the * same basename) */ targetfullpath = g_strdup_printf ("%s%c%s%c%u_%s", targetpath, G_DIR_SEPARATOR, in_cur ? "cur" : "new", G_DIR_SEPARATOR, g_str_hash(src), srcfile); g_free (srcfile); return targetfullpath; } gboolean mu_maildir_link (const char* src, const char *targetpath, GError **err) { gchar *targetfullpath; int rv; g_return_val_if_fail (src, FALSE); g_return_val_if_fail (targetpath, FALSE); targetfullpath = get_target_fullpath (src, targetpath, err); if (!targetfullpath) return FALSE; rv = symlink (src, targetfullpath); if (rv != 0) mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_LINK, "error creating link %s => %s: %s", targetfullpath, src, strerror (errno)); g_free (targetfullpath); return rv == 0 ? TRUE: FALSE; } static MuError process_dir (const char* path, const gchar *mdir, MuMaildirWalkMsgCallback msg_cb, MuMaildirWalkDirCallback dir_cb, gboolean full, void *data); static MuError process_file (const char* fullpath, const gchar* mdir, MuMaildirWalkMsgCallback msg_cb, void *data) { MuError result; struct stat statbuf; if (!msg_cb) return MU_OK; if (G_UNLIKELY(access(fullpath, R_OK) != 0)) { g_warning ("cannot access %s: %s", fullpath, strerror(errno)); return MU_ERROR; } if (G_UNLIKELY(stat (fullpath, &statbuf) != 0)) { g_warning ("cannot stat %s: %s", fullpath, strerror(errno)); return MU_ERROR; } result = (msg_cb)(fullpath, mdir, &statbuf, data); if (result == MU_STOP) g_debug ("callback said 'MU_STOP' for %s", fullpath); else if (result == MU_ERROR) g_warning ("%s: error in callback (%s)", __func__, fullpath); return result; } /* * determine if path is a maildir leaf-dir; ie. if it's 'cur' or 'new' * (we're skipping 'tmp' for obvious reasons) */ gboolean mu_maildir_is_leaf_dir (const char *path) { size_t len; /* path is the full path; it cannot possibly be shorter * than 4 for a maildir (/cur or /new) */ len = path ? strlen (path) : 0; if (G_UNLIKELY(len < 4)) return FALSE; /* optimization; one further idea would be cast the 4 bytes to an * integer and compare that -- need to think about alignment, * endianness */ if (path[len - 4] == G_DIR_SEPARATOR && path[len - 3] == 'c' && path[len - 2] == 'u' && path[len - 1] == 'r') return TRUE; if (path[len - 4] == G_DIR_SEPARATOR && path[len - 3] == 'n' && path[len - 2] == 'e' && path[len - 1] == 'w') return TRUE; return FALSE; } /* check if there path contains file; used for checking if there is * MU_MAILDIR_NOINDEX_FILE or MU_MAILDIR_NOUPDATE_FILE in this * dir; */ static gboolean dir_contains_file (const char *path, const char *file) { const char* fullpath; /* static buffer */ fullpath = mu_str_fullpath_s (path, file); if (access (fullpath, F_OK) == 0) return TRUE; else if (G_UNLIKELY(errno != ENOENT && errno != EACCES)) g_warning ("error testing for %s/%s: %s", fullpath, file, strerror(errno)); return FALSE; } static gboolean is_dotdir_to_ignore (const char* dir) { int i; const char* ignore[] = { ".notmuch", ".nnmaildir", ".#evolution" }; /* when adding names, check the optimization below */ if (dir[0] != '.') return FALSE; /* not a dotdir */ if (dir[1] == '\0' || (dir[1] == '.' && dir[2] == '\0')) return TRUE; /* ignore '.' and '..' */ /* optimization: special dirs have 'n' or '#' in pos 1 */ if (dir[1] != 'n' && dir[1] != '#') return FALSE; /* not special: don't ignore */ for (i = 0; i != G_N_ELEMENTS(ignore); ++i) if (strcmp(dir, ignore[i]) == 0) return TRUE; return FALSE; /* don't ignore */ } static gboolean ignore_dir_entry (struct dirent *entry, unsigned char d_type) { if (G_LIKELY(d_type == DT_REG)) { guint u; /* ignore emacs tempfiles */ if (entry->d_name[0] == '#') return TRUE; /* ignore dovecot metadata */ if (entry->d_name[0] == 'd' && strncmp (entry->d_name, "dovecot", 7) == 0) return TRUE; /* ignore special files */ if (entry->d_name[0] == '.') return TRUE; /* ignore core files */ if (entry->d_name[0] == 'c' && strncmp (entry->d_name, "core", 4) == 0) return TRUE; /* ignore tmp/backup files; find the last char */ for (u = 0; entry->d_name[u] != '\0'; ++u) { switch (entry->d_name[u]) { case '#': case '~': /* looks like a backup / tempsave file */ if (entry->d_name[u + 1] == '\0') return TRUE; default: continue; } } return FALSE; /* other files: don't ignore */ } else if (d_type == DT_DIR) return is_dotdir_to_ignore (entry->d_name); else return TRUE; /* ignore non-normal files, non-dirs */ } /* * return the maildir value for the the path - this is the directory * for the message (with the top-level dir as "/"), and without the * leaf "/cur" or "/new". In other words, contatenate old_mdir + "/" + dir, * unless dir is either 'new' or 'cur'. The value will be used in queries. */ static gchar* get_mdir_for_path (const gchar *old_mdir, const gchar *dir) { /* if the current dir is not 'new' or 'cur', contatenate * old_mdir an dir */ if ((dir[0] == 'n' && strcmp(dir, "new") == 0) || (dir[0] == 'c' && strcmp(dir, "cur") == 0) || (dir[0] == 't' && strcmp(dir, "tmp") == 0)) return strdup (old_mdir ? old_mdir : G_DIR_SEPARATOR_S); else return g_strconcat (old_mdir ? old_mdir : "", G_DIR_SEPARATOR_S, dir, NULL); } static MuError process_dir_entry (const char* path, const char* mdir, struct dirent *entry, MuMaildirWalkMsgCallback cb_msg, MuMaildirWalkDirCallback cb_dir, gboolean full, void *data) { const char *fp; char* fullpath; unsigned char d_type; /* we have to copy the buffer from fullpath_s, because it * returns a static buffer, and we maybe called reentrantly */ fp = mu_str_fullpath_s (path, entry->d_name); fullpath = g_newa (char, strlen(fp) + 1); strcpy (fullpath, fp); d_type = GET_DTYPE(entry, fullpath); /* ignore special files/dirs */ if (ignore_dir_entry (entry, d_type)) { /* g_debug ("ignoring %s\n", entry->d_name); */ return MU_OK; } switch (d_type) { case DT_REG: /* we only want files in cur/ and new/ */ if (!mu_maildir_is_leaf_dir (path)) return MU_OK; return process_file (fullpath, mdir, cb_msg, data); case DT_DIR: { char *my_mdir; MuError rv; /* my_mdir is the search maildir (the dir starting * with the top-level maildir as /, and without the * /tmp, /cur, /new */ my_mdir = get_mdir_for_path (mdir, entry->d_name); rv = process_dir (fullpath, my_mdir, cb_msg, cb_dir, full, data); g_free (my_mdir); return rv; } default: return MU_OK; /* ignore other types */ } } static const size_t DIRENT_ALLOC_SIZE = offsetof (struct dirent, d_name) + PATH_MAX; static struct dirent* dirent_new (void) { return (struct dirent*) g_slice_alloc (DIRENT_ALLOC_SIZE); } static void dirent_destroy (struct dirent *entry) { g_slice_free1 (DIRENT_ALLOC_SIZE, entry); } #ifdef HAVE_STRUCT_DIRENT_D_INO static int dirent_cmp (struct dirent *d1, struct dirent *d2) { /* we do it his way instead of a simple d1->d_ino - d2->d_ino * because this way, we don't need 64-bit numbers for the * actual sorting */ if (d1->d_ino < d2->d_ino) return -1; else if (d1->d_ino > d2->d_ino) return 1; else return 0; } #endif /*HAVE_STRUCT_DIRENT_D_INO*/ static MuError process_dir_entries (DIR *dir, const char* path, const char* mdir, MuMaildirWalkMsgCallback msg_cb, MuMaildirWalkDirCallback dir_cb, gboolean full, void *data) { MuError result; GSList *lst, *c; for (lst = NULL;;) { int rv; struct dirent *entry, *res; entry = dirent_new (); rv = readdir_r (dir, entry, &res); if (rv == 0) { if (res) lst = g_slist_prepend (lst, entry); else { dirent_destroy (entry); break; /* last direntry reached */ } } else { dirent_destroy (entry); g_warning ("error scanning dir: %s", strerror(rv)); return MU_ERROR_FILE; } } /* we sort by inode; this makes things much faster on * extfs2,3 */ #if HAVE_STRUCT_DIRENT_D_INO c = lst = g_slist_sort (lst, (GCompareFunc)dirent_cmp); #endif /*HAVE_STRUCT_DIRENT_D_INO*/ for (c = lst, result = MU_OK; c && result == MU_OK; c = g_slist_next(c)) result = process_dir_entry (path, mdir, (struct dirent*)c->data, msg_cb, dir_cb, full, data); g_slist_foreach (lst, (GFunc)dirent_destroy, NULL); g_slist_free (lst); return result; } static MuError process_dir (const char* path, const char* mdir, MuMaildirWalkMsgCallback msg_cb, MuMaildirWalkDirCallback dir_cb, gboolean full, void *data) { MuError result; DIR* dir; /* if it has a noindex file, we ignore this dir */ if (dir_contains_file (path, MU_MAILDIR_NOINDEX_FILE) || (!full && dir_contains_file (path, MU_MAILDIR_NOUPDATE_FILE))) { g_debug ("found noindex/noupdate: ignoring dir %s", path); return MU_OK; } if (dir_cb) { MuError rv; rv = dir_cb (path, TRUE/*enter*/, data); /* ignore this dir; not necessarily an _error_, dir might * be up-to-date and return MU_IGNORE */ if (rv == MU_IGNORE) return MU_OK; else if (rv != MU_OK) return rv; } dir = opendir (path); if (!dir) { g_warning ("cannot access %s: %s", path, strerror(errno)); return MU_OK; } result = process_dir_entries (dir, path, mdir, msg_cb, dir_cb, full, data); closedir (dir); /* only run dir_cb if it exists and so far, things went ok */ if (dir_cb && result == MU_OK) return dir_cb (path, FALSE/*leave*/, data); return result; } MuError mu_maildir_walk (const char *path, MuMaildirWalkMsgCallback cb_msg, MuMaildirWalkDirCallback cb_dir, gboolean full, void *data) { MuError rv; char *mypath; g_return_val_if_fail (path && cb_msg, MU_ERROR); g_return_val_if_fail (mu_util_check_dir(path, TRUE, FALSE), MU_ERROR); /* strip the final / or \ */ mypath = g_strdup (path); if (mypath[strlen(mypath)-1] == G_DIR_SEPARATOR) mypath[strlen(mypath)-1] = '\0'; rv = process_dir (mypath, NULL, cb_msg, cb_dir, full, data); g_free (mypath); return rv; } static gboolean clear_links (const char *path, DIR *dir) { gboolean rv; struct dirent *dentry; rv = TRUE; errno = 0; while ((dentry = readdir (dir))) { guint8 d_type; char *fullpath; if (dentry->d_name[0] == '.') continue; /* ignore .,.. other dotdirs */ fullpath = g_build_path ("/", path, dentry->d_name, NULL); d_type = GET_DTYPE (dentry, fullpath); if (d_type == DT_LNK) { if (unlink (fullpath) != 0 ) { g_warning ("error unlinking %s: %s", fullpath, strerror(errno)); rv = FALSE; } } else if (d_type == DT_DIR) { DIR *subdir; subdir = opendir (fullpath); if (!subdir) { g_warning ("failed to open dir %s: %s", fullpath, strerror(errno)); rv = FALSE; goto next; } if (!clear_links (fullpath, subdir)) rv = FALSE; closedir (subdir); } next: g_free (fullpath); } return rv; } gboolean mu_maildir_clear_links (const char *path, GError **err) { DIR *dir; gboolean rv; g_return_val_if_fail (path, FALSE); dir = opendir (path); if (!dir) { g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_FILE_CANNOT_OPEN, "failed to open %s: %s", path, strerror(errno)); return FALSE; } rv = clear_links (path, dir); closedir (dir); return rv; } MuFlags mu_maildir_get_flags_from_path (const char *path) { g_return_val_if_fail (path, MU_FLAG_INVALID); /* try to find the info part */ /* note that we can use either the ':' or '!' as separator; * the former is the official, but as it does not work on e.g. VFAT * file systems, some Maildir implementations use the latter instead * (or both). For example, Tinymail/modest does this. The python * documentation at http://docs.python.org/lib/mailbox-maildir.html * mentions the '!' as well as a 'popular choice' */ /* we check the dir -- */ if (strstr (path, G_DIR_SEPARATOR_S "new" G_DIR_SEPARATOR_S)) { char *dir, *dir2; MuFlags flags; dir = g_path_get_dirname (path); dir2 = g_path_get_basename (dir); flags = MU_FLAG_NONE; if (g_strcmp0 (dir2, "new") == 0) flags = MU_FLAG_NEW; g_free (dir); g_free (dir2); /* NOTE: new/ message should not have :2,-stuff, as * per http://cr.yp.to/proto/maildir.html. If they, do * we ignore it */ if (flags == MU_FLAG_NEW) return flags; } /* get the file flags */ { char *info; info = strrchr (path, '2'); if (!info || info == path || (info[-1] != ':' && info[-1] != '!') || (info[1] != ',')) return MU_FLAG_NONE; else return mu_flags_from_str (&info[2], MU_FLAG_TYPE_MAILFILE, TRUE /*ignore invalid */); } } /* * take an exising message path, and return a new path, based on * whether it should be in 'new' or 'cur'; ie. * * /home/user/Maildir/foo/bar/cur/abc:2,F and flags == MU_FLAG_NEW * => /home/user/Maildir/foo/bar/new * and * /home/user/Maildir/foo/bar/new/abc and flags == MU_FLAG_REPLIED * => /home/user/Maildir/foo/bar/cur * * so the difference is whether MU_FLAG_NEW is set or not; and in the * latter case, no other flags are allowed. * */ static gchar* get_new_path (const char *mdir, const char *mfile, MuFlags flags, const char* custom_flags) { if (flags & MU_FLAG_NEW) return g_strdup_printf ("%s%cnew%c%s", mdir, G_DIR_SEPARATOR, G_DIR_SEPARATOR, mfile); else { const char *flagstr; flagstr = mu_flags_to_str_s (flags, MU_FLAG_TYPE_MAILFILE); return g_strdup_printf ("%s%ccur%c%s:2,%s%s", mdir, G_DIR_SEPARATOR, G_DIR_SEPARATOR, mfile, flagstr, custom_flags ? custom_flags : ""); } } char* mu_maildir_get_maildir_from_path (const char* path) { gchar *mdir; /* determine the maildir */ mdir = g_path_get_dirname (path); if (!g_str_has_suffix (mdir, "cur") && !g_str_has_suffix (mdir, "new")) { g_warning ("%s: not a valid maildir path: %s", __func__, path); g_free (mdir); return NULL; } /* remove the 'cur' or 'new' */ mdir[strlen(mdir) - 4] = '\0'; return mdir; } static char* get_new_basename (void) { return g_strdup_printf ("%u.%08x%08x.%s", (guint)time(NULL), g_random_int(), (gint32)g_get_monotonic_time (), g_get_host_name ()); } char* mu_maildir_get_new_path (const char *oldpath, const char *new_mdir, MuFlags newflags, gboolean new_name) { char *mfile, *mdir, *custom_flags, *newpath; g_return_val_if_fail (oldpath, NULL); mfile = newpath = custom_flags = NULL; /* determine the maildir */ mdir = mu_maildir_get_maildir_from_path (oldpath); if (!mdir) return NULL; if (new_name) mfile = get_new_basename (); else { /* determine the name of the mailfile, stripped of its flags, as * well as any custom (non-standard) flags */ char *cur; mfile = g_path_get_basename (oldpath); for (cur = &mfile[strlen(mfile)-1]; cur > mfile; --cur) { if ((*cur == ':' || *cur == '!') && (cur[1] == '2' && cur[2] == ',')) { /* get the custom flags (if any) */ custom_flags = mu_flags_custom_from_str (cur + 3); cur[0] = '\0'; /* strip the flags */ break; } } } newpath = get_new_path (new_mdir ? new_mdir : mdir, mfile, newflags, custom_flags); g_free (mfile); g_free (mdir); g_free (custom_flags); return newpath; } static gint64 get_file_size (const char* path) { int rv; struct stat statbuf; rv = stat (path, &statbuf); if (rv != 0) { /* g_warning ("error: %s", strerror (errno)); */ return -1; } return (gint64)statbuf.st_size; } static gboolean msg_move_check_pre (const gchar *src, const gchar *dst, GError **err) { gint size1, size2; if (!g_path_is_absolute(src)) return mu_util_g_set_error (err, MU_ERROR_FILE, "source is not an absolute path: '%s'", src); if (!g_path_is_absolute(dst)) return mu_util_g_set_error (err, MU_ERROR_FILE, "target is not an absolute path: '%s'", dst); if (access (src, R_OK) != 0) return mu_util_g_set_error (err, MU_ERROR_FILE, "cannot read %s", src); if (access (dst, F_OK) != 0) return TRUE; /* target exist; we simply overwrite it, unless target has a different * size. ignore the exceedingly rare case where have duplicate message * file names with different content yet the same length. (md5 etc. is a * bit slow) */ size1 = get_file_size (src); size2 = get_file_size (dst); if (size1 != size2) return mu_util_g_set_error (err, MU_ERROR_FILE, "%s already exists", dst); return TRUE; } static gboolean msg_move_check_post (const char *src, const char *dst, GError **err) { /* double check -- is the target really there? */ if (access (dst, F_OK) != 0) return mu_util_g_set_error (err, MU_ERROR_FILE, "can't find target (%s)", dst); if (access (src, F_OK) == 0) return mu_util_g_set_error (err, MU_ERROR_FILE, "source still there (%s)", src); return TRUE; } static gboolean msg_move (const char* src, const char *dst, GError **err) { if (!msg_move_check_pre (src, dst, err)) return FALSE; if (rename (src, dst) != 0) return mu_util_g_set_error (err, MU_ERROR_FILE,"error moving %s to %s", src, dst); return msg_move_check_post (src, dst, err); } gchar* mu_maildir_move_message (const char* oldpath, const char* targetmdir, MuFlags newflags, gboolean ignore_dups, gboolean new_name, GError **err) { char *newfullpath; gboolean rv; gboolean src_is_target; g_return_val_if_fail (oldpath, FALSE); newfullpath = mu_maildir_get_new_path (oldpath, targetmdir, newflags, new_name); if (!newfullpath) { mu_util_g_set_error (err, MU_ERROR_FILE, "failed to determine targetpath"); return NULL; } src_is_target = (g_strcmp0 (oldpath, newfullpath) == 0); if (!ignore_dups && src_is_target) { mu_util_g_set_error (err, MU_ERROR_FILE_TARGET_EQUALS_SOURCE, "target equals source"); return NULL; } if (!src_is_target) { rv = msg_move (oldpath, newfullpath, err); if (!rv) { g_free (newfullpath); return NULL; } } return newfullpath; } mu-0.9.18/lib/mu-log.c0000644000175000017500000001531613020504331011262 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2016 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #if HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include "mu-log.h" #include #include #include #include #include #include #include #include #include #include "mu-util.h" #define MU_MAX_LOG_FILE_SIZE 1000 * 1000 /* 1 MB (SI units) */ #define MU_LOG_FILE "mu.log" struct _MuLog { int _fd; /* log file descriptor */ MuLogOptions _opts; gboolean _color_stdout; /* whether to use color */ gboolean _color_stderr; GLogFunc _old_log_func; }; typedef struct _MuLog MuLog; /* we use globals, because logging is a global operation as it * globally modifies the behaviour of g_warning and friends */ static MuLog* MU_LOG = NULL; static void log_write (const char* domain, GLogLevelFlags level, const gchar *msg); static void try_close (int fd) { if (fd < 0) return; if (close (fd) < 0) g_printerr ("%s: close() of fd %d failed: %s\n", __func__, fd, strerror(errno)); } static void silence (void) { return; } gboolean mu_log_init_silence (void) { g_return_val_if_fail (!MU_LOG, FALSE); MU_LOG = g_new0 (MuLog, 1); MU_LOG->_fd = -1; mu_log_options_set (MU_LOG_OPTIONS_NONE); MU_LOG->_old_log_func = g_log_set_default_handler ((GLogFunc)silence, NULL); return TRUE; } static void log_handler (const gchar* log_domain, GLogLevelFlags log_level, const gchar* msg) { if ((log_level & G_LOG_LEVEL_DEBUG) && !(MU_LOG->_opts & MU_LOG_OPTIONS_DEBUG)) return; log_write (log_domain ? log_domain : "mu", log_level, msg); } void mu_log_options_set (MuLogOptions opts) { g_return_if_fail (MU_LOG); MU_LOG->_opts = opts; /* when color is, only enable it when output is to a tty */ if (MU_LOG->_opts & MU_LOG_OPTIONS_COLOR) { MU_LOG->_color_stdout = isatty(fileno(stdout)); MU_LOG->_color_stderr = isatty(fileno(stderr)); } } MuLogOptions mu_log_options_get (void) { g_return_val_if_fail (MU_LOG, MU_LOG_OPTIONS_NONE); return MU_LOG->_opts; } static gboolean move_log_file (const char *logfile) { gchar *logfile_old; int rv; logfile_old = g_strdup_printf ("%s.old", logfile); rv = rename (logfile, logfile_old); g_free (logfile_old); if (rv != 0) { g_warning ("failed to move %s to %s.old: %s", logfile, logfile, strerror(rv)); return FALSE; } else return TRUE; } static gboolean log_file_backup_maybe (const char *logfile) { struct stat statbuf; if (stat (logfile, &statbuf) != 0) { if (errno == ENOENT) return TRUE; /* it did not exist yet, no problem */ else { g_warning ("failed to stat(2) %s: %s", logfile, strerror(errno)); return FALSE; } } /* log file is still below the max size? */ if (statbuf.st_size <= MU_MAX_LOG_FILE_SIZE) return TRUE; /* log file is too big!; we move it to .old, overwriting */ return move_log_file (logfile); } gboolean mu_log_init (const char* logfile, MuLogOptions opts) { int fd; /* only init once... */ g_return_val_if_fail (!MU_LOG, FALSE); g_return_val_if_fail (logfile, FALSE); if (opts & MU_LOG_OPTIONS_BACKUP) if (!log_file_backup_maybe(logfile)) { g_warning ("failed to backup log file"); return FALSE; } fd = open (logfile, O_WRONLY|O_CREAT|O_APPEND, 00600); if (fd < 0) { g_warning ("%s: open() of '%s' failed: %s", __func__, logfile, strerror(errno)); return FALSE; } MU_LOG = g_new0 (MuLog, 1); MU_LOG->_fd = fd; mu_log_options_set (opts); MU_LOG->_old_log_func = g_log_set_default_handler ((GLogFunc)log_handler, NULL); MU_WRITE_LOG ("logging started"); return TRUE; } void mu_log_uninit (void) { if (!MU_LOG) return; MU_WRITE_LOG ("logging stopped"); try_close (MU_LOG->_fd); g_free (MU_LOG); MU_LOG = NULL; } static const char* levelstr (GLogLevelFlags level) { switch (level) { case G_LOG_LEVEL_WARNING: return " [WARN] "; case G_LOG_LEVEL_ERROR : return " [ERR ] "; case G_LOG_LEVEL_DEBUG: return " [DBG ] "; case G_LOG_LEVEL_CRITICAL: return " [CRIT] "; case G_LOG_LEVEL_MESSAGE: return " [MSG ] "; case G_LOG_LEVEL_INFO : return " [INFO] "; default: return " [LOG ] "; } } #define color_stdout_maybe(C) \ do{if (MU_LOG->_color_stdout) fputs ((C),stdout);} while (0) #define color_stderr_maybe(C) \ do{if (MU_LOG->_color_stderr) fputs ((C),stderr);} while (0) static void log_write_fd (GLogLevelFlags level, const gchar *msg) { time_t now; char timebuf [22]; const char *mylevel; /* get the time/date string */ now = time(NULL); strftime (timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", localtime(&now)); if (write (MU_LOG->_fd, timebuf, strlen (timebuf)) < 0) goto err; mylevel = levelstr (level); if (write (MU_LOG->_fd, mylevel, strlen (mylevel)) < 0) goto err; if (write (MU_LOG->_fd, msg, strlen (msg)) < 0) goto err; if (write (MU_LOG->_fd, "\n", strlen ("\n")) < 0) goto err; return; /* all went well */ err: fprintf (stderr, "%s: failed to write to log: %s\n", __func__, strerror(errno)); } static void log_write_stdout_stderr (GLogLevelFlags level, const gchar *msg) { const char *mu; mu = MU_LOG->_opts & MU_LOG_OPTIONS_NEWLINE ? "\nmu: " : "mu: "; if (!(MU_LOG->_opts & MU_LOG_OPTIONS_QUIET) && (level & G_LOG_LEVEL_MESSAGE)) { color_stdout_maybe (MU_COLOR_GREEN); fputs (mu, stdout); fputs (msg, stdout); fputs ("\n", stdout); color_stdout_maybe (MU_COLOR_DEFAULT); } /* for errors, log them to stderr as well */ if (level & G_LOG_LEVEL_ERROR || level & G_LOG_LEVEL_CRITICAL || level & G_LOG_LEVEL_WARNING) { color_stderr_maybe (MU_COLOR_RED); fputs (mu, stderr); fputs (msg, stderr); fputs ("\n", stderr); color_stderr_maybe (MU_COLOR_DEFAULT); } } static void log_write (const char* domain, GLogLevelFlags level, const gchar *msg) { g_return_if_fail (MU_LOG); log_write_fd (level, msg); log_write_stdout_stderr (level, msg); } mu-0.9.18/lib/mu-contacts.c0000644000175000017500000002701013020504331012311 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2016 Dirk-Jan C. Binnema ** ** 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, 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. ** */ #include #include #include #include #include "mu-contacts.h" #include "mu-util.h" #include "mu-str.h" #define EMAIL_KEY "email" #define NAME_KEY "name" #define TSTAMP_KEY "tstamp" #define PERSONAL_KEY "personal" #define FREQ_KEY "frequency" /* note: 'personal' here means a mail where my e-mail addresses is explicitly * in one of the address fields, ie., it's not some mailing list message */ struct _ContactInfo { gchar *_name, *_email; gboolean _personal; time_t _tstamp; unsigned _freq; }; typedef struct _ContactInfo ContactInfo; static void contact_info_destroy (ContactInfo *cinfo); static ContactInfo *contact_info_new (char *email, char *name, gboolean personal, time_t tstamp, unsigned freq); struct _MuContacts { GKeyFile *_ccache; gchar *_path; GHashTable *_hash; gboolean _dirty; }; static GKeyFile* load_key_file (const char *path) { GError *err; GKeyFile *keyfile; gboolean file_exists; err = NULL; /* of course this is racy, but it's only for giving more * meaningful errors to users */ file_exists = TRUE; if (access(path, F_OK) != 0) { if (errno != ENOENT) { g_warning ("cannot open %s: %s", path, strerror(errno)); return NULL; } file_exists = FALSE; } err = NULL; keyfile = g_key_file_new (); if (file_exists && !g_key_file_load_from_file (keyfile, path, G_KEY_FILE_KEEP_COMMENTS, &err)) { g_warning ("could not load keyfile %s: %s", path, err->message); g_error_free (err); g_key_file_free (keyfile); return NULL; } return keyfile; } static gboolean get_values (GKeyFile *kfile, const gchar *group, gchar **email, gchar **name, gboolean *personal, size_t *tstamp, unsigned *freq) { GError *err; err = NULL; do { int i; *email = g_key_file_get_value (kfile, group, EMAIL_KEY, &err); if (!*email) break; *tstamp = (time_t)g_key_file_get_integer (kfile, group, TSTAMP_KEY, &err); if (err) break; *personal = g_key_file_get_boolean (kfile, group, PERSONAL_KEY, NULL); *name = g_key_file_get_value (kfile, group, NAME_KEY, NULL); i = g_key_file_get_integer (kfile, group, FREQ_KEY, NULL); *freq = (unsigned)(i >= 0) ? i : 1; return TRUE; } while (0); g_warning ("error getting value for %s: %s", group, err->message ? err->message: "error"); g_clear_error (&err); return FALSE; } static gboolean deserialize_cache (MuContacts *self) { gchar **groups; gsize i, len; groups = g_key_file_get_groups (self->_ccache, &len); for (i = 0; i != len; ++i) { ContactInfo *cinfo; char *name, *email; size_t tstamp; gboolean personal; unsigned freq; if (!get_values (self->_ccache, groups[i], &email, &name, &personal, &tstamp, &freq)) continue; /* ignore this one... */ cinfo = contact_info_new (email, name, personal, tstamp, freq); /* note, we're using the groups[i], so don't free with g_strfreev */ g_hash_table_insert (self->_hash, groups[i], cinfo); } g_free (groups); return TRUE; } static gboolean set_comment (GKeyFile *kfile) { GError *err; const char *comment = " automatically generated -- do not edit"; err = NULL; if (!g_key_file_set_comment (kfile, NULL, NULL, comment, &err)) { g_warning ("could not write comment to keyfile: %s", err->message); g_error_free (err); return FALSE; } return TRUE; } MuContacts* mu_contacts_new (const gchar *path) { MuContacts *self; g_return_val_if_fail (path, NULL); self = g_new0 (MuContacts, 1); self->_path = g_strdup (path); self->_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)contact_info_destroy); self->_ccache = load_key_file (path); if (!self->_ccache || !set_comment (self->_ccache)) { mu_contacts_destroy (self); return NULL; } deserialize_cache (self); MU_WRITE_LOG("deserialized contacts from cache %s", path); self->_dirty = FALSE; return self; } void mu_contacts_clear (MuContacts *self) { g_return_if_fail (self); if (self->_ccache) g_key_file_free (self->_ccache); g_hash_table_remove_all (self->_hash); self->_ccache = g_key_file_new (); self->_dirty = FALSE; } /* * we use the e-mail address to create a key in the GKeyFile, but we * have to mutilate a bit so that it's (a) *cough* practically-unique * and (b) valid as a GKeyFile group name (ie., valid utf8, no control * chars, no '[' or ']') */ static const char* encode_email_address (const char *addr) { static char enc[254 + 1]; /* max size for an e-mail addr */ char *cur; if (!addr) return FALSE; /* make sure chars are with {' ' .. '~'}, and not '[' ']' */ for (cur = strncpy(enc, addr, sizeof(enc)); *cur != '\0'; ++cur) if (!isalnum(*cur)) { *cur = 'A' + (*cur % ('Z' - 'A')); } else *cur = tolower(*cur); return enc; } /* downcase the domain-part of the email address, but only if it * consists of ascii (to prevent screwing up idna addresses) */ char* downcase_domain_maybe (const char *addr) { char *addr_conv, *at, *cur; addr_conv = g_strdup (addr); if (!(at = strchr (addr_conv, '@'))) { /*huh?*/ g_free (addr_conv); return NULL; } for (cur = at + 1; *cur; ++cur) { if (isascii(*cur)) *cur = g_ascii_tolower (*cur); else { /* non-ascii; return the unchanged original */ g_free (addr_conv); return g_strdup (addr); } } return addr_conv; } static void clear_str (char* str) { if (str) { mu_str_remove_ctrl_in_place (str); g_strstrip (str); } } gboolean mu_contacts_add (MuContacts *self, const char *addr, const char *name, gboolean personal, time_t tstamp) { ContactInfo *cinfo; const char *group; g_return_val_if_fail (self, FALSE); g_return_val_if_fail (addr, FALSE); group = encode_email_address (addr); cinfo = (ContactInfo*) g_hash_table_lookup (self->_hash, group); if (!cinfo) { char *addr_dc; if (!(addr_dc = downcase_domain_maybe (addr))) return FALSE; cinfo = contact_info_new (addr_dc, name ? g_strdup(name) : NULL, personal, tstamp, 1); g_hash_table_insert (self->_hash, g_strdup(group), cinfo); } else { /* if the contact is ever used in a personal way, it's * personal */ if (personal) cinfo->_personal = TRUE; if (cinfo->_tstamp < tstamp) { if (!mu_str_is_empty(name)) { /* update the name to the last one used, unless it's * empty*/ g_free (cinfo->_name); cinfo->_name = g_strdup (name); if (cinfo->_name) mu_str_remove_ctrl_in_place (cinfo->_name); } cinfo->_tstamp = tstamp; } ++cinfo->_freq; } self->_dirty = TRUE; return TRUE; } struct _EachContactData { MuContactsForeachFunc _func; gpointer _user_data; GRegex *_rx; size_t _num; }; typedef struct _EachContactData EachContactData; static void /* email will never be NULL, but ci->_name may be */ each_contact (const char *group, ContactInfo *ci, EachContactData *ecdata) { if (!ci->_email) g_warning ("missing email: %u", (unsigned)ci->_tstamp); /* ignore this contact if we have a regexp, and it matches * neither email nor name (if we have a name) */ while (ecdata->_rx) { /* note, only once */ if (g_regex_match (ecdata->_rx, ci->_email, 0, NULL)) break; /* email matches? continue! */ if (!ci->_name) return; /* email did not match, no name? ignore this one */ if (g_regex_match (ecdata->_rx, ci->_name, 0, NULL)) break; /* name matches? continue! */ return; /* nothing matched, ignore this one */ } ecdata->_func (ci->_email, ci->_name, ci->_personal, ci->_tstamp, ci->_freq, ecdata->_user_data); ++ecdata->_num; } gboolean mu_contacts_foreach (MuContacts *self, MuContactsForeachFunc func, gpointer user_data, const char *pattern, size_t *num) { EachContactData ecdata; g_return_val_if_fail (self, FALSE); g_return_val_if_fail (func, FALSE); if (pattern) { GError *err; err = NULL; ecdata._rx = g_regex_new (pattern, G_REGEX_CASELESS|G_REGEX_OPTIMIZE, 0, &err); if (!ecdata._rx) { g_warning ("error in regexp '%s': %s", pattern, err->message); g_error_free (err); return FALSE; } } else ecdata._rx = NULL; ecdata._func = func; ecdata._user_data = user_data; ecdata._num = 0; g_hash_table_foreach (self->_hash, (GHFunc)each_contact, &ecdata); if (ecdata._rx) g_regex_unref (ecdata._rx); if (num) *num = ecdata._num; return TRUE; } static void each_keyval (const char *group, ContactInfo *cinfo, MuContacts *self) { /* use set value so the string do not necessarily have to be * valid utf-8 */ if (cinfo->_name) g_key_file_set_value (self->_ccache, group, NAME_KEY, cinfo->_name); g_key_file_set_value (self->_ccache, group, EMAIL_KEY, cinfo->_email); g_key_file_set_boolean (self->_ccache, group, PERSONAL_KEY, cinfo->_personal); g_key_file_set_integer (self->_ccache, group, TSTAMP_KEY, (int)cinfo->_tstamp); g_key_file_set_integer (self->_ccache, group, FREQ_KEY, (int)cinfo->_freq); } gboolean mu_contacts_serialize (MuContacts *self) { gchar *data; gsize len; gboolean rv; g_return_val_if_fail (self, FALSE); g_hash_table_foreach (self->_hash, (GHFunc)each_keyval, self); /* Note: err arg is unused */ data = g_key_file_to_data (self->_ccache, &len, NULL); if (len) { GError *err; err = NULL; rv = g_file_set_contents (self->_path, data, len, &err); if (!rv) { g_warning ("failed to serialize cache to %s: %s", self->_path, err->message); g_error_free (err); } g_free (data); } else rv = TRUE; return rv; } void mu_contacts_destroy (MuContacts *self) { if (!self) return; if (self->_ccache && self->_dirty && mu_contacts_serialize (self)) MU_WRITE_LOG("serialized contacts cache %s", self->_path); if (self->_ccache) g_key_file_free (self->_ccache); g_free (self->_path); if (self->_hash) g_hash_table_destroy (self->_hash); g_free (self); } /* note, we will *own* the name, email we get, and we'll free them in the * end... */ static ContactInfo * contact_info_new (char *email, char *name, gboolean personal, time_t tstamp, unsigned freq) { ContactInfo *cinfo; /* email should not be NULL, name can */ g_return_val_if_fail (email, NULL); cinfo = g_slice_new (ContactInfo); /* we need to clear the strings from control chars because * they could screw up the keyfile */ clear_str (email); clear_str (name); cinfo->_email = email; cinfo->_name = name; cinfo->_personal = personal; cinfo->_tstamp = tstamp; cinfo->_freq = freq; return cinfo; } static void contact_info_destroy (ContactInfo *cinfo) { if (!cinfo) return; g_free (cinfo->_email); g_free (cinfo->_name); g_slice_free (ContactInfo, cinfo); } size_t mu_contacts_count (MuContacts *self) { g_return_val_if_fail (self, 0); return g_hash_table_size (self->_hash); } mu-0.9.18/lib/mu-util.h0000644000175000017500000003554213020504332011467 00000000000000/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* ** Copyright (C) 2008-2013 Dirk-Jan C. Binnema ** ** 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, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef __MU_UTIL_H__ #define __MU_UTIL_H__ #include #include #include #include /* for mode_t */ /* hopefully, this should get us a sane PATH_MAX */ #include /* not all systems provide PATH_MAX in limits.h */ #ifndef PATH_MAX #include #ifndef PATH_MAX #define PATH_MAX MAXPATHLEN #endif /*!PATH_MAX*/ #endif /*PATH_MAX*/ G_BEGIN_DECLS /** * get the expanded path; ie. perform shell expansion on the path. the * path does not have to exist * * @param path path to expand * * @return the expanded path as a newly allocated string, or NULL in * case of error */ char* mu_util_dir_expand (const char* path) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * guess the maildir; first try $MAILDIR; if it is unset or * non-existant, try ~/Maildir if both fail, return NULL * * @return full path of the guessed Maildir, or NULL; must be freed (gfree) */ char* mu_util_guess_maildir (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * guess the place of the mu homedir (typically, ~/.mu). Note, this * directory does not necessarily exist. mu_util_check_dir can be used * to check that * * @return the guessed mu homedir, which needs to be freed with g_free * when no longer needed. */ gchar* mu_util_guess_mu_homedir (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * if path exists, check that's a read/writeable dir; otherwise try to * create it (with perms 0700) * * @param path path to the dir * @param mode to set for the dir (as per chmod(1)) * @param nowarn, if TRUE, don't write warnings (if any) to stderr * * @return TRUE if a read/writeable directory `path' exists after * leaving this function, FALSE otherwise */ gboolean mu_util_create_dir_maybe (const gchar *path, mode_t mode, gboolean nowarn) G_GNUC_WARN_UNUSED_RESULT; /** * check whether path is a directory, and optionally, if it's readable * and/or writeable * * @param path dir path * @param readable check for readability * @param writeable check for writability * * @return TRUE if dir exist and has the specified properties */ gboolean mu_util_check_dir (const gchar* path, gboolean readable, gboolean writeable) G_GNUC_WARN_UNUSED_RESULT; /** * get our the cache directory, typically, /tmp/mu-/ * * @return the cache directory; don't free */ const char* mu_util_cache_dir (void) G_GNUC_CONST; /** * create a writeable file and return its file descriptor (which * you'll need to close(2) when done with it.) * * @param path the full path of the file to create * @param the mode to open (ie. 0644 or 0600 etc., see chmod(3) * @param overwrite should we allow for overwriting existing files? * * @return a file descriptor, or -1 in case of error. If it's a file * system error, 'errno' may contain more info. use 'close()' when done * with the file descriptor */ int mu_util_create_writeable_fd (const char* path, mode_t mode, gboolean overwrite) G_GNUC_WARN_UNUSED_RESULT; /** * check if file is local, ie. on the local file system. this means * that it's either having a file URI, *or* that it's an existing file * * @param path a path * * @return TRUE if the file is local, FALSE otherwise */ gboolean mu_util_is_local_file (const char* path); /** * is the current locale utf-8 compatible? * * @return TRUE if it's utf8 compatible, FALSE otherwise */ gboolean mu_util_locale_is_utf8 (void) G_GNUC_CONST; /** * write a string (assumed to be in utf8-format) to a stream, * converted to the current locale * * @param str a string * @param stream a stream * * @return TRUE if printing worked, FALSE otherwise */ gboolean mu_util_fputs_encoded (const char *str, FILE *stream); /** * print a formatted string (assumed to be in utf8-format) to stdout, * converted to the current locale * * @param a standard printf() format string, followed by a parameter list * * @return TRUE if printing worked, FALSE otherwise */ gboolean mu_util_print_encoded (const char *frm, ...) G_GNUC_PRINTF(1,2); /** * print a formatted string (assumed to be in utf8-format) to stderr, * converted to the current locale * * @param a standard printf() format string, followed by a parameter list * * @return TRUE if printing worked, FALSE otherwise */ gboolean mu_util_printerr_encoded (const char *frm, ...) G_GNUC_PRINTF(1,2); /** * read a password from stdin (without echoing), and return it. * * @param prompt the prompt text before the password * * @return the password (free with g_free), or NULL */ char* mu_util_read_password (const char *prompt) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * Try to 'play' (ie., open with it's associated program) a file. On * MacOS, the the program 'open' is used for this; on other platforms * 'xdg-open' to do the actual opening. In addition you can set it to another program * by setting the MU_PLAY_PROGRAM environment variable * * @param path full path of the file to open * @param allow_local allow local files (ie. with file:// prefix or fs paths) * @param allow_remote allow URIs (ie., http, mailto) * @param err receives error information, if any * * @return TRUE if it succeeded, FALSE otherwise */ gboolean mu_util_play (const char *path, gboolean allow_local, gboolean allow_remote, GError **err); /** * Check whether program prog exists in PATH * * @param prog a program (executable) * * @return TRUE if it exists and is executable, FALSE otherwise */ gboolean mu_util_program_in_path (const char *prog); enum _MuFeature { MU_FEATURE_GUILE = 1 << 0, /* do we support Guile 2.0? */ MU_FEATURE_GNUPLOT = 1 << 1, /* do we have gnuplot installed? */ MU_FEATURE_CRYPTO = 1 << 2 /* do we support crypto (Gmime >= 2.6) */ }; typedef enum _MuFeature MuFeature; /** * Check whether mu supports some particular feature * * @param feature a feature (multiple features can be logical-or'd together) * * @return TRUE if the feature is supported, FALSE otherwise */ gboolean mu_util_supports (MuFeature feature); /** * Get an error-query for mu, to be used in `g_set_error'. Recent * version of Glib warn when using 0 for the error-domain in * g_set_error. * * * @return an error quark for mu */ GQuark mu_util_error_quark (void) G_GNUC_CONST; #define MU_ERROR_DOMAIN (mu_util_error_quark()) /** * convert a string array in to a string, with the elements separated * by ' ' * * @param params a non-NULL, NULL-terminated string array * * @return a newly allocated string */ gchar* mu_util_str_from_strv (const gchar **params) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /* * for OSs with out support for direntry->d_type, like Solaris */ #ifndef DT_UNKNOWN enum { DT_UNKNOWN = 0, #define DT_UNKNOWN DT_UNKNOWN DT_FIFO = 1, #define DT_FIFO DT_FIFO DT_CHR = 2, #define DT_CHR DT_CHR DT_DIR = 4, #define DT_DIR DT_DIR DT_BLK = 6, #define DT_BLK DT_BLK DT_REG = 8, #define DT_REG DT_REG DT_LNK = 10, #define DT_LNK DT_LNK DT_SOCK = 12, #define DT_SOCK DT_SOCK DT_WHT = 14 #define DT_WHT DT_WHT }; #endif /*DT_UNKNOWN*/ /** * get the d_type (as in direntry->d_type) for the file at path, using * lstat(3) * * @param path full path * * @return DT_REG, DT_DIR, DT_LNK, or DT_UNKNOWN (other values are not * supported currently ) */ unsigned char mu_util_get_dtype_with_lstat (const char *path); /** * we need this when using Xapian::Document* from C * */ typedef gpointer XapianDocument; /** * we need this when using Xapian::Enquire* from C * */ typedef gpointer XapianEnquire; /* print a warning for a GError, and free it */ #define MU_HANDLE_G_ERROR(GE) \ do { \ if (!(GE)) \ g_warning ("%s:%u: an error occured in %s", \ __FILE__, __LINE__, __func__); \ else { \ g_warning ("error %u: %s", (GE)->code, (GE)->message); \ g_error_free ((GE)); \ } \ } while (0) /** * * don't repeat these catch blocks everywhere... * */ #define MU_STORE_CATCH_BLOCK_RETURN(GE,R) \ catch (const MuStoreError& merr) { \ mu_util_g_set_error ((GE), \ merr.mu_error(), "%s", \ merr.what().c_str()); \ return (R); \ } \ #define MU_XAPIAN_CATCH_BLOCK \ catch (const Xapian::Error &xerr) { \ g_critical ("%s: xapian error '%s'", \ __func__, xerr.get_msg().c_str()); \ } catch (...) { \ g_critical ("%s: caught exception", __func__); \ } #define MU_XAPIAN_CATCH_BLOCK_G_ERROR(GE,E) \ catch (const Xapian::DatabaseLockError &xerr) { \ mu_util_g_set_error ((GE), \ MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK, \ "%s: xapian error '%s'", \ __func__, xerr.get_msg().c_str()); \ } catch (const Xapian::DatabaseCorruptError &xerr) { \ mu_util_g_set_error ((GE), \ MU_ERROR_XAPIAN_CORRUPTION, \ "%s: xapian error '%s'", \ __func__, xerr.get_msg().c_str()); \ } catch (const Xapian::DatabaseError &xerr) { \ mu_util_g_set_error ((GE),MU_ERROR_XAPIAN, \ "%s: xapian error '%s'", \ __func__, xerr.get_msg().c_str()); \ } catch (const Xapian::Error &xerr) { \ mu_util_g_set_error ((GE),(E), \ "%s: xapian error '%s'", \ __func__, xerr.get_msg().c_str()); \ } catch (...) { \ mu_util_g_set_error ((GE),(MU_ERROR_INTERNAL), \ "%s: caught exception", __func__); \ } #define MU_XAPIAN_CATCH_BLOCK_RETURN(R) \ catch (const Xapian::Error &xerr) { \ g_critical ("%s: xapian error '%s'", \ __func__, xerr.get_msg().c_str()); \ return (R); \ } catch (...) { \ g_critical ("%s: caught exception", __func__); \ return (R); \ } #define MU_XAPIAN_CATCH_BLOCK_G_ERROR_RETURN(GE,E,R) \ catch (const Xapian::Error &xerr) { \ mu_util_g_set_error ((GE),(E), \ "%s: xapian error '%s'", \ __func__, xerr.get_msg().c_str()); \ return (R); \ } catch (...) { \ if ((GE)&&!(*(GE))) \ mu_util_g_set_error ((GE), \ (MU_ERROR_INTERNAL), \ "%s: caught exception", __func__); \ return (R); \ } /* the name of the (leaf) dir which has the xapian database */ #define MU_XAPIAN_DIR_NAME "xapian" /* name of the bookmark file */ #define MU_BOOKMARK_FILENAME "bookmarks" /* metadata key for the xapian 'schema' version */ #define MU_STORE_VERSION_KEY "db_version" /** * log something in the log file; note, we use G_LOG_LEVEL_INFO * for such messages */ #define MU_WRITE_LOG(...) \ G_STMT_START { \ g_log (G_LOG_DOMAIN, \ G_LOG_LEVEL_INFO, \ __VA_ARGS__); \ } G_STMT_END #define MU_G_ERROR_CODE(GE) ((GE)&&(*(GE))?(*(GE))->code:MU_ERROR) enum _MuError { /* no error at all! */ MU_OK = 0, /* generic error */ MU_ERROR = 1, MU_ERROR_IN_PARAMETERS = 2, MU_ERROR_INTERNAL = 3, MU_ERROR_NO_MATCHES = 4, /* not really an error; for callbacks */ MU_IGNORE = 5, MU_ERROR_SCRIPT_NOT_FOUND = 8, /* general xapian related error */ MU_ERROR_XAPIAN = 11, /* (parsing) error in the query */ MU_ERROR_XAPIAN_QUERY = 13, /* xapian dir is not accessible */ MU_ERROR_XAPIAN_DIR_NOT_ACCESSIBLE = 14, /* database version is not up-to-date */ MU_ERROR_XAPIAN_VERSION_MISMATCH = 15, /* missing data for a document */ MU_ERROR_XAPIAN_MISSING_DATA = 16, /* database corruption */ MU_ERROR_XAPIAN_CORRUPTION = 17, /* can't get write lock */ MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK = 18, /* database is empty */ MU_ERROR_XAPIAN_IS_EMPTY = 19, /* could not write */ MU_ERROR_XAPIAN_STORE_FAILED = 20, /* could not remove */ MU_ERROR_XAPIAN_REMOVE_FAILED = 21, /* database was modified; reload */ MU_ERROR_XAPIAN_MODIFIED = 22, /* GMime related errors */ /* gmime parsing related error */ MU_ERROR_GMIME = 30, /* contacts related errors */ MU_ERROR_CONTACTS = 50, MU_ERROR_CONTACTS_CANNOT_RETRIEVE = 51, /* crypto related errors */ MU_ERROR_CRYPTO = 60, /* File errors */ /* generic file-related error */ MU_ERROR_FILE = 70, MU_ERROR_FILE_INVALID_NAME = 71, MU_ERROR_FILE_CANNOT_LINK = 72, MU_ERROR_FILE_CANNOT_OPEN = 73, MU_ERROR_FILE_CANNOT_READ = 74, MU_ERROR_FILE_CANNOT_EXECUTE = 75, MU_ERROR_FILE_CANNOT_CREATE = 76, MU_ERROR_FILE_CANNOT_MKDIR = 77, MU_ERROR_FILE_STAT_FAILED = 78, MU_ERROR_FILE_READDIR_FAILED = 79, MU_ERROR_FILE_INVALID_SOURCE = 80, MU_ERROR_FILE_TARGET_EQUALS_SOURCE = 81, MU_ERROR_FILE_CANNOT_WRITE = 82, MU_ERROR_FILE_CANNOT_UNLINK = 83, /* not really an error, used in callbacks */ MU_STOP = 99 }; typedef enum _MuError MuError; /** * set an error if it's not already set, and return FALSE * * @param err errptr, or NULL * @param errcode error code * @param frm printf-style format, followed by paremeters * * @return FALSE */ gboolean mu_util_g_set_error (GError **err, MuError errcode, const char *frm, ...) G_GNUC_PRINTF(3,4); /** * calculate a 64-bit hash for the given string, based on a * combination of the DJB and BKDR hash functions * * @param a string * * @return the hash as a static string, which stays valid until this * function is called again. */ static inline const char* mu_util_get_hash (const char* str) { unsigned djbhash, bkdrhash, bkdrseed; unsigned u; static char hex[18]; djbhash = 5381; bkdrhash = 0; bkdrseed = 1313; for(u = 0; str[u]; ++u) { djbhash = ((djbhash << 5) + djbhash) + str[u]; bkdrhash = bkdrhash * bkdrseed + str[u]; } snprintf (hex, sizeof(hex), "%08x%08x", djbhash, bkdrhash); return hex; } #define MU_COLOR_RED "\x1b[31m" #define MU_COLOR_GREEN "\x1b[32m" #define MU_COLOR_YELLOW "\x1b[33m" #define MU_COLOR_BLUE "\x1b[34m" #define MU_COLOR_MAGENTA "\x1b[35m" #define MU_COLOR_CYAN "\x1b[36m" #define MU_COLOR_DEFAULT "\x1b[0m" G_END_DECLS #endif /*__MU_UTIL_H__*/ mu-0.9.18/config.guess0000755000175000017500000012367212605152261011506 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2015 Free Software Foundation, Inc. timestamp='2015-01-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 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 . # # 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 Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp 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` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: mu-0.9.18/INSTALL0000644000175000017500000003661012605152261010212 00000000000000Installation Instructions ************************* Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell command `./configure && make && make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. 5. Optionally, type `make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior `make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type `make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide `make distcheck', which can by used by developers to test that all other targets like `make install' and `make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. This is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of `${prefix}', so that specifying just `--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to `configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the `make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, `make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of `${prefix}'. Any directories that were specified during `configure', but not in terms of `${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the `DESTDIR' variable. For example, `make install DESTDIR=/alternate/directory' will prepend `/alternate/directory' before all installation names. The approach of `DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of `${prefix}' at `configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of `make' will be. For these packages, running `./configure --enable-silent-rules' sets the default to minimal output, which can be overridden with `make V=1'; while running `./configure --disable-silent-rules' sets the default to verbose, which can be overridden with `make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX `make' updates targets which have the same time stamps as their prerequisites, which makes it generally unusable when shipped generated files such as `configure' are involved. Use GNU `make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. mu-0.9.18/NEWS.org0000644000175000017500000006343713020504331010444 00000000000000#+STARTUP:showall * NEWS (user visible changes) ** 0.9.18 New development series which will lead to 0.9.18. *** mu - Increase the default maximum size for messages to index to 500 Mb; you can customize this using the --max-msg-size parameter to mu index. - implement "lazy-checking", which makes mu not descend into subdirectories when the directory-timestamp is up to date; greatly speeds up indexing (see --lazy-check) - prefer gpg2 for crypto - fix a crash when running on OpenBSD - fix --clear-links (broken filenames) *** mu4e **** reading messages - Add `mu4e-action-view-with-xwidget`, and action for viewing e-mails inside a Webkit-widget inside emacs (requires emacs 25.x with xwidget/webkit/gtk3 support) - Explicitly specify utf8 for external html viewing, so browsers can handle it correctly. - Make `shr' the default renderer for rich-text emails (when available) - Add a :user-agent field to the message-sexp (in mu4e-view), which is either the User-Agent or X-Mailer field, when present. **** composing messages - Cleanly handle early exits from message composition as well as while composing. - Allow for resending existing messages, possibly editing them. M-x mu4e-compose-resend, or use the menu; no shortcut. - Better handle the closing of separate compose frames - Improved font-locking for the compose buffers, and more extensive checks for cited parts. - automatically sign/encrypt replies to signed/encrypted messages (subject to `mu4e-compose-crypto-reply-policy') **** searching & marking - Add a hook `mu4e-mark-execute-pre-hook`, which is run just before executing marks. - Just before executing any search, a hook-function `mu4e-headers-search-hook` is invoked, which receives the search expression as its parameter. - In addition, there's a `mu4e-headers-search-bookmark-hook` which gets called when searches get invoked as a bookmark (note that `mu4e-headers-search-hook` will also be called just afterwards). This hook also receives the search expression as its parameter. - Remove the 'z' keybinding for leaving the headers view. Keybindings are precious! - Fix parentheses/precedence in narrowing search terms **** indexing - Allow for indexing in the background; see `mu4e-index-update-in-background`. - Better handle mbsync output in the update buffer - Add variables mu4e-index-cleanup and mu4e-index-lazy to enable lazy checking from mu4e; you can sit from mu4e using something like: #+BEGIN_SRC elisp (setq mu4e-index-cleanup nil ;; don't do a full cleanup check mu4e-index-lazy-check t) ;; don't consider up-to-date dirs #+END_SRC **** misc - don't overwrite global-mode-string, append to it. - Make org-links (and more general, all users of mu4e-view-message-with-message-id) use a headers buffer, then view the message. This way, those linked message are just like any other, and can be deleted, moved etc. - Support org-mode 9.x - Improve file-name escaping, and make it support non-ascii filenames - Attempt to jump to the same messages after a re-search update operation - Add action for spam-filter options - Let `mu4e~read-char-choice' become case-insensitive if there is no exact match; small convenience that affects most the single-char option-reading in mu4e. *** Perl - an experimental Perl binding ("mup") is available now. See perl/README.md for details. ** Contributors: Aaron LI, Abdo Roig-Maranges, Ævar Arnfjörð Bjarmason, Alex Bennée, Allen, Anders Johansson, Antoine Levitt, Arthur Lee, attila, Charles-H. Schulz, Christophe Troestler, Chunyang Xu, Dirk-Jan C. Binnema, Jakub Sitnicki, Josiah Schwab, jsrjenkins, Jun Hao, Klaus Holst, Lukas Fürmetz, Magnus Therning, Maximilian Matthe, Nicolas Richard, Piotr Trojanek, Prashant Sachdeva, Remco van 't Veer, Stephen Eglen, Stig Brautaset, Thierry Volpiatto, Thomas Moulia, Titus von der Malsburg, Yuri D'Elia * Old news :PROPERTIES: :VISIBILITY: folded :END: ** 0.9.16 *** Release 2016-01-20: Release from the 0.9.15 series *** Contributors: Adam Sampson, Ævar Arnfjörð Bjarmason, Bar Shirtcliff, Charles-H. Schulz, Clément Pit--Claudel, Damien Cassou, Declan Qian, Dima Kogan, Dirk-Jan C. Binnema, Foivos S. Zakkak, Hinrik Örn Sigurðsson, Jeroen Tiebout, JJ Asghar, Jonas Bernoulli, Jun Hao, Martin Yrjölä, Maximilian Matthé, Piotr Trojanek, prsarv, Thierry Volpiatto, Titus von der Malsburg (and of course all people who reported issues, provided suggestions etc.) ** 0.9.15 - bump version to 0.9.15. From now on, odd minor version numbers are for development versions; thus, 0.9.16 is to be the next stable release. - special case text/calendar attachements to get .vcs extensions. This makes it easier to process those with external tools. - change the message file names to better conform to the maildir spec; this was confusing some tools. - fix navigation when not running in split-view mode - add `mu4e-view-body-face', so the body-face for message in the view can be customized; e.g. (set-face-attribute 'mu4e-view-body-face nil :font "Liberation Serif-10") - add `mu4e-action-show-thread`, an action for the headers and view buffers to search for messages in the same thread as the current one. - allow for transforming mailing-list names for display, using `mu4e-mailing-list-patterns'. - some optimizations in indexing (~30% faster in some cases) - new variable mu4e-user-agent-string, to customize the User-Agent: header. - when removing the "In-reply-to" header from replies, mu4e will also remove the (hidden) References header, effectively creating a new message-thread. - implement 'mu4e-context', for defining and switching between various contexts, which are groups of settings. This can be used for instance for switch between e-mail accounts. See the section in the manual for details. - correctly decode mailing-list headers - allow for "fancy" mark-characters; and improve the default set - by default, the maildirs are no longer cached; please see the variable ~mu4e-cache-maildir-list~ if you have a lot of maildirs and it gets slow. - change the default value for ~org-mu4e-link-query-in-headers-mode~ to ~nil~, ie. by default link to the message, not the query, as this is usually more useful behavior. - overwrite target message files that already exist, rather than erroring out. - set mu4e-view-html-plaintext-ratio-heuristic to 5, as 10 was too high to detect some effectively html-only messages - add mu4e-view-toggle-html (keybinding: 'h') to toggle between text and html display. The existing 'mu4e-view-toggle-hide-cited' gets the new binding '#'. - add a customization variable `mu4e-view-auto-mark-as-read' (defaults to t); if set to nil, mu4e won't mark messages as read when you open them. This can be useful on read-only file-systems, since marking-as-read implies a file-move operation. - use smaller chunks for mu server on Cygwin, allowing for better mu4e support there. ** 0.9.13 *** contributors Attila, Daniele Pizzolli, Charles-H.Schulz, David C Sterrat, Dirk-Jan C. Binnema, Eike Kettner, Florian Lindner, Foivos S. Zakkak, Gour, KOMURA Takaaki, Pan Jie, Phil Hagelberg, thdox, Tiago Saboga, Titus von der Malsburg (and of course all people who reported issues, provided suggestions etc.) *** mu/mu4e/guile - NEWS (this file) is now visible from within mu4e – "N" in the main-menu. - make `mu4e-headers-sort-field', `mu4e-headers-sort-direction' public (that, is change the prefix from mu4e~ to mu4e-), so users can manipulate them - make it possible the 'fancy' (unicode) characters separately for headers and marks (see the variable `mu4e-use-fancy-chars'.) - allow for composing in a separate frame (see `mu4e-compose-in-new-frame') - add the `:thread-subject' header field, for showing the subject for a thread only once. So, instead of (from the manual): #+BEGIN_EXAMPLE 06:32 Nu To Edmund Dantès GstDev + Re: Gstreamer-V4L... 15:08 Nu Abbé Busoni GstDev + Re: Gstreamer-V... 18:20 Nu Pierre Morrel GstDev \ Re: Gstreamer... 2013-03-18 S Jacopo EmacsUsr + emacs server on win... 2013-03-18 S Mercédès EmacsUsr \ RE: emacs server ... 2013-03-18 S Beachamp EmacsUsr + Re: Copying a whole... 22:07 Nu Albert de Moncerf EmacsUsr \ Re: Copying a who... 2013-03-18 S Gaspard Caderousse GstDev | Issue with GESSimpl... 2013-03-18 Ss Baron Danglars GuileUsr | Guile-SDL 0.4.2 ava... End of search results #+END_EXAMPLE the headers list would now look something like: #+BEGIN_EXAMPLE 10:26 ⭑☐ Nicolas Goaziou Orgmode /bulk ◼ Re: [O] 2 issue with Include function 11:00 ⭑☐ Leonard Randall Orgmode /bulk ┗▶ 10:55 ⭑☐ Guillermo Rodrigu... GstDev /bulk ◼ Re: stop pipeline into a callback funtion. 12:04 ⭑☐ Enrique Ocaña Gon... GstDev /bulk ┗▶ 11:27 ⭑☐ Tim Müller GstDev /bulk ◼ 09:34 ⭑☐ Robert Klein Orgmode /bulk ◼ Re: [O] Agenda Tag filtering - has the behaviour changed? #+END_EXAMPLE This is a feature known from e.g. `mutt' and `gnus` and many other clients, and can be enabled by customizing `mu4e-headers-fields' (replacing `:subject' with `:thread-subject') It's not the default yet, but may become so in the future. - add some spam-handling actions to mu4e-contrib.el - mu4e now targets org 8.x, which support for previous versions relegated to `org-old-mu4e.el`. Some of the new org-features are improved capture templates. - updates to the documentation, in particular about using BBDB. - improved URL-handling (use emacs built-in functionality) - many bug fixes, including some crash fixes on BSD *** guile – add --delete option to the find-dups scripts, to automatically delete them. Use with care! ** Release 0.9.12 *** mu - truncate /all/ terms the go beyond xapian's max term length - lowercase the domain-part of email addresses in mu cfind (and mu4e), if the domain is in ascii - give messages without msgids fake-message-ids; this fixes the problem where such messages were not found in --include-related queries - cleanup of the query parser - provide fake message-ids for messages without it; fixes #183 - allow showing tags in 'mu find' output - fix CSV quoting *** mu4e - update the emacs <-> backend protocol; documented in the mu-server man page - show 'None' as date for messages without it (Headers View) - add `mu4e-headers-found-hook', `mu4e-update-pre-hook'. - split org support in org-old-mu4e.el (org <= 7.x) and org-mu4e.el - org: improve template keywords - rework URL handling ** Release 0.9.5 *** mu - allow 'contact:' as a shortcut in queries for 'from:foo OR to:foo OR cc:foo OR bcc:foo', and 'recip:' as a shortcut for 'to:foo OR cc:foo OR bcc:foo' - support getting related messages (--include-related), which includes messages that may not match the query, but that are in the same threads as messages that were - support "list:"/"v:" for matching mailing list names, and the "v" format-field to show them. E.g 'mu find list:emacs-orgmode.gnu.org' *** mu4e - scroll down in message view takes you to next message (but see `mu4e-view-scroll-to-next') - support 'human dates', that is, show the time for today's messages, and the date for older messages in the headers view - replace `mu4e-user-mail-address-regexp' and `mu4e-my-mail-addresses' with `mu4e-user-mail-address-list' - support tags (i.e.., X-Keywords and friends) in the headers-view, and the message view. Thanks to Abdó Roig-Maranges. New field ":tags". - automatically update the headers buffer when new messages are found during indexing; set `mu4e-headers-auto-update' to nil to disable this. - update mail/index with M-x mu4e-update-mail-and-index; which everywhere in mu4e is available with key C-S-u. Use prefix argument to run in background. - add function `mu4e-update-index' to only update the index - add 'friendly-names' for mailing lists, so they should up nicely in the headers view *** guile - add 'mu script' command to run mu script, for example to do statistics on your message corpus. See the mu-script man-page. *** mug - ported to gtk+ 3; remove gtk+ 2.x code ** Release 0.9.9 <2012-10-14> *** mu4e - view: address can be toggled long/short, compose message - sanitize opening urls (mouse-1, and not too eager) - tooltips for header labels, flags - add sort buttons to header-labels - support signing / decryption of messages - improve address-autocompletion (e.g., ensure it's case-insensitive) - much faster when there are many maildirs - improved line wrapping - better handle attached messages - improved URL-matching - improved messages to user (mu4e-(warn|error|message)) - add refiling functionality - support fancy non-ascii in the UI - dynamic folders (i.e.., allow mu4e-(sent|draft|trash|refile)-folder) to be a function - dynamic attachment download folder (can be a function now) - much improved manual *** mu - remove --summary (use --summary-len instead) - add --after for mu find, to limit to messages after T - add new command `mu verify', to verify signatures - fix iso-2022-jp decoding (and other 7-bit clean non-ascii) - add support for X-keywords - performance improvements for threaded display (~ 25% for 23K msgs) - mu improved user-help (and the 'mu help' command) - toys/mug2 replaces toys/mug *** mu-guile - automated tests - add mu:timestamp, mu:count - handle db reopenings in the background ** Release 0.9.8.5 <2012-07-01> *** mu4e - auto-completion of e-mail addresses - inline display of images (see `mu4e-view-show-images'), uses imagemagick if available - interactively change number of headers / columns for showing headers with C-+ and C-- in headers, view mode - support flagging message - navigate to previous/next queries like a web browser (with , ) - narrow search results with '/' - next/previous take a prefix arg now, to move to the nth previous/next message - allow for writing rich-text messages with org-mode - enable marking messages as Flagged - custom marker functions (see manual) - better "dwim" handling of buffer switching / killing - deferred marking of message (i.e.., mark now, decide what to mark for later) - enable changing of sort order, display of threads - clearer marks for marked messages - fix sorting by subject (disregarding Re:, Fwd: etc.) - much faster handling when there are many maildirs (speedbar) - handle mailto: links - improved, extended documentation *** mu - support .noupdate files (parallel to .noindex, dir is ignored unless we're doing a --rebuild). - append all inline text parts, when getting the text body - respect custom maildir flags - correctly handle the case where g_utf8_strdown (str) > len (str) - make gtk, guile, webkit dependency optional, even if they are installed ** Release 0.9.8.4 <2012-05-08> *** mu4e - much faster header buffers - split view mode (headers, view); see `mu4e-split-view'. - add search history for queries - ability to open attachments with arbitrary programs, pipe through shell commands or open in the current emacs - quote names in recipient addresses - mu4e-get-maildirs works now for recursive maildirs as well - define arbitrary operations for headers/messages/attachments using the actions system -- see the chapter 'Actions' in the manual - allow mu4e to be uses as the default emacs mailer (`mu4e-user-agent') - mark headers based on a regexp, `mu4e-mark-matches', or '%' - mark threads, sub-threads (mu4e-hdrs-mark-thread, mu4e-hdrs-mark-subthread, or 'T', 't') - add msg2pdf toy - easy logging (using `mu4e-toggle-logging') - improve mu4e-speedbar for use in headers/view - use the message-mode FCC system for saving messages to the sent-messages folder - fix: off-by-one in number of matches shown *** general - fix for opening files with non-ascii names - much improved support for searching non-Latin (Cyrillic etc.) languages we can now match 'Тесла' or 'Аркона' without problems - smarter escaping (fixes issues with finding message ids) - fixes for queries with brackets - allow --summary-len for the length of message summaries - numerous other small fixes ** Release 0.9.8.3 <2012-04-06> *NOTE*: existing mu/mu4e are recommended to run `mu index --rebuild' after installation. *** mu4e - allow for searching by editing bookmarks (`mu4e-search-bookmark-edit-first') (keybinding 'B') - make it configurable what to do with sent messages (see `mu4e-sent-messages-behavior') - speedbar support (initial patch by Antono V) - better handling of drafts: - don't save too early - more descriptive buffer names (based on Subject, if any) - don't put "--text-follows-this-line--" markers in files - automatically include signatures, if set - add user-settable variables mu4e-view-wrap-lines and mu4e-view-hide-cited, which determine the initial way a message is displayed - improved documentation *** general - much improved searching for GMail folders (i.e. maildir:/ matching); this requires a 'mu index --rebuild' - correctly handle utf-8 messages, even if they don't specify this explicitly - fix compiler warnings for newer/older gcc and clang/clang++ - fix unit tests (and some code) for Ubuntu 10.04 and FreeBSD9 - fix warnings for compilation with GTK+ 3.2 and recent glib (g_set_error) - fix mu_msg_move_to_maildir for top-level messages - fix in maildir scanning - plug some memleaks ** Release 0.9.8.2 <2012-03-11> *** mu4e: - make mail updating non-blocking - allow for automatic periodic update ('mu4e-update-interval') - allow for external triggering of update - make behavior when leaving the headers buffer customizable, ie. ask/apply/ignore ('mu4e-headers-leave-behaviour') *** general - fix output for some non-UTF8 locales - open ('play') file names with spaces - don't show unnecessary errors for --format=links - make build warning-free for clang/clang++ - allow for slightly older autotools - fix unit tests for some hidden assumptions (locale, dir structure etc.) - some documentation updates / clarifications ** Release 0.9.8.1 <2012-02-18 Sat> *** mu - show only leaf/rfc822 MIME-parts *** mu4e - allow for shell commands with arguments in `mu4e-get-mail-command'. - support marking messages as 'read' and 'unread' - show the current query in the the mode-line (`global-mode-string'). - don't repeat 'Re:' / 'Fwd:' - colorize cited message parts - better handling of text-based, embedded message attachments - for text-bodies, concatenate all text/plain parts - make filladapt dep optional - documentation improvements ** Release 0.9.8 <2012-01-31> - '--descending' has been renamed into '--reverse' - search for attachment MIME-type using 'mime:' or 'y:' - search for text in text-attachments using 'embed:' or 'e:' - searching for attachment file names now uses 'file:' (was: 'attach:') - experimental emacs-based mail client -- "mu4e" - added more unit tests - improved guile binding - no special binary is needed anymore, it's installable are works with the normal guile system; code has been substantially improved. still 'experimental' ** Release 0.9.7 <2011-09-03 Sat> - don't enforce UTF-8 output, use locale (fixes issue #11) - add mail threading to mu-find (using -t/--threads) (sorta fixes issue #13) - add header line to --format=mutt-ab (mu cfind), (fixes issue #42) - terminate mu view results with a form-feed marker (use --terminate) (fixes issue #41) - search X-Label: tags (fixes issue #40) - added toys/muile, the mu guile shells, which allows for message stats etc. - fix date handling (timezones) ** Release 0.9.6 <2011-05-28 Sat> - FreeBSD build fix - fix matching for mu cfind to be as expected - fix mu-contacts for broken names/emails - clear the contacts-cache too when doing a --rebuild - wildcard searches ('*') for fields (except for path/maildir) - search for attachment file names (with 'a:'/'attach:') -- also works with wildcards - remove --xquery completely; use --output=xquery instead - fix progress info in 'mu index' - display the references for a message using the 'r' character (xmu find) - remove --summary-len/-k, instead use --summary for mu view and mu find, and - support colorized output for some sub-commands (view, cfind and extract). Disabled by default, use --color to enable, or set env MU_COLORS to non-empty - update documentation, added more examples ** Release 0.9.5 <2011-04-25 Mon> - bug fix for infinite loop in Maildir detection - minor fixes in tests, small optimizations ** Release 0.9.4 <2011-04-12 Tue> - add the 'cfind' command, to search/export contact information - add 'flag:unread' as a synonym for 'flag:new OR NOT flag:unseen' - updated documentation ** Release 0.9.3 <2011-02-13 Sun> - don't warn about missing files with --quiet ** Release 0.9.2 <2011-02-02 Wed> - stricter checking of options; and options must now *follow* the sub-command (if any); so, something like: 'mu index --maildir=/foo/bar' - output searches as plain text (default), XML, JSON or s-expressions using --format=plain|xml|json|sexp. For example: 'mu find foobar --output=json'. These format options are experimental (except for 'plain') - the --xquery option should now be used as --format=xquery, for output symlinks, use --format=links. This is a change in the options. - search output can include the message size using the 'z' shortcut - match message size ranges (i.e.. size:500k..2M) - fix: honor the --overwrite (or lack thereof) parameter - support folder names with special characters (@, ' ', '.' and so on) - better check for already-running mu index - when --maildir= is not provided for mu index, default to the last one - add --max-msg-size, to specify a new maximum message size - move the 'mug' UI to toys/mug; no longer installable - better support for Solaris builds, Gentoo. ** Release 0.9.1 <2010-12-05 Sun> - Add missing icon for mug - Fix unit tests (Issue #30) - Fix Fedora 14 build (broken GTK+ 3) (Issue #31) ** Release 0.9 <2010-12-04 Sat> - you can now search for the message priority ('prio:high', 'prio:low', 'prio:normal') - you can now search for message flags, e.g. 'flag:attach' for messages with attachment, or 'flag:encrypted' for encrypted messages - you can search for time-intervals, e.g. 'date:2010-11-26..2010-11-29' for messages in that range. See the mu-find(1) and mu-easy(1) man-pages for details and examples. - you can store bookmarked queries in ~/.mu/bookmarks - the 'flags' parameter has been renamed in 'flag' - add a simple graphical UI for searching, called 'mug' - fix --clearlinks for file systems without entry->d_type (fixes issue #28) - make matching case-insensitive and accent-insensitive (accent-insensitive for characters in Unicode Blocks 'Latin-1 Supplement' and 'Latin Extended-A') - more extensive pre-processing is done to make searching for email-addresses and message-ids less likely to not work (issue #21) - updated the man-pages - experimental support for Fedora 14, which uses GMime 2.5.x (fixes issue #29) ** Release 0.8 <2010-10-30 Sat> - There's now 'mu extract' for getting information about MIME-parts (attachments) and extracting them - Queries are now internally converted to lowercase; this solves some of the false-negative issues - All mu sub-commands now have their own man-page - 'mu find' now takes a --summary-len= argument to print a summary of up-to-n lines of the message - Same for 'mu view'; the summary replaces the full body - Setting the mu home dir now goes with -m, --muhome - --log-stderr, --reindex, --rebuild, --autoupgrade, --nocleanup, --mode, --linksdir, --clearlinks lost their single char version ** Release 0.7 <2010-02-27 Sat> - Database format changed - Automatic database scheme version check, notifies users when an upgrade is needed - 'mu view', to view mail message files - Support for >10K matches - Support for unattended upgrades - that is, the database can automatically by upgraded (--autoupgrade). Also, the log file is automatically cleaned when it gets too big (unless you use --nocleanup) - Search for a certain Maildir using the maildir:,m: search prefixes. For example, you can find all messages located in ~/Maildir/foo/bar/cur/msg ~/Maildir/foo/bar/new/msg and with m:/foo/bar this replace the search for path/p in 0.6 - Fixes for reported issues () - A test suite with a growing number of unit tests ** Release 0.6 <2010-01-23 Sat> - First new release of mu since 2008 - No longer depends on sqlite # Local Variables: # mode: org; org-startup-folded: nil # End: mu-0.9.18/NEWS0000644000175000017500000000001613020504331007636 00000000000000See NEWS.org mu-0.9.18/install-sh0000755000175000017500000003452312605152261011166 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2013-12-25.23; # 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 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 the last 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. -s $stripprog installed files. -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 " 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;; -s) stripcmd=$stripprog;; -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=$? 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; won't work # if double slashes aren't ignored. 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 dst=$dstdir/`basename "$src"` dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # 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 case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/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. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or 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=$dstdir/_inst.$$_ rmtmp=$dstdir/_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 && $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 # 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 -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$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 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: mu-0.9.18/config.sub0000755000175000017500000010624612605152261011147 00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2015 Free Software Foundation, Inc. timestamp='2015-01-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 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 . # # 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 Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-* | ppc64p7-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: mu-0.9.18/Makefile.am0000644000175000017500000000472013020504331011201 00000000000000## Copyright (C) 2008-2013 Dirk-Jan C. Binnema ## ## 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, write to the Free Software Foundation, ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. include $(top_srcdir)/gtest.mk if BUILD_GUILE guile=guile else guile= endif if BUILD_MU4E mu4e=mu4e else mu4e= endif if BUILD_PERL perl=perl else perl= endif SUBDIRS=m4 man lib $(guile) mu $(mu4e) contrib toys # $(perl) ACLOCAL_AMFLAGS=-I m4 # so we can say 'make test' check: test cleanupnote cleanupnote: @echo -e "\nNote: you can remove the mu-test- dir in your tempdir" @echo "after 'make check' has finished." tags: gtags # this warns about function that have a cyclomatic complexity of > 10, # which is a sign that it needs some refactoring. requires the pmccabe # tool. If all is fine, it outputs nothing cc10: @$(PMCCABE) `find . -name '*.c' -o -name '*.cc'` \ | grep -v mu-str-normalize.c \ | grep -v mu_str_subject_normalize \ | grep -v tests \ | sort -nr | awk '($$1 > 10)' # this warns about functions that are over 35 non-comment lines long, which is a # sign that they need some refactoring. requires the pmccabe tool. if # all is fine, it outputs nothing # note, some functions are exempted from this rule. line35: @$(PMCCABE) -c `find . -name '*.c' -o -name '*.cc'` \ | grep -v mu-str-normalize.c \ | grep -v mu_str_subject_normalize \ | grep -v config_options_group_find \ | grep -v SCM_DEFINE \ | grep -v tests \ | awk '($$5 > 35)' # get all todo/fixme messages fixme: @grep -i 'FIXME\|TODO' `find src -type f` # check whether we can run make distcheck from the repo version gitcheck: cd `mktemp -d`; \ git clone git://github.com/djcb/mu.git ; \ cd mu; \ autoreconf -i ; \ ./configure ; \ make distcheck EXTRA_DIST= \ TODO \ HACKING \ gtest.mk \ NEWS \ NEWS.org \ autogen.sh doc_DATA = \ NEWS.org mu-0.9.18/ltmain.sh0000644000175000017500000117077112605152254011013 00000000000000#! /bin/sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2014-01-03.01 # libtool (GNU libtool) 2.4.6 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool 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. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool 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 . PROGRAM=libtool PACKAGE=libtool VERSION=2.4.6 package_revision=2.4.6 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2015-01-20.17; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # Copyright (C) 2004-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 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. # As a special exception to the GNU General Public License, if you distribute # this file as part of a program or library that is built using GNU Libtool, # you may include this file under the same distribution terms that you use # for the rest of that program. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNES 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 . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! 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 # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some retarded systems that use ';' as a PATH separator! 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 ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # We should try to minimise forks, especially on Windows where they are # unreasonably slow, so skip the feature probes when bash or zsh are # being used: if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then : ${_G_HAVE_ARITH_OP="yes"} : ${_G_HAVE_XSI_OPS="yes"} # The += operator was introduced in bash 3.1 case $BASH_VERSION in [12].* | 3.0 | 3.0*) ;; *) : ${_G_HAVE_PLUSEQ_OP="yes"} ;; esac fi # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # useable or anything else if it does not work. test -z "$_G_HAVE_PLUSEQ_OP" \ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ && _G_HAVE_PLUSEQ_OP=yes if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1+=\\ \$func_quote_for_eval_result" }' else func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1=\$$1\\ \$func_quote_for_eval_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_for_eval ARG... # -------------------------- # Aesthetically quote ARGs to be evaled later. # This function returns two values: # i) func_quote_for_eval_result # double-quoted, suitable for a subsequent eval # ii) func_quote_for_eval_unquoted_result # has all characters that are still active within double # quotes backslashified. func_quote_for_eval () { $debug_cmd func_quote_for_eval_unquoted_result= func_quote_for_eval_result= while test 0 -lt $#; do case $1 in *[\\\`\"\$]*) _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; *) _G_unquoted_arg=$1 ;; esac if test -n "$func_quote_for_eval_unquoted_result"; then func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" else func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" fi case $_G_unquoted_arg in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_quoted_arg=\"$_G_unquoted_arg\" ;; *) _G_quoted_arg=$_G_unquoted_arg ;; esac if test -n "$func_quote_for_eval_result"; then func_append func_quote_for_eval_result " $_G_quoted_arg" else func_append func_quote_for_eval_result "$_G_quoted_arg" fi shift done } # func_quote_for_expand ARG # ------------------------- # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { $debug_cmd case $1 in *[\\\`\"]*) _G_arg=`$ECHO "$1" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; *) _G_arg=$1 ;; esac case $_G_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_arg=\"$_G_arg\" ;; esac func_quote_for_expand_result=$_G_arg } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_for_expand "$_G_cmd" eval "func_notquiet $func_quote_for_expand_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_for_expand "$_G_cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # Set a version string for this script. scriptversion=2014-01-07.03; # UTC # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # Copyright (C) 2010-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 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 . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# warranty; '. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # to the main code. A hook is just a named list of of function, that can # be run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of functions called by FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It is assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook funcions.n" ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do eval $_G_hook '"$@"' # store returned options list back into positional # parameters for next 'cmd' execution. eval _G_hook_result=\$${_G_hook}_result eval set dummy "$_G_hook_result"; shift done func_quote_for_eval ${1+"$@"} func_run_hooks_result=$func_quote_for_eval_result } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list in your hook function, remove any # options that you action, and then pass back the remaining unprocessed # options in '_result', escaped suitably for # 'eval'. Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # func_quote_for_eval ${1+"$@"} # my_options_prep_result=$func_quote_for_eval_result # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # # Note that for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # ;; # *) set dummy "$_G_opt" "$*"; shift; break ;; # esac # done # # func_quote_for_eval ${1+"$@"} # my_silent_option_result=$func_quote_for_eval_result # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # # func_quote_for_eval ${1+"$@"} # my_option_validation_result=$func_quote_for_eval_result # } # func_add_hook func_validate_options my_option_validation # # You'll alse need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd func_options_prep ${1+"$@"} eval func_parse_options \ ${func_options_prep_result+"$func_options_prep_result"} eval func_validate_options \ ${func_parse_options_result+"$func_parse_options_result"} eval func_run_hooks func_options \ ${func_validate_options_result+"$func_validate_options_result"} # save modified positional parameters for caller func_options_result=$func_run_hooks_result } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propogate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before # returning. func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} # save modified positional parameters for caller func_options_prep_result=$func_run_hooks_result } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd func_parse_options_result= # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} # Adjust func_parse_options positional parameters to match eval set dummy "$func_run_hooks_result"; shift # Break out of the loop if we already parsed every option. test $# -gt 0 || break _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) test $# = 0 && func_missing_arg $_G_opt && break case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac done # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} func_parse_options_result=$func_quote_for_eval_result } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE # save modified positional parameters for caller func_validate_options_result=$func_run_hooks_result } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables after # splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} test "x$func_split_equals_lhs" = "x$1" \ && func_split_equals_rhs= }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /(C)/!b go :more /\./!{ N s|\n# | | b more } :go /^# Written by /,/# warranty; / { s|^# || s|^# *$|| s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| p } /^# Written by / { s|^# || p } /^warranty; /q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.4.6' # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { $debug_cmd $warning_func ${1+"$@"} } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information and exit --mode=MODE use operation mode MODE --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname (GNU libtool) 2.4.6 automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func__fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_preserve_dup_deps=false opt_quiet=false nonopt= preserve_args= # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; esac # Pass back the list of options. func_quote_for_eval ${1+"$@"} libtool_options_prep_result=$func_quote_for_eval_result } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $_G_opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac done # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} libtool_parse_options_result=$func_quote_for_eval_result } func_add_hook func_parse_options libtool_parse_options # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" case $host in # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote_for_eval ${1+"$@"} libtool_validate_options_result=$func_quote_for_eval_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_for_eval "$libobj" test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" func_append install_prog "$func_quote_for_eval_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" func_append install_prog " $func_quote_for_eval_result" if test -n "$arg2"; then func_quote_for_eval "$arg2" fi func_append install_shared_prog " $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_for_eval "$install_override_mode" func_append install_shared_prog " -m $func_quote_for_eval_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs 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 BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=\"$qECHO\" fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module=$wl-multi_module continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "'-no-install' is ignored for $host" func_warning "assuming '-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -os2dllname) prev=os2dllname continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $func_quote_for_eval_result" func_append compiler_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $wl$func_quote_for_eval_result" func_append compiler_flags " $wl$func_quote_for_eval_result" func_append linker_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # -fstack-protector* stack protector flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -stdlib=* select c++ std lib with clang -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; -Z*) if test os2 = "`expr $host : '.*\(os2\)'`"; then # OS/2 uses -Zxxx to specify OS/2-specific options compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case $arg in -Zlinker | -Zstack) prev=xcompiler ;; esac continue else # Otherwise treat like 'Some other compiler flag' below func_quote_for_eval "$arg" arg=$func_quote_for_eval_result fi ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result test none = "$pic_object" || { # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object } # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test dlfiles = "$prev"; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test dlprefiles = "$prev"; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the '$prevarg' option requires an argument" if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname=$func_basename_result libobjs_save=$libobjs if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" # Definition is injected by LT_CONFIG during libtool generation. func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" func_dirname "$output" "/" "" output_objdir=$func_dirname_result$objdir func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test lib = "$linkmode"; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=false newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test lib,link = "$linkmode,$pass"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs=$tmp_deplibs fi if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass"; then libs=$deplibs deplibs= fi if test prog = "$linkmode"; then case $pass in dlopen) libs=$dlfiles ;; dlpreopen) libs=$dlprefiles ;; link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; esac fi if test lib,dlpreopen = "$linkmode,$pass"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs=$dlprefiles fi if test dlopen = "$pass"; then # Collect dlpreopened libraries save_deplibs=$deplibs deplibs= fi for deplib in $libs; do lib= found=false case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test lib != "$linkmode" && test prog != "$linkmode"; then func_warning "'-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test lib = "$linkmode"; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib=$searchdir/lib$name$search_ext if test -f "$lib"; then if test .la = "$search_ext"; then found=: else found=false fi break 2 fi done done if $found; then # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll=$l done if test "X$ll" = "X$old_library"; then # only static version available found=false func_dirname "$lib" "" "." ladir=$func_dirname_result lib=$ladir/$old_library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi else # deplib doesn't seem to be a libtool library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi ;; # -l *.ltframework) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test conv = "$pass" && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi if test scan = "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "'-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test link = "$pass"; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=false case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=: fi ;; pass_all) valid_a_lib=: ;; esac if $valid_a_lib; then echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" else echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." fi ;; esac continue ;; prog) if test link != "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test conv = "$pass"; then deplibs="$deplib $deplibs" elif test prog = "$linkmode"; then if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=: continue ;; esac # case $deplib $found || test -f "$lib" \ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "'$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir=$func_dirname_result dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass" || { test prog != "$linkmode" && test lib != "$linkmode"; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test conv = "$pass"; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for '$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" elif test prog != "$linkmode" && test lib != "$linkmode"; then func_fatal_error "'$lib' is not a convenience library" fi tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test yes = "$prefer_static_libs" || test built,no = "$prefer_static_libs,$installed"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib=$l done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for '$lib'" fi # This library was specified with -dlopen. if test dlopen = "$pass"; then test -z "$libdir" \ && func_fatal_error "cannot -dlopen a convenience library: '$lib'" if test -z "$dlname" || test yes != "$dlopen_support" || test no = "$build_libtool_libs" then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of '$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir=$ladir fi ;; esac func_basename "$lib" laname=$func_basename_result # Find the relevant object directory and library name. if test yes = "$installed"; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library '$lib' was moved." dir=$ladir absdir=$abs_ladir libdir=$abs_ladir else dir=$lt_sysroot$libdir absdir=$lt_sysroot$libdir fi test yes = "$hardcode_automatic" && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir=$ladir absdir=$abs_ladir # Remove this search path later func_append notinst_path " $abs_ladir" else dir=$ladir/$objdir absdir=$abs_ladir/$objdir # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test dlpreopen = "$pass"; then if test -z "$libdir" && test prog = "$linkmode"; then func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" fi case $host in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test lib = "$linkmode"; then deplibs="$dir/$old_library $deplibs" elif test prog,link = "$linkmode,$pass"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test prog = "$linkmode" && test link != "$pass"; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=false if test no != "$link_all_deplibs" || test -z "$library_names" || test no = "$build_libtool_libs"; then linkalldeplibs=: fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if $linkalldeplibs; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test prog,link = "$linkmode,$pass"; then if test -n "$library_names" && { { test no = "$prefer_static_libs" || test built,yes = "$prefer_static_libs,$installed"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then # Make sure the rpath contains only unique directories. case $temp_rpath: in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if $alldeplibs && { test pass_all = "$deplibs_check_method" || { test yes = "$build_libtool_libs" && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test built = "$use_static_libs" && test yes = "$installed"; then use_static_libs=no fi if test -n "$library_names" && { test no = "$use_static_libs" || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc* | *os2*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test no = "$installed"; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule= for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule=$dlpremoduletest break fi done if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then echo if test prog = "$linkmode"; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test lib = "$linkmode" && test yes = "$hardcode_into_libs"; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname=$1 shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname=$dlname elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc* | *os2*) func_arith $current - $age major=$func_arith_result versuffix=-$major ;; esac eval soname=\"$soname_spec\" else soname=$realname fi # Make a new name for the extract_expsyms_cmds to use soroot=$soname func_basename "$soroot" soname=$func_basename_result func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from '$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for '$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test prog = "$linkmode" || test relink != "$opt_mode"; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test no = "$hardcode_direct"; then add=$dir/$linklib case $host in *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; *-*-sysv4*uw2*) add_dir=-L$dir ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir=-L$dir ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we cannot # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library"; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add=$dir/$old_library fi elif test -n "$old_library"; then add=$dir/$old_library fi fi esac elif test no = "$hardcode_minus_L"; then case $host in *-*-sunos*) add_shlibpath=$dir ;; esac add_dir=-L$dir add=-l$name elif test no = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; relink) if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$dir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$absdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name elif test yes = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; *) lib_linked=no ;; esac if test yes != "$lib_linked"; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test prog = "$linkmode"; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test yes != "$hardcode_direct" && test yes != "$hardcode_minus_L" && test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test prog = "$linkmode" || test relink = "$opt_mode"; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$libdir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$libdir add=-l$name elif test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add=-l$name elif test yes = "$hardcode_automatic"; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib"; then add=$inst_prefix_dir$libdir/$linklib else add=$libdir/$linklib fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir=-L$libdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name fi if test prog = "$linkmode"; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test prog = "$linkmode"; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test unsupported != "$hardcode_direct"; then test -n "$old_library" && linklib=$old_library compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test yes = "$build_libtool_libs"; then # Not a shared library if test pass_all != "$deplibs_check_method"; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system cannot link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test yes = "$module"; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test lib = "$linkmode"; then if test -n "$dependency_libs" && { test yes != "$hardcode_into_libs" || test yes = "$build_old_libs" || test yes = "$link_static"; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs=$temp_deplibs fi func_append newlib_search_path " $absdir" # Link against this library test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test no != "$link_all_deplibs"; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path=$deplib ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of '$dir'" absdir=$dir fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names"; then for tmp in $deplibrary_names; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl"; then depdepl=$absdir/$objdir/$depdepl darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" path= fi fi ;; *) path=-L$absdir/$objdir ;; esac else eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "'$deplib' seems to be moved" path=-L$absdir fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test link = "$pass"; then if test prog = "$linkmode"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs=$newdependency_libs if test dlpreopen = "$pass"; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test dlopen != "$pass"; then test conv = "$pass" || { # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= } if test prog,link = "$linkmode,$pass"; then vars="compile_deplibs finalize_deplibs" else vars=deplibs fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Add Sun CC postdeps if required: test CXX = "$tagname" && { case $host_os in linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; solaris*) func_cc_basename "$CC" case $func_cc_basename_result in CC* | sunCC*) func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; esac } # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i= ;; esac if test -n "$i"; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test prog = "$linkmode"; then dlfiles=$newdlfiles fi if test prog = "$linkmode" || test lib = "$linkmode"; then dlprefiles=$newdlprefiles fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "'-R' is ignored for archives" test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "'-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "'-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs=$output func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form 'libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test no = "$module" \ && func_fatal_help "libtool library '$output' must begin with 'lib'" if test no != "$need_lib_prefix"; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test pass_all != "$deplibs_check_method"; then func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test no = "$dlself" \ || func_warning "'-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test 1 -lt "$#" \ && func_warning "ignoring multiple '-rpath's for a libtool library" install_libdir=$1 oldlibs= if test -z "$rpath"; then if test yes = "$build_libtool_libs"; then # Building a libtool convenience library. # Some compilers have problems with a '.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "'-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs=$IFS; IFS=: set dummy $vinfo 0 0 0 shift IFS=$save_ifs test -n "$7" && \ func_fatal_help "too many parameters to '-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major=$1 number_minor=$2 number_revision=$3 # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # that has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|freebsd-elf|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_revision ;; freebsd-aout|qnx|sunos) current=$number_major revision=$number_minor age=0 ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_minor lt_irix_increment=no ;; esac ;; no) current=$1 revision=$2 age=$3 ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT '$current' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION '$revision' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE '$age' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE '$age' is greater than the current interface number '$current'" func_fatal_error "'$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" # On Darwin other compilers case $CC in nagfor*) verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" ;; *) verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; esac ;; freebsd-aout) major=.$current versuffix=.$current.$revision ;; freebsd-elf) func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; irix | nonstopux) if test no = "$lt_irix_increment"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring=$verstring_prefix$major.$revision # Add in all the interfaces that we are compatible with. loop=$revision while test 0 -ne "$loop"; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring_prefix$major.$iface:$verstring done # Before this point, $major must not contain '.'. major=.$major versuffix=$major.$revision ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=.$current.$age.$revision verstring=$current.$age.$revision # Add in all the interfaces that we are compatible with. loop=$age while test 0 -ne "$loop"; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring:$iface.0 done # Make executables depend on our current version. func_append verstring ":$current.0" ;; qnx) major=.$current versuffix=.$current ;; sco) major=.$current versuffix=.$current ;; sunos) major=.$current versuffix=.$current.$revision ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 file systems. func_arith $current - $age major=$func_arith_result versuffix=-$major ;; *) func_fatal_configuration "unknown library version type '$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring=0.0 ;; esac if test no = "$need_version"; then versuffix= else versuffix=.0.0 fi fi # Remove version info from name if versioning should be avoided if test yes,no = "$avoid_version,$need_version"; then major= versuffix= verstring= fi # Check to see if the archive will have undefined symbols. if test yes = "$allow_undefined"; then if test unsupported = "$allow_undefined_flag"; then if test yes = "$build_old_libs"; then func_warning "undefined symbols not allowed in $host shared libraries; building static only" build_libtool_libs=no else func_fatal_error "can't build $host shared library unless -no-undefined is specified" fi fi else # Don't allow undefined symbols. allow_undefined_flag=$no_undefined_flag fi fi func_generate_dlsyms "$libname" "$libname" : func_append libobjs " $symfileobj" test " " = "$libobjs" && libobjs= if test relink != "$opt_mode"; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) if test -n "$precious_files_regex"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles=$dlfiles dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles=$dlprefiles dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test yes = "$build_libtool_libs"; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test yes = "$build_libtool_need_lc"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release= versuffix= major= newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib=$potent_lib while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | $SED 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib= ;; esac fi if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib=$potent_lib # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs= tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test yes = "$allow_libtool_libs_with_static_runtimes"; then for i in $predeps $postdeps; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test none = "$deplibs_check_method"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test yes = "$droppeddeps"; then if test yes = "$module"; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test no = "$allow_undefined"; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs=$new_libs # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test yes = "$build_libtool_libs"; then # Remove $wl instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test yes = "$hardcode_into_libs"; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath=$finalize_rpath test relink = "$opt_mode" || rpath=$compile_rpath$rpath for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath=$finalize_shlibpath test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname=$1 shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname=$realname fi if test -z "$dlname"; then dlname=$soname fi lib=$output_objdir/$realname linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols=$output_objdir/$libname.uexp func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile func_dll_def_p "$export_symbols" || { # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols=$export_symbols export_symbols= always_export_symbols=yes } fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs=$IFS; IFS='~' for cmd1 in $cmds; do IFS=$save_ifs # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test yes = "$try_normal_branch" \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=$output_objdir/$output_la.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS=$save_ifs if test -n "$export_symbols_regex" && test : != "$skipped_export"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test : != "$skipped_export" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs=$tmp_deplibs if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test yes = "$compiler_needs_object" && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test : != "$skipped_export" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then output=$output_objdir/$output_la.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then output=$output_objdir/$output_la.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test yes = "$compiler_needs_object"; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-$k.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test -z "$objlist" || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test 1 -eq "$k"; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-$k.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-$k.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi ${skipped_export-false} && { func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi } test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs=$IFS; IFS='~' for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi ${skipped_export-false} && { if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi } libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs=$IFS; IFS='~' for cmd in $cmds; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs # Restore the uninstalled library and exit if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test yes = "$module" || test yes = "$export_dynamic"; then # On all known operating systems, these are identical. dlname=$soname fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "'-R' is ignored for objects" test -n "$vinfo" && \ func_warning "'-version-info' is ignored for objects" test -n "$release" && \ func_warning "'-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object '$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj=$output ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # if reload_cmds runs $LD directly, get rid of -Wl from # whole_archive_flag_spec and hope we can get by with turning comma # into space. case $reload_cmds in *\$LD[\ \$]*) wl= ;; esac if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags else gentop=$output_objdir/${obj}x func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test yes = "$build_libtool_libs" || libobjs=$non_pic_objects # Create the old-style object. reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs output=$obj func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi test yes = "$build_libtool_libs" || { if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS } if test -n "$pic_flag" || test default != "$pic_mode"; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output=$libobj func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "'-version-info' is ignored for programs" test -n "$release" && \ func_warning "'-release' is ignored for programs" $preload \ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test CXX = "$tagname"; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " $wl-bind_at_load" func_append finalize_command " $wl-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs=$new_libs func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath=$rpath rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath=$rpath if test -n "$libobjs" && test yes = "$build_old_libs"; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" false # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=: case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=false ;; *cygwin* | *mingw* ) test yes = "$build_libtool_libs" || wrappers_required=false ;; *) if test no = "$need_relink" || test yes != "$build_libtool_libs"; then wrappers_required=false fi ;; esac $wrappers_required || { # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command=$compile_command$compile_rpath # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.$objext"; then func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' fi exit $exit_status } if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test yes = "$no_install"; then # We don't need to create a wrapper script. link_command=$compile_var$compile_command$compile_rpath # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi case $hardcode_action,$fast_install in relink,*) # Fast installation is not supported link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath func_warning "this platform does not like uninstalled shared libraries" func_warning "'$output' will be relinked during installation" ;; *,yes) link_command=$finalize_var$compile_command$finalize_rpath relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` ;; *,no) link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath ;; *,needless) link_command=$finalize_var$compile_command$finalize_rpath relink_command= ;; esac # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource=$output_path/$objdir/lt-$output_name.c cwrapper=$output_path/$output_name.exe $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host"; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do case $build_libtool_libs in convenience) oldobjs="$libobjs_save $symfileobj" addlibs=$convenience build_libtool_libs=no ;; module) oldobjs=$libobjs_save addlibs=$old_convenience build_libtool_libs=no ;; *) oldobjs="$old_deplibs $non_pic_objects" $preload && test -f "$symfileobj" \ && func_append oldobjs " $symfileobj" addlibs=$old_convenience ;; esac if test -n "$addlibs"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase=$func_basename_result case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj"; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test -z "$oldobjs"; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test yes = "$build_old_libs" && old_library=$libname.$libext func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` if test yes = "$hardcode_automatic"; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test yes = "$installed"; then if test -z "$install_libdir"; then break fi output=$output_objdir/${outputname}i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name=$func_basename_result func_resolve_sysroot "$deplib" eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs=$newdependency_libs newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles=$newdlprefiles else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles=$newdlprefiles fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test -n "$bindir"; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result/$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that cannot go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test no,yes = "$installed,$need_relink"; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } if test link = "$opt_mode" || test relink = "$opt_mode"; then func_mode_link ${1+"$@"} fi # func_mode_uninstall arg... func_mode_uninstall () { $debug_cmd RM=$nonopt files= rmforce=false exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic for arg do case $arg in -f) func_append RM " $arg"; rmforce=: ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir=$func_dirname_result if test . = "$dir"; then odir=$objdir else odir=$dir/$objdir fi func_basename "$file" name=$func_basename_result test uninstall = "$opt_mode" && odir=$dir # Remember odir for removal later, being careful to avoid duplicates if test clean = "$opt_mode"; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif $rmforce; then continue fi rmfiles=$file case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case $opt_mode in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test none != "$pic_object"; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test none != "$non_pic_object"; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test clean = "$opt_mode"; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.$objext" if test yes = "$fast_install" && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name"; then func_append rmfiles " $odir/lt-$noexename.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the $objdir's in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then func_mode_uninstall ${1+"$@"} fi test -z "$opt_mode" && { help=$generic_help func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode '$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # where we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: mu-0.9.18/mu/0000755000175000017500000000000013021065721007651 500000000000000mu-0.9.18/mu/Makefile.in0000644000175000017500000007115313021065705011647 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 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 = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = mu$(EXEEXT) subdir = mu ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/perlmod.m4 $(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)" PROGRAMS = $(bin_PROGRAMS) am_mu_OBJECTS = mu.$(OBJEXT) mu-cmd-cfind.$(OBJEXT) \ mu-config.$(OBJEXT) mu-cmd-extract.$(OBJEXT) \ mu-cmd-find.$(OBJEXT) mu-cmd-index.$(OBJEXT) \ mu-cmd-server.$(OBJEXT) mu-cmd-script.$(OBJEXT) \ mu-cmd.$(OBJEXT) mu_OBJECTS = $(am_mu_OBJECTS) am__DEPENDENCIES_1 = mu_DEPENDENCIES = ${top_builddir}/lib/libmu.la $(am__DEPENDENCIES_1) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = 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__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=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 = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=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 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(mu_SOURCES) DIST_SOURCES = $(mu_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 \ distdir 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 DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ $(top_srcdir)/gtest.mk DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) 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" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EMACS = @EMACS@ EMACSLOADPATH = @EMACSLOADPATH@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ GMIME_CFLAGS = @GMIME_CFLAGS@ GMIME_LIBS = @GMIME_LIBS@ GREP = @GREP@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ GUILE_BINARY = @GUILE_BINARY@ GUILE_CFLAGS = @GUILE_CFLAGS@ GUILE_LIBS = @GUILE_LIBS@ GUILE_SITEDIR = @GUILE_SITEDIR@ GUILE_SNARF = @GUILE_SNARF@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MU_DOC_DIR = @MU_DOC_DIR@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ 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@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PMCCABE = @PMCCABE@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SORT = @SORT@ STRIP = @STRIP@ VERSION = @VERSION@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ XAPIAN_CONFIG = @XAPIAN_CONFIG@ XAPIAN_CXXFLAGS = @XAPIAN_CXXFLAGS@ XAPIAN_LIBS = @XAPIAN_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ 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 = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ have_makeinfo = @have_makeinfo@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ lispdir = @lispdir@ 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@ TEST_PROGS = # enforce compiling guile (optionally) first,then this dir first # before decending into tests/ SUBDIRS = . tests AM_CPPFLAGS = -I${top_srcdir}/lib $(GLIB_CFLAGS) # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not # really need all the params they get AM_CFLAGS = -Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement \ -Wno-variadic-macros \ -DMU_SCRIPTS_DIR="\"$(pkgdatadir)/scripts/\"" AM_CXXFLAGS = -Wall -Wextra -Wno-unused-parameter # note, mu.cc is only '.cc' and not '.c' because libmu must explicitly # be linked as c++, not c. mu_SOURCES = \ mu.cc \ mu-cmd-cfind.c \ mu-config.c \ mu-config.h \ mu-cmd-extract.c \ mu-cmd-find.c \ mu-cmd-index.c \ mu-cmd-server.c \ mu-cmd-script.c \ mu-cmd.c \ mu-cmd.h BUILT_SOURCES = \ mu-help-strings.h mu_LDADD = \ ${top_builddir}/lib/libmu.la \ $(GLIB_LIBS) EXTRA_DIST = \ mu-help-strings.awk \ mu-help-strings.txt CLEANFILES = $(BUILT_SOURCES) all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: .SUFFIXES: .c .cc .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/gtest.mk $(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) --gnu mu/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu mu/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__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_srcdir)/gtest.mk $(am__empty): $(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 \ || test -f $$p1 \ ; 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) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(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: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list mu$(EXEEXT): $(mu_OBJECTS) $(mu_DEPENDENCIES) $(EXTRA_mu_DEPENDENCIES) @rm -f mu$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(mu_OBJECTS) $(mu_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-cmd-cfind.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-cmd-extract.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-cmd-find.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-cmd-index.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-cmd-script.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-cmd-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-cmd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu-config.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mu.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< .cc.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cc.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cc.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # 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" 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 distdir: $(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 @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 check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-recursive all-am: Makefile $(PROGRAMS) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(bindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) 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: -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) 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-recursive clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ 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-binPROGRAMS 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 -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-binPROGRAMS .MAKE: $(am__recursive_targets) all check install install-am \ install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-binPROGRAMS clean-generic clean-libtool \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool 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-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-binPROGRAMS .PRECIOUS: Makefile # # NOTE: we set the locale/tz to some well-know values, so the tests # (at least when running under 'make check') run in a predictable # environment. There are specific tests different timezone, though. # test: all $(TEST_PROGS) @export LC_ALL="en_US.utf8" @export TZ="Europe/Helsinki" @test -z "$(TEST_PROGS)" || gtester --verbose $(TEST_PROGS) || exit $$?; \ test -z "$(SUBDIRS)" || \ for subdir in $(SUBDIRS); do \ test "$$subdir" = "." || \ (cd ./$$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? ; \ done .PHONY: test gprof mu-help-strings.h: mu-help-strings.txt mu-help-strings.awk $(AWK) -f ${top_srcdir}/mu/mu-help-strings.awk < $< > $@ # 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: mu-0.9.18/mu/mu-help-strings.txt0000644000175000017500000001207213020504332013365 00000000000000#-*-mode:org-*- # # Copyright (C) 2012-2013 Dirk-Jan C. Binnema # # 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, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #BEGIN MU_CONFIG_CMD_ADD #STRING mu add [] #STRING mu add is the command to add specific measage files to the database. Each of the files must be specified with an absolute path. #END #BEGIN MU_CONFIG_CMD_CFIND #STRING mu cfind [options] [--format=] [--personal] [--after=] [] #STRING mu cfind is the mu command to find contacts in the mu database and export them for use in other programs. is one of: plain mutt-alias mutt-ab wl csv org-contact bbdb 'plain' is the default. If you specify '--personal', only addresses that were found in mails that include 'my' e-mail addres will be listed - so to exclude e.g. mailing-list posts. Use the --my-address= option in 'mu index' to specify what addresses are considered 'my' address. With '--after=T' you can tell mu to only show addresses that were seen after T. T is a Unix timestamp. For example, to get only addresses seen after the beginning of 2012, you could use --after=`date +%%s -d 2012-01-01` #END #BEGIN MU_CONFIG_CMD_EXTRACT #STRING mu extract [options] #STRING mu extract is the mu command to display and save message parts (attachments), and open them with other tools. #END #BEGIN MU_CONFIG_CMD_FIND #STRING mu find [options] #STRING mu find is the mu command for searching e-mail message that were stored earlier using mu index(1). Some examples: # get all messages with 'bananas' in body, subject or recipient fields: $ mu find bananas # get all messages regarding bananas from John with an attachment: $ mu find from:john flag:attach bananas # get all messages with subject wombat in June 2009 $ mu find subject:wombat date:20090601..20090630 See the `mu-find' and `mu-easy' man-pages for more information. #END #BEGIN MU_CONFIG_CMD_HELP #STRING mu help #STRING mu find is the mu command to get help about , where is one of: add - add message to database cfind - find a contact extract - extract parts/attachments from messages find - query the message database help - get help index - index messages mkdir - create a maildir remove - remove a message from the database script - run a script (available only when mu was built with guile-support) server - start mu server verify - verify signatures of a message view - view a specific message #END #BEGIN MU_CONFIG_CMD_INDEX #STRING mu index [options] #STRING mu index is the mu command for scanning the contents of Maildir directories and storing the results in a Xapian database.The data can then be queried using mu-find(1). #END #BEGIN MU_CONFIG_CMD_MKDIR #STRING mu mkdir [options] [] #STRING mu mkdir is the mu command for creating Maildirs.It does not use the mu database. #END #BEGIN MU_CONFIG_CMD_REMOVE #STRING mu remove [options] [] #STRING mu remove is the mu command to remove messages from the database. #END #BEGIN MU_CONFIG_CMD_SERVER #STRING mu server [options] #STRING mu server starts a simple shell in which one can query and manipulate the mu database.The output of the commands is terms of Lisp symbolic expressions (s-exps).mu server is not meant for use by humans; instead, it is designed specificallyfor the mu4e e-mail client. #END #BEGIN MU_CONFIG_CMD_SCRIPT #STRING mu script [] [-v] mu [