--- /dev/null
+Most of this package's source code is distributed under
+the GNU Public license and copyright (c) 1996, 1997 by
+Michael Elkins. pgppubring.c is copyright(c) 1997 by
+Thomas Roessler and also distributed under the GPL. The
+SHA1 implementation has been taken from Eric A. Young's
+SSLeay package; a different copyright notice applies; see
+below.
+\f
+
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+\f
+The following copyright notice applies to the SHA1
+implementation:
+
+
+ Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ All rights reserved.
+
+ This package is an SSL implementation written
+ by Eric Young (eay@cryptsoft.com).
+ The implementation was written so as to conform with Netscapes SSL.
+
+ This library is free for commercial and non-commercial use as long as
+ the following conditions are aheared to. The following conditions
+ apply to all code found in this distribution, be it the RC4, RSA,
+ lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ included with this distribution is covered by the same copyright terms
+ except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+ Copyright remains Eric Young's, and as such any Copyright notices in
+ the code are not to be removed.
+ If this package is used in a product, Eric Young should be given attribution
+ as the author of the parts of the library used.
+ This can be in the form of a textual message at program startup or
+ in documentation (online or textual) provided with the package.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ "This product includes cryptographic software written by
+ Eric Young (eay@cryptsoft.com)"
+ The word 'cryptographic' can be left out if the rouines from the library
+ being used are not cryptographic related :-).
+ 4. If you include any Windows specific code (or a derivative thereof) from
+ the apps directory (application code) you must include an acknowledgement:
+ "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+ THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ The licence and distribution terms for any publically available version or
+ derivative of this code cannot be changed. i.e. this code cannot simply be
+ copied and put under another distribution licence
+ [including the GNU Public Licence.]
--- /dev/null
+Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
--- /dev/null
+Changes since 0.88
+------------------
+
+- [patch-0.88.me.buffy_zero.1] 0 length mbox and MMDF mailboxes should never
+ be marked as having new mail in them
+
+- [patch-0.88.me.enter_string.1] rewrote ci_enter_string() to remove
+ duplicated redraw code, and renamed to mutt_enter_string()
+
+- [patch-0.88.ld.ctx_changed.1] fixed problem with setting the changed flag
+ for the mailbox when recovering from an externally modified mailbox
+
+- [patch-0.88.pk.signed.1] fixed comparison between short and unsigned short
+ in the pager
+
+- [patch-0.88.me.set_no.1] makes "unset no<var>" equivalent to "set <var>"
+
+- [patch-0.88.me.state_flag] removes the OPTVERIFYSIG pseudo-option and
+ replaces it with a "flags" member in the STATE structure
+
+- [patch-0.88.me.short] use 'int' instead of 'unsigned short' for message
+ numbers to allow mutt to read mailboxes larger than 65535 messages
+
+- [patch-0.88.me.hold.1] removed the $hold variable. the same functionality
+ is provided by "set nomove"
+
+- [patch-0.88.me.buffy_notify.1] move the notification of new mail out of
+ mutt_buffy_check() into a separate routine
+
+- [patch-0.88.me.empty_fields.1] skip processing of empty header fields in
+ _mutt_read_rfc822_header()
+
+- [patch-0.88.me.var_table.1] use the .data field instead of .bit for
+ storing the option for DT_BOOL and DT_QUAD vars
+
+- [patch-0.88.me.rx_not.1] regexps in the *-hook commands can now optionally
+ be prefixed with a "!" (exclamation point) to indicate that the hook
+ should be executed when the target does NOT match the following pattern
+
+- [patch-0.88.me.sig.2] removes the $local_site, $remote_sig and $local_sig
+ variables. the same functionality is provided by using a send-hook to set
+ the $signature variable:
+ send-hook . set signature=~/.sig-local
+ send-hook !hmc\\.edu set signature=~/.sig-remote
+
+- [patch-0.88.me.from.1] honor $reverse_name even when $use_from is unset
+
+- [patch-0.88.bj.status_color.1] removed hack needed to reset the background
+ color before the buffy_notify patch
+
+- [patch-0.88.me.parse_param.2] whitespace after a MIME content-type
+ parameter was not stripped (eg. "charset =us-ascii" would result in the
+ parameter "charset " in the structure)
+
+Changes since 0.88.1
+--------------------
+
+- split up mutt.h into several smaller files with groups of related data
+ in order to reduce the dependencies on mutt.h
+
+- removed the $point_new variable
+
+- the .not member of the REGEXP struct was not initialized for the *-hook
+ commands, thus causing them not to work properly
+
+- removed bogus ! handling code in the DT_RX variables
+
+- [patch-0.88.ld.magic.1] fix to return type MBOX for the default magic
+ for zero length files if $mbox_type is something other than "mbox" or
+ "mmdf"
+
+Changes since 0.88.2
+--------------------
+
+- [patch-0.88.2.kd.buffy_pop.2] support for --enable-buffy-size and
+ --enable-pop were broken after the reorganization of mutt.h
+
+- [patch-0.88.2.me.ignore_rx.1] removes the ignore and unignore commands and
+ replaces them with the $ignore and $unignore variables, which are regular
+ expressions used for matching lines to weed/show
+
+- [patch-0.88.2.me.followup.1] adds support for the Mail-Reply-To: and
+ Mail-Followup-To: header fields
+
+- [patch-0.88.me.hdrline.1] fixed segfault with %t in $hdr_format if neither
+ the to: or cc: field exists
+
+- [patch-0.88.me.abort_subject.1] print error message if
+ $abort_nosubject=yes and the user deletes the default subject and tries to
+ send instead of just silently ignoring the send
+
+- [patch-0.88.me.long_re.1] increased the max length for regexp's in
+ send-hook's to be 256 chars
+
+- [patch-0.88.bj.isprint.1] treat chars >= 0xa0 as printable
+
+- [patch-0.88.me.half_page.1] adds the half-up and half-down functions to
+ the index and generic key maps. scrolls the page 1/2 screen up or down
+
+- [patch-0.88.me.useraddr.1] $alternates would not be consulted when the
+ address to be matches was not fully qualified
+
+- [patch-0.88.me.from_quote.1] add support for messages separators in mbox
+ mailboxes where the mailbox part of the return-path is a quoted-string
+ eg: From "michael elkins"@cs.hmc.edu Sat Nov 22 15:55:07 PST 1997
+
+- [patch-0.88.me.mx.1] moved code for adding the From_ line to new messages
+ in mbox/mmdf mailboxes to mx_open_new_message(), and removes the
+ postponed_mail() func from send.c in favor of mutt_num_postponed()
+
+- [patch-0.88.2.me.post_err.1] abort exiting the compose menu if there was a
+ problem writing the message to the $postponed mailbox
+
+- [patch-0.88.2.me.find_lists.1] if the to: list was missing, the cc: list
+ was not searched for mailing lists when doing a list-reply
+
+- [patch-0.88.2.me.buffy_notify.1] makes mutt keep track of the number of
+ mailboxes for which the user has not been notified of new mail so that
+ mutt_buffy_notify() can exit immediately without traversing the mailbox
+ list if there are no pending notifications
+
+- [patch-0.88.2.bugfix.mtsirkin.buffy_size.1] fixed some bugs with the
+ buffy_size pacth
+
+Changes since 0.88.3
+--------------------
+
+- undid previous patch-0.88.2.me.ignore_rx.1 which changed `ignore' and
+ `unignore' to regexp vars instead of commands
+
+- [patch-0.88.3.me.attach_file.2] with $edit_hdrs set, files can be attached
+ to the message by placing `Attach: <filename>' in the message header while
+ editing the message
+
+- [patch-0.88.3.me.find_lists.1] fixed bug in the list-reply function
+
+- [patch-0.88.3.me.followup.1] the mail-followup-to: field should be set for
+ all types of messages, not just group-reply
+
+- [patch-0.88.3.pk.fccdate.1] the date in fcc:'s to mbox/mmdf mailboxes was
+ incorrect
+
+- [patch-0.88.3.bugfix.mtsirkin.buffy_size.1] fixed some more bugs in the
+ buffy_size handling
+
+- [patch-0.88.3.me.menu.1] adds the $menu_scroll which causes mutt not to do
+ an implicit next-page when doing a next-message on the last message
+ displayed on the screen, and adds the current-top, current-middle,
+ current-bottom functions to reposition the current message at the top,
+ middle and bottom of the screen, respectively
+
+- [patch-0.88.3.me.buffy.1] avoid the double-stat() in mutt_buffy_check() by
+ saving the magic in the BUFFY struct
+
+- [patch-0.88.3.me.buffy_config.1] the interval that mutt will check for new
+ mail in the `mailboxes' list is now configurable via the $mail_check
+ variable (default: 3 seconds)
+
+- [patch-0.88.3.me.hook.1] fixed the !-handling in the *-hook commands to
+ allow specification of ! (shortcut for $spoolfile) in folder-hook by
+ placing quotes around it (eg., folder-hook '!' set signature=~/.signature)
+
+Changes since 0.88.4
+--------------------
+
+- changed default value of $move to `no'
+
+- [patch-0.88.4.me.from.1] fixed problem with duplicate From_ lines when
+ saving from mbox/mmdf mailboxes
+
+- [patch-0.88.4.thread.1] modified threading algorithm such that the entire
+ mailbox does not need to be rethreaded when new mail arrives or when
+ switching among the different sorting methods
+
+- [patch-0.88.4.de.buffy_stuff.1] removed code for testing newly created
+ mailboxes based upon the magic type
+
+- [patch-0.884.de.buffy_new_folder.1] adds support for detecting created
+ buffy mailboxes
+
+- [patch-0.88.4.speedup.mtsirkin.buffy_size.1] speeds up startup time with
+ the --enable-buffy-size configure option
+
+Changes since 0.88.5
+--------------------
+
+- [patch-0.88.5.bugfix.mtsirkin.buffy_size.1] fix bug in buffy_size
+
+- [patch-0.88.5.de.pop_var.1] remove unneeded variable
+
+- [patch-0.88.5.me.thread.2] fix segfault on sync
+
+- [patch-0.88.5.me.rx.1] a path may now be specified with --with-rx
+
+- [patch-0.88.5.de.aliasnull.1] fixed segfault when creating an alias while
+ in limited view
+
+- [patch-0.88.5.me.hook.1] fixed a bug which caused *-hook's with the same
+ regexp to be ignored
+
+- [patch-0.88.5.me.followup.1] the Mail-Followup-To: field was not rfc2047
+ encoded
+
+- [patch-0.88.5.me.menu.2] fixed some miscellaneous redraw problems related
+ to the menu movement commands
+
+- [patch-0.88.4.me.str_sub.2] moves handling of backquotes (``) in muttrc
+ commands to mutt_extract_token() so that they are only evaluated in
+ appropriate places
+
+- [patch-0.88.5.me.generic_menu.2] created the `generic' keymap which the
+ other menus (except for pager and editor) default to if a key is not found
+ in the menu specific keymap
+
+- [patch-0.88.5.nit.mtsirkin.buffy_time.1] disable the utime() call when
+ BUFFY_SIZE is enabled
+
+- [patch-0.88.5.me.mx_cleanup.1] separates append code out of
+ mx_open_mailbox() into mx_open_mailbox_append() for better code
+ readability.
+
+- [patch-0.88.5.me.pattern.2] rewrite of searching/limiting code to
+ pre-compile the search pattern
+
+Changes since 0.88.6
+--------------------
+
+- [patch-0.88.6.bigfix.init.mtsirkin.1] fixed variable type problem
+
+- [patch-0.88.6.jf.followup.1] fix off by 1 error in parsing of the
+ mail-followup-to: field
+
+- [patch-0.88.6.me.backtic.2] backtics were not handled in the alias and
+ my_hdr commands
+
+- [patch-0.88.6.me.bindings.1] added missing J and K bindings for the index
+ menu
+
+- [patch-0.88.6.me.pattern.2] fixed bugs in the search pattern compiler
+
+- [patch-0.88.6.de.sortbug.1] fixed coredump when resorting when new mail
+ arrives in an empty box
+
+- [patch-0.88.6.de.in_reply_to.1] increased length of buffer used to store
+ the message-id from the in-reply-to: header field
+
+- [patch-0.88.6.bl.doc_nits.1] updated docs on the alias expansion
+
+- [patch-0.88.6.de.remove_search_body.1] removes the search-body function
+ from the index menu now that the same feature is supported with the
+ rewritten search functions
+
+- [patch-0.88.6.de.search-body-doc.1] removes documentation references for
+ the search-body command
+
+- [patch-0.88.6.nit.pattern.mtsirkin.1] fixed compiler warning about const
+ being ignored
+
+- [patch-0.88.6.me.help_unbound.1] unbound functions are now displayed in
+ the help menus
+
+- [patch-0.88.6.me.received.1] if no From_ line is found, parse the first
+ Received: header field to get the local delivery date instead of using the
+ file modification time
+
+- [patch-0.88.6.kd.half_up.1] fixed bug in half-up function
+
+- fixed the eat_regexp() function to use _mutt_extract_token() so that
+ things like \n and \t get expanded
+
+- extended _mutt_extract_token() to be able to be used in eat_regexp()
+ by adding the option not to reap quotes, and to recognize the logic
+ operators for the pattern matching
+
+Changes since 0.88.7
+--------------------
+
+- changed handling of \ to not be interpreted inside of single quotes
+
+- runs of quotes in muttrc commands are now supported, so that
+ the following are equivalent:
+ michael\ elkins
+ michael' 'elkins
+ "michael"' 'elkins
+
+- fixed backtic handling so that the output is considered as separate tokens
+ unless, so that you may do: mailboxes `echo ~/Mail/Lists/*`
+
+- "ignore from_" is no longer supported, you should use
+ ignore "From "
+
+- created new menu function menu_check_recenter() in menu.c which is
+ responsible for checking if the current message has moved off the current
+ displayed range and recenters if needed
+
+- removed support for old-style Mutt postponed mailboxes which used the
+ X-Mutt-Attachment: header field
+
+- [patch-0.88.7.me.alias.1] fix segfault on recursive aliases
+
+- [patch-0.88.7.me.attach_desc.1] allow optional specification of the
+ attachment description after the filename in the 'Attach:' header field
+ with $edit_hdrs
+
+- [patch-0.88.7.me.help.1] fixed display of unbound functions in help to
+ crossref between the generic and menu specific keymaps so that functions
+ which are bound do not show up as unbound
+
+- [patch-0.88.7.me.pager_search.1] regexps should be compiled with
+ REG_NEWLINE
+
+- [patch-0.88.7.me.rx.1] use macro REGCOMP() instead of directly calling
+ regcomp()
+
+- [patch-0.88.7.me.parse.1] received: parsing did not work, and copying a
+ message to =postponed and recalling it resulted in some duplicate fields
+ in the header
+
+- [patch-0.88.6.kd.help_length.1] shortened descriptions of several help
+ strings
+
+- [patch-0.88.7.de.pattern.1] fixed off by one errors in selecting ranges of
+ messages
+
+- [patch-0.88.7.kd.half_down.1] fixed half-down op to mimic the next-page op
+ where if there is no next page, the pointer is moved to the last entry in
+ the menu instead of printing an error
+
+- added a sleep() call after the info about the state of the mailbox after a
+ sync operation so that the user has time to read it before the message
+ about sorting pops up
+
+Changes since 0.88.8
+--------------------
+
+- [patch-0.88.8.me.no_url.1, patch-0.88.8.no_url.1-2] the url menu and all
+ referecnes to it has been removed. equivalent functionality can be
+ obtained by using the external program
+ ftp://ftp.cs.hmc.edu/pub/me/urlview-0.3.tar.gz and the following macro:
+ macro index \cb |urlview\n
+
+- [patch-0.88.8.me.refresh.1] patch to avoid calls to refresh() in the
+ middle of executing macros
+
+- [patch-0.88.8.me.nested_quote.1] fixed problem with nested quotes in
+ muttrc commands
+
+- [patch-0.88.8.me.tag_thread.1] adds the tag-thread (ESC-t) function
+
+- [patch-0.88.me.reply_self.1] change handling of $metoo so that it will
+ leave the user's address on the to: line if it is the only address in the
+ list
+
+- [patch-0.88.me.decode_copy.1] removes the $forw_decode, $decode_format
+ variables, the decode-save and decode-copy functions, and rewrite of the
+ attachment handling functions to be much simpler
+
+- [patch-0.88.8.me.thread.1] fixed segfault on thread resort when new mail
+ arrives is a message in the mailbox has no message-id: field
+
+- mutt would dump core if given a bad date for the ~d search pattern
+
+- [patch-0.88.8i.de.fast_forward.1] skip prompt for subject when forwarding
+ if $fast_reply is set
+
+- fixed compilation error on systems without support for color related to
+ the color quoting support (SunOS 4.1.x)
+
+- [patch-0.88.8-bugfix.buffy_size.mtsirkin.2] fixed a bug in buffy_size
+ support
+
+- [patch-0.88.8.kd.help_length.1] fix to truncate macro descriptions in the
+ help menu at the screen edge, and updated docs with new short function
+ descriptions
+
+- increased size of buffer used to store regepxs for the *-hook commands
+ from SHORT_STRING to STRING
+
+Changes since 0.88.9
+--------------------
+
+- [patch-0.88.8-bugfix.buffy_size.mtsirkin.1] missed patch from previous
+ buffy_size fix
+
+- [patch-0.89.9.bl.fcc_fix.1] body of message was not written out for fcc
+ and postponement
+
+- [patch-0.88.9i.me.copy.1] created unified functions to copying/appending
+ messages. adds $forw_decode, $forw_quote, decode-save and decode-copy
+
+- [patch-0.88.9.me.refresh.1] use mutt_refresh() instead of refresh() in
+ mutt_enter_string()
+
+- with $edit_hdrs, if you delete the in-reply-to: field Mutt will
+ automatically remove the References: field as well
+
+- mutt_query_exit() should call timeout(-1) so the prompt doesn't abort.
+
+- [patch-0.88.9.me.fcc.1] removed the fcc-hook command
+
+- the return value of mutt_send_message() was not checked so if sending mail
+ failed for some reason (like a full $tmpdir or error invoking $sendmail),
+ Mutt would incorrectly report that the mail had been sent
+
+- [patch-0.88.9.me.pseudo_thread.1] fixed problem with rethreading messages
+ with no real reference when new mail arrives
+
+- [patch-0.88.9i.kd.first_entry.1] fixed segfault on first-entry function if
+ there are no entries in the menu
+
+- added a section to the doc describing the features of $edit_hdrs (fcc:,
+ attach: and references-magic)
+
+- [patch-0.88.bj.browser.1] fixed some problems with the file browser
+
+Changes since 0.88.10
+---------------------
+
+- return is now bound to `tag-entry' for the alias menu
+
+- configure --with-homespool is now equivalent to --with-homespool=mailbox
+
+- [patch-0.88.10.me.invoke_sendmail.1] remove check for return value of
+ invoke_sendmail() since mutt_system() always returns -1 on some systems
+
+- [patch-0.88.10.me.push_refresh.1] fixed bug with mailbox parsing status
+ not being displayed if the user has a `push' command in their muttrc or in
+ a folder-hook
+
+- [patch-0.88.10.de.thread-commands.1] cleans up the subthread operations
+
+- [patch-0.88.10i.de.sort_case.1] sorting by subject/author should be case
+ insensitive
+
+- [patch-0.88.10.me.pipe_attach.2] piping attachments did not work
+
+- [patch-0.88.10.kd.doc_nit.1] fixed doc bug wrt $forw_quote
+
+- manual.html readded to the `all' target in doc/makefile
+
+Changes since 0.88.11
+---------------------
+
+- changed default binding of pipe-entry to `|' in the compose menu
+
+- changed default binding of filter-entry to `F' in the compose menu
+
+- auto_view was not used when decoding messages with $pipe_decode set
+
+- piping messages did not use the new mutt_copy_message() function
+
+- use AC_REPLACE_FUNCS for check of strcasecmp() instead of AC_CHECK_FUNCS
+
+- [patch-0.88.11.me.copy_header.1] fixed problem when message header is
+ terminated by CRLF instead of just LF
+
+- [patch-0.88.11.de.reverse_name.1] $reverse_name should override a default
+ `my_hdr' command
+
+Changes since 0.88.12
+---------------------
+
+- [patch-0.88.12.me.broswer.1] fixed bug in status line for generic menu
+ code, and added entry numbers so that `jump' can be used effectively in
+ the file browser
+
+- [patch-0.88.12.me.attach_reply.1] fixed segfault when replying to a single
+ message/rfc822 attachment
+
+- [patch-0.88.12.kd.alias_redraw.1] fixed redraw bug when exiting the alias
+ menu
+
+- [patch-0.88.12.kd.help_wrap.2] fixed display of long macros in the help
+ display
+
+- [patch-0.88.12.kd.resort.1] cause a resort on ':set sort=xxx'
+
+- fixed bug where it was impossible to search for strings with any of
+ !, ~ or | in them
+
+- removed prototype for mutt_new_address() in rfc822.h since it is a macro
+
+- added doc blurb for $forw_decode
+
+- optimized loop for marking messages as `old' by checking if OPTMARKOLD is
+ set before entering the loop
+
+- $timeout should only affect the first keystroke of a multi-key command in
+ the `index' menu
+
+Changes since 0.88.13
+---------------------
+
+- [patch-0.88.13.me.date.1] fixed problems with the date (~d) pattern
+ matching code
+
+- [patch-0.88.13.me.send.2] fixed some deficiencies in replying to tagged
+ messages
+
+- fixed segfault when downloading POP3 mail
+
+- [patch-0.88.13.me.cache.1] fixed problem with search cache when status
+ flags on messages change
+
+- [patch-0.88.13.me.fcc_hook.1] restored the fcc-hook command
+
+
+PGP related changes since 0.88.13i
+----------------------------------
+
+- [patch-0.88.13i.tlr.pgp3_fingerprint.1] Code clean-up
+ with respect to PGP version 3 fingerprints.
+
+- [patch-0.88.13i.tlr.pgp_invoke.1] New PGP invocation
+ code which enables PGP 5 to check signatures and will
+ make the integration of other PGP implementations with
+ mutt easier.
+
+Changes since 0.88.15
+---------------------
+
+- [patch-0.88.14i.ds.flagdoc.1] fixed the description of the flags in
+ $hdr_format in the manual
+
+- messages were not included in replies in the non-PGP version of Mutt
+
+- [patch-0.88.15.me.reply_attach.1] fixed segfault when using replying to an
+ attachment from the `attach' menu
+
+- [patch-0.88.15.me.followup.1] fixed bug in generation of the
+ mail-followup-to: field
+
+- [patch-0.88.15.me.ret_val.1] fixed some unchecked return values from
+ mx_open_new_message() and mx_append()
+
+
+PGP related changes since 0.88.14i
+----------------------------------
+
+- [patch-0.88.14i.tlr.pgp_cleanup.1] Random clean-up
+
+- [patch-0.88.14i.tlr.pgp_flags.2] Display key
+ capabilities as ls(1) style flags on the key selection
+ menu.
+
+- [patch-0.88.14i.tlr.pgp_invoke.1] Fix a typo in
+ pgpinvoke.c.
+
+- [patch-0.88.14i.tlr.pgp_long_ids.1] Introduces a
+ pgp_long_ids option. If set, 64 Bit Key IDs will be
+ passed to PGP.
+
+- [patch-0.88.14i.tlr.pgp_sendkey.1] Fixes a segmentation
+ fault when sending PGP keys.
+
+- [patch-0.88.14i.tlr.pgp_tempfiles.2] Cleans up a
+ temporary file leak in the PGP code.
+
+Changes since 0.89
+------------------
+
+- [patch-0.89i.de.no_subj.1] fixed possible coredump on NULL subject
+
+- [patch-0.89i.tlr.snprintf.1] added unsigned hexadecimal support to the
+ supplied snprintf() routine
+
+- [patch-0.89.bl.rfc1524_ws.1] fixed problem with whitespace in mailcap
+ entries
+
+- [patch-0.89.bj.slang10config.1] fixed configure script to work with
+ slang-1.0 beta
+
+- [patch-0.89.bugfix.buffycheck.mtsirkin.1] fixed bug with buffy
+ notification not being updated when entering a mailbox
+
+- when compiling with slang, set ESCDELAY to 300 because the default is too
+ fast for slow terminals (this is what ncurses uses by default)
+
+- [patch-0.88.13i.de.buffy_notify.1] fixed problem with new mail
+ notification in the current mailbox being clobbered by notifications in
+ other mailboxes
+
+- truncated the ChangeLog to entries after version 0.88
+
+- [patch-0.88.14.tlr.msg_7bit.1] fixed problem with $mime_fwd
+
+- [patch-0.89.bj.set_followup_to.1] the user's address was added to the
+ mail-followup-to field if $metoo is set
+
+PGP related changes since 0.89i
+-------------------------------
+
+- [patch-0.89i.tlr.pgp_sigvrfy.1] When verifying PGP/MIME
+ signatures, the LF -> CRLF could fail under certain
+ circumstances.
+
+- [tlr] Some long -> unsigned long changes in the pgp code
+ which are more of an aesthetical nature.
+
+- [tlr] The default of the $pgp_strict_enc variable has
+ been changed to "set".
+
+- [tlr] pgp-Notes.txt has been somewhat extended.
+
+Changes since 0.89.1
+--------------------
+
+- [patch-0.89i.me.pointer.1] fix to use `unsigned long' instead of `void *'
+ for arguments where the data may be either an integer or a pointer. this
+ was needed for 64bit systems where `sizeof(void*)!=sizeof(int)'
+
+- [patch-0.89i.me.pattern_hook.4] the pattern matching language can now be
+ used in send-hook, save-hook and fcc-hook instead of a single regular
+ expression
+
+- [patch-0.89.bl.alter_weed.1] improves the mutlipart/alternative handler to
+ use auto_view, and adds viewing of MIME headers for attachments with
+ 'display-headers'
+
+- [patch-0.89.1.sec.expires_hdr.1] added support for the Expires: field.
+ auto-deletion of expiried messages is controlled with $expires
+
+- [patch-0.89.1i.co.mailx.1] added feedback for the `~r' command in mailx
+ mode
+
+- [patch-0.89.r.buf.1] buffer for backtic expansion of `mailboxes' was not
+ long enough
+
+- [patch-0.89.1i.de.mail_followup_to-leak.1] fixed memory leak in freeing
+ the mail-followup-to: field from the ENVELOPE struct
+
+- [patch-0.89.1i.de.list-reply-leak.1] fixed memory leak in the list-reply
+ function
+
+- [patch-0.89.1i.de.send-menu-leak.1] fixed memory leak when attaching files
+ to messages
+
+- [patch-0.89.1i.de.remove_user.1] fixed bug in removing the user's address
+ from the list if it is the only recipient
+
+- [patch-0.89.1i.de.pattern-regexp-leak.1] fixed memory leak with regular
+ expression compiled by mutt_pattern_comp() were never free()d
+
+- [patch-0.89.1i.de.beep_new.1] adds the $beep_new variable to control
+ whether or not mutt will beep when new mail arrives
+
+- [patch-0.89i.de.fcc-save-hook.1] adds new command `fcc-save-hook' which
+ creates a hook for use with either fcc-hook or save-hook
+
+- [patch-0.89.1i.de.buffy_sanity.1] fixes a problem with the buffy code
+
+- restored blank line between the "----- Forwarded message from..." line and
+ the text of the message
+
+- [patch-0.89.1i.de.tag-subthread.1] adds the `tag-subthread' command to the
+ pager
+
+PGP related changes since 0.89.1i
+---------------------------------
+
+- [patch-0.89i.dme.attach-key.1], [patch-0.89i.dme.extract-key.1] Fixes to
+ the PGP 5 invocation code.
+
+- [patch-0.89.1i.tlr.pgp_extract.2] Fixes various problems with the PGP
+ code and attachment menu, in particular with respect to extracting keys.
+
+Changes since 0.90
+--------------------
+
+- [patch-0.90.me.sort.2] $sort_aux is now used for all primary sorting
+ methods
+
+- [patch-0.90i.me.pattern.1] extended date matching language (~d) to accept
+ relative dates offset from the current date
+
+- [patch-0.90.me.thread_hash.3] modified threading algorithm to use hash
+ tables instead of a linear search
+
+- [patch-0.90i.de.difftime.1] remove use of difftime() since it is not
+ portable
+
+- [patch-0.89.1i.de-bj.buffy_sanity.1-2] cleanup of the buffy check routine
+
+- [patch-0.90.me.addr_pattern.1] fixed some problems with the address
+ matching routines which caused problems for the send-hook and fcc-hook
+ commands
+
+- the size of the temp buffer used to expand regexps into patterns in
+ mutt_check_simple() was too short (SHORT_STRING) to handle the
+ patterns for send-hook, save-hook and fcc-hook, so it was increased to
+ LONG_STRING (1024) bytes
+
+- [patch-0.90.me.safe_path.1] fixed segfault when trying to save a message
+ with no recipients
+
+- [patch-0.89i.de.index-percentage.1] adds `%P' to $status_format to display
+ the percentage of the menu already seen (similar to the way the pager
+ works)
+
+- [patch-0.89.1i.bj.pager-redraw-clenaup.1] fixed some redraw problems in
+ the pager with $pager_index_lines set
+
+- [patch-0.89.1i.fp.filter.1] screen was not updated when filtering an
+ attachment an the the cte changed
+
+PGP related changes since 0.90i
+-------------------------------
+
+- [patch-0.90i.tlr.pgp_attach_keys.1] Fixes a core dump on
+ the compose menu when attaching PGP public keys.
+
+- [patch-0.90i.tlr.pgp_encrflags.1] There were several
+ points in the code where a message's pgp flag was
+ evaluated the wrong way (namely, ...->pgp == PGPENCRYPT
+ instead of ... & PGPENCR).
+
+Changes since 0.90.1
+--------------------
+
+- [patch-0.90.1i.tlr.hdrignore.1] fixed bug with weeding headers with the
+ CH_MIME bit set in mutt_copy_hdr()
+
+- [patch-0.90.1i.de.buffy-cleanup.1] cleaned up buffy to remove some
+ duplicated code and share some common global information
+
+- [patch-0.90.1i.arl.fcc-save.1] fixed some typos in the manual wrt to the
+ fcc-save-hook command
+
+- expansion of environment variables in mutt_extract_token() did not accept
+ underscore (_) as a valid value in variable names
+
+- fixed possible segfault in mutt_extract_token() if the result of a backtic
+ expansion was longer than the buffer used to store the expanded version of
+ the string
+
+- expansion of backtics is now option and controlled by whether or not the
+ `expn' argument in non-NULL. this is needed by eat_regexp() where we
+ don't want backtic expansion
+
+- the ->personal member of the address was not matched in match_adrlist()
+
+- [patch-0.90.1.me.hash.1] fixed bug in hash_delete_hash() where the wrong
+ entry could be deleted when the key matched, but had a different data
+ pointer even if the data pointer was specified in the call
+
+- changed default expansion for save-hook to '~f %s' and fcc/send-hook to
+ '~t %s | ~c %s'
+
+- the current search pattern was not invalidated if $simple_search changed
+ while in the middle of a search
+
+- [patch-0.90.1i.dme.nit-to_chars.1] fixed bug in docs wrt to $to_chars
+
+- added documentation for the `pattern_hook' patch
+
+- [patch-0.90.1.me.sort_to.1] adds support for sorting by the to: field with
+ `set sort=to'
+
+Changes since 0.90.2
+--------------------
+
+- [patch-0.90.2i.de.toggle-quoted-redraw.1] fixed redraw problem with the
+ toggle-quoted function
+
+- [patch-0.90.2.de.buffy_check.1] fixed problem with -Z option
+
+- [patch-0.90.2.de.buffy_times.1] fixed problem of buffy reporting new mail
+ when saving messags from within mutt
+
+- [patch-0.90.1i.bj.in-reply-to-decode.1] fix to rfc2047 decode the
+ in-reply-to field before extracting the message-id
+
+- [patch-0.90.2.me.resort.1] fix to resort mailbox with $sort_aux or
+ $strict_threads changes
+
+- [patch-0.90.2.de.pretty_size.1] fixed rounding error which could cause the
+ display to be more than 4 chars
+
+- [patch-0.90.2.me.score.3] adds scoring support
+
+- [patch-0.90.2i.jg.pipe_from.1] the `From ' line was missing when piping a
+ message
+
+- [patch-0.89.1i.bj.skip_quoted.1] adds a `skip-quoted' (default: S)
+ function to the pager
+
+- tag-prefix was broken in the generic menu code when $auto_tag was unset
+
+- fixed bug in mutt_extract_token() wrt expanding backtics (``)
+
+- [patch-90.2i.tlr.addrbook.1] fixes address book so that select-entry
+ returns the current or tagged messages and exits
+
+- ESCDELAY default for slang is used again instead of setting it to 300ms
+
+- added `~R' to match references in the mutt_pattern_comp()
+
+PGP related changes since 0.90.2i
+---------------------------------
+
+- [patch-0.90.2i.tlr.pgp-disptrust.1] The trust status of
+ a key-user id relation is always displayed on the key
+ selection menu.
+
+- [patch-0.90.2i.tlr.pgp-subkeys.1] Subkeys are always
+ referred to using the principal key's numerical user ID.
+ This seems to be necessary to make PGP 5.0 extract them
+ properly.
+
+- The bit count for DSA keys is now displayed correctly.
+
+- A new option pgp_language (default: "en") is introduced.
+ This can be used to achieve an effect similar to the
+ alt_pgp patch. See doc/pgp-Notes.txt for details. A
+ big Thank You to Roland Rosenfeld for this idea!
+
+Changes since 0.90.3
+--------------------
+
+- [patch-0.90.3.me.sort.1] fixed several bug relating to sorting by to or
+ score
+
+- %s in $status_format no longer includes $sort_aux, use %S to get the
+ $sort_aux value if desired (default value for $status_format now uses
+ (%s/%S) to mimic the old behavior)
+
+- [patch-0.90.3i.tlr.addrbook.1] fixed bug selecting the first alias in the
+ address book display
+
+- fixed bug in create-alias where mutt would dump core on a message with a
+ malformed address
+
+- parse_date() now always returns -1 on error
+
+- mailbox was not rethreaded when $sort_re changes
+
+- [patch-0.90.3.de.I_DECL.1] changes a few remaining variables in init.h to
+ the new I_DECL format
+
+- since most of the pattern matching commands were `envelope' types, changed
+ the semantics so that header and body matching are only done if the
+ M_FULL_MSG flag is set with mutt_pattern_comp()
+
+- backslash quoting did not work for the folder-hook and send-hook commands
+
+- use mutt_mktime() instead of mktime() in is_from()
+
+- [patch-0.90.3i.bj.skip-quoted.2] fixed bug in the skip-quoted function
+
+- [patch-0.90.3i.me.attach_pager.1] allows use of save/pipe/next/prev while
+ viewing attachments from the attach or compose menus
+
+- mutt_gen_msgid() changed to use getpid() so that unique id's are created
+ when mutt is invoked quickly in batch mode
+
+- Mutt now reports an error if the file specified by the `-F' command line
+ option does not exist
+
+- [jkj@sco.com] adds the $meta_key boolean option to case 8bit input to be
+ delivered as ESC + the char with the hi bit stripped
+
+- [patch-0.90.3i.me.editor_ext.1] improved line editor to be able to handle
+ unprintable control chars and to be able to bind the completion character
+
+- [patch-0.90.2i.bj.copy_hdr.2] rewrite of mutt_copy_hdr() to do reordering
+ in a single pass and to correctly unwrap fields for rfc2047 decoding
+
+- moved mutt_copy_hdr() to copy.c, and mutt_print_message() to commands.c
+ and eliminated print.c
+
+- `mutt -v' now displays the release date along with the version number.
+ the release date is also displayed with the `display-version' function
+
+
+PGP related changes since 0.90.3i
+---------------------------------
+
+- [patch-0.90.3i.tlr.pgpkeyssort.1] If two keys have the
+ same User ID (i.e., mail address), use the numerical key
+ ID as the second sorting criteria for the key selection
+ menu.
+
+- [patch-0.90.3i.doc_pgp.1] Documentation for the PGP
+ support. (By Roland Rosenfeld
+ <roland@spinnaker.rhein.de>).
+
+Changes since 0.90.4
+--------------------
+
+- [patch-0.90.4i.bj.show_version.1] reduce code size required to print
+ compile time options
+
+- fix so that specifying more than two -v's on the command line always
+ prints the copyright statement instead of ignoring them
+
+- [patch-0.90.4.me.veryclean.1] adds a `veryclean' target to the Makefile to
+ remove files created by `configure'
+
+- the `inv' prefix now works for quadoptions
+
+- [patch-0.90.4.me.editor.1] adds `quote-char' function, and displays
+ control chars with the `markers' color, fixes bug in `buffy-cycle'
+
+- [patch-0.90.4i.me.unscore.1] adds the `unscore' command
+
+- [patch-0.89.1i.bj.tag-save.1] fixed bug in tag-save command
+
+- [patch-0.90.4i.me.help.1] added mutt_compile_help() to generate help
+ strings from `struct mapping_t'
+
+- [patch-0.90.4i.bj.hdr_fmt_recv.1] adds the %(fmt) to $hdr_format to
+ display the received time of a message
+
+- [patch-0.90.4i.bj.compose-disp-hdrs.3] fixes bug wrt to header weeding
+ with display-headers in the compose menu
+
+- [patch-0.90.4i.bjdeme.time.1] fixed calculation of local timezone and
+ added speedup when parsing mbox/mmdf mailbox and calculating the received
+ time from the `From ' line
+
+- added `flag' argument to mutt_pattern_exec() so that the
+ M_MATCH_FULL_ADDRESS option could be passed so that searching will match
+ the real name while the *-hook commands will not
+
+
+PGP related changes since 0.90.3i
+---------------------------------
+
+- [patch-0.90.3i.tlr.pgpkeyssort.1] If two keys have the
+ same User ID (i.e., mail address), use the numerical key
+ ID as the second sorting criteria for the key selection
+ menu.
+
+- [patch-0.90.3i.doc_pgp.1] Documentation for the PGP
+ support. (By Roland Rosenfeld
+ <roland@spinnaker.rhein.de>).
+Changes since 0.90.5
+--------------------
+
+- [patch-0.90.5i.me.new_addr.1] replaces rfc822 address parser and related
+ data structures
+
+- [patch-0.90.5.bj.parse-speedup.new_addr-2] rewrite of
+ mutt_read_rfc822_line() and mutt_read_rfc822_header() to be more efficient
+
+- [patch-0.90.5i.me.hash_ext.1] added support for unique keys in the hash
+ table, and optional argument to the destroy functions to free the
+ associated data
+
+- [patch-0.90.5i.me.alias_sort.1] the sorting method and alias menu format
+ are now configurable via $alias_sort and $alias_format, respectively
+
+- [tlr] mx_open_mailbox() did not correctly free the created CONTEXT struct
+ upon failure
+
+- created function mutt_version() to display Mutt's version number
+
+- [patch-0.90.5i.rc.exactcompile.1] fixed warning about unused function when
+ compiling with `#undef EXACT_ADDRESS' in rfc822.c
+
+- [patch-0.90.5.sec.expires_new.1] adds ~S and ~E to match superceded and
+ expired messages
+
+- [patch-0.90.5.me.decode_followup.1] addresses in mail-followup-to: were
+ not rfc2047 decoded
+
+- [patch-0.90.5.bj.manual-nits.1] adds missing docs relating $status_format
+
+Changes since 0.90.6
+--------------------
+
+- [patch-0.90.6.me.unwrap_line.1] fixed bug in read_rfc822_line() wrt to
+ unwrapping folded lines
+
+- [patch-0.90.6.co.exactadd.1] fixed botched #ifdef in rfc822.c when
+ compiling with EXACT_ADDRESS
+
+- [patch-0.90.6i.jg.supersedes.2] changed `supercedes' to `supersedes' as
+ described in the current mailext I-D
+
+- added blurb on `--enable-exact-address' to the INSTALL file
+
+- fixed bug where messages with invalid dates showed up as being sent on Dec
+ 31, 1969
+
+- added '-e' command line option to specify a config command to be run
+ _after_ parsing of initialization files
+ (eg. mutt -e 'set edit_hdrs' mutt-dev)
+
+- added `edit-from' function to the `compose' menu (the From: field is now
+ also displayed)
+
+- added real bounds checking to rfc2047_encode_string() and friends
+
+- [tlr] fixed several bugs in the rfc2047 `Q' encoding (tabs were not
+ encoded, chars with hex value less than 0x10 were not padded with 0, =? in
+ a string was not encoded)
+
+- [patch-0.90.6.bugfix.locktimeout.1] added optional timeout to the locking
+ code so that the check for new mail will not to acquire a lock wait while
+ large mail is being delivered
+
+- [fvl] mutt_get_name() would segfault if a NULL address was passed in
+
+- [patch-0.90.6i.de.old-unread.1] adds %o and %u to $status_format to allow
+ display the number of `old' and `unread' messages
+
+- [patch-0.90.6.co.fwd.1] adds a blank line before the `---End of forwarded
+ message' line
+
+- the confirmation prompt for bounce-message will now only display the
+ address without the personal name to conserve space
+
+- [vl] arg to isspace() was not cast to `unsigned char' in rfc822.c
+
+- updated the pattern matching functions to add the following operators:
+ ~p matches personal mail (finds $alternates in to: or cc:)
+ ~l matches mail addressed to `lists'
+ ~L<pattern> finds <pattern> in to:, cc:, from: or sender:
+ ~C<pattern> finds <pattern> in to: or cc:
+
+
+PGP related changes since 0.90.6
+--------------------------------
+
+- [patch-0.90.5i.rs.pgp_hdr.1] Preserve a message's PGP
+ settings when postponing
+
+Changes since 0.90.7
+--------------------
+
+- included state.h, list.h, send.h in mutt.h and removed the others from the
+ distribution
+
+- [patch-0.90.7.me.default_from.1]
+
+- [patch-0.90.7i.de.limit.1] full name in addresses was not matched for `limit'
+
+- [patch-0.90.6.bugfix.locktimeout.1-2] changed `timeout' to `retry' in the
+ locking code since the former was misleading
+
+- [patch-0.90.7i.jg.help_line.1] offset of attachment was not calculated
+ correctly in the compose menu, cause the help line to be overwritten
+
+- fixed memory leak in rfc822_parse_address() when a parse error occurs (the
+ previously parsed address list was not returned and not freed)
+
+- [patch-0.90.7.me.vsnprintf.1] fixed bug where existence of snprintf()
+ assumed the existence of vsnprintf()
+
+- create-alias suggested the full email address as the default alias name
+ instead of just the mailbox portion
+
+- documented the `score' and `unscore' commands, and added %N to the
+ $hdr_format description
+
+- [patch-0.90.7i.bj.fgets_nit.1] size of buffer passed to fgets() was
+ incorrect
+
+- [patch-0.90.7i.bj.read_line.1] create common function mutt_read_line() for
+ reading lines which may contain a backslash on the end to wrap to the next
+ line
+
+- [patch-0.90.7i.bj.source_rc.1] makes source_rc() use the new
+ mutt_read_line() function
+
+- [patch-0.90.7.bugfix.buffycheck.mtsirkin.1] fixed another problem with
+ buffy
+
+- [patch-0.90.7.me.group_addr.1] fixed segfault when $use_domain is set and
+ you try to send to a group mailbox address
+
+- you can now use `mono <object> normal' to set to normal attribute
+
+- [patch-0.90.7i.de.hook.2] adds the $default_hook variable to control the
+ expansion of `simple' patterns in the send-hook, fcc-hook and save-hook
+ commands
+
+- it was not possible to use the $hdr_format sequences in $indent_str when
+ the switch to the new mutt_copy_message() and mutt_copy_hdr()
+
+- new common function km_get_table() which retrives the mapping of function
+ names based upon the MENU_* value
+
+- [patch-0.90.7i.bj.mailcap_parse.1] rewrote the mailcap parsing routines to
+ fix some bugs and be more efficient
+
+- [mac@eecg.utoronto.ca] added the `search-opposite' to search for the
+ current pattern in the opposite direction from the last search
+
+
+Changes since 0.90.8
+--------------------
+
+- [patch-0.90.8.bj.display_loop.1] toggle of header weeding in the attach
+ and compose menus did not work
+
+- [patch-0.90.8.bj.manual_nit.1] documentation for displaying recieve date
+ in $hdr_format was missing
+
+- [patch-0.90.8.bj.pager_compile_help.1] pager did not use new function
+ mutt_compile_help()
+
+- [patch-0.90.8.bj.rename_file.1] changes mutt_rename_file() return codes
+ to be consistent with rename()
+
+- [patch-0.90.8.me.search.1] was not possible to bind the search functions
+ in the pager
+
+- [patch-0.90.8.me.time.1] fixed bug with the `~d >N' pattern
+
+- [patch-0.90.8.bj.reading_attachments.1] switching between viewing
+ attachments without returing to the attach menu was broken
+
+- [patch-0.90.8.bj.SKIPWS.1] removed unnecessary check for \0 in the
+ SKIPWS() macro
+
+- dest->fp was used directly in mx_open_new_message() where it should have
+ used msg->fp
+
+- `shell-escape' was moved to the generic keymap so that all menus can make
+ use of the function
+
+- added documentation on the `search-opposite' function
+
+- [patch-0.90.8i.tlr.rfc1524_fclose.1] fixed bug where fclose() could be
+ called with a NULL argument
+
+
+PGP related changes since 0.90.7
+--------------------------------
+
+- [patch-0.90.7i.bj.pgp_hdr.1] Fixes some nits in
+ conection with the pgp_hdr patch.
+
+- [patch-0.90.7i.rs.pgp_hdr_doc.1] Adds documentation for
+ the pgp_hdr patche's effects.
+
+- [patch-0.90.7i.tlr.pgp_keysel.1] Fixes uniqueness
+ problems with the pgp key selection code.
+
+- [patch-0.90.8i.rr.pgp_get_table.1] The help function
+ wouldn't work on the PGP key selection menu.
+
+- [patch-0.90.8i.tlr.pgp_asymm.1] This patch replaces the
+ pgp_version variable by three variables:
+ pgp_receive_version, pgp_send_version, pgp_key_version.
+
+- [patch-0.90.8i.tlr.pgp_micalg.1] This patch enables the
+ user to generate correct "micalg" parameters when
+ composing PGP/MIME signed messages.
+
+Changes since 0.90.9
+--------------------
+
+- [patch-0.90.9.me.personal_hook.1] fixed bug where ~P in send-hook patterns
+ did not work
+
+- [patch-0.90.9i.tlr.mutt_add_list.1] mutt_add_list() could create memory
+ which was unitialized to 0
+
+- [patch-0.90.9.me.group_alias.1] fixed bug where group address names were
+ expanded if a matching alias was found
+
+- definition of SKIPWS() in rfc822.c changed to match that in protos.h
+
+- [patch-0.90.9.josh.write_percent.1] the %-done is now shown when writing
+ out mbox/mmdf mailboxes just as is done for reading
+
+- body of message was not quoted with $indent_str if $forw_quote was set,
+ but $forw_decode is unset
+
+- changed initial value of $default_hook to "~f %s !~P | (~P ~C %s)"
+
+
+PGP related changes since 0.90.9i
+---------------------------------
+
+- [patch-0.90.9i.tlr.pgppath.1] Properly check whether
+ PGPV2PATH and PGPV3PATH are defined in init.c.
+
+- [patch-0.90.9i.rr.pgp_key_version.1] Fixes a typo in the
+ pgp_key_version entry in init.h.
+
+- configure.in has been fixed so that _PGPPATH is not
+ defined unless at least one version of PGP exists on the
+ system.
+
+- A +nobatchinvalidkeys=off parameter has been added to
+ the pgpe invocation in pgpinvoke.c.
+Changes since 0.90.10
+---------------------
+
+- added check for empty string arguments to the bind and macro commands
+
+- moved bit fields in CONTEXT and BODY structs to the end of the struct def
+ since there were not enough bits to fit into a 32-bit word
+
+- split include_message() into include_forward() and include_reply() since
+ little code was shared for the two cases
+
+- $forw_decode no longer controls weeding of the header when replying and
+ $headers is set
+
+- added doc blurb for $mime_fwd on the effects of decoding when this
+ variable is unset
+
+- [patch-0.90.10.me.unread.1] fixed error in calculation of unread messages
+ when the `new' bit is cleared from a message
+
+- fixed y2k problem in ~d pattern
+
+- an error was reported in the ~d pattern if using the open-ended form (eg.
+ 1/1/98-) and any more text followed the pattern
+
+
+PGP related changes since 0.90.10i
+----------------------------------
+
+- mutt -v now correctly displays the various PGPPATH
+ variables.
+Changes since 0.90.11
+---------------------
+
+- [patch-0.89.1i.bj.enriched.1] fixed buffer overflow in the text/enriched
+ handler
+
+- [patch-0.90.10.bl.sort_alias.1] adds the `unsorted' method for $sort_alias
+
+- [patch-0.90.10i.co.reply_regexp.1] changed initial value for $reply_regexp
+ to catch the `Re[2]:' style
+
+- changed check for new mail in MH-style mailboxes to consider messages new
+ if mtime >= atime
+
+- forwarded messages with $mime_fwd set are no longer forced to 7-bits when
+ not using PGP
+
+- updated the NEWS file with comments from the dev list
+
+- [patch-0.90.11i.de.stdin.2] fixed bugs with using stdin for -i or -H
+ command line arguments
+
+- [patch-0.90.11i.de.safe_fopen.1] changed many instances of fopen() to
+ safe_fopen() to avoid tempfile race conditions
+
+- the generic menu code did not print "Tag-" when using tag-prefix
+
+- [0.90.11-vikas.doc_nit] fixed bug with use of > in the description of
+ $status_format
+
+- [patch-0.90.11i.ds.man_tilde_nit.1] fixed use of ~ where ˜ should
+ have been used in manual
+
+- [patch-0.90.11i.rr.date_received.1] the parser table for ~r in the pattern
+ language used eat_regexp() instead of eat_date()
+
+- [patch-0.90.11i.me.attach_reply] fixes reply/forward of (tagged)
+ attachments from the attach menu
+
+- [patch-0.90.11.bj.make_string.1] fixes display of control chars and other
+ non-printable chars in the $*_format variables
+
+- [patch-0.90.11i.tlr.cdisp.2] fixes message_to_7bit() to not destroy the
+ existing content-disposition `filename' information
+
+- fixed bug in rfc822 parser where quoted-pair was not accepted in the
+ `mailbox' or `domain' tokens
+
+
+PGP related changes since 0.90.11i
+----------------------------------
+
+- [patch-0.90.11i.roro.doc.1.gz] Added some more PGP
+ documentation. Spelling and grammar corrections from
+ native speakers are welcome!
+Changes since 0.90.12
+---------------------
+
+- [patch-0.90.11i.me.boundary.1] mutt_write_mime_body() did not use
+ mutt_get_parameter() to fetch the MIME boundary
+
+- [patch-0.90.12i.tlr.recall.1] fixed segfault when attempting to recall
+ postponed messages
+
+- [patch-0.90.12i.rr.hdrline.1] fixed misapplied patch for the make_string
+ support
+
+- fixed error message on nonexistant file specified with -i or -H
+
+- [patch-0.90.12.me.safe_fopen.1] safe_fopen should not be used when it is
+ desirable for the specified file to be overwritten
+
+
+PGP related changes since 0.90.12i
+----------------------------------
+
+- [patch-0.90.12i.maj.pgp_extract_keys_nit.1] Correct the
+ behaviour when typing C-K in an empty folder.
+Changes since 0.90.13
+---------------------
+
+- [patch-0.90.13i.de.enter_fname.1] buffy-cycle should act like complete
+ when in the save/copy message prompts
+
+- [patch-0.90.13i.rr.normalize_time.1] mutt_normalize_time() didn't handle
+ the case where a date rolled over to the previous year when selecting by
+ number of months in the past
+
+Changes since 0.91
+------------------
+
+- fixed bug in rfc822_parse_adrlist() where it would segfault on a bad address
+ which contained only a comment
+
+- [patch-0.90.12i.tlr.recall.1] fixed segfault when attempting to recall a
+ postponed message which was a reply to another message
+
+- [patch-0.91.me.exit_main.1] fixes collision in function name for "exit" in
+ the "generic" and "index" keymaps
+
+- corrected documentation on $hdrs which erroneously stated that user defined
+ headers are never included in batch mode sending
+
+Changes since 0.91.1
+--------------------
+
+- [patch-0.91i.me.dyn_str.2] changes global string variables to `char *'
+ instead of fixed size arrays, and moves defaults for variables to the
+ MuttVars[] array
+
+- [patch-0.91.me.format_string.1] creates common function
+ mutt_FormatString() for handling %-substituted strings in a generic manner
+
+- [patch-0.91.vikas.limit_size.4] adds %L to $hdr_format to display the
+ total size of all visible messages when limited
+
+- [patch-0.91.me.followup.1] Adds boolean $followup_to to control
+ generatation of the Mail-Followup-To field. Also allows user to edit this
+ field and not have it overwritten.
+
+- [patch-0.91i.me.sendmail.1] Removes $sendmail_bounce. Mutt now passes all
+ recipients on the command line for normal sending.
+
+- [patch-0.90.10i.bj.current_time.1] adds sequence %<fmt> to $hdr_format to
+ display the current time using strftime(fmt)
+
+- [patch-0.90.10.bl.alternative_order.1] allows user specification of the
+ order in which to choose types from multipart/alternative messages
+
+- [my_hdr-subject-patch2] allows specification of a default subject (if none is
+ given using the `my_hdr' command
+
+- [patch-0.91i.as.revname.1] makes $reverse_name look at from: if the message
+ you are replying to is from you
+
+- added new operator ~B to pattern matching language which matches a regexp
+ anywhere in a message (more efficient than ~h %s | ~b %s).
+
+- changed the `source' command to use mutt_open_read() instead of fopen() so
+ that you can read from the output of a script (eg. "source
+ ~/bin/mutt_commands|").
+
+- fixed typos in the description of `forget-passphrase' in the manual
+
+- the -e option was not document in either the man page nor the manual
+
+- [patch-0.91.bl.composetyped.1] adds support for creating non-text body
+ parts from within the compose menu
+
+- $reverse_name is now handled before send-hook so that user's can match on
+ the from field with ~f
+
+- [patch-0.91i.de.threadlimit.1] allows drawing of thread trees while in
+ limited display
+
+- [mutt-0.89i-quadopt_mimefwd.patch] turns $mime_fwd into a quad-option
+
+- [patch-0.91i.jmy.folder_format.1] the display of the `browser' menu is now
+ configurable via the $folder_format variable
+
+- [patch-0.91i.jmy.pattern-size.1] adds ~z operator to the pattern matching
+ function to match by size
+
+- rfc822.c now uses some functions from mutt proper (safe_strdup and
+ mutt_substrdup) instead of attempting to be a self-contained library
+
+
+PGP related changes since 0.91.1
+--------------------------------
+
+- [patch-0.91.1i.tlr.pgp_obsolete_vars.1] Removes the
+ obsolete pgp_v3_* variables.
+Changes since 0.92
+------------------
+
+- [patch-0.91.me.reuse_hook.1] the result portion of those hooks which can
+ have only a single matching pattern (ie. send-hook) were not updated if the
+ same pattern was specified with a different result.
+
+- [patch-0.92.me.empty_pop_str.1] fixes core when fetching POP3 mail if any
+ of the $pop_* string variables were empty
+
+- [patch-0.92.me.empty_route.1]
+
+- [patch-0.92.me.regexp_case.1] REG_ICASE was not set for regexp variable
+ defaults in mutt_restore_default()
+
+- [patch-0.92.de.threadlimitfix.1] fixes small bug in the threadlimit code
+ which could cause extra work to be done when not needed
+
+- [patch-0.92.me.format_pad.1] fixed segfault when the value of
+ $status_format is wider than the screen
+
+- [patch-0.92.me.draft_path.1] the paths for neither -i nor -H were being
+ expanded before use
+
+- [patch-0.92.vikas.nits]
+ 1. %M in $status_format was getting truncated after 3 digits because of a
+ tiny mistake in mu
+ 2. The ~z pattern matching has a small nit at the boundaries where
+ ~z<1024 matches Content-length: 1024 as well. I just changed the inclusive
+ ranges ( <= and >= ) to exclusive ranges ( < and > ) in pattern.c
+ 3. Added a line about the new %L format specifier for $status_format
+
+
+PGP related changes since 0.92
+------------------------------
+
+- No more (char *) deferences of (char **) in pgp_invoke.c.
+Changes since 0.92.1
+--------------------
+
+- [0.92.1.me.format_pad.1] fixed another problem with the result of
+ subtracting two size_t's and the result being negative
+
+- [0.92.1.me.batch_copy.1] $copy was ignored when sending mail in batch mode
+
+- [patch-0.92.1.josh.sort_re-tilde.1] reset default values of $sort_re and
+ $tilde to their 0.91.1 values.
+
+- [patch-0.92.me.func_macro.1] changes function key names to use <> around
+ then (eg. <PageUp>) and allows them to be used in macros as well
+ (eg. macro index Y *<PageUp>)
+
+- [patch-0.92.1i.rr.reset.2] adds the `reset' command and the `&' prefix to
+ `set' to allow resetting variables to the default value
+
+- changed the %g (group) and %u (userid) sequences of $folder_format to
+ print the gid and uid, respectively, if they can't be resolved to names,
+ instead of diplaying "(null)"
+
+- [patch-0.92.1i.me.sendmail_wait.2] adds $sendmail_wait which specifies the
+ number of seconds to wait for sendmail to finish before background it
+
+- [patch-0.92.1.co.bounce.2] fixes problem with the bounce-message
+ confirmation prompt
+
+- [patch-0.92.1.bl.attach_del.2] adds the ability to delete attachments
+
+- [patch-0.92.1i.jg.default_opts.2] changed var defaults to match what is in
+ the manual
+
+- [patch-0.92.1i.rr.no_signature.1] fixed segfault with
+ set signature=''
+ when composing mail
+
+- [patch-0.92.1i.rr.manual_nits.1] fixed some spelling errors in the manual
+
+- [mutt-0.91.1a.patch (partial)] if KEY_RESIZE is defined, mutt_getch() will
+ ignore it (generated by ncurses 4.2 on SIGWINCH)
+
+- [patch-0.92.1.bl.query.1] adds support for querying an external database
+ for email addresses
+
+
+PGP related changes since 0.92.1i
+---------------------------------
+
+- [patch-0.92.1i.wn.verify_sig-pgp_timeout.1] Make the
+ verify_sig and pgp_timeout defaults consistent between
+ init.h and the manual.
+
+- [patch-0.92.1i.tlr.pgp_keysel.1] Fix a segmentation
+ fault in the key selection code.
+
+- [patch-0.92.1i.tlr.pgpsign.1] The PGP signing code works
+ again.
+
+- Changed $verify_sig to $pgp_verify_sig.
+Changes since 0.92.2
+--------------------
+
+- [patch-0.92.2i.tlr.sendlib_args.1] fixes segfault when sending mail
+
+- [patch-0.92.2.me.reverse_name.1] fixed problem with $reverse_name altering
+ the HEADER struct for the message being replied to.
+
+- [patch-0.92.2.sort_flag.1] fixed problem with `score' command not updating
+ the score when $sort or $sort_aux was not `score'
+
+- [patch-0.92.2i.me.sendmail.1] adds debugging info to the send_msg()
+ command
+
+- [patch-0.92.2i.dme.generic-op_jump.1] fixed missing binding for 3-9 to
+ `jump' in the generic keymap
+
+- [patch-0.92.2.me.sort_alias.1] fixed problems with $sort_alias not working
+
+- [patch-0.92.2i.me.msg_search.1] fixed bugs in the ~h, ~B and ~b search
+ patterns
+
+- [patch-0.92.2i.me.postpone.1] x-mutt-references was not removed when
+ recalling a postponed reply when no mailbox is open
+
+- included <unistd.h> for def of SEEK_SET in copy.c (SunOS 4.1.4)
+
+- [patch.sendlib.2] fixes signal handling in send_msg(), and fixes leftover
+ temporary files after sending the message
+
+- [patch-0.91.1i.aw.purgeprompt.1] the prompt for $delete now shows how many
+ messages will be purged
+
+- [patch-0.92.1i.jg.empty_to.1] makes the string used for the group mailbox
+ name when there are no to: or cc: recipients configurable via the
+ $empty_to variable
+
+- moved mutt_signal_init() before initscr() in start_curses() so that
+ ncurses 4.2 will not attempt to install its on SIGWINCH handler
+
+
+PGP related changes since 0.92.2i
+---------------------------------
+
+- White space problems around "#ifdef _PGPPATH" should be
+ fixed now.
+
+- The .spec file for RedHat is gone.
+Changes since 0.92.3
+--------------------
+
+- alarm() was not actually set to put sendmail in the background when
+ $sendmail_wait > 0
+
+- [patch-0.92.3.de.threadlimitfix.1]
+
+- [patch-0.92.3.josh.delete_format.1] changes default for $delete_format
+
+- [patch-0.92.3.me.var_syn.1] renames some variables and creats a synonym
+ data type
+
+- [patch-0.92.3.vikas.sort_alias] fixed bug with `set sort_alias=unsorted'
+
+- [patch-0.92.3i.de.hashtable-leak.1] fixed memory leak when destroying
+ hash tables
+
+- [de] fixed memory leak in init.c where the value of $alias_file was not
+ free()d before assigning the default from the default muttrc to use
+
+- [patch-0.92.3i.tlr.keymaps.1] fixed missing keybindings for the `jump'
+ command
+
+- fixed typo "Purpge" in mx_close_mailbox()
+
+- [patch-0.91.1.ldm.ignore_list_reply_to] fixed bug with handling of
+ $ignore_list_reply_to
+
+- fixed signal initialization to call before initscr() for ncurses and after
+ initscr() for slang
+
+- [patch-0.92.3.vikas.alias_search] makes searching in the alias menu
+ WYSIWYG
+
+- fixed memory leak in parse_route_addr() when the route-addr portion of an
+ address was unable to be parsed
+
+- [patch-0.92.3i.tlr.query_real.1]
+
+- [patch-0.92.3.color_index.7] adds the ability to color lines in the index
+ using the searching language
+
+
+PGP related changes since 0.92.3i
+---------------------------------
+
+- [patch-0.92.3i.tlr.keymaps.1] Add some lost keymap
+ bindings for the generic menu.
+
+- [patch-0.92.3i.tlr.pgp_default_version.1] The default
+ value of $pgp_default_version is now set correctly.
+
+- [patch-0.92.3.bl.pgp_attach_del.1] Handle PGP/MIME
+ messages correctly when deleting attachments.
+
+- [patch-0.92.3i.rr.postpone_pgp.1] Corrects some bugs
+ with respect to postponing PGP signed messages.
+Changes since 0.92.4
+--------------------
+
+- [patch-0.92.4.josh.empty_querycmd.2] fixed segfault on $query_command
+ being set to ''
+
+- [recv.diff] the warning from filter-attachment was not cleared when you
+ answer no or abort
+
+- [patch-0.92.4i.rr.mime_forward_decode.1] splits $forward_decode into
+ $forward_decode and $mime_forward_decode
+
+- [patch-0.92.1.optional_list.2] makes the %L in $header_format an optional
+ string using the %?L?...? syntax
+
+- [patch-0.92.4.me.savehook.1] makes it possible to use ~b and ~h in
+ save-hook commands
+
+- [patch-0.92.4i.me.token.2] upgrades the muttrc parser to use dynamic
+ buffer allocation, and allows multiple commands on a single line separated
+ by semicolons
+
+- the bcc: field is again output to the $sendmail program, and is assumed
+ that the MTA will take care of removing it
+
+- fixed memory leak in parse_route_addr()
+
+
+PGP related changes since 0.92.3i
+---------------------------------
+
+- [patch-0.92.3i.tlr.keymaps.1] Add some lost keymap
+ bindings for the generic menu.
+
+- [patch-0.92.3i.tlr.pgp_default_version.1] The default
+ value of $pgp_default_version is now set correctly.
+
+- [patch-0.92.3.bl.pgp_attach_del.1] Handle PGP/MIME
+ messages correctly when deleting attachments.
+
+- [patch-0.92.3i.rr.postpone_pgp.1] Corrects some bugs
+ with respect to postponing PGP signed messages.
+Changes since 0.92.5
+--------------------
+
+- [patch-0.92.4i.de.parse_date.1] better recovery from badly formatted
+ timezones
+
+- [patch-0.92.5.de.Muttrc-memleak.1] fixed memory leak when using the -F
+ command line option
+
+- [patch-0.92.5i.bj.pgp_bitfield.1]
+
+- [patch-0.92.5i.jg.empty_to_doc.1] adds documentation for the $empty_to
+ variable
+
+- [patch-0.92.5i.rr.ask_quit.1] new variable $quit which controls the prompt
+ for exiting mutt
+
+- [patch-0.92.5.vikas.color_index.7-10] fixed bugs in the `uncolor' command
+
+- fixed coredump if $to_chars is set to ''
+
+- [patch-0.92.5.vikas.wrap_search] new variable $wrap_search which controls
+ whether or not searches wrap around the first or last message
+
+- [patch-0.92.4i.dj.addr_hook_patterns.3] the mailbox specified in a
+ save-hook is now %-expanded using $header_format
+
+- the default save mailbox no longer looks at defined `lists'. to get the
+ old behavior, use
+ save-hook ~l %B
+
+- optional strings in format strings have been extended to allow for an
+ `else' clause. the new syntax is:
+ %?<testchar>?<if-string>&<else-string>?
+ or if no `else' clause is desired (backwards compatibility):
+ %?<testchar>?<if-string>?
+
+- [patch-0.92.5.bj.interrupt_search.1] allows interruption of `search' using
+ the shell's interupt (SIGINT) character
+
+- [patch-0.92.5i.jg.attach_split_sep.1] removed useless code and docs
+ relating to $attach_split and $attach_sep
+
+- [patch-0.92.5.vikas.toggle_write] if the mailbox is read-only and the
+ user attempts to sync, a more informative error message is now displayed
+
+
+Changes since 0.92.6
+--------------------
+
+- [patch-0.92.6.vikas.limit_new.3] adds support for new mail delivery while
+ a limit is in effect
+
+- [patch-0.92.6.vikas.wrap_search.2] adds docs for $wrap_search
+
+- [patch-0.92.6i.de.quit.1] changes the default for $quit to `yes'
+
+- [patch-0.92.6.me.rfc822.1] fixed buffer overrun error in
+ rfc822_write_address_real()
+
+- [mutt-0.92.6i-mhsupport.patch]
+
+- [patch-0.92.6i.de.reopen_unread.1]
+
+- consolidated common code for editing address lists in edit_envelope()
+
+- changed parse_alias() to use mutt_parse_adrlist() instead of
+ rfc822_parse_adrlist() so that you can have simple aliases like:
+ alias group bob joe
+ with no commas (mailx compatibility)
+
--- /dev/null
+Changes since 0.92.7
+--------------------
+
+- [patch-0.92.7.handler.empty_cc_bcc.1] fixed bug in edit_envelope() which
+ caused the message to be aborted if the cc or bcc field was empty
+
+- buffer used to store the error message in mutt_pattern_comp() called
+ sizeof() on the wrong variable
+
+- renamed $header_format to $index_format ($hdr_format is still accepted)
+
+- [patch-0.92.7.vikas.postpone.1] fixed typo for binding the `postpone'
+ menu, and changes the menu to use $index_format for display of postponed
+ messages
+
+- mutt_error() is now a function pointer which either has the value of
+ mutt_curses_error() or mutt_nocurses_error() which simplifies the muttrc
+ parsing code
+
+- "source '~/bin/myscript|'" was fixed so that it works correctly
+
+- [patch-0.92.7.me.mh_sync.1] fixed bug where syncing mh mailboxes silently
+ failed
+
+- [patch-0.92.7.me.pattern.1] fixes bug with mismatched backtics in the
+ pattern language
+
+- mutt_default_from() no longer sets the "real name" portion of the return
+ address so that it can be delayed until after execution of send-hook
+ commands
+
+
+PGP-related changes since 0.92.6i
+---------------------------------
+
+- [patch-0.92.6i.tlr.pgp_longids.1] Correct the
+ calculation of 64 bit "v4" key IDs.
+
--- /dev/null
+Supported platforms
+===================
+
+Mutt has been reported to compile and run under the following Unix operating
+systems:
+
+ AIX
+ BSDI
+ Convex
+ Data General Unix (DG/UX)
+ Digital Unix (OSF/1)
+ DYNIX/ptx
+ FreeBSD
+ HP-UX
+ IRIX
+ Linux
+ Atari MiNT
+ MkLinux
+ NetBSD
+ QNX
+ Solaris
+ SunOS
+ Ultrix
+ UnixWare
+
+- An ANSI C compiler (such as gcc) is required.
+
+- You must also have a SysV compatible curses library, or you must
+ install either
+
+ GNU ncurses, ftp://prep.ai.mit.edu/pub/gnu/
+
+ or
+
+ S-Lang, ftp://space.mit.edu/pub/davis/slang/
+
+Installation
+============
+
+Installing Mutt is rather painless through the use of the GNU autoconf
+package. Simply untar the Mutt distribution, and run the ``configure''
+script. In most cases, it will automatically determine everything it needs
+to know in order to compile. However, there are a few options to
+``configure'' to help it out, or change the default behavior:
+
+--prefix=DIR
+ install Mutt in DIR instead of /usr/local
+
+--with-sharedir=DIR
+ specify where to put architecture independent data files
+
+--with-curses=DIR
+ use the curses lib in DIR/lib. If you have ncurses, ``configure''
+ will automatically look in /usr/include/ncurses for the include
+ files.
+
+--with-slang[=DIR]
+ use the S-Lang library instead of ncurses. This library seems to
+ work better for some people because it is less picky about proper
+ termcap entries than ncurses. It is recommended that you use at
+ *least* version 0.99-38 with Mutt.
+
+--with-mailpath=DIR
+ specify where the spool mailboxes are located on your system
+
+--with-homespool[=FILE]
+ treat file in the user's home directory as the spool mailbox. Note
+ that this is *not* the full pathname, but relative to the user's
+ home directory. Defaults to "mailbox" if FILE is not specified.
+
+--enable-pop
+ enable POP3 support
+
+--enable-hidden-host
+ local hostname is not part of the FQDN.
+
+--with-rx
+ use GNU rx instead of local regexp routines. Many systems don't
+ have the POSIX compliant regcomp/regexec/regfree routines, so this
+ provides a way to support them.
+
+--enable-flock
+ use flock() to lock files
+
+--disable-fcntl
+ by default, Mutt uses fcntl() to lock files. Over NFS this can
+ result in poor performance on read/write. Note that using this
+ option could be dangerous if dotlocking is also disabled
+
+--enable-nfs-fix
+ some implementations of NFS do not always write the
+ atime/mtime of small files. This means that Mutt's ``mailboxes''
+ feature does not always work properly, as it uses these
+ attributes to work out whether the file has new mail. This
+ option enables a workaround to this bug.
+
+--enable-locales-fix
+ on some systems, the result of isprint() can't be used reliably
+ to decide which characters are printable, even if you set the
+ LANG environment variable. If you set this option, Mutt will
+ assume all characters in the ISO-8859-* range are printable. If
+ you leave it unset, Mutt will attempt to use isprint() if either
+ of the environment variables LANG, LC_ALL or LC_CTYPE is set,
+ and will revert to the ISO-8859-* range if they aren't.
+
+--with-exec-shell=SHELL
+ on some versions of unix, /bin/sh has a bug that makes using emacs
+ with mutt very difficult. If you have the problem that whenever
+ you press control-G in emacs, mutt and emacs become very confused,
+ you may want to try using a Bourne-derived shell other than
+ /bin/sh here. Some shells that may work are bash, zsh, and ksh.
+ C shells such as csh and tcsh will amost certainly not work right.
+ Note that this option is unrelated to what shell mutt gives you
+ when you press '!'. Only use this option to solve the above problem,
+ and only specify one of the above shells as its argument.
+
+--enable-exact-address
+ By default, Mutt will rewrite all addresses in the form
+ Personal Name <user@host.domain>
+ regardless of the input. By enabling this option, Mutt will write
+ addresses in the same form they are parsed. NOTE: this requires
+ significantly more memory
+
+Once ``configure'' has completed, simply type ``make install.''
+
+Mutt should compile cleanly (without errors) and you should end up with a
+binary called ``mutt.'' If you get errors about undefined symbols like
+A_NORMAL or KEY_MIN, then you probably don't have a SysV compliant curses
+library. You should install either ncurses or S-Lang (see above), and then
+run the ``configure'' script again.
+
+Platform Notes
+==============
+
+Solaris 2.4
+
+ The system regcomp() and regexec() routines are very badly broken.
+ So much so that using them will cause Mutt to be totally unusable.
+ The --with-rx option to `configure' should always be used. (Note:
+ the problems have apparently been fixed in Solaris 2.5)
+
+ Color does not work right with Solaris curses. You will have to
+ compile with either ncurses or slang to get working color support.
+
--- /dev/null
+#
+# Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+SHELL=/bin/sh
+VERSION=@VERSION@
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+bindir=@bindir@
+libdir=@libdir@
+mandir=@mandir@
+sharedir=@sharedir@
+srcdir=@srcdir@
+VPATH=@srcdir@
+@SET_MAKE@
+
+INSTALL=@INSTALL@
+CC=@CC@
+XCPPFLAGS=-I. @CPPFLAGS@
+CFLAGS=@CFLAGS@ -DSHAREDIR=\"$(sharedir)\" $(XCPPFLAGS)
+LDFLAGS=@LDFLAGS@
+LIBS=@LIBS@
+OPS=@OPS@
+OBJS= addrbook.o alias.o attach.o browser.o buffy.o color.o \
+ commands.o complete.o compose.o copy.o curs_lib.o curs_main.o date.o \
+ edit.o enter.o flags.o init.o filter.o from.o getdomain.o \
+ handler.o hash.o hdrline.o headers.o help.o hook.o keymap.o lib.o \
+ main.o mbox.o menu.o mh.o mx.o pager.o parse.o pattern.o \
+ postpone.o query.o recvattach.o rfc822.o \
+ rfc1524.o rfc2047.o score.o send.o sendlib.o signal.o sort.o \
+ status.o system.o thread.o @LIBOBJS@
+
+CLEANFILES=mutt *.o core
+VERYCLEANFILES=$(CLEANFILES) Makefile config.cache config.log \
+ config.status config.h
+DISTCLEANFILES=$(VERYCLEANFILES) tags keymap_defs.h *.rej *.orig *~ Makefile.bak
+
+# kill these files when making new export distributions
+NONEXPORT=pgp.c pgp.h pgpinvoke.c pgpkey.c pgppubring.c sha.h sha1dgst.c \
+ sha_locl.h OPS.PGP doc/pgp-Notes.txt doc/language.txt \
+ doc/language50.txt
+
+all: mutt
+
+mutt: keymap_defs.h $(OBJS) $(REGEX)
+ $(CC) -o mutt $(OBJS) $(REGEX) $(LDFLAGS) $(LIBS)
+
+keymap_defs.h: Makefile $(OPS)
+ rm -f keymap_defs.h
+ $(srcdir)/gen_defs $(OPS) > keymap_defs.h
+
+install: mutt
+ $(srcdir)/mkinstalldirs $(bindir)
+ -mv -f $(bindir)/mutt $(bindir)/mutt.old
+ $(INSTALL) @MUTT_GROUP@ -m @MUTT_PERMISSION@ mutt $(bindir)
+ $(srcdir)/mkinstalldirs $(mandir)/man1
+ $(INSTALL) -m 644 $(srcdir)/doc/mutt.man $(mandir)/man1/mutt.1
+ -if [ ! -f $(sharedir)/Muttrc ]; then \
+ $(srcdir)/mkinstalldirs $(sharedir); \
+ $(INSTALL) -m 644 $(srcdir)/Muttrc $(sharedir); \
+ fi
+ -if [ ! -f $(sharedir)/mime.types ]; then \
+ $(INSTALL) -m 644 $(srcdir)/mime.types $(sharedir); \
+ fi
+
+uninstall:
+ rm -f $(bindir)/mutt $(sharedir)/Muttrc $(mandir)/man1/mutt.1
+
+$(srcdir)/configure: $(srcdir)/configure.in
+ autoconf
+
+Makefile: $(srcdir)/Makefile.in
+ ./config.status
+
+config.h.in: $(srcdir)/acconfig.h
+ autoheader
+
+config.h: $(srcdir)/config.h.in
+ ./config.status
+
+tags:
+ (cd $(srcdir) && ctags *.[ch])
+
+dep: Makefile
+ mv Makefile Makefile.bak
+ awk -f $(srcdir)/depend.awk < Makefile.bak > Makefile
+ echo '# DO NOT REMOVE THIS LINE' >> Makefile
+ $(CC) -MM $(XCPPFLAGS) $(srcdir)/*.c >> Makefile
+
+clean-real:
+ (cd $(srcdir) && rm -f $(CLEANFILES))
+ (cd $(srcdir)/doc && $(MAKE) $@)
+
+clean:
+ rm -f $(CLEANFILES)
+
+veryclean:
+ rm -f $(VERYCLEANFILES)
+
+distclean:
+ (cd $(srcdir) && rm -f $(DISTCLEANFILES))
+ (cd $(srcdir)/doc && $(MAKE) $@)
+
+reldate:
+ rm -f $(srcdir)/reldate.h
+ echo 'const char *ReleaseDate = "'`date +%Y-%m-%d`'";' > $(srcdir)/reldate.h
+
+# make export version
+usdist: distclean reldate
+ rm -rf /tmp/mutt-$(VERSION)
+ (cd .. && cp -r mutt-$(VERSION) /tmp)
+ for i in `grep _PGPPATH $(srcdir)/*.[ch] | sed 's/^\([^:]*\).*/\1/' | grep -v '/main\.c' | grep -v '/pgp'` ; do \
+ target=`basename $$i` ; \
+ $(srcdir)/reap.pl _PGPPATH < $(srcdir)/$$target > /tmp/mutt-$(VERSION)/$$target; \
+ done
+ for i in $(NONEXPORT); do \
+ rm -f /tmp/mutt-$(VERSION)/$$i; \
+ done
+ (cd /tmp && tar cvf mutt-$(VERSION).tar mutt-$(VERSION) && gzip mutt-$(VERSION).tar)
+ rm -rf /tmp/mutt-$(VERSION)
+
+# make international distribution
+dist: distclean reldate
+ (cd $(srcdir)/.. && tar cfz mutt-$(VERSION)i.tar.gz mutt-$(VERSION)i)
+
+rx/librx.a:
+ (cd rx && $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)")
+
--- /dev/null
+#
+# Key bindings similar to those of MUSH
+#
+
+bind index . display-message
+bind index t display-message
+macro index n j\n
+bind index + next-message
+bind index j next-message
+bind index J next-message
+bind index - previous-message
+bind index k previous-message
+bind index K previous-message
+bind index { top-page
+bind index } bottom-page
+bind index f change-folder
+bind index \cu sync-mailbox
+bind index * flag-message
--- /dev/null
+#
+# System configuration file for Mutt
+#
+
+# default list of header fields to weed when displaying
+#
+ignore "from " received content- mime-version status x-status message-id
+ignore sender references return-path lines
+
+# imitate the old search-body function
+macro index \eb '/~b '
+
+# simluate the old url menu
+macro index \cb |urlview\n
+macro pager \cb |urlview\n
+
+# If Mutt is unable to determine your sites domain name correctly, you can
+# set the default here.
+#
+# set hostname=cs.hmc.edu
+
+# If your sendmail supports the -B8BITMIME flag, enable the following
+#
+# set use_8bitmime
--- /dev/null
+Announcing Mutt 0.91
+====================
+
+NOTE: PLEASE read the following list of changes carefully, especially the
+first couple entries related to changes in variables/commands and see the
+manual before posting questions. Taking a little time to read this notice
+will significantly reduce the amount of confusion as to why some things
+might seem broken at first.
+
+Major changes since 0.89.1:
+
+- The matching for the send-hook, save-hook and fcc-hook commands has been
+ extended so that it understands Mutt's matching language (used for
+ searching and limits). You may still use a "simple" regular expression,
+ but Mutt will expand that with the value of $default_hook (similar to
+ $simple_search). Additionally, all hooks are now executed in the order
+ they appear in your .muttrc instead of first doing `to', then `cc'.
+
+- new config command `score' which allows you to assign a numerical value to
+ a message based upon header information (similar to scoring in slrn). the
+ score can be displed in $hdr_format with %N, you can sort by `score' and
+ you can match messages with a certain score (or range) with the `~n'
+ operator
+
+- new variable $beep_new which causes Mutt to beep when new mail is
+ delivered to a mailbox specified by the `mailboxes' command
+
+- new sort method `to' for sorting by who messages are addressed to
+
+- new variable $meta_key which causes Mutt to interpet keystrokes with the
+ hi bit set as ESC plus the key with the hi bit stripped (eg. ALT-x will
+ get interpetted as "\ex")
+
+- new variable $sort_alias which controls the order the aliases appear in
+ the menu (may be either "alias" or "address")
+
+- new variable $alias_format to control the way the aliases are displayed in
+ the alias menu
+
+- additional operators to the pattern matching lanauge:
+ ~S superseded messages
+ ~E expired messages
+ ~p personal mail (finds $alternates in to: or cc:)
+ ~P mail from you (finds $alternates in from: or sender:)
+ ~l list mail (finds `lists' in to: or cc:)
+ ~L<pattern> finds <pattern> in to:, cc:, from: or sender:
+ ~C<pattern> finds <pattern> in to: or cc:
+
+- additional formats for $hdr_format
+ %N displays the message score
+ %(fmt) displayes the message's received time using strftime()
+
+- additional formats for $status_format
+ %P percentage of menu seen
+ %S current value of $sort_aux (%s no longer displays $sort_aux)
+ %o number of old messages
+ %u number of unread messages
+
+- new configure option `--enable-exact-address' which causes Mutt to rewrite
+ addresses in the same format they were parsed instead of rewriting them in
+ the form `Name <user@host>'
+
+- new command line argument `-e' which allows you to specify configuration
+ commands to be executed *after* your .muttrc is parsed
+ (eg. mutt -e 'set edit_hdrs' mutt-dev)
+
+- you can now use `mono <object> normal' to set to normal attribute
+
+- new function `search-opposite' which searches for the current search
+ pattern in the opposition direction from the previous search
+
+Mutt's primary distribution point is ftp://ftp.cs.hmc.edu/pub/me/mutt.
+See the Mutt Home Page (http://www.cs.hmc.edu/~me/mutt/) for mirror sites.
+
+Bug reports should be sent to the Mutt User's Mailing List
+<mutt-users@cs.hmc.edu>.
+
+Michael Elkins <me@cs.hmc.edu>
+April 10, 1998
--- /dev/null
+OP_NULL "null operation"
+OP_ATTACH_VIEW_MAILCAP "force viewing of attachment using mailcap"
+OP_ATTACH_VIEW_TEXT "view attachment as text"
+OP_BOTTOM_PAGE "move to the bottom of the page"
+OP_BOUNCE_MESSAGE "remail a message to another user"
+OP_BROWSER_NEW_FILE "select a new file in this directory"
+OP_CHANGE_DIRECTORY "change directories"
+OP_CHECK_NEW "check mailboxes for new mail"
+OP_COMPOSE_ATTACH_FILE "attach a file(s) to this message"
+OP_COMPOSE_EDIT_BCC "edit the BCC list"
+OP_COMPOSE_EDIT_CC "edit the CC list"
+OP_COMPOSE_EDIT_DESCRIPTION "edit attachment description"
+OP_COMPOSE_EDIT_ENCODING "edit attachment transfer-encoding"
+OP_COMPOSE_EDIT_FCC "enter a file to save a copy of this message in"
+OP_COMPOSE_EDIT_FILE "edit the file to be attached"
+OP_COMPOSE_EDIT_FROM "edit the from field"
+OP_COMPOSE_EDIT_HEADERS "edit the message with headers"
+OP_COMPOSE_EDIT_MESSAGE "edit the message"
+OP_COMPOSE_EDIT_MIME "edit attachment using mailcap entry"
+OP_COMPOSE_EDIT_REPLY_TO "edit the Reply-To field"
+OP_COMPOSE_EDIT_SUBJECT "edit the subject of this message"
+OP_COMPOSE_EDIT_TO "edit the TO list"
+OP_COMPOSE_EDIT_TYPE "edit attachment type"
+OP_COMPOSE_ISPELL "run ispell on the message"
+OP_COMPOSE_NEW_MIME "compose new attachment using mailcap entry"
+OP_COMPOSE_POSTPONE_MESSAGE "save this message to send later"
+OP_COMPOSE_RENAME_FILE "rename/move an attached file"
+OP_COMPOSE_SEND_MESSAGE "send the message"
+OP_COMPOSE_TOGGLE_UNLINK "toggle whether to delete file after sending it"
+OP_COPY_MESSAGE "copy a message to a file/mailbox"
+OP_CREATE_ALIAS "create an alias from a message sender"
+OP_CURRENT_BOTTOM "move entry to bottom of screen"
+OP_CURRENT_MIDDLE "move entry to middle of screen"
+OP_CURRENT_TOP "move entry to top of screen"
+OP_DECODE_COPY "make decoded (text/plain) copy"
+OP_DECODE_SAVE "make decoded copy (text/plain) and delete"
+OP_DELETE "delete the current entry"
+OP_DELETE_SUBTHREAD "delete all messages in subthread"
+OP_DELETE_THREAD "delete all messages in thread"
+OP_DISPLAY_ADDRESS "display full address of sender"
+OP_DISPLAY_HEADERS "display message with full headers"
+OP_DISPLAY_MESSAGE "display a message"
+OP_EDITOR_BACKSPACE "delete the char in front of the cursor"
+OP_EDITOR_BACKWARD_CHAR "move the cursor one character to the left"
+OP_EDITOR_BOL "jump to the beginning of the line"
+OP_EDITOR_BUFFY_CYCLE "cycle among incoming mailboxes"
+OP_EDITOR_COMPLETE "complete filename or alias"
+OP_EDITOR_COMPLETE_QUERY "complete address with query"
+OP_EDITOR_DELETE_CHAR "delete the char under the cursor"
+OP_EDITOR_EOL "jump to the end of the line"
+OP_EDITOR_FORWARD_CHAR "move the cursor one character to the right"
+OP_EDITOR_HISTORY_DOWN "scroll up through the history list"
+OP_EDITOR_HISTORY_UP "scroll up through the history list"
+OP_EDITOR_KILL_EOL "delete chars from cursor to end of line"
+OP_EDITOR_KILL_LINE "delete all chars on the line"
+OP_EDITOR_KILL_WORD "delete the word in front of the cursor"
+OP_EDITOR_QUOTE_CHAR "quote the next typed key"
+OP_ENTER_COMMAND "enter a muttrc command"
+OP_ENTER_MASK "enter a file mask"
+OP_EXIT "exit this menu"
+OP_FILTER "filter attachment through a shell command"
+OP_FIRST_ENTRY "move to the first entry"
+OP_FLAG_MESSAGE "toggle a message's 'important' flag"
+OP_FORWARD_MESSAGE "forward a message with comments"
+OP_GENERIC_SELECT_ENTRY "select the current entry"
+OP_GROUP_REPLY "reply to all recipients"
+OP_HALF_DOWN "scroll down 1/2 page"
+OP_HALF_UP "scroll up 1/2 page"
+OP_HELP "this screen"
+OP_JUMP "jump to an index number"
+OP_LAST_ENTRY "move to the last entry"
+OP_LIST_REPLY "reply to specified mailing list"
+OP_MACRO "execute a macro"
+OP_MAIL "compose a new mail message"
+OP_MAIN_CHANGE_FOLDER "open a different folder"
+OP_MAIN_CHANGE_FOLDER_READONLY "open a different folder in read only mode"
+OP_MAIN_CLEAR_FLAG "clear a status flag from a message"
+OP_MAIN_DELETE_PATTERN "delete messages matching a pattern"
+OP_MAIN_FETCH_MAIL "retrieve mail from POP server"
+OP_MAIN_FIRST_MESSAGE "move to the first message"
+OP_MAIN_LAST_MESSAGE "move to the last message"
+OP_MAIN_LIMIT "show only messages matching a pattern"
+OP_MAIN_NEXT_NEW "jump to the next new message"
+OP_MAIN_NEXT_SUBTHREAD "jump to the next subthread"
+OP_MAIN_NEXT_THREAD "jump to the next thread"
+OP_MAIN_NEXT_UNDELETED "move to the next undeleted message"
+OP_MAIN_NEXT_UNREAD "jump to the next unread message"
+OP_MAIN_PREV_THREAD "jump to previous thread"
+OP_MAIN_PREV_SUBTHREAD "jump to previous subthread"
+OP_MAIN_PREV_UNDELETED "move to the last undelete message"
+OP_MAIN_PREV_NEW "jump to the previous new message"
+OP_MAIN_PREV_UNREAD "jump to the previous unread message"
+OP_MAIN_READ_THREAD "mark the current thread as read"
+OP_MAIN_READ_SUBTHREAD "mark the current subthread as read"
+OP_MAIN_SET_FLAG "set a status flag on a message"
+OP_MAIN_SYNC_FOLDER "save changes to mailbox"
+OP_MAIN_TAG_PATTERN "tag messages matching a pattern"
+OP_MAIN_UNDELETE_PATTERN "undelete messages matching a pattern"
+OP_MAIN_UNTAG_PATTERN "untag messages matching a pattern"
+OP_MIDDLE_PAGE "move to the middle of the page"
+OP_NEXT_ENTRY "move to the next entry"
+OP_NEXT_LINE "scroll down one line"
+OP_NEXT_PAGE "move to the next page"
+OP_PAGER_BOTTOM "jump to the bottom of the message"
+OP_PAGER_EXIT "return to the main-menu"
+OP_PAGER_HIDE_QUOTED "toggle display of quoted text"
+OP_PAGER_SKIP_QUOTED "skip beyond quoted text"
+OP_PAGER_TOP "jump to the top of the message"
+OP_PIPE "pipe message/attachment to a shell command"
+OP_PREV_ENTRY "move to the previous entry"
+OP_PREV_LINE "scroll up one line"
+OP_PREV_PAGE "move to the previous page"
+OP_PRINT "print the current entry"
+OP_QUERY "query external program for addresses"
+OP_QUERY_APPEND "append new query results to current results"
+OP_QUIT "save changes to mailbox and quit"
+OP_RECALL_MESSAGE "recall a postponed message"
+OP_REDRAW "clear and redraw the screen"
+OP_REPLY "reply to a message"
+OP_SAVE "save message/attachment to a file"
+OP_SEARCH "search for a regular expression"
+OP_SEARCH_REVERSE "search backwards for a regular expression"
+OP_SEARCH_NEXT "search for next match"
+OP_SEARCH_OPPOSITE "search for next match in opposite direction"
+OP_SEARCH_TOGGLE "toggle search pattern coloring"
+OP_SHELL_ESCAPE "invoke a command in a subshell"
+OP_SORT "sort messages"
+OP_SORT_REVERSE "sort messages in reverse order"
+OP_TAG "tag the current entry"
+OP_TAG_PREFIX "apply next function to tagged messages"
+OP_TAG_SUBTHREAD "tag the current subthread"
+OP_TAG_THREAD "tag the current thread"
+OP_TOGGLE_NEW "toggle a message's 'new' flag"
+OP_TOGGLE_WRITE "toggle whether the mailbox will be rewritten"
+OP_TOP_PAGE "move to the top of the page"
+OP_UNDELETE "undelete the current entry"
+OP_UNDELETE_THREAD "undelete all messages in thread"
+OP_UNDELETE_SUBTHREAD "undelete all messages in subthread"
+OP_VERSION "show the Mutt version number and date"
+OP_VIEW_ATTACH "view attachment using mailcap entry if necessary"
+OP_VIEW_ATTACHMENTS "show MIME attachments"
+OP_MAIN_SHOW_LIMIT "show currently active limit pattern"
--- /dev/null
+OP_COMPOSE_ATTACH_KEY "attach a PGP public key"
+OP_COMPOSE_PGP_MENU "show PGP options"
+OP_EXTRACT_KEYS "extract PGP public keys"
+OP_FORGET_PASSPHRASE "wipe PGP passphrase from memory"
+OP_MAIL_KEY "mail a PGP public key"
+OP_VERIFY_KEY "verify a PGP public key"
+OP_VIEW_ID "view the key's user id"
--- /dev/null
+#
+# This file contains commands to change the keybindings in Mutt to be
+# similar to those of PINE 3.95.
+#
+
+bind index v display-message
+bind index p previous-undeleted
+bind index n next-undeleted
+bind index ' ' next-page
+bind index c mail
+bind index g change-folder
+bind index w search
+bind index y print-message
+bind index x sync-mailbox
+bind index $ sort-mailbox
+bind index a tag-prefix
+bind index ; tag-message
+
+# Not possible to simulate zoom-out...
+macro index z ltagged\r
+
+bind pager p previous-undeleted
+bind pager n next-undeleted
+bind pager ' ' next-page
+bind pager g change-folder
+bind pager c mail
+bind pager w search
+bind pager y print-message
+bind pager \n noop # PINE prints "No default action for this menu."
+bind pager up previous-line
+bind pager down next-line
+
+bind compose \cx send-message
+
+# PINE has different defaults for this variables
+set folder=~/mail
+set record=+sent-mail
+set nosave_name
+set postponed=~/postponed-msgs
+set hdr_format="%Z %3C %{%b %d} %-19.19L (%5c) %s"
--- /dev/null
+README for mutt-0.90i
+=======================
+
+Installation instructructions are detailed in ``INSTALL''.
+
+The user manual is in doc/manual.txt.
+
+PGP users please read doc/pgp-Notes.txt before proceeding.
+
+For more information, see the Mutt home page,
+http://www.cs.hmc.edu/~me/mutt/index.html.
+
+The primary distribution point for Mutt is
+ftp://ftp.cs.hmc.edu/pub/me/mutt. See the home page for mirror sites.
+
+Michael Elkins <me@cs.hmc.edu>, January 22, 1998
+Thomas Roessler <roessler@guug.de>, February 3, 1998
--- /dev/null
+- Other than multipart/mixed and PGP/MIME, Mutt should allow the user to
+ specify what to do with other types of multipart messages (i.e., so a user
+ can deal with S/MIME messages reasonably)
+
+- option to not include attachments in replies
+
+- handle message/external-body in some fashion
+
+- handle message/partial reconstruction
+
+- not possible to view the header of a single part message which contains
+ something that requires a mailcap entry to view
+
+- need to clean up the error recovery when running out of space when syncing
+ a mbox/mmdf mailbox
+
+- BODY struct should probably have a pointer to its corresponding HEADER
+ struct. this is needed for mh/maildir mailboxes so the correct pathname
+ can be found. Or perhaps all we need is a .hdr member of the STATE struct
+ so that all of the MIME handlers can look up the corresponding HEADERs if
+ need be?
+
+- mailbox resync code for mh and maildir mailboxes
+
+- fold long user-defined header fields
+
+- history classes
+
+- document honored environment variables
+
+- add -v flag to pass to sendmail...
+
+- ordered tag
+
+- rfc822 parser needs to do something with the @host1@host2: portion of the
+ route-addr
+
+- command completion for `enter-command'
+
+- new forward command to include first attachment in the editor and attach
+ other attachments to the message
+
+- adjust the names of *adr() and *adrlist() and calling routines for
+ consistency
+
+- mbox-hook entries should override $move?
+
+- add a preserved flag to messages
+
--- /dev/null
+
+/* Enable debugging info */
+#define DEBUG
+
+/* Does your version of PGP support the PGPPASSFD environment variable? */
+#define HAVE_PGPPASSFD
+
+/* Disable the X-Mailer header? */
+#undef NO_XMAILER
+
+/* What is your domain name? */
+#undef DOMAIN
+@TOP@
+
+/* Mutt version info */
+#undef VERSION
+
+/* use dotlocking to lock mailboxes? */
+#undef USE_DOTLOCK
+
+/* use flock() to lock mailboxes? */
+#undef USE_FLOCK
+
+/* Use fcntl() to lock folders? */
+#undef USE_FCNTL
+
+/*
+ * Define if you have problems with mutt not detecting new/old mailboxes
+ * over NFS. Some NFS implementations incorrectly cache the attributes
+ * of small files.
+ */
+#undef NFS_ATTRIBUTE_HACK
+
+/* Do you want support for the POP3 protocol? (--enable-pop) */
+#undef USE_POP
+
+/*
+ * Is mail spooled to the user's home directory? If defined, MAILPATH should
+ * be set to the filename of the spool mailbox relative the the home
+ * directory.
+ * use: configure --with-homespool=FILE
+ */
+#undef HOMESPOOL
+
+/* Where new mail is spooled */
+#undef MAILPATH
+
+/* Should I just use the domain name? (--enable-hidden-host) */
+#undef HIDDEN_HOST
+
+/* Does your system have the srand48() suite? */
+#undef HAVE_SRAND48
+
+/* Where to find sendmail on your system */
+#undef SENDMAIL
+
+/* Where is PGP located on your system? */
+#undef _PGPPATH
+
+/* Where is PGP 2.* located on your system? */
+#undef _PGPV2PATH
+
+/* Where is PGP 5 located on your system? */
+#undef _PGPV3PATH
+
+/* Do we have PGP 2.*? */
+#undef HAVE_PGP2
+
+/* Do we have PGP 5.0 or up? */
+#undef HAVE_PGP5
+
+/* Where to find ispell on your system? */
+#undef ISPELL
+
+/* Should Mutt run setgid "mail" ? */
+#undef USE_SETGID
+
+/* Does your curses library support color? */
+#undef HAVE_COLOR
+
+/* Are we using GNU rx? */
+#undef USE_GNU_RX
+
+/* Compiling with SLang instead of curses/ncurses? */
+#undef USE_SLANG_CURSES
+
+/* program to use for shell commands */
+#define EXECSHELL "/bin/sh"
+
+/* The "buffy_size" feature */
+#undef BUFFY_SIZE
+
+/* The result of isprint() is unreliable? */
+#undef LOCALES_HACK
+
+/* Enable exact regeneration of email addresses as parsed? NOTE: this requires
+ significant more memory when defined. */
+#undef EXACT_ADDRESS
+
+/* Does your system have the snprintf() call? */
+#undef HAVE_SNPRINTF
+
+/* Does your system have the vsnprintf() call? */
+#undef HAVE_VSNPRINTF
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_menu.h"
+#include "sort.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#define RSORT(x) (SortAlias & SORT_REVERSE) ? -x : x
+
+static struct mapping_t AliasHelp[] = {
+ { "Exit", OP_EXIT },
+ { "Select", OP_GENERIC_SELECT_ENTRY },
+ { "Help", OP_HELP },
+ { NULL }
+};
+
+static const char *
+alias_format_str (char *dest, size_t destlen, char op, const char *src,
+ const char *fmt, const char *ifstring, const char *elsestring,
+ unsigned long data, format_flag flags)
+{
+ char tmp[SHORT_STRING], adr[SHORT_STRING];
+ ALIAS *alias = (ALIAS *) data;
+
+ switch (op)
+ {
+ case 'a':
+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
+ snprintf (dest, destlen, tmp, alias->name);
+ break;
+ case 'r':
+ adr[0] = 0;
+ rfc822_write_address (adr, sizeof (adr), alias->addr);
+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
+ snprintf (dest, destlen, tmp, adr);
+ break;
+ case 'n':
+ snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
+ snprintf (dest, destlen, tmp, alias->num + 1);
+ break;
+ case 't':
+ dest[0] = alias->tagged ? '*' : ' ';
+ dest[1] = 0;
+ break;
+ }
+
+ return (src);
+}
+
+int alias_search (MUTTMENU *m, regex_t *re, int n)
+{
+ int i, match = 0;
+ char s[LONG_STRING];
+ int slen = sizeof(s);
+
+ for (i=0; i < m->max; i++)
+ {
+ mutt_FormatString (s, slen, NONULL (AliasFmt), alias_format_str,
+ (unsigned long) ((ALIAS **) m->data)[n], 0);
+ if ((match = regexec (re, s, 0, NULL, 0))) return match;
+ }
+ return (0);
+}
+
+
+void alias_entry (char *s, size_t slen, MUTTMENU *m, int num)
+{
+ mutt_FormatString (s, slen, NONULL (AliasFmt), alias_format_str, (unsigned long) ((ALIAS **) m->data)[num], 0);
+}
+
+int alias_tag (MUTTMENU *menu, int n)
+{
+ return (((ALIAS **) menu->data)[n]->tagged = !((ALIAS **) menu->data)[n]->tagged);
+}
+
+static int alias_SortAlias (const void *a, const void *b)
+{
+ ALIAS *pa = *(ALIAS **) a;
+ ALIAS *pb = *(ALIAS **) b;
+ int r = strcasecmp (pa->name, pb->name);
+
+ return (RSORT (r));
+}
+
+static int alias_SortAddress (const void *a, const void *b)
+{
+ ADDRESS *pa = (*(ALIAS **) a)->addr;
+ ADDRESS *pb = (*(ALIAS **) b)->addr;
+ int r;
+
+ if (pa->personal)
+ {
+ if (pb->personal)
+ r = strcasecmp (pa->personal, pb->personal);
+ else
+ r = 1;
+ }
+ else if (pb->personal)
+ r = -1;
+ else
+ r = strcasecmp (pa->mailbox, pb->mailbox);
+ return (RSORT (r));
+}
+
+void mutt_alias_menu (char *buf, size_t buflen, ALIAS *aliases)
+{
+ ALIAS *aliasp;
+ MUTTMENU *menu;
+ ALIAS **AliasTable = NULL;
+ int t = -1;
+ int i, done = 0;
+ char helpstr[SHORT_STRING];
+
+ if (!aliases)
+ {
+ mutt_error ("You have no aliases!");
+ return;
+ }
+
+ /* tell whoever called me to redraw the screen when I return */
+ set_option (OPTNEEDREDRAW);
+
+ menu = mutt_new_menu ();
+ menu->make_entry = alias_entry;
+ menu->search = alias_search;
+ menu->tag = alias_tag;
+ menu->menu = MENU_ALIAS;
+ menu->title = "Aliases";
+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_ALIAS, AliasHelp);
+
+ /* count the number of aliases */
+ for (aliasp = aliases; aliasp; aliasp = aliasp->next)
+ menu->max++;
+
+ menu->data = AliasTable = (ALIAS **) safe_calloc (menu->max, sizeof (ALIAS *));
+
+ for (i = 0, aliasp = aliases; aliasp; aliasp = aliasp->next, i++)
+ AliasTable[i] = aliasp;
+
+ if ((SortAlias & SORT_MASK) != SORT_ORDER)
+ {
+ qsort (AliasTable, i, sizeof (ALIAS *),
+ (SortAlias & SORT_MASK) == SORT_ADDRESS ? alias_SortAddress : alias_SortAlias);
+ }
+
+ for (i=0; i<menu->max; i++) AliasTable[i]->num = i;
+
+ while (!done)
+ {
+ switch (mutt_menuLoop (menu))
+ {
+ case OP_GENERIC_SELECT_ENTRY:
+ t = menu->current;
+ case OP_EXIT:
+ done = 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < menu->max; i++)
+ {
+ if (AliasTable[i]->tagged)
+ {
+ rfc822_write_address (buf, buflen, AliasTable[i]->addr);
+ t = -1;
+ }
+ }
+
+ if(t != -1)
+ rfc822_write_address (buf, buflen, AliasTable[t]->addr);
+
+ mutt_menuDestroy (&menu);
+ safe_free ((void **) &AliasTable);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_regex.h"
+
+#include <pwd.h>
+#include <string.h>
+
+static ADDRESS *lookup_alias (const char *s)
+{
+ ALIAS *t = Aliases;
+
+ for (; t; t = t->next)
+ if (!strcasecmp (s, t->name))
+ return (t->addr);
+ return (NULL); /* no such alias */
+}
+
+static ADDRESS *mutt_expand_aliases_r (ADDRESS *a, LIST **expn)
+{
+ ADDRESS *head = NULL, *last = NULL, *t, *w;
+ LIST *u;
+ char i;
+
+ while (a)
+ {
+ if (!a->group && !a->personal && a->mailbox && strchr (a->mailbox, '@') == NULL)
+ {
+ t = lookup_alias (a->mailbox);
+
+ if (t)
+ {
+ i = 0;
+ for (u = *expn; u; u = u->next)
+ {
+ if (strcmp (a->mailbox, u->data) == 0) /* alias already found */
+ {
+ dprint (1, (debugfile, "mutt_expand_aliases_r(): loop in alias found for '%s'\n", a->mailbox));
+ i = 1;
+ break;
+ }
+ }
+
+ if (!i)
+ {
+ u = safe_malloc (sizeof (LIST));
+ u->data = safe_strdup (a->mailbox);
+ u->next = *expn;
+ *expn = u;
+ w = rfc822_cpy_adr (t);
+ w = mutt_expand_aliases_r (w, expn);
+ if (head)
+ last->next = w;
+ else
+ head = last = w;
+ while (last && last->next)
+ last = last->next;
+ }
+ t = a;
+ a = a->next;
+ t->next = NULL;
+ rfc822_free_address (&t);
+ continue;
+ }
+ else
+ {
+ struct passwd *pw = getpwnam (a->mailbox);
+ char buffer[256], *p;
+
+ if (pw)
+ {
+ strfcpy (buffer, pw->pw_gecos, sizeof (buffer));
+ if ((p = strchr (buffer, ',')))
+ *p = 0;
+ a->personal = safe_strdup (buffer);
+#ifdef EXACT_ADDRESS
+ free (a->val);
+ a->val = NULL;
+#endif
+ }
+ }
+ }
+
+ if (head)
+ {
+ last->next = a;
+ last = last->next;
+ }
+ else
+ head = last = a;
+ a = a->next;
+ last->next = NULL;
+ }
+
+ if (option (OPTUSEDOMAIN) && Fqdn[0] != '@')
+ {
+ /* now qualify all local addresses */
+ rfc822_qualify (head, Fqdn);
+ }
+
+ return (head);
+}
+
+ADDRESS *mutt_expand_aliases (ADDRESS *a)
+{
+ ADDRESS *t;
+ LIST *expn = NULL; /* previously expanded aliases to avoid loops */
+
+ t = mutt_expand_aliases_r (a, &expn);
+ mutt_free_list (&expn);
+ return (t);
+}
+
+/* if someone has an address like
+ * From: Michael `/bin/rm -f ~` Elkins <me@cs.hmc.edu>
+ * and the user creates an alias for this, Mutt could wind up executing
+ * the backtics because it writes aliases like
+ * alias me Michael `/bin/rm -f ~` Elkins <me@cs.hmc.edu>
+ * To avoid this problem, use a backslash (\) to quote any backtics. We also
+ * need to quote backslashes as well, since you could defeat the above by
+ * doing
+ * From: Michael \`/bin/rm -f ~\` Elkins <me@cs.hmc.edu>
+ * since that would get aliased as
+ * alias me Michael \\`/bin/rm -f ~\\` Elkins <me@cs.hmc.edu>
+ * which still gets evaluated because the double backslash is not a quote.
+ */
+static void write_safe_address (FILE *fp, char *s)
+{
+ while (*s)
+ {
+ if (*s == '\\' || *s == '`')
+ fputc ('\\', fp);
+ fputc (*s, fp);
+ s++;
+ }
+}
+
+void mutt_create_alias (ENVELOPE *cur, ADDRESS *iadr)
+{
+ ALIAS *new, *t;
+ char buf[LONG_STRING], prompt[SHORT_STRING], *pc;
+ FILE *rc;
+ ADDRESS *adr = NULL;
+
+ if (cur)
+ {
+ if (mutt_addr_is_user (cur->from))
+ {
+ if (cur->to && !mutt_is_mail_list (cur->to))
+ adr = cur->to;
+ else
+ adr = cur->cc;
+ }
+ else if (cur->reply_to && !mutt_is_mail_list (cur->reply_to))
+ adr = cur->reply_to;
+ else
+ adr = cur->from;
+
+ }
+ else if (iadr)
+ {
+ adr = iadr;
+ }
+
+ if (adr && adr->mailbox)
+ {
+ strfcpy (buf, adr->mailbox, sizeof (buf));
+ if ((pc = strchr (buf, '@')))
+ *pc = 0;
+ }
+ else
+ buf[0] = '\0';
+
+ if (mutt_get_field ("Alias as: ", buf, sizeof (buf), 0) != 0 || !buf[0])
+ return;
+
+ /* check to see if the user already has an alias defined */
+ if (lookup_alias (buf))
+ {
+ mutt_error ("You already have an alias defined with that name!");
+ return;
+ }
+
+ new = safe_calloc (1, sizeof (ALIAS));
+ new->name = safe_strdup (buf);
+
+ if (adr)
+ strfcpy (buf, adr->mailbox, sizeof (buf));
+ else
+ buf[0] = 0;
+
+ if (mutt_get_field ("Address: ", buf, sizeof (buf), 0) != 0 || !buf[0])
+ {
+ mutt_free_alias (&new);
+ return;
+ }
+ new->addr = rfc822_parse_adrlist (new->addr, buf);
+
+ if (adr && adr->personal && !mutt_is_mail_list (adr))
+ strfcpy (buf, adr->personal, sizeof (buf));
+ else
+ buf[0] = 0;
+
+ if (mutt_get_field ("Personal name: ", buf, sizeof (buf), 0) != 0)
+ {
+ mutt_free_alias (&new);
+ return;
+ }
+ new->addr->personal = safe_strdup (buf);
+
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), new->addr);
+ snprintf (prompt, sizeof (prompt), "[%s = %s] Accept?", new->name, buf);
+ if (mutt_yesorno (prompt, 1) != 1)
+ {
+ mutt_free_alias (&new);
+ return;
+ }
+
+ if ((t = Aliases))
+ {
+ while (t->next)
+ t = t->next;
+ t->next = new;
+ }
+ else
+ Aliases = new;
+
+ strfcpy (buf, NONULL (AliasFile), sizeof (buf));
+ if (mutt_get_field ("Save to file: ", buf, sizeof (buf), M_FILE) != 0)
+ return;
+ mutt_expand_path (buf, sizeof (buf));
+ if ((rc = fopen (buf, "a")))
+ {
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), new->addr);
+ fprintf (rc, "alias %s ", new->name);
+ write_safe_address (rc, buf);
+ fputc ('\n', rc);
+ fclose (rc);
+ mutt_message ("Alias added.");
+ }
+ else
+ mutt_perror (buf);
+}
+
+/*
+ * This routine looks to see if the user has an alias defined for the given
+ * address.
+ */
+ADDRESS *alias_reverse_lookup (ADDRESS *a)
+{
+ ALIAS *t = Aliases;
+ ADDRESS *ap;
+
+ if (!a || !a->mailbox)
+ return NULL;
+
+ for (; t; t = t->next)
+ {
+ /* cycle through all addresses if this is a group alias */
+ for (ap = t->addr; ap; ap = ap->next)
+ {
+ if (!ap->group && ap->mailbox &&
+ strcasecmp (ap->mailbox, a->mailbox) == 0)
+ return ap;
+ }
+ }
+ return 0;
+}
+
+/* alias_complete() -- alias completion routine
+ *
+ * given a partial alias, this routine attempts to fill in the alias
+ * from the alias list as much as possible
+ */
+int mutt_alias_complete (char *s, size_t buflen)
+{
+ ALIAS *a = Aliases;
+ ALIAS *a_list = NULL, *a_cur = NULL;
+ char bestname[STRING];
+ int i;
+
+ memset (bestname, 0, sizeof (bestname));
+
+#define min(a,b) ((a<b)?a:b)
+
+ while (a)
+ {
+ if (a->name && strstr (a->name, s) == a->name)
+ {
+ if (!bestname[0]) /* init */
+ strfcpy (bestname, a->name, min (strlen (a->name) + 1, sizeof (bestname)));
+ else
+ {
+ for (i = 0 ; a->name[i] && a->name[i] == bestname[i] ; i++)
+ ;
+ bestname[i] = 0;
+ }
+ }
+ a = a->next;
+ }
+
+ if ((bestname[0] == 0) || /* if we didn't find anything */
+ (s[0] == 0)) /* or we weren't given anything */
+ {
+ mutt_alias_menu (s, buflen, Aliases);
+ return 0;
+ }
+ else
+ {
+ if (strcmp (bestname, s) == 0) /* add anything to the completion? */
+ {
+ /* build alias list and show it */
+ a = Aliases;
+ while (a)
+ {
+ if (a->name && (strstr (a->name, s) == a->name))
+ {
+ if (!a_list) /* init */
+ a_cur = a_list = (ALIAS *) safe_malloc (sizeof (ALIAS));
+ else
+ {
+ a_cur->next = (ALIAS *) safe_malloc (sizeof (ALIAS));
+ a_cur = a_cur->next;
+ }
+ memcpy (a_cur, a, sizeof (ALIAS));
+ a_cur->next = NULL;
+ }
+ a = a->next;
+ }
+
+ s[0] = 0; /* reset string before passing to alias_menu */
+ mutt_alias_menu (s, buflen, a_list);
+
+ /* free the alias list */
+ while (a_list)
+ {
+ a_cur = a_list;
+ a_list = a_list->next;
+ safe_free ((void **) &a_cur);
+ }
+
+ return 0;
+ }
+ else /* we are adding something to the completion */
+ strfcpy (s, bestname, strlen (bestname) + 1);
+ }
+
+ return 1;
+}
+
+/* returns TRUE if the given address belongs to the user. */
+int mutt_addr_is_user (ADDRESS *addr)
+{
+ char buf[LONG_STRING];
+
+ /* NULL address is assumed to be the user. */
+ if (!addr)
+ return 1;
+ if (!addr->mailbox)
+ return 0;
+ if (strcasecmp (addr->mailbox, Username) == 0)
+ return 1;
+ snprintf (buf, sizeof (buf), "%s@%s", Username, Hostname);
+ if (strcasecmp (addr->mailbox, buf) == 0)
+ return 1;
+ snprintf (buf, sizeof (buf), "%s@%s", Username, Fqdn);
+ if (strcasecmp (addr->mailbox, buf) == 0)
+ return 1;
+
+ if (Alternates.pattern &&
+ regexec (Alternates.rx, addr->mailbox, 0, NULL, 0) == 0)
+ return 1;
+ return 0;
+}
+
+/* returns 1 if the list of address contains a known mailing list */
+int mutt_is_list_recipient (ADDRESS *a)
+{
+ for (; a; a = a->next)
+ if (mutt_is_mail_list (a))
+ return 1;
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_menu.h"
+#include "mutt_curses.h"
+#include "keymap.h"
+#include "rfc1524.h"
+#include "mime.h"
+#include "pager.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+
+/* return 1 if require full screen redraw, 0 otherwise */
+int mutt_compose_attachment (BODY *a)
+{
+ char type[STRING];
+ char command[STRING];
+ rfc1524_entry *entry = rfc1524_new_entry ();
+
+ snprintf (type, sizeof (type), "%s/%s", TYPE (a->type), a->subtype);
+ if (rfc1524_mailcap_lookup (a, type, entry, M_COMPOSE))
+ {
+ if (entry->composecommand || entry->composetypecommand)
+ {
+ char newfile[_POSIX_PATH_MAX] = "";
+
+ if (entry->composetypecommand)
+ strfcpy (command, entry->composetypecommand, sizeof (command));
+ else
+ strfcpy (command, entry->composecommand, sizeof (command));
+ if (rfc1524_expand_filename (entry->nametemplate,
+ a->filename, newfile, sizeof (newfile)))
+ {
+ dprint(1, (debugfile, "oldfile: %s\t newfile: %s\n",
+ a->filename, newfile));
+ if (!mutt_rename_file (a->filename, newfile))
+ {
+ if (!mutt_yesorno ("Can't match nametemplate, continue?", 1))
+ return 0;
+ }
+ else
+ {
+ safe_free ((void **) &a->filename);
+ a->filename = safe_strdup (newfile);
+ }
+ }
+
+ if (rfc1524_expand_command (a, a->filename, type,
+ command, sizeof (command)))
+ {
+ /* For now, editing requires a file, no piping */
+ mutt_error ("Mailcap compose entry requires %%s");
+ }
+ else
+ {
+ endwin ();
+ mutt_system (command);
+ if (entry->composetypecommand)
+ {
+ BODY *b;
+ FILE *fp, *tfp;
+ char tempfile[_POSIX_PATH_MAX];
+
+ if ((fp = safe_fopen (a->filename, "r")) == NULL)
+ {
+ mutt_perror ("Failure to open file to parse headers.");
+ return 0;
+ }
+
+ b = mutt_read_mime_header (fp, 0);
+ if (b)
+ {
+ if (b->parameter)
+ {
+ mutt_free_parameter (&a->parameter);
+ a->parameter = b->parameter;
+ b->parameter = NULL;
+ }
+ if (b->description) {
+ safe_free ((void **) &a->description);
+ a->description = b->description;
+ b->description = NULL;
+ }
+ if (b->form_name)
+ {
+ safe_free ((void **) &a->form_name);
+ a->form_name = b->form_name;
+ b->form_name = NULL;
+ }
+
+ /* Remove headers by copying out data to another file, then
+ * copying the file back */
+ fseek (fp, b->offset, 0);
+ mutt_mktemp (tempfile);
+ if ((tfp = safe_fopen (tempfile, "w")) == NULL)
+ {
+ mutt_perror ("Failure to open file to strip headers.");
+ return 0;
+ }
+ mutt_copy_stream (fp, tfp);
+ fclose (fp);
+ fclose (tfp);
+ mutt_unlink (a->filename);
+ mutt_rename_file (tempfile, a->filename);
+
+ mutt_free_body (&b);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ rfc1524_free_entry (&entry);
+ mutt_message ("No mailcap compose entry for %s, creating empty file.",type);
+ return 1;
+ }
+
+ rfc1524_free_entry (&entry);
+ return 1;
+}
+
+/*
+ * Currently, this only works for send mode, as it assumes that the
+ * BODY->filename actually contains the information. I'm not sure
+ * we want to deal with editing attachments we've already received,
+ * so this should be ok.
+ *
+ * Returns 1 if editor found, 0 if not (useful to tell calling menu to
+ * redraw)
+ */
+int mutt_edit_attachment (BODY *a, int opt)
+{
+ char type[STRING];
+ char command[STRING];
+ rfc1524_entry *entry = rfc1524_new_entry ();
+
+ snprintf (type, sizeof (type), "%s/%s", TYPE (a->type), a->subtype);
+ if (rfc1524_mailcap_lookup (a, type, entry, M_EDIT))
+ {
+ if (entry->editcommand)
+ {
+ char newfile[_POSIX_PATH_MAX] = "";
+
+ strfcpy (command, entry->editcommand, sizeof (command));
+ if (rfc1524_expand_filename (entry->nametemplate,
+ a->filename, newfile, sizeof (newfile)))
+ {
+ dprint(1, (debugfile, "oldfile: %s\t newfile: %s\n",
+ a->filename, newfile));
+ if (mutt_rename_file (a->filename, newfile))
+ {
+ if (!mutt_yesorno ("Can't match nametemplate, continue?", 1))
+ return 0;
+ }
+ else
+ {
+ safe_free ((void **) &a->filename);
+ a->filename = safe_strdup (newfile);
+ }
+ }
+
+ if (rfc1524_expand_command (a, a->filename, type,
+ command, sizeof (command)))
+ {
+ /* For now, editing requires a file, no piping */
+ mutt_error ("Mailcap Edit entry requires %%s");
+ }
+ else
+ {
+ endwin ();
+ mutt_system (command);
+ }
+ }
+ }
+ else if (a->type == TYPETEXT)
+ {
+ /* On text, default to editor */
+ mutt_edit_file (strcmp ("builtin", Editor) == 0 ? Visual : Editor,
+ a->filename);
+ }
+ else
+ {
+ rfc1524_free_entry (&entry);
+ mutt_error ("No mailcap edit entry for %s",type);
+ return 0;
+ }
+
+ rfc1524_free_entry (&entry);
+ return 1;
+}
+
+int mutt_is_autoview (char *type)
+{
+ LIST *t = AutoViewList;
+ int i;
+
+ while (t)
+ {
+ i = strlen (t->data) - 1;
+ if ((i > 0 && t->data[i-1] == '/' && t->data[i] == '*' &&
+ strncasecmp (type, t->data, i) == 0) ||
+ strcasecmp (type, t->data) == 0)
+ return 1;
+ t = t->next;
+ }
+
+ return 0;
+}
+
+/* returns -1 on error, 0 or the return code from mutt_do_pager() on success */
+int mutt_view_attachment (FILE *fp, BODY *a, int flag)
+{
+ char tempfile[_POSIX_PATH_MAX] = "";
+ char pagerfile[_POSIX_PATH_MAX] = "";
+ int is_message;
+ int use_mailcap;
+ int use_pipe = 0;
+ int use_pager = 1;
+ char type[STRING];
+ char command[STRING];
+ char descrip[STRING];
+ rfc1524_entry *entry = NULL;
+ int rc = -1;
+
+ is_message = (a->type == TYPEMESSAGE && a->subtype &&
+ (!strcasecmp (a->subtype,"rfc822") ||
+ !strcasecmp (a->subtype, "news")));
+ use_mailcap = (flag == M_MAILCAP ||
+ (flag == M_REGULAR && mutt_needs_mailcap (a)));
+ snprintf (type, sizeof (type), "%s/%s", TYPE (a->type), a->subtype);
+
+ if (use_mailcap)
+ {
+ entry = rfc1524_new_entry ();
+ if (!rfc1524_mailcap_lookup (a, type, entry, 0))
+ {
+ if (flag == M_REGULAR)
+ {
+ /* fallback to view as text */
+ rfc1524_free_entry (&entry);
+ mutt_error ("No matching mailcap entry found. Viewing as text.");
+ flag = M_AS_TEXT;
+ use_mailcap = 0;
+ }
+ else
+ goto return_error;
+ }
+ }
+
+ if (use_mailcap)
+ {
+ if (!entry->command)
+ {
+ mutt_error ("MIME type not defined. Cannot view attachment.");
+ goto return_error;
+ }
+ strfcpy (command, entry->command, sizeof (command));
+
+ if (rfc1524_expand_filename (entry->nametemplate, a->filename,
+ tempfile, sizeof (tempfile)))
+ {
+ if (fp == NULL)
+ {
+ /* send case: the file is already there */
+ if (mutt_rename_file (a->filename, tempfile))
+ {
+ if (mutt_yesorno ("Can't match nametemplate, continue?", 1) == M_YES)
+ strfcpy (tempfile, a->filename, sizeof (tempfile));
+ else
+ goto return_error;
+ }
+ else
+ {
+ safe_free ((void **) &a->filename);
+ a->filename = safe_strdup (tempfile);
+ }
+ }
+ }
+ else if (fp == NULL) /* send case */
+ strfcpy (tempfile, a->filename, sizeof (tempfile));
+
+ if (fp)
+ {
+ /* recv case: we need to save the attachment to a file */
+ if (mutt_save_attachment (fp, a, tempfile, 0) == -1)
+ goto return_error;
+ }
+
+ use_pipe = rfc1524_expand_command (a, tempfile, type,
+ command, sizeof (command));
+ use_pager = entry->copiousoutput;
+ }
+
+ if (use_pager)
+ {
+ if (fp && !use_mailcap && a->filename)
+ {
+ /* recv case */
+ strfcpy (pagerfile, a->filename, sizeof (pagerfile));
+ mutt_adv_mktemp (pagerfile);
+ }
+ else
+ mutt_mktemp (pagerfile);
+ }
+
+ if (use_mailcap)
+ {
+ pid_t thepid = 0;
+ FILE *pagerfp = NULL;
+ FILE *tempfp = NULL;
+ FILE *filter_in;
+ FILE *filter_out;
+
+ if (!use_pager)
+ endwin ();
+
+ if (use_pager || use_pipe)
+ {
+ if (use_pager && ((pagerfp = safe_fopen (pagerfile, "w")) == NULL))
+ {
+ mutt_perror ("fopen");
+ goto return_error;
+ }
+ if (use_pipe && ((tempfp = fopen (tempfile, "r")) == NULL))
+ {
+ if (pagerfp)
+ fclose (pagerfp);
+ mutt_perror ("fopen");
+ goto return_error;
+ }
+
+ if ((thepid = mutt_create_filter (command, use_pipe ? &filter_in : NULL,
+ use_pager ? &filter_out : NULL, NULL)) == -1)
+ {
+ if (pagerfp)
+ fclose (pagerfp);
+ if (tempfp)
+ fclose (tempfp);
+ mutt_error ("Cannot create filter");
+ goto return_error;
+ }
+
+ if (use_pipe)
+ {
+ mutt_copy_stream (tempfp, filter_in);
+ fclose (tempfp);
+ fclose (filter_in);
+ }
+ if (use_pager)
+ {
+ mutt_copy_stream (filter_out, pagerfp);
+ fclose (filter_out);
+ fclose (pagerfp);
+ if (a->description)
+ snprintf (descrip, sizeof (descrip),
+ "---Command: %-20.20s Description: %s",
+ command, a->description);
+ else
+ snprintf (descrip, sizeof (descrip),
+ "---Command: %-30.30s Attachment: %s", command, type);
+ }
+
+ if ((mutt_wait_filter (thepid) || (entry->needsterminal &&
+ option (OPTWAITKEY))) && !use_pager)
+ mutt_any_key_to_continue (NULL);
+ }
+ else
+ {
+ /* interactive command */
+ if (mutt_system (command) ||
+ (entry->needsterminal && option (OPTWAITKEY)))
+ mutt_any_key_to_continue (NULL);
+ }
+ }
+ else
+ {
+ /* Don't use mailcap; the attachment is viewed in the pager */
+
+ if (flag == M_AS_TEXT)
+ {
+ /* just let me see the raw data */
+ if (mutt_save_attachment (fp, a, pagerfile, 0))
+ goto return_error;
+ }
+ else
+ {
+ /* Use built-in handler */
+ set_option (OPTVIEWATTACH); /* disable the "use 'v' to view this part"
+ * message in case of error */
+ if (mutt_decode_save_attachment (fp, a, pagerfile, 1, 0))
+ {
+ unset_option (OPTVIEWATTACH);
+ goto return_error;
+ }
+ unset_option (OPTVIEWATTACH);
+ }
+
+ if (a->description)
+ strfcpy (descrip, a->description, sizeof (descrip));
+ else
+ snprintf (descrip, sizeof (descrip), "---Attachment: %s", type);
+ }
+
+ /* We only reach this point if there have been no errors */
+
+ if (use_pager)
+ {
+ pager_t info;
+
+ memset (&info, 0, sizeof (info));
+ info.fp = fp;
+ info.bdy = a;
+ info.ctx = Context;
+ rc = mutt_do_pager (descrip, pagerfile, is_message, &info);
+ }
+ else
+ rc = 0;
+
+ return_error:
+
+ if (entry)
+ rfc1524_free_entry (&entry);
+ if (fp && tempfile[0])
+ mutt_unlink (tempfile);
+ if (pagerfile[0])
+ mutt_unlink (pagerfile);
+
+ return rc;
+}
+
+/* returns 1 on success, 0 on error */
+int mutt_pipe_attachment (FILE *fp, BODY *b, const char *path, char *outfile)
+{
+ STATE o;
+ pid_t thepid;
+
+ memset (&o, 0, sizeof (STATE));
+
+ if (outfile && *outfile)
+ if ((o.fpout = safe_fopen (outfile, "w")) == NULL)
+ {
+ mutt_perror ("fopen");
+ return 0;
+ }
+
+ endwin ();
+
+ if (fp)
+ {
+ /* recv case */
+
+ STATE s;
+
+ memset (&s, 0, sizeof (STATE));
+
+ if (outfile && *outfile)
+ thepid = mutt_create_filter (path, &s.fpout, &o.fpin, NULL);
+ else
+ thepid = mutt_create_filter (path, &s.fpout, NULL, NULL);
+
+ s.fpin = fp;
+ mutt_decode_attachment (b, &s);
+ fclose (s.fpout);
+ }
+ else
+ {
+ /* send case */
+
+ FILE *ifp, *ofp;
+
+ if ((ifp = fopen (b->filename, "r")) == NULL)
+ {
+ mutt_perror ("fopen");
+ fclose (o.fpout);
+ return 0;
+ }
+
+ if (outfile && *outfile)
+ thepid = mutt_create_filter (path, &ofp, &o.fpin, NULL);
+ else
+ thepid = mutt_create_filter (path, &ofp, NULL, NULL);
+
+ mutt_copy_stream (ifp, ofp);
+ fclose (ofp);
+ fclose (ifp);
+ }
+
+ if (outfile && *outfile)
+ {
+ mutt_copy_stream (o.fpin, o.fpout);
+ fclose (o.fpin);
+ fclose (o.fpout);
+ }
+
+ if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY))
+ mutt_any_key_to_continue (NULL);
+ return 1;
+}
+
+/* returns 0 on success, -1 on error */
+int mutt_save_attachment (FILE *fp, BODY *m, char *path, int flags)
+{
+ if (fp)
+ {
+ /* In recv mode, extract from folder and decode */
+
+ STATE s;
+
+ memset (&s, 0, sizeof (s));
+ if (flags == M_SAVE_APPEND)
+ s.fpout = safe_fopen (path, "a");
+ else
+ s.fpout = fopen (path, "w");
+ if (s.fpout == NULL)
+ {
+ mutt_perror ("fopen");
+ return (-1);
+ }
+ fseek ((s.fpin = fp), m->offset, 0);
+ mutt_decode_attachment (m, &s);
+
+ if (fclose (s.fpout) != 0)
+ {
+ mutt_perror ("fclose");
+ return (-1);
+ }
+ }
+ else
+ {
+ /* In send mode, just copy file */
+
+ FILE *ofp, *nfp;
+
+ if ((ofp = fopen (m->filename, "r")) == NULL)
+ {
+ mutt_perror ("fopen");
+ return (-1);
+ }
+
+ if ((nfp = safe_fopen (path, "w")) == NULL)
+ {
+ mutt_perror ("fopen");
+ fclose (ofp);
+ return (-1);
+ }
+
+ if (mutt_copy_stream (ofp, nfp) == -1)
+ {
+ mutt_error ("Write fault!");
+ fclose (ofp);
+ fclose (nfp);
+ return (-1);
+ }
+ fclose (ofp);
+ fclose (nfp);
+ }
+
+ return 0;
+}
+
+/* returns 0 on success, -1 on error */
+int mutt_decode_save_attachment (FILE *fp, BODY *m, char *path,
+ int displaying, int flags)
+{
+ STATE s;
+ unsigned int saved_encoding = 0;
+
+ memset (&s, 0, sizeof (s));
+ s.flags = displaying ? M_DISPLAY : 0;
+
+ if (flags == M_SAVE_APPEND)
+ s.fpout = safe_fopen (path, "a");
+ else
+ s.fpout = fopen (path, "w");
+ if (s.fpout == NULL)
+ {
+ perror ("fopen");
+ return (-1);
+ }
+
+ if (fp == NULL)
+ {
+ /* When called from the compose menu, the attachment isn't parsed,
+ * so we need to do it here. */
+ struct stat st;
+
+ if (stat (m->filename, &st) == -1)
+ {
+ perror ("stat");
+ fclose (s.fpout);
+ return (-1);
+ }
+
+ if ((s.fpin = fopen (m->filename, "r")) == NULL)
+ {
+ perror ("fopen");
+ return (-1);
+ }
+
+ saved_encoding = m->encoding;
+ m->length = st.st_size;
+ m->encoding = ENC8BIT;
+ m->offset = 0;
+ if (m->type == TYPEMESSAGE && m->subtype &&
+ (!strcasecmp (m->subtype,"rfc822") ||
+ !strcasecmp (m->subtype, "news")))
+ m->parts = mutt_parse_messageRFC822 (s.fpin, m);
+ }
+ else
+ s.fpin = fp;
+
+ mutt_body_handler (m, &s);
+
+ fclose (s.fpout);
+ if (fp == NULL)
+ {
+ m->length = 0;
+ m->encoding = saved_encoding;
+ if (m->parts)
+ mutt_free_body (&m->parts);
+ fclose (s.fpin);
+ }
+
+ return (0);
+}
+
+/* Ok, the difference between send and receive:
+ * recv: BODY->filename is a suggested name, and Context|HEADER points
+ * to the attachment in mailbox which is encooded
+ * send: BODY->filename points to the un-encoded file which contains the
+ * attachment
+ */
+
+int mutt_print_attachment (FILE *fp, BODY *a)
+{
+ char newfile[_POSIX_PATH_MAX] = "";
+ char type[STRING];
+ pid_t thepid;
+ FILE *ifp, *fpout;
+
+ snprintf (type, sizeof (type), "%s/%s", TYPE (a->type), a->subtype);
+
+ if (rfc1524_mailcap_lookup (a, type, NULL, M_PRINT))
+ {
+ char command[_POSIX_PATH_MAX+STRING];
+ rfc1524_entry *entry;
+ int piped = FALSE;
+
+ entry = rfc1524_new_entry ();
+ rfc1524_mailcap_lookup (a, type, entry, M_PRINT);
+ if (rfc1524_expand_filename (entry->nametemplate, a->filename,
+ newfile, sizeof (newfile)))
+ {
+ if (!fp)
+ {
+ /* only attempt file move in send mode */
+
+ if (mutt_rename_file (a->filename, newfile))
+ {
+ if (mutt_yesorno ("Can't match nametemplate, continue?", 1) != M_YES)
+ {
+ rfc1524_free_entry (&entry);
+ return 0;
+ }
+ strfcpy (newfile, a->filename, sizeof (newfile));
+ }
+ else
+ {
+ safe_free ((void **)&a->filename);
+ a->filename = safe_strdup (newfile);
+ }
+ }
+ }
+
+ /* in recv mode, save file to newfile first */
+ if (fp)
+ mutt_save_attachment (fp, a, newfile, 0);
+
+ strfcpy (command, entry->printcommand, sizeof (command));
+ piped = rfc1524_expand_command (a, newfile, type, command, sizeof (command));
+
+ endwin ();
+
+ /* interactive program */
+ if (piped)
+ {
+ if ((ifp = fopen (newfile, "r")) == NULL)
+ {
+ mutt_perror ("fopen");
+ rfc1524_free_entry (&entry);
+ return (0);
+ }
+
+ thepid = mutt_create_filter (command, &fpout, NULL, NULL);
+ mutt_copy_stream (ifp, fpout);
+ fclose (fpout);
+ fclose (ifp);
+ if (mutt_wait_filter (thepid) || option (OPTWAITKEY))
+ mutt_any_key_to_continue (NULL);
+ }
+ else
+ {
+ if (mutt_system (command) || option (OPTWAITKEY))
+ mutt_any_key_to_continue (NULL);
+ }
+
+ if (fp)
+ mutt_unlink (newfile);
+
+ rfc1524_free_entry (&entry);
+ return (1);
+ }
+
+ if (!strcasecmp ("text/plain", a->subtype) ||
+ !strcasecmp ("application/postscript", a->subtype))
+ return (mutt_pipe_attachment (fp, a, PrintCmd, NULL));
+ else if (mutt_can_decode (a))
+ {
+ /* decode and print */
+
+ int rc = 0;
+
+ mutt_mktemp (newfile);
+ if (mutt_decode_save_attachment (fp, a, newfile, 0, 0) == 0)
+ {
+ if ((ifp = fopen (newfile, "r")) != NULL)
+ {
+ endwin ();
+ thepid = mutt_create_filter (PrintCmd, &fpout, NULL, NULL);
+ mutt_copy_stream (ifp, fpout);
+ fclose (ifp);
+ fclose (fpout);
+ if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY))
+ mutt_any_key_to_continue (NULL);
+ rc = 1;
+ }
+ }
+ mutt_unlink (newfile);
+ return rc;
+ }
+ else
+ {
+ mutt_error ("I don't know how to print that!");
+ return 0;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* common protos for compose / attach menus */
+
+int mutt_tag_attach (MUTTMENU *menu, int n);
+
+void mutt_save_attachment_list (FILE *fp, int tag, BODY *top);
+void mutt_pipe_attachment_list (FILE *fp, int tag, BODY *top, int filter);
+void mutt_print_attachment_list (FILE *fp, int tag, BODY *top);
+void mutt_attach_display_loop (MUTTMENU *menu, int op, FILE *fp, ATTACHPTR **idx);
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "keymap.h"
+#include "mapping.h"
+
+#include <string.h>
+
+static struct mapping_t Menus[] = {
+ { "alias", MENU_ALIAS },
+ { "attach", MENU_ATTACH },
+ { "browser", MENU_FOLDER },
+ { "compose", MENU_COMPOSE },
+ { "editor", MENU_EDITOR },
+ { "generic", MENU_GENERIC },
+ { "index", MENU_MAIN },
+ { "pager", MENU_PAGER },
+
+
+
+#ifdef _PGPPATH
+ { "pgp", MENU_PGP },
+#endif /* _PGPPATH */
+
+
+
+ { NULL, 0 }
+};
+
+#define mutt_check_menu(s) mutt_getvaluebyname(s, Menus)
+
+/* expects to see: <menu-string> <key-string> */
+static const char *parse_keymap (int *menu,
+ char *key,
+ size_t keylen,
+ const char *s,
+ char *err,
+ size_t errlen)
+{
+ char buf[SHORT_STRING];
+ char expn[SHORT_STRING];
+
+ /* menu name */
+ s = mutt_extract_token (buf, sizeof (buf), s, expn, sizeof (expn), 0);
+
+ if (s)
+ {
+ if ((*menu = mutt_check_menu (buf)) == -1)
+ {
+ snprintf (err, errlen, "%s: no such menu", s);
+ return (NULL);
+ }
+
+ /* key sequence */
+ s = mutt_extract_token (key, keylen, s, expn, sizeof (expn), 0);
+
+ if (s)
+ return s;
+ }
+
+ strfcpy (err, "too few arguments", errlen);
+ return (NULL);
+}
+
+static int
+try_bind (char *key, int menu, char *func, struct binding_t *bindings)
+{
+ int i;
+
+ for (i = 0; bindings[i].name; i++)
+ if (strcmp (func, bindings[i].name) == 0)
+ {
+ km_bindkey (key, menu, bindings[i].op, NULL);
+ return (0);
+ }
+ return (-1);
+}
+
+/* bind menu-name '<key_sequence>' function-name */
+int mutt_parse_bind (const char *s, unsigned long data, char *err, size_t errlen)
+{
+ struct binding_t *bindings = NULL;
+ char buf[SHORT_STRING];
+ char key[SHORT_STRING];
+ char expn[SHORT_STRING];
+ int menu;
+
+ if ((s = parse_keymap (&menu, key, sizeof (key), s, err, errlen)) == NULL)
+ return (-1);
+
+ switch (menu)
+ {
+ case MENU_MAIN:
+ bindings = OpMain;
+ break;
+ case MENU_GENERIC:
+ bindings = OpGeneric;
+ break;
+ case MENU_COMPOSE:
+ bindings = OpCompose;
+ break;
+ case MENU_PAGER:
+ bindings = OpPager;
+ break;
+ case MENU_POST:
+ bindings = OpPost;
+ break;
+ case MENU_FOLDER:
+ bindings = OpBrowser;
+ break;
+ case MENU_ATTACH:
+ bindings = OpAttach;
+ break;
+ case MENU_EDITOR:
+ bindings = OpEditor;
+ break;
+ case MENU_ALIAS:
+ bindings = OpAlias;
+ break;
+
+
+
+#ifdef _PGPPATH
+ case MENU_PGP:
+ bindings = OpPgp;
+ break;
+#endif /* _PGPPATH */
+
+
+
+ }
+
+ /* function to execute */
+ s = mutt_extract_token (buf, sizeof (buf), s, expn, sizeof (expn), 0);
+ if (s)
+ {
+ strfcpy (err, "too many arguments", errlen);
+ return (-1);
+ }
+
+ if (strcasecmp ("noop", buf) == 0)
+ {
+ km_bindkey (key, menu, OP_NULL, NULL);
+ return 0;
+ }
+
+ if (menu != MENU_PAGER && menu != MENU_EDITOR && menu != MENU_GENERIC)
+ {
+ /* First check the "generic" list of commands. */
+ if (try_bind (key, menu, buf, OpGeneric) == 0)
+ return 0;
+ }
+
+ /* Now check the menu-specific list of commands (if they exist). */
+ if (bindings && try_bind (key, menu, buf, bindings) == 0)
+ return 0;
+
+ snprintf (err, errlen, "%s: no such function in map", buf);
+ return (-1);
+}
+
+/* macro <menu> <key> <macro> */
+int mutt_parse_macro (const char *s, unsigned long data, char *err, size_t errlen)
+{
+ int menu;
+ char key[SHORT_STRING];
+ char buf[SHORT_STRING];
+ char expn[SHORT_STRING];
+
+ if ((s = parse_keymap (&menu, key, sizeof (key), s, err, errlen)) == NULL)
+ return (-1);
+
+ s = mutt_extract_token (buf, sizeof (buf), s, expn, sizeof (expn), M_CONDENSE);
+ if (s)
+ {
+ strfcpy (err, "too many arguments", errlen);
+ return (-1);
+ }
+
+ km_bindkey (key, menu, OP_MACRO, buf);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mutt_menu.h"
+#include "buffy.h"
+#include "sort.h"
+#include "mailbox.h"
+
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* HP-UX and ConvexOS don't have this macro */
+#ifndef S_ISLNK
+#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK ? 1 : 0)
+#endif
+
+struct folder_file
+{
+ mode_t mode;
+ time_t mtime;
+ off_t size;
+ char *name;
+ char *desc;
+};
+
+struct browser_state
+{
+ struct folder_file *entry;
+ short entrylen; /* number of real entries */
+ short entrymax; /* max entry */
+};
+
+static struct mapping_t FolderHelp[] = {
+ { "Exit", OP_EXIT },
+ { "Chdir", OP_CHANGE_DIRECTORY },
+ { "Mask", OP_ENTER_MASK },
+ { "Help", OP_HELP },
+ { NULL }
+};
+
+typedef struct folder_t
+{
+ const char *name;
+ const struct stat *f;
+ int new;
+} FOLDER;
+
+static char LastDir[_POSIX_PATH_MAX] = "";
+
+/* Frees up the memory allocated for the local-global variables. */
+static void destroy_state (struct browser_state *state)
+{
+ int c;
+
+ for (c = 0; c < state->entrylen; c++)
+ {
+ safe_free ((void **) &((state->entry)[c].name));
+ safe_free ((void **) &((state->entry)[c].desc));
+ }
+ safe_free ((void **) &state->entry);
+}
+
+static int browser_compare_subject (const void *a, const void *b)
+{
+ struct folder_file *pa = (struct folder_file *) a;
+ struct folder_file *pb = (struct folder_file *) b;
+
+ int r = strcmp (pa->name, pb->name);
+
+ return ((BrowserSort & SORT_REVERSE) ? -r : r);
+}
+
+static int browser_compare_date (const void *a, const void *b)
+{
+ struct folder_file *pa = (struct folder_file *) a;
+ struct folder_file *pb = (struct folder_file *) b;
+
+ int r = pa->mtime - pb->mtime;
+
+ return ((BrowserSort & SORT_REVERSE) ? -r : r);
+}
+
+static int browser_compare_size (const void *a, const void *b)
+{
+ struct folder_file *pa = (struct folder_file *) a;
+ struct folder_file *pb = (struct folder_file *) b;
+
+ int r = pa->size - pb->size;
+
+ return ((BrowserSort & SORT_REVERSE) ? -r : r);
+}
+
+static void browser_sort (struct browser_state *state)
+{
+ int (*f) (const void *, const void *);
+
+ switch (BrowserSort & SORT_MASK)
+ {
+ case SORT_ORDER:
+ return;
+ case SORT_DATE:
+ f = browser_compare_date;
+ break;
+ case SORT_SIZE:
+ f = browser_compare_size;
+ break;
+ case SORT_SUBJECT:
+ default:
+ f = browser_compare_subject;
+ break;
+ }
+ qsort (state->entry, state->entrylen, sizeof (struct folder_file), f);
+}
+
+static int link_is_dir (const char *path)
+{
+ struct stat st;
+
+ if (stat (path, &st) == 0)
+ return (S_ISDIR (st.st_mode));
+ else
+ return (-1);
+}
+
+static const char *
+folder_format_str (char *dest, size_t destlen, char op, const char *src,
+ const char *fmt, const char *ifstring, const char *elsestring,
+ unsigned long data, format_flag flags)
+{
+ char fn[SHORT_STRING], tmp[SHORT_STRING], permission[10];
+ char date[16], *t_fmt;
+ time_t tnow;
+ FOLDER *folder = (FOLDER *) data;
+ struct passwd *pw;
+ struct group *gr;
+
+ switch (op)
+ {
+ case 'd':
+ tnow = time (NULL);
+ t_fmt = tnow - folder->f->st_mtime < 31536000 ? "%b %d %H:%M" : "%b %d %Y";
+ strftime (date, sizeof (date), t_fmt, localtime (&folder->f->st_mtime));
+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
+ snprintf (dest, destlen, tmp, date);
+ break;
+ case 'f':
+ strncpy (fn, folder->name, sizeof(fn) - 1);
+ strcat (fn, S_ISLNK (folder->f->st_mode) ? "@" :
+ (S_ISDIR (folder->f->st_mode) ? "/" :
+ ((folder->f->st_mode & S_IXUSR) != 0 ? "*" : "")));
+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
+ snprintf (dest, destlen, tmp, fn);
+ break;
+ case 'F':
+ sprintf (permission, "%c%c%c%c%c%c%c%c%c%c",
+ S_ISDIR(folder->f->st_mode) ? 'd' : (S_ISLNK(folder->f->st_mode) ? 'l' : '-'),
+ (folder->f->st_mode & S_IRUSR) != 0 ? 'r': '-',
+ (folder->f->st_mode & S_IWUSR) != 0 ? 'w' : '-',
+ (folder->f->st_mode & S_ISUID) != 0 ? 's' : (folder->f->st_mode & S_IXUSR) != 0 ? 'x': '-',
+ (folder->f->st_mode & S_IRGRP) != 0 ? 'r' : '-',
+ (folder->f->st_mode & S_IWGRP) != 0 ? 'w' : '-',
+ (folder->f->st_mode & S_ISGID) != 0 ? 's' : (folder->f->st_mode & S_IXGRP) != 0 ? 'x': '-',
+ (folder->f->st_mode & S_IROTH) != 0 ? 'r' : '-',
+ (folder->f->st_mode & S_IWOTH) != 0 ? 'w' : '-',
+ (folder->f->st_mode & S_ISVTX) != 0 ? 't' : (folder->f->st_mode & S_IXOTH) != 0 ? 'x': '-');
+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
+ snprintf (dest, destlen, tmp, permission);
+ break;
+ case 'g':
+ if ((gr = getgrgid (folder->f->st_gid)))
+ {
+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
+ snprintf (dest, destlen, tmp, gr->gr_name);
+ }
+ else
+ {
+ snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
+ snprintf (dest, destlen, tmp, folder->f->st_gid);
+ }
+ break;
+ case 'l':
+ snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
+ snprintf (dest, destlen, tmp, folder->f->st_nlink);
+ break;
+ case 'N':
+ snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
+ snprintf (dest, destlen, tmp, folder->new ? 'N' : ' ');
+ break;
+ case 's':
+ snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
+ snprintf (dest, destlen, tmp, (long) folder->f->st_size);
+ break;
+ case 'u':
+ if ((pw = getpwuid (folder->f->st_uid)))
+ {
+ snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
+ snprintf (dest, destlen, tmp, pw->pw_name);
+ }
+ else
+ {
+ snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
+ snprintf (dest, destlen, tmp, folder->f->st_uid);
+ }
+ break;
+ }
+ return (src);
+}
+
+static void add_folder (MUTTMENU *m, struct browser_state *state,
+ const char *name, const struct stat *s, int new)
+{
+ char buffer[_POSIX_PATH_MAX + SHORT_STRING];
+ FOLDER folder;
+
+ folder.name = name;
+ folder.f = s;
+ folder.new = new;
+ mutt_FormatString (buffer, sizeof (buffer), FolderFormat, folder_format_str,
+ (unsigned long) &folder, 0);
+
+ if (state->entrylen == state->entrymax)
+ {
+ /* need to allocate more space */
+ safe_realloc ((void **) &state->entry,
+ sizeof (struct folder_file) * (state->entrymax += 256));
+ if (m)
+ m->data = state->entry;
+ }
+
+ (state->entry)[state->entrylen].mode = s->st_mode;
+ (state->entry)[state->entrylen].mtime = s->st_mtime;
+ (state->entry)[state->entrylen].size = s->st_size;
+ (state->entry)[state->entrylen].name = safe_strdup (name);
+ (state->entry)[state->entrylen].desc = safe_strdup (buffer);
+
+ (state->entrylen)++;
+}
+
+static void init_state (struct browser_state *state, MUTTMENU *menu)
+{
+ state->entrylen = 0;
+ state->entrymax = 256;
+ state->entry = (struct folder_file *) safe_malloc (sizeof (struct folder_file) * state->entrymax);
+ if (menu)
+ menu->data = state->entry;
+}
+
+static int examine_directory (MUTTMENU *menu, struct browser_state *state,
+ const char *d, const char *prefix)
+{
+ struct stat s;
+ DIR *dp;
+ struct dirent *de;
+ char buffer[_POSIX_PATH_MAX + SHORT_STRING];
+ BUFFY *tmp;
+
+ if (stat (d, &s) == -1)
+ {
+ mutt_perror (d);
+ return (-1);
+ }
+
+ if (!S_ISDIR (s.st_mode))
+ {
+ mutt_error ("%s is not a directory", d);
+ return (-1);
+ }
+
+ mutt_buffy_check (0);
+
+ if ((dp = opendir (d)) == NULL)
+ {
+ mutt_perror (d);
+ return (-1);
+ }
+
+ init_state (state, menu);
+
+ while ((de = readdir (dp)) != NULL)
+ {
+ if (strcmp (de->d_name, ".") == 0)
+ continue; /* we don't need . */
+
+ if (prefix && *prefix && strncmp (prefix, de->d_name, strlen (prefix)) != 0)
+ continue;
+ if (regexec (Mask.rx, de->d_name, 0, NULL, 0) != 0)
+ continue;
+
+ snprintf (buffer, sizeof (buffer), "%s/%s", d, de->d_name);
+ if (lstat (buffer, &s) == -1)
+ continue;
+
+ if ((! S_ISREG (s.st_mode)) && (! S_ISDIR (s.st_mode)) &&
+ (! S_ISLNK (s.st_mode)))
+ continue;
+
+ tmp = Incoming;
+ while (tmp && strcmp (buffer, tmp->path))
+ tmp = tmp->next;
+ add_folder (menu, state, de->d_name, &s, (tmp) ? tmp->new : 0);
+ }
+ closedir (dp);
+ browser_sort (state);
+ return 0;
+}
+
+static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
+{
+ struct stat s;
+ char buffer[LONG_STRING];
+ BUFFY *tmp = Incoming;
+
+ if (!Incoming)
+ return (-1);
+ mutt_buffy_check (0);
+
+ init_state (state, menu);
+
+ do
+ {
+ if (lstat (tmp->path, &s) == -1)
+ continue;
+
+ if ((! S_ISREG (s.st_mode)) && (! S_ISDIR (s.st_mode)) &&
+ (! S_ISLNK (s.st_mode)))
+ continue;
+
+ strfcpy (buffer, tmp->path, sizeof (buffer));
+ mutt_pretty_mailbox (buffer);
+
+ add_folder (menu, state, buffer, &s, tmp->new);
+ }
+ while ((tmp = tmp->next));
+ browser_sort (state);
+ return 0;
+}
+
+int select_file_search (MUTTMENU *menu, regex_t *re, int n)
+{
+ return (regexec (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
+}
+
+void folder_entry (char *s, size_t slen, MUTTMENU *menu, int num)
+{
+ snprintf (s, slen, "%2d %s", num + 1, ((struct folder_file *) menu->data)[num].desc);
+}
+
+static void init_menu (struct browser_state *state, MUTTMENU *menu, char *title,
+ size_t titlelen, int buffy)
+{
+ char path[_POSIX_PATH_MAX];
+
+ menu->current = 0;
+ menu->top = 0;
+ menu->max = state->entrylen;
+ if (buffy)
+ snprintf (title, titlelen, "Mailboxes [%d]", mutt_buffy_check (0));
+ else
+ {
+ strfcpy (path, LastDir, sizeof (path));
+ mutt_pretty_mailbox (path);
+ snprintf (title, titlelen, "Directory [%s], File mask: %s",
+ path, Mask.pattern);
+ }
+ menu->redraw = REDRAW_FULL;
+}
+
+void mutt_select_file (char *f, size_t flen, int buffy)
+{
+ char buf[STRING];
+ char prefix[_POSIX_PATH_MAX] = "";
+ char helpstr[SHORT_STRING];
+ char title[STRING];
+ struct browser_state state;
+ MUTTMENU *menu;
+ struct stat st;
+ int i, killPrefix = 0;
+
+ memset (&state, 0, sizeof (struct browser_state));
+
+ if (*f)
+ {
+ mutt_expand_path (f, flen);
+ for (i = strlen (f) - 1; i > 0 && f[i] != '/' ; i--);
+ if (i > 0)
+ {
+ if (f[0] == '/')
+ {
+ if (i > sizeof (LastDir) - 1) i = sizeof (LastDir) - 1;
+ strncpy (LastDir, f, i);
+ LastDir[i] = 0;
+ }
+ else
+ {
+ getcwd (LastDir, sizeof (LastDir));
+ strcat (LastDir, "/");
+ strncat (LastDir, f, i);
+ }
+ }
+ else
+ {
+ if (f[0] == '/')
+ strcpy (LastDir, "/");
+ else
+ getcwd (LastDir, sizeof (LastDir));
+ }
+
+ if (i <= 0 && f[0] != '/')
+ strfcpy (prefix, f, sizeof (prefix));
+ else
+ strfcpy (prefix, f + i + 1, sizeof (prefix));
+ killPrefix = 1;
+ }
+ else if (!LastDir[0])
+ strfcpy (LastDir, Maildir, sizeof (LastDir));
+
+ *f = 0;
+
+ if (buffy)
+ {
+ if (examine_mailboxes (NULL, &state) == -1)
+ return;
+ }
+ else if (examine_directory (NULL, &state, LastDir, prefix) == -1)
+ return;
+
+ menu = mutt_new_menu ();
+ menu->menu = MENU_FOLDER;
+ menu->make_entry = folder_entry;
+ menu->search = select_file_search;
+ menu->title = title;
+ menu->data = state.entry;
+
+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER, FolderHelp);
+
+ init_menu (&state, menu, title, sizeof (title), buffy);
+
+ FOREVER
+ {
+ switch (i = mutt_menuLoop (menu))
+ {
+ case OP_GENERIC_SELECT_ENTRY:
+
+ if (!state.entrylen)
+ {
+ mutt_error ("No files match the file mask");
+ break;
+ }
+
+ if (S_ISDIR (state.entry[menu->current].mode) ||
+ (S_ISLNK (state.entry[menu->current].mode) &&
+ link_is_dir (state.entry[menu->current].name)))
+ {
+ /* make sure this isn't a MH or maildir mailbox */
+ if (buffy)
+ {
+ strfcpy (buf, state.entry[menu->current].name, sizeof (buf));
+ mutt_expand_path (buf, sizeof (buf));
+ }
+ else
+ snprintf (buf, sizeof (buf), "%s/%s", LastDir,
+ state.entry[menu->current].name);
+
+ if (mx_get_magic (buf) <= 0)
+ {
+ char OldLastDir[_POSIX_PATH_MAX];
+
+ /* save the old directory */
+ strfcpy (OldLastDir, LastDir, sizeof (OldLastDir));
+
+ if (strcmp (state.entry[menu->current].name, "..") == 0)
+ {
+ if (strcmp ("..", LastDir + strlen (LastDir) - 2) == 0)
+ strcat (LastDir, "/..");
+ else
+ {
+ char *p = strrchr (LastDir + 1, '/');
+
+ if (p)
+ *p = 0;
+ else
+ {
+ if (LastDir[0] == '/')
+ LastDir[1] = 0;
+ else
+ strcat (LastDir, "/..");
+ }
+ }
+ }
+ else if (buffy)
+ {
+ sprintf (LastDir, "%s", state.entry[menu->current].name);
+ mutt_expand_path (LastDir, sizeof (LastDir));
+ }
+ else
+ sprintf (LastDir + strlen (LastDir), "/%s",
+ state.entry[menu->current].name);
+
+ destroy_state (&state);
+ if (killPrefix)
+ {
+ prefix[0] = 0;
+ killPrefix = 0;
+ }
+ buffy = 0;
+ if (examine_directory (menu, &state, LastDir, prefix) == -1)
+ {
+ /* try to restore the old values */
+ strfcpy (LastDir, OldLastDir, sizeof (LastDir));
+ if (examine_directory (menu, &state, LastDir, prefix) == -1)
+ {
+ strfcpy (LastDir, Homedir, sizeof (LastDir));
+ return;
+ }
+ }
+ init_menu (&state, menu, title, sizeof (title), buffy);
+ break;
+ }
+ }
+
+ if (buffy)
+ {
+ strfcpy (f, state.entry[menu->current].name, flen);
+ mutt_expand_path (f, flen);
+ }
+ else
+ snprintf (f, flen, "%s/%s", LastDir, state.entry[menu->current].name);
+
+ /* Fall through to OP_EXIT */
+
+ case OP_EXIT:
+
+ destroy_state (&state);
+ mutt_menuDestroy (&menu);
+ return;
+
+ case OP_CHANGE_DIRECTORY:
+
+ strfcpy (buf, LastDir, sizeof (buf));
+ if (mutt_get_field ("Chdir to: ", buf, sizeof (buf), M_FILE) == 0 &&
+ buf[0])
+ {
+ buffy = 0;
+ mutt_expand_path (buf, sizeof (buf));
+ if (stat (buf, &st) == 0)
+ {
+ if (S_ISDIR (st.st_mode))
+ {
+ strfcpy (LastDir, buf, sizeof (LastDir));
+ destroy_state (&state);
+ if (examine_directory (menu, &state, LastDir, prefix) == 0)
+ init_menu (&state, menu, title, sizeof (title), buffy);
+ else
+ {
+ mutt_error ("Error scanning directory.");
+ destroy_state (&state);
+ mutt_menuDestroy (&menu);
+ return;
+ }
+ }
+ else
+ mutt_error ("%s is not a directory.", buf);
+ }
+ else
+ mutt_perror (buf);
+ }
+ MAYBE_REDRAW (menu->redraw);
+ break;
+
+ case OP_ENTER_MASK:
+
+ strfcpy (buf, Mask.pattern, sizeof (buf));
+ if (mutt_get_field ("File Mask: ", buf, sizeof (buf), 0) == 0)
+ {
+ regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
+ int err;
+
+ buffy = 0;
+ /* assume that the user wants to see everything */
+ if (!buf[0])
+ strfcpy (buf, ".", sizeof (buf));
+
+ if ((err = REGCOMP (rx, buf, REG_NOSUB | mutt_which_case (buf))) != 0)
+ {
+ regerror (err, rx, buf, sizeof (buf));
+ regfree (rx);
+ safe_free ((void **) &rx);
+ mutt_error ("%s", buf);
+ }
+ else
+ {
+ safe_free ((void **) &Mask.pattern);
+ regfree (Mask.rx);
+ safe_free ((void **) &Mask.rx);
+ Mask.pattern = safe_strdup (buf);
+ Mask.rx = rx;
+
+ destroy_state (&state);
+ if (examine_directory (menu, &state, LastDir, NULL) == 0)
+ init_menu (&state, menu, title, sizeof (title), buffy);
+ else
+ {
+ mutt_error ("Error scanning directory.");
+ mutt_menuDestroy (&menu);
+ return;
+ }
+ killPrefix = 0;
+ }
+ }
+ MAYBE_REDRAW (menu->redraw);
+ break;
+
+ case OP_SORT:
+ case OP_SORT_REVERSE:
+
+ {
+ int reverse = 0;
+
+ move (LINES - 1, 0);
+ if (i == OP_SORT_REVERSE)
+ {
+ reverse = SORT_REVERSE;
+ addstr ("Reverse ");
+ }
+ addstr ("Sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? ");
+ clrtoeol ();
+
+ while ((i = mutt_getch ()) != EOF && i != 'a' && i != 'd' && i != 'z'
+ && i != 'n')
+ {
+ if (i == ERR || CI_is_return (i))
+ break;
+ else
+ BEEP ();
+ }
+
+ if (i != EOF)
+ {
+ switch (i)
+ {
+ case 'a':
+ BrowserSort = reverse | SORT_SUBJECT;
+ break;
+ case 'd':
+ BrowserSort = reverse | SORT_DATE;
+ break;
+ case 'z':
+ BrowserSort = reverse | SORT_SIZE;
+ break;
+ case 'n':
+ BrowserSort = SORT_ORDER;
+ break;
+ }
+ browser_sort (&state);
+ menu->redraw = REDRAW_FULL;
+ }
+ }
+
+ break;
+
+ case OP_CHECK_NEW:
+
+ buffy = 1 - buffy;
+ destroy_state (&state);
+ prefix[0] = 0;
+ killPrefix = 0;
+ if (buffy)
+ {
+ if (examine_mailboxes (menu, &state) == -1)
+ return;
+ }
+ else if (examine_directory (menu, &state, LastDir, prefix) == -1)
+ return;
+ init_menu (&state, menu, title, sizeof (title), buffy);
+ break;
+
+ case OP_BROWSER_NEW_FILE:
+
+ snprintf (buf, sizeof (buf), "%s/", LastDir);
+ if (mutt_get_field ("New file name: ", buf, sizeof (buf), M_FILE) == 0)
+ {
+ strfcpy (f, buf, flen);
+ destroy_state (&state);
+ mutt_menuDestroy (&menu);
+ return;
+ }
+ MAYBE_REDRAW (menu->redraw);
+ break;
+ }
+ }
+ /* not reached */
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "buffy.h"
+#include "mx.h"
+#include "mailbox.h"
+
+#include <string.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <utime.h>
+#include <ctype.h>
+
+#include <stdio.h>
+
+static time_t BuffyTime = 0; /* last time we started checking for mail */
+time_t BuffyDoneTime = 0; /* last time we knew for sure how much mail there was. */
+static short BuffyCount = 0; /* how many boxes with new mail */
+static short BuffyNotify = 0; /* # of unnotified new boxes */
+
+#ifdef BUFFY_SIZE
+
+/* Find the last message in the file. * upon success return 0. * If no
+ * message found - return -1 */
+int fseek_last_message (FILE * f)
+{
+ long int pos;
+ char buffer[BUFSIZ + 7]; /* 7 for "\n\nFrom " */
+ int bytes_read;
+ int i; /* Index into `buffer' for scanning. */
+ memset (buffer, 0, BUFSIZ+7);
+ fseek (f, 0, SEEK_END);
+ pos = ftell (f);
+
+ /* Set `bytes_read' to the size of the last, probably partial, buffer; 0 <
+ * `bytes_read' <= `BUFSIZ'. */
+ bytes_read = pos % BUFSIZ;
+ if (bytes_read == 0)
+ bytes_read = BUFSIZ;
+ /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
+ * reads will be on block boundaries, which might increase efficiency. */
+ while ((pos -= bytes_read) >= 0)
+ {
+ /* we save in the buffer at the end the first 7 chars from the last read */
+ strncpy (buffer + BUFSIZ, buffer, 7);
+ fseek (f, pos, SEEK_SET);
+ bytes_read = fread (buffer, sizeof (char), bytes_read, f);
+ if (bytes_read == -1)
+ return -1;
+ for (i = bytes_read; --i >= 0;)
+ if (!strncmp (buffer + i, "\n\nFrom ", strlen ("\n\nFrom ")))
+ { /* found it - go to the beginning of the From */
+ fseek (f, pos + i + 2, SEEK_SET);
+ return 0;
+ }
+ bytes_read = BUFSIZ;
+ }
+
+ /* here we are at the beginning of the file */
+ if (!strncmp ("From ", buffer, 5))
+ {
+ fseek (f, 0, 0);
+ return (0);
+ }
+
+ return (-1);
+}
+
+/* Return 1 if the last message is new */
+int test_last_status_new (FILE * f)
+{
+ HEADER *hdr;
+ int result = 0;
+
+ if (fseek_last_message (f) == -1)
+ return (0);
+
+ hdr = mutt_new_header ();
+ mutt_read_rfc822_header (f, hdr);
+ if (!(hdr->read || hdr->old))
+ result = 1;
+ mutt_free_header (&hdr);
+
+ return result;
+}
+
+int test_new_folder (const char *path)
+{
+ FILE *f;
+ int rc = 0;
+ int typ;
+
+ typ = mx_get_magic (path);
+
+ if (typ != M_MBOX && typ != M_MMDF)
+ return 0;
+
+ f = fopen (path, "rb");
+ rc = test_last_status_new (f);
+ fclose (f);
+
+ return rc;
+}
+
+BUFFY *mutt_find_mailbox (const char *path)
+{
+ BUFFY *tmp = NULL;
+ struct stat sb;
+ struct stat tmp_sb;
+
+ if (stat (path,&sb) != 0)
+ return NULL;
+
+ for (tmp = Incoming; tmp; tmp = tmp->next)
+ {
+ if (stat (tmp->path,&tmp_sb) ==0 &&
+ sb.st_dev == tmp_sb.st_dev && sb.st_ino == tmp_sb.st_ino)
+ break;
+ }
+ return tmp;
+}
+
+void mutt_update_mailbox (BUFFY * b)
+{
+ struct stat sb;
+
+ if (!b)
+ return;
+
+ if (stat (b->path, &sb) == 0)
+ b->size = (long) sb.st_size;
+ else
+ b->size = 0;
+ return;
+}
+#endif
+
+int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ BUFFY **tmp;
+ char buf[_POSIX_PATH_MAX];
+#ifdef BUFFY_SIZE
+ struct stat sb;
+#endif /* BUFFY_SIZE */
+
+ while (MoreArgs (s))
+ {
+ mutt_extract_token (path, s, 0);
+ strfcpy (buf, path->data, sizeof (buf));
+ mutt_expand_path (buf, sizeof (buf));
+ /* simple check to avoid duplicates */
+ for (tmp = &Incoming; *tmp; tmp = &((*tmp)->next))
+ {
+ if (strcmp (buf, (*tmp)->path) == 0)
+ break;
+ }
+
+ if (!*tmp)
+ {
+ *tmp = (BUFFY *) safe_calloc (1, sizeof (BUFFY));
+ (*tmp)->path = safe_strdup (buf);
+ (*tmp)->next = NULL;
+ }
+
+ (*tmp)->new = 0;
+ (*tmp)->notified = 1;
+ (*tmp)->newly_created = 0;
+
+#ifdef BUFFY_SIZE
+ /* for buffy_size, it is important that if the folder is new (tested by
+ * reading it), the size is set to 0 so that later when we check we see
+ * that it increased . without buffy_size we probably don't care.
+ */
+ if (stat ((*tmp)->path, &sb) == 0 && !test_new_folder ((*tmp)->path))
+ {
+ /* some systems out there don't have an off_t type */
+ (*tmp)->size = (long) sb.st_size;
+ }
+ else
+ (*tmp)->size = 0;
+#endif /* BUFFY_SIZE */
+ }
+ return 0;
+}
+
+#ifdef BUFFY_SIZE
+/* people use buffy_size on systems where modified time attributes are BADLY
+ * broken. Ignore them.
+ */
+#define STAT_CHECK (sb.st_size > tmp->size)
+#else
+#define STAT_CHECK (sb.st_mtime > sb.st_atime || (tmp->newly_created && sb.st_ctime == sb.st_mtime && sb.st_ctime == sb.st_atime))
+#endif /* BUFFY_SIZE */
+
+int mutt_buffy_check (int force)
+{
+ BUFFY *tmp;
+ struct stat sb;
+ struct dirent *de;
+ DIR *dirp;
+ char path[_POSIX_PATH_MAX];
+ struct stat contex_sb;
+ int res;
+ time_t t;
+
+ /* fastest return if there are no mailboxes */
+ if (!Incoming)
+ return 0;
+ t = time (NULL);
+ if (!force && t - BuffyTime < BuffyTimeout)
+ return BuffyCount;
+
+ BuffyTime = t;
+ BuffyCount = 0;
+ BuffyNotify = 0;
+
+ /* check device ID and serial number instead of comparing paths */
+ if (!Context || !Context->path || stat (Context->path, &contex_sb) != 0)
+ {
+ contex_sb.st_dev=0;
+ contex_sb.st_ino=0;
+ }
+
+ for (tmp = Incoming; tmp; tmp = tmp->next)
+ {
+ tmp->new = 0;
+
+ if (stat (tmp->path, &sb) != 0 ||
+ (!tmp->magic && (tmp->magic = mx_get_magic (tmp->path)) <= 0))
+ {
+ /* if the mailbox still doesn't exist, set the newly created flag to
+ * be ready for when it does.
+ */
+ tmp->newly_created = 1;
+ tmp->magic = 0;
+#ifdef BUFFY_SIZE
+ tmp->size = 0;
+#endif
+ continue;
+ }
+
+ if (!Context || !Context->path ||
+ sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino)
+ {
+ switch (tmp->magic)
+ {
+ case M_MBOX:
+ case M_MMDF:
+
+ if (STAT_CHECK)
+ {
+ BuffyCount++;
+ tmp->new = 1;
+ }
+#ifdef BUFFY_SIZE
+ else
+ {
+ /* some other program has deleted mail from the folder */
+ tmp->size = (long) sb.st_size;
+ }
+#endif
+ if (tmp->newly_created &&
+ (sb.st_ctime != sb.st_mtime || sb.st_ctime != sb.st_atime))
+ tmp->newly_created = 0;
+
+ break;
+
+ case M_MAILDIR:
+
+ snprintf (path, sizeof (path), "%s/new", tmp->path);
+ if ((dirp = opendir (path)) == NULL)
+ {
+ tmp->magic = 0;
+ break;
+ }
+ while ((de = readdir (dirp)) != NULL)
+ {
+ if (*de->d_name != '.')
+ {
+ /* one new message is enough */
+ BuffyCount++;
+ tmp->new = 1;
+ break;
+ }
+ }
+ closedir (dirp);
+ break;
+
+ case M_MH:
+
+ res = mh_parse_sequences (NULL, tmp->path);
+ if (res >= 0)
+ {
+ BuffyCount += res;
+ tmp->new = res;
+ }
+ else
+ {
+ tmp->magic = 0;
+ }
+ break;
+ }
+ }
+#ifdef BUFFY_SIZE
+ else if (Context && Context->path)
+ tmp->size = (long) sb.st_size; /* update the size */
+#endif
+
+ if (!tmp->new)
+ tmp->notified = 0;
+ else if (!tmp->notified)
+ BuffyNotify++;
+ }
+ BuffyDoneTime = BuffyTime;
+ return (BuffyCount);
+}
+
+int mutt_buffy_notify (void)
+{
+ BUFFY *tmp;
+ char path[_POSIX_PATH_MAX];
+
+ if (mutt_buffy_check (0) && BuffyNotify)
+ {
+ for (tmp = Incoming; tmp; tmp = tmp->next)
+ {
+ if (tmp->new && !tmp->notified)
+ {
+ strfcpy (path, tmp->path, sizeof (path));
+ mutt_pretty_mailbox (path);
+ mutt_message ("New mail in %s.", path);
+ tmp->notified = 1;
+ BuffyNotify--;
+ return (1);
+ }
+ }
+ /* there were no mailboxes needing to be notified, so clean up since
+ * BuffyNotify has somehow gottten out of sync
+ */
+ BuffyNotify = 0;
+ }
+ return (0);
+}
+
+/*
+ * mutt_buffy() -- incoming folders completion routine
+ *
+ * given a folder name, this routine gives the next incoming folder with new
+ * new mail.
+ */
+void mutt_buffy (char *s)
+{
+ int count;
+ BUFFY *tmp = Incoming;
+
+ mutt_expand_path (s, _POSIX_PATH_MAX);
+ switch (mutt_buffy_check (0))
+ {
+ case 0:
+
+ s = '\0';
+ break;
+
+ case 1:
+
+ while (tmp && !tmp->new)
+ tmp = tmp->next;
+ if (!tmp)
+ {
+ s = '\0';
+ mutt_buffy_check (1); /* buffy was wrong - resync things */
+ break;
+ }
+ strcpy (s, tmp->path);
+ mutt_pretty_mailbox (s);
+ break;
+
+ default:
+
+ count = 0;
+ while (count < 3)
+ {
+ if (strcmp (s, tmp->path) == 0)
+ count++;
+ else if (count && tmp->new)
+ break;
+ tmp = tmp->next;
+ if (!tmp)
+ {
+ tmp = Incoming;
+ count++;
+ }
+ }
+ if (count >= 3)
+ {
+ s = '\0';
+ mutt_buffy_check (1); /* buffy was wrong - resync things */
+ break;
+ }
+ strcpy (s, tmp->path);
+ mutt_pretty_mailbox (s);
+ break;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+typedef struct buffy_t
+{
+ char *path;
+#ifdef BUFFY_SIZE
+ long size;
+#endif /* BUFFY_SIZE */
+ struct buffy_t *next;
+ short new; /* mailbox has new mail */
+ short notified; /* user has been notified */
+ short magic; /* mailbox type */
+ short newly_created; /* mbox or mmdf just popped into existence */
+}
+BUFFY;
+
+WHERE BUFFY *Incoming INITVAL (0);
+WHERE short BuffyTimeout INITVAL (3);
+
+extern time_t BuffyDoneTime; /* last time we knew for sure how much mail there was */
+
+#ifdef BUFFY_SIZE
+BUFFY *mutt_find_mailbox (const char *path);
+void mutt_update_mailbox (BUFFY * b);
+#endif
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mapping.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+/* globals */
+int *ColorQuote;
+int ColorQuoteUsed;
+int ColorDefs[MT_COLOR_MAX];
+COLOR_LINE *ColorHdrList = NULL;
+COLOR_LINE *ColorBodyList = NULL;
+COLOR_LINE *ColorIndexList = NULL;
+
+/* local to this file */
+static int ColorQuoteSize;
+
+#ifdef HAVE_COLOR
+
+#define COLOR_DEFAULT (-2)
+
+typedef struct color_list
+{
+ short fg;
+ short bg;
+ short index;
+ short count;
+ struct color_list *next;
+} COLOR_LIST;
+
+static COLOR_LIST *ColorList = NULL;
+static int UserColors = 0;
+
+static struct mapping_t Colors[] =
+{
+ { "black", COLOR_BLACK },
+ { "blue", COLOR_BLUE },
+ { "cyan", COLOR_CYAN },
+ { "green", COLOR_GREEN },
+ { "magenta", COLOR_MAGENTA },
+ { "red", COLOR_RED },
+ { "white", COLOR_WHITE },
+ { "yellow", COLOR_YELLOW },
+#if defined (USE_SLANG_CURSES) || defined (HAVE_USE_DEFAULT_COLORS)
+ { "default", COLOR_DEFAULT },
+#endif
+ { 0, 0 }
+};
+
+#endif /* HAVE_COLOR */
+
+static struct mapping_t Fields[] =
+{
+ { "hdrdefault", MT_COLOR_HDEFAULT },
+ { "quoted", MT_COLOR_QUOTED },
+ { "signature", MT_COLOR_SIGNATURE },
+ { "indicator", MT_COLOR_INDICATOR },
+ { "status", MT_COLOR_STATUS },
+ { "tree", MT_COLOR_TREE },
+ { "error", MT_COLOR_ERROR },
+ { "normal", MT_COLOR_NORMAL },
+ { "tilde", MT_COLOR_TILDE },
+ { "markers", MT_COLOR_MARKERS },
+ { "header", MT_COLOR_HEADER },
+ { "body", MT_COLOR_BODY },
+ { "message", MT_COLOR_MESSAGE },
+ { "attachment", MT_COLOR_ATTACHMENT },
+ { "search", MT_COLOR_SEARCH },
+ { "bold", MT_COLOR_BOLD },
+ { "underline", MT_COLOR_UNDERLINE },
+ { "index", MT_COLOR_INDEX },
+ { NULL, 0 }
+};
+
+#define COLOR_QUOTE_INIT 8
+
+void ci_start_color (void)
+{
+ memset (ColorDefs, A_NORMAL, sizeof (int) * MT_COLOR_MAX);
+ ColorQuote = (int *) safe_malloc (COLOR_QUOTE_INIT * sizeof (int));
+ memset (ColorQuote, A_NORMAL, sizeof (int) * COLOR_QUOTE_INIT);
+ ColorQuoteSize = COLOR_QUOTE_INIT;
+ ColorQuoteUsed = 0;
+
+ /* set some defaults */
+ ColorDefs[MT_COLOR_STATUS] = A_REVERSE;
+ ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE;
+ ColorDefs[MT_COLOR_SEARCH] = A_REVERSE;
+ ColorDefs[MT_COLOR_MARKERS] = A_REVERSE;
+ /* special meaning: toggle the relevant attribute */
+ ColorDefs[MT_COLOR_BOLD] = 0;
+ ColorDefs[MT_COLOR_UNDERLINE] = 0;
+
+#ifdef HAVE_COLOR
+ start_color ();
+#endif
+}
+
+#ifdef HAVE_COLOR
+
+#ifdef USE_SLANG_CURSES
+static char * get_color_name (int val)
+{
+ static char * missing[3] = {"brown", "lightgray", ""};
+ int i;
+
+ switch (val)
+ {
+ case COLOR_YELLOW:
+ return (missing[0]);
+
+ case COLOR_WHITE:
+ return (missing[1]);
+
+ case COLOR_DEFAULT:
+ return (missing[2]);
+ }
+
+ for (i = 0; Colors[i].name; i++)
+ {
+ if (Colors[i].value == val)
+ return (Colors[i].name);
+ }
+ return (Colors[0].name);
+}
+#endif
+
+int mutt_alloc_color (int fg, int bg)
+{
+ COLOR_LIST *p = ColorList;
+ int i;
+
+ /* check to see if this color is already allocated to save space */
+ while (p)
+ {
+ if (p->fg == fg && p->bg == bg)
+ {
+ (p->count)++;
+ return (COLOR_PAIR (p->index));
+ }
+ p = p->next;
+ }
+
+ /* check to see if there are colors left */
+ if (++UserColors > COLOR_PAIRS) return (A_NORMAL);
+
+ /* find the smallest available index (object) */
+ i = 1;
+ FOREVER
+ {
+ p = ColorList;
+ while (p)
+ {
+ if (p->index == i) break;
+ p = p->next;
+ }
+ if (p == NULL) break;
+ i++;
+ }
+
+ p = (COLOR_LIST *) safe_malloc (sizeof (COLOR_LIST));
+ p->next = ColorList;
+ ColorList = p;
+
+ p->index = i;
+ p->count = 1;
+ p->bg = bg;
+ p->fg = fg;
+
+#if defined (USE_SLANG_CURSES)
+ if (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
+ SLtt_set_color (i, NULL, get_color_name (fg), get_color_name (bg));
+ else
+#elif defined (HAVE_USE_DEFAULT_COLORS)
+ if (fg == COLOR_DEFAULT)
+ fg = -1;
+ if (bg == COLOR_DEFAULT)
+ bg = -1;
+#endif
+
+ init_pair (i, fg, bg);
+
+ dprint(1,(debugfile,"mutt_alloc_color(): Color pairs used so far: %d\n",
+ UserColors));
+
+ return (COLOR_PAIR (p->index));
+}
+
+void mutt_free_color (int fg, int bg)
+{
+ COLOR_LIST *p, *q;
+
+ p = ColorList;
+ while (p)
+ {
+ if (p->fg == fg && p->bg == bg)
+ {
+ (p->count)--;
+ if (p->count > 0) return;
+
+ UserColors--;
+ dprint(1,(debugfile,"mutt_free_color(): Color pairs used so far: %d\n",
+ UserColors));
+
+ if (p == ColorList)
+ {
+ ColorList = ColorList->next;
+ safe_free ((void **) &p);
+ return;
+ }
+ q = ColorList;
+ while (q)
+ {
+ if (q->next == p)
+ {
+ q->next = p->next;
+ safe_free ((void **) &p);
+ return;
+ }
+ q = q->next;
+ }
+ /* can't get here */
+ }
+ p = p->next;
+ }
+}
+
+#endif /* HAVE_COLOR */
+
+static COLOR_LINE *mutt_new_color_line (void)
+{
+ COLOR_LINE *p = safe_calloc (1, sizeof (COLOR_LINE));
+
+ return (p);
+}
+
+static int add_pattern (COLOR_LINE **top, const char *s, int sensitive,
+ int fg, int bg, int attr, BUFFER *err,
+ /* is_index used to store compiled pattern
+ only for `index' color object
+ when called from mutt_parse_color() */
+ int is_index)
+{
+ COLOR_LINE *tmp = *top;
+
+ while (tmp)
+ {
+ if (sensitive)
+ {
+ if (strcmp (s, tmp->pattern) == 0)
+ break;
+ }
+ else
+ {
+ if (strcasecmp (s, tmp->pattern) == 0)
+ break;
+ }
+ tmp = tmp->next;
+ }
+
+ if (tmp)
+ {
+#ifdef HAVE_COLOR
+ if (fg != -1 && bg != -1)
+ {
+ if (tmp->fg != fg || tmp->bg != bg)
+ {
+ mutt_free_color (tmp->fg, tmp->bg);
+ tmp->fg = fg;
+ tmp->bg = bg;
+ attr |= mutt_alloc_color (fg, bg);
+ }
+ else
+ attr |= (tmp->pair & ~A_BOLD);
+ }
+#endif /* HAVE_COLOR */
+ tmp->pair = attr;
+ }
+ else
+ {
+ int r;
+ char buf[STRING];
+
+ tmp = mutt_new_color_line ();
+ if ((r = REGCOMP (&tmp->rx, s, (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0)
+ {
+ regerror (r, &tmp->rx, err->data, err->dsize);
+ regfree (&tmp->rx);
+ safe_free ((void **) &tmp);
+ return (-1);
+ }
+ tmp->next = *top;
+ tmp->pattern = safe_strdup (s);
+#ifdef HAVE_COLOR
+ tmp->fg = fg;
+ tmp->bg = bg;
+ attr |= mutt_alloc_color (fg, bg);
+ if (is_index)
+ {
+ strcpy(buf,tmp->pattern);
+ mutt_check_simple (buf, sizeof (buf), SimpleSearch);
+ tmp->color_pattern = mutt_pattern_comp (buf, M_FULL_MSG, err);
+ }
+#endif
+ tmp->pair = attr;
+ *top = tmp;
+ }
+
+ return 0;
+}
+
+#ifdef HAVE_COLOR
+
+static int
+parse_color_name (const char *s, int *col, int *attr, int brite, BUFFER *err)
+{
+ char *eptr;
+
+ if (strncasecmp (s, "bright", 6) == 0)
+ {
+ *attr |= brite;
+ s += 6;
+ }
+
+ /* allow aliases for xterm color resources */
+ if (strncasecmp (s, "color", 5) == 0)
+ {
+ s += 5;
+ *col = strtol (s, &eptr, 10);
+ if (!*s || *eptr || *col < 0 || *col >= COLORS)
+ {
+ snprintf (err->data, err->dsize, "%s: color not supported by term", s);
+ return (-1);
+ }
+ }
+ else if ((*col = mutt_getvaluebyname (s, Colors)) == -1)
+ {
+ snprintf (err->data, err->dsize, "%s: no such color", s);
+ return (-1);
+ }
+
+#ifdef HAVE_USE_DEFAULT_COLORS
+ if (*col == COLOR_DEFAULT && use_default_colors () != OK)
+ {
+ strfcpy (err->data, "default colors not supported", err->dsize);
+ return (-1);
+ }
+#endif
+
+ return 0;
+}
+
+/* usage: uncolor index pattern [pattern...] */
+int mutt_parse_uncolor (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ int object = 0, do_cache = 0;
+ COLOR_LINE *tmp, *last = NULL;
+
+ mutt_extract_token (buf, s, 0);
+
+ if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1)
+ {
+ snprintf (err->data, err->dsize, "%s: no such object", buf->data);
+ return (-1);
+ }
+
+ if (strncmp (buf->data, "index", 5) != 0)
+ {
+ strfcpy (err->data, "uncolor: command valid only for index object", err->dsize);
+ return (-1);
+ }
+
+ if (!MoreArgs (s))
+ {
+ strfcpy (err->data, "uncolor: too few arguments", err->dsize);
+ return (-1);
+ }
+
+ while (MoreArgs (s))
+ {
+ mutt_extract_token (buf, s, 0);
+ if (!strcmp ("*", buf->data))
+ {
+ for (tmp = ColorIndexList; tmp; )
+ {
+ if (!do_cache)
+ do_cache = 1;
+ last = tmp;
+ tmp = tmp->next;
+ mutt_pattern_free (&last->color_pattern);
+ mutt_free_color(last->fg,last->bg);
+ safe_free ((void **) &last);
+ }
+ ColorIndexList = NULL;
+ }
+ else
+ {
+ for (last = NULL, tmp = ColorIndexList; tmp; last = tmp, tmp = tmp->next)
+ {
+ if (!strcmp (buf->data, tmp->pattern))
+ {
+ if (!do_cache)
+ do_cache = 1;
+ dprint(1,(debugfile,"Freeing pattern \"%s\" from ColorIndexList\n",
+ tmp->pattern));
+ if (last)
+ last->next = tmp->next;
+ else
+ ColorIndexList = tmp->next;
+ mutt_pattern_free (&tmp->color_pattern);
+ mutt_free_color(tmp->fg,tmp->bg);
+ safe_free ((void **) &tmp);
+ break;
+ }
+ }
+ }
+ }
+
+ if (do_cache && !option (OPTNOCURSES) && has_colors ())
+ {
+ mutt_cache_index_colors (Context);
+ set_option (OPTFORCEREDRAWINDEX);
+ }
+
+ return (0);
+}
+
+/* usage: color <object> <fg> <bg> [ <regexp> ] */
+int mutt_parse_color (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ int object = 0, bold = 0, fg = 0, bg = 0, q_level = 0;
+ int r = 0;
+ char *eptr;
+
+ mutt_extract_token (buf, s, 0);
+ if (!strncmp (buf->data, "quoted", 6))
+ {
+ if (buf->data[6])
+ {
+ q_level = strtol (buf->data + 6, &eptr, 10);
+ if (*eptr || q_level < 0)
+ {
+ snprintf (err->data, err->dsize, "%s: no such object", buf->data);
+ return (-1);
+ }
+ }
+ object = MT_COLOR_QUOTED;
+ }
+ else if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1)
+ {
+ snprintf (err->data, err->dsize, "%s: no such object", buf->data);
+ return (-1);
+ }
+
+ /* first color */
+ if (! MoreArgs (s))
+ {
+ strfcpy (err->data, "color: too few arguments", err->dsize);
+ return (-1);
+ }
+ mutt_extract_token (buf, s, 0);
+
+ if (parse_color_name (buf->data, &fg, &bold, A_BOLD, err) != 0)
+ return (-1);
+
+ /* second color */
+ if (! MoreArgs (s))
+ {
+ strfcpy (err->data, "color: too few arguments", err->dsize);
+ return (-1);
+ }
+ mutt_extract_token (buf, s, 0);
+
+ /* A_BLINK turns the background color brite on some terms */
+ if (parse_color_name (buf->data, &bg, &bold, A_BLINK, err) != 0)
+ return (-1);
+
+ if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY || object == MT_COLOR_INDEX)
+ {
+ if (! MoreArgs (s))
+ {
+ strfcpy (err->data, "color: too few arguments", err->dsize);
+ return (-1);
+ }
+
+ mutt_extract_token (buf, s, 0);
+ if (MoreArgs (s))
+ {
+ strfcpy (err->data, "color: too many arguments", err->dsize);
+ return (-1);
+ }
+
+ /* don't parse curses command if we're not using the screen */
+ /* ignore color commands if we're running on a mono terminal */
+ if (option (OPTNOCURSES) || !has_colors ())
+ {
+ return 0;
+ }
+
+ if (object == MT_COLOR_HEADER)
+ r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, bold, err,0);
+ else if (object == MT_COLOR_BODY)
+ r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, bold, err, 0);
+ else if (object == MT_COLOR_INDEX)
+ {
+ pattern_t *pat;
+ char tempbuf[LONG_STRING];
+
+ strfcpy (tempbuf, buf->data, sizeof (tempbuf));
+ mutt_check_simple (tempbuf, sizeof (tempbuf), SimpleSearch);
+ if ((pat = mutt_pattern_comp (tempbuf, M_FULL_MSG, err)) == NULL)
+ {
+ mutt_pattern_free (&pat);
+ return (-1);
+ }
+ r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, bold, err, 1);
+ mutt_cache_index_colors(Context);
+ set_option (OPTFORCEREDRAWINDEX);
+ }
+ }
+ else
+ {
+ /* don't parse curses command if we're not using the screen */
+ /* ignore color commands if we're running on a mono terminal */
+ if (option (OPTNOCURSES) || !has_colors ())
+ {
+ return 0;
+ }
+
+ if (object == MT_COLOR_QUOTED)
+ {
+ if (q_level >= ColorQuoteSize)
+ {
+ safe_realloc ((void **) &ColorQuote, (ColorQuoteSize += 2) * sizeof (int));
+ ColorQuote[ColorQuoteSize-2] = ColorDefs[MT_COLOR_QUOTED];
+ ColorQuote[ColorQuoteSize-1] = ColorDefs[MT_COLOR_QUOTED];
+ }
+ if (q_level >= ColorQuoteUsed)
+ ColorQuoteUsed = q_level + 1;
+ if (q_level == 0)
+ {
+ ColorDefs[MT_COLOR_QUOTED] = bold | mutt_alloc_color (fg, bg);
+
+ ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
+ for (q_level = 1; q_level < ColorQuoteUsed; q_level++)
+ {
+ if (ColorQuote[q_level] == A_NORMAL)
+ ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
+ }
+ }
+ else
+ ColorQuote[q_level] = bold | mutt_alloc_color (fg, bg);
+ }
+ else
+ ColorDefs[object] = bold | mutt_alloc_color (fg, bg);
+ }
+
+#ifdef HAVE_BKGDSET
+ if (object == MT_COLOR_NORMAL)
+ BKGDSET (MT_COLOR_NORMAL);
+#endif
+
+ return (r);
+}
+
+#endif /* HAVE_COLOR */
+
+/*
+ * command: mono <object> <attribute>
+ *
+ * set attribute for an object when using a terminal with no color support
+ */
+int mutt_parse_mono (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ int r = 0, object, q_level = 0, attr = A_NORMAL;
+ char *eptr;
+
+ mutt_extract_token (buf, s, 0);
+ if (strncmp (buf->data, "quoted", 6) == 0)
+ {
+ if (buf->data[6])
+ {
+ q_level = strtol (buf->data + 6, &eptr, 10);
+ if (*eptr || q_level < 0)
+ {
+ snprintf (err->data, err->dsize, "%s: no such object", buf->data);
+ return (-1);
+ }
+ }
+ object = MT_COLOR_QUOTED;
+ }
+ else if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1)
+ {
+ snprintf (err->data, err->dsize, "%s: no such object", buf->data);
+ return (-1);
+ }
+
+ if (! MoreArgs (s))
+ {
+ strfcpy (err->data, "mono: too few arguments", err->dsize);
+ return (-1);
+ }
+
+ mutt_extract_token (buf, s, 0);
+
+ if (strcasecmp ("bold", buf->data) == 0)
+ attr |= A_BOLD;
+ else if (strcasecmp ("underline", buf->data) == 0)
+ attr |= A_UNDERLINE;
+ else if (strcasecmp ("none", buf->data) == 0)
+ attr = A_NORMAL;
+ else if (strcasecmp ("reverse", buf->data) == 0)
+ attr |= A_REVERSE;
+ else if (strcasecmp ("standout", buf->data) == 0)
+ attr |= A_STANDOUT;
+ else if (strcasecmp ("normal", buf->data) == 0)
+ attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
+ else
+ {
+ snprintf (err->data, err->dsize, "%s: no such attribute", buf->data);
+ return (-1);
+ }
+
+ if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY || object == MT_COLOR_INDEX)
+ {
+ if (! MoreArgs (s))
+ {
+ snprintf (err->data, err->dsize, "mono: missing regexp");
+ return (-1);
+ }
+
+ mutt_extract_token (buf, s, 0);
+ if (MoreArgs (s))
+ {
+ strfcpy (err->data, "mono: too many arguments", err->dsize);
+ return (-1);
+ }
+
+ /* if we have color, ignore the mono commands */
+ if (option (OPTNOCURSES)
+#ifdef HAVE_COLOR
+ || has_colors ()
+#endif
+ )
+ {
+ return 0;
+ }
+
+ if (object == MT_COLOR_HEADER)
+ r = add_pattern (&ColorHdrList, buf->data, 0, -1, -1, attr, err, 0);
+ else if (object == MT_COLOR_BODY)
+ r = add_pattern (&ColorBodyList, buf->data, 1, -1, -1, attr, err, 0);
+ else if (object == MT_COLOR_INDEX)
+ {
+ pattern_t *pat;
+ char tempbuf[LONG_STRING];
+
+ strfcpy (tempbuf, buf->data, sizeof (tempbuf));
+ mutt_check_simple (tempbuf, sizeof (tempbuf), SimpleSearch);
+ if ((pat = mutt_pattern_comp (tempbuf, M_FULL_MSG, err)) == NULL)
+ {
+ mutt_pattern_free (&pat);
+ return (-1);
+ }
+ r = add_pattern (&ColorIndexList, buf->data, 1, -1, -1, attr, err, 1);
+ mutt_cache_index_colors (Context);
+ set_option (OPTFORCEREDRAWINDEX);
+ }
+ }
+ else
+ {
+ /* if we have color, ignore the mono commands */
+ if (option (OPTNOCURSES)
+#ifdef HAVE_COLOR
+ || has_colors ()
+#endif
+ )
+ {
+ return 0;
+ }
+
+ if (object == MT_COLOR_QUOTED)
+ {
+ if (q_level >= ColorQuoteSize)
+ {
+ safe_realloc ((void **) &ColorQuote, (ColorQuoteSize += 2) * sizeof (int));
+ ColorQuote[ColorQuoteSize-2] = ColorDefs[MT_COLOR_QUOTED];
+ ColorQuote[ColorQuoteSize-1] = ColorDefs[MT_COLOR_QUOTED];
+ }
+ if (q_level >= ColorQuoteUsed)
+ ColorQuoteUsed = q_level + 1;
+ if (q_level == 0)
+ {
+ ColorDefs[MT_COLOR_QUOTED] = attr;
+
+ ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
+ for (q_level = 1; q_level < ColorQuoteUsed; q_level++)
+ {
+ if (ColorQuote[q_level] == A_NORMAL)
+ ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
+ }
+ }
+ else
+ ColorQuote[q_level] = attr;
+ }
+ else
+ ColorDefs[object] = attr;
+ }
+
+ return (r);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mutt_menu.h"
+#include "mime.h"
+#include "sort.h"
+#include "mailbox.h"
+#include "copy.h"
+#include "mx.h"
+#include "pager.h"
+
+#ifdef BUFFY_SIZE
+#include "buffy.h"
+#endif
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+
+
+
+
+
+
+
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <utime.h>
+
+extern char *ReleaseDate;
+
+/* The folder the user last saved to. Used by ci_save_message() */
+static char LastSaveFolder[_POSIX_PATH_MAX] = "";
+
+/* for compatibility with metamail */
+static int is_mmnoask (const char *buf)
+{
+ char tmp[LONG_STRING], *p, *q;
+ int lng;
+
+ if ((p = getenv ("MM_NOASK")) != NULL && *p)
+ {
+ if (strcmp (p, "1") == 0)
+ return (1);
+
+ strfcpy (tmp, p, sizeof (tmp));
+ p = tmp;
+
+ while ((p = strtok (p, ",")) != NULL)
+ {
+ if ((q = strrchr (p, '/')) != NULL)
+ {
+ if (*(q+1) == '*')
+ {
+ if (strncasecmp (buf, p, q-p) == 0)
+ return (1);
+ }
+ else
+ {
+ if (strcasecmp (buf, p) == 0)
+ return (1);
+ }
+ }
+ else
+ {
+ lng = strlen (p);
+ if (buf[lng] == '/' && strncasecmp (buf, p, lng) == 0)
+ return (1);
+ }
+
+ p = NULL;
+ }
+ }
+
+ return (0);
+}
+
+int mutt_display_message (HEADER *cur)
+{
+ char tempfile[_POSIX_PATH_MAX], buf[LONG_STRING];
+ int rc = 0, builtin = 0;
+ int cmflags = M_CM_DECODE | M_CM_DISPLAY;
+ FILE *fpout;
+
+ snprintf (buf, sizeof (buf), "%s/%s", TYPE (cur->content->type),
+ cur->content->subtype);
+
+ if (cur->mailcap && !mutt_is_autoview (buf))
+ {
+ if (is_mmnoask (buf))
+ rc = M_YES;
+ else
+ rc = query_quadoption (OPT_USEMAILCAP, "Display message using mailcap?");
+ if (rc < 0)
+ return 0;
+ else if (rc == M_YES)
+ {
+ MESSAGE *msg;
+
+ if ((msg = mx_open_message (Context, cur->msgno)) != NULL)
+ {
+ mutt_view_attachment (msg->fp, cur->content, M_REGULAR);
+ mx_close_message (&msg);
+ mutt_set_flag (Context, cur, M_READ, 1);
+ }
+ return 0;
+ }
+ }
+
+ mutt_parse_mime_message (Context, cur);
+
+
+
+#ifdef _PGPPATH
+ /* see if PGP is needed for this message. if so, we should exit curses */
+ if (cur->pgp)
+ {
+ if (cur->pgp & PGPENCRYPT)
+ {
+ if (!pgp_valid_passphrase ())
+ return 0;
+
+ cmflags |= M_CM_VERIFY;
+ mutt_message ("Invoking PGP...");
+ }
+ else if (cur->pgp & PGPSIGN)
+ {
+ /* find out whether or not the verify signature */
+ if (query_quadoption (OPT_VERIFYSIG, "Verify PGP signature?") == M_YES)
+ {
+ cmflags |= M_CM_VERIFY;
+ mutt_message ("Invoking PGP...");
+ }
+ }
+ }
+#endif
+
+
+
+
+
+
+
+ mutt_mktemp (tempfile);
+ if ((fpout = safe_fopen (tempfile, "w")) == NULL)
+ {
+ mutt_error ("Could not create temporary file!");
+ return (0);
+ }
+
+ if (strcmp (Pager, "builtin") == 0)
+ builtin = 1;
+ else
+ {
+ mutt_make_string (buf, sizeof (buf), PagerFmt, cur);
+ fputs (buf, fpout);
+ fputs ("\n\n", fpout);
+ }
+
+ if (mutt_copy_message (fpout, Context, cur, cmflags,
+ (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | CH_DECODE | CH_FROM) == -1)
+ {
+ fclose (fpout);
+ unlink (tempfile);
+ return 0;
+ }
+
+ if (fclose (fpout) != 0 && errno != EPIPE)
+ {
+ mutt_perror ("fclose");
+ mutt_unlink (tempfile);
+ return (0);
+ }
+
+ if (builtin)
+ {
+ pager_t info;
+
+ /* Invoke the builtin pager */
+ memset (&info, 0, sizeof (pager_t));
+ info.hdr = cur;
+ info.ctx = Context;
+ rc = mutt_pager (NULL, tempfile, 1, &info);
+ }
+ else
+ {
+ endwin ();
+ snprintf (buf, sizeof (buf), "%s %s", Pager, tempfile);
+ mutt_system (buf);
+ unlink (tempfile);
+ keypad (stdscr, TRUE);
+ mutt_set_flag (Context, cur, M_READ, 1);
+ if (option (OPTPROMPTAFTER))
+ {
+ mutt_ungetch (mutt_any_key_to_continue ("Command: "));
+ rc = km_dokey (MENU_PAGER);
+ }
+ else
+ rc = 0;
+ }
+
+ return rc;
+}
+
+void ci_bounce_message (HEADER *h, int *redraw)
+{
+ char prompt[SHORT_STRING];
+ char buf[HUGE_STRING] = { 0 };
+ ADDRESS *adr = NULL;
+ int rc;
+
+ snprintf (prompt, sizeof(prompt), "Bounce %smessage%s to: ",
+ h ? "" : "tagged ", h ? "" : "s");
+ rc = mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS);
+
+ if (option (OPTNEEDREDRAW))
+ {
+ unset_option (OPTNEEDREDRAW);
+ *redraw = REDRAW_FULL;
+ }
+
+ if (rc || !buf[0])
+ return;
+
+ if (!(adr = rfc822_parse_adrlist (adr, buf)))
+ {
+ mutt_error ("Error parsing address!");
+ return;
+ }
+
+ adr = mutt_expand_aliases (adr);
+
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), adr);
+
+ snprintf (prompt, (COLS > sizeof(prompt) ? sizeof(prompt) : COLS) - 13,
+ "Bounce message%s to %s", (h ? "" : "s"), buf);
+ strcat(prompt, "...?");
+ if (mutt_yesorno (prompt, 1) != 1)
+ {
+ rfc822_free_address (&adr);
+ CLEARLINE (LINES-1);
+ return;
+ }
+
+ mutt_bounce_message (h, adr);
+ rfc822_free_address (&adr);
+ mutt_message ("Message%s bounced.", h ? "" : "s");
+}
+
+void mutt_pipe_message_to_state (HEADER *h, STATE *s)
+{
+ if (option (OPTPIPEDECODE))
+ mutt_parse_mime_message (Context, h);
+ mutt_copy_message (s->fpout, Context, h,
+ option (OPTPIPEDECODE) ? M_CM_DECODE : 0,
+ option (OPTPIPEDECODE) ? CH_FROM | CH_WEED | CH_DECODE : CH_FROM);
+}
+
+int mutt_pipe_message (HEADER *h)
+{
+ STATE s;
+ char buffer[LONG_STRING];
+ int i, rc = 0;
+ pid_t thepid;
+
+ buffer[0] = 0;
+ if (mutt_get_field ("Pipe to command: ", buffer, sizeof (buffer), 0) != 0 ||
+ !buffer[0])
+ return 0;
+ mutt_expand_path (buffer, sizeof (buffer));
+
+ memset (&s, 0, sizeof (s));
+
+ endwin ();
+ if (h)
+ {
+
+
+
+#ifdef _PGPPATH
+ if (option (OPTPIPEDECODE))
+ {
+ mutt_parse_mime_message (Context, h);
+ if(h->pgp & PGPENCRYPT && !pgp_valid_passphrase())
+ return 1;
+ }
+ endwin ();
+#endif
+
+
+
+ thepid = mutt_create_filter (buffer, &s.fpout, NULL, NULL);
+ mutt_pipe_message_to_state (h, &s);
+ fclose (s.fpout);
+ rc = mutt_wait_filter (thepid);
+ }
+ else
+ { /* handle tagged messages */
+
+
+
+#ifdef _PGPPATH
+
+ if(option(OPTPIPEDECODE))
+ {
+ for (i = 0; i < Context->vcount; i++)
+ if(Context->hdrs[Context->v2r[i]]->tagged)
+ {
+ mutt_parse_mime_message(Context, Context->hdrs[Context->v2r[i]]);
+ if (Context->hdrs[Context->v2r[i]]->pgp & PGPENCRYPT &&
+ !pgp_valid_passphrase())
+ return 1;
+ }
+ }
+#endif
+
+
+
+ if (option (OPTPIPESPLIT))
+ {
+ for (i = 0; i < Context->vcount; i++)
+ {
+ if (Context->hdrs[Context->v2r[i]]->tagged)
+ {
+ endwin ();
+ thepid = mutt_create_filter (buffer, &(s.fpout), NULL, NULL);
+ mutt_pipe_message_to_state (Context->hdrs[Context->v2r[i]], &s);
+ /* add the message separator */
+ if (PipeSep)
+ state_puts (PipeSep, &s);
+ fclose (s.fpout);
+ if (mutt_wait_filter (thepid) != 0)
+ rc = 1;
+ }
+ }
+ }
+ else
+ {
+ endwin ();
+ thepid = mutt_create_filter (buffer, &(s.fpout), NULL, NULL);
+ for (i = 0; i < Context->vcount; i++)
+ {
+ if (Context->hdrs[Context->v2r[i]]->tagged)
+ {
+ mutt_pipe_message_to_state (Context->hdrs[Context->v2r[i]], &s);
+ /* add the message separator */
+ if (PipeSep)
+ state_puts (PipeSep, &s);
+ }
+ }
+ fclose (s.fpout);
+ if (mutt_wait_filter (thepid) != 0)
+ rc = 1;
+ }
+ }
+
+ if (rc || option (OPTWAITKEY))
+ mutt_any_key_to_continue (NULL);
+ return 1;
+}
+
+int mutt_select_sort (int reverse)
+{
+ int method = Sort; /* save the current method in case of abort */
+ int ch;
+
+ Sort = 0;
+ while (!Sort)
+ {
+ mvprintw (LINES - 1, 0,
+"%sSort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore?: ",
+ reverse ? "Rev-" : "");
+ ch = mutt_getch ();
+ if (ch == ERR || CI_is_return (ch))
+ {
+ Sort = method;
+ CLEARLINE (LINES-1);
+ return (-1);
+ }
+ switch (ch)
+ {
+ case 'c':
+ Sort = SORT_SCORE;
+ break;
+ case 'd':
+ Sort = SORT_DATE;
+ break;
+ case 'f':
+ Sort = SORT_FROM;
+ break;
+ case 'o':
+ Sort = SORT_TO;
+ break;
+ case 'r':
+ Sort = SORT_RECEIVED;
+ break;
+ case 's':
+ Sort = SORT_SUBJECT;
+ break;
+ case 't':
+ Sort = SORT_THREADS;
+ break;
+ case 'u':
+ Sort = SORT_ORDER;
+ break;
+ case 'z':
+ Sort = SORT_SIZE;
+ break;
+ default:
+ BEEP ();
+ break;
+ }
+ }
+ CLEARLINE (LINES-1);
+ if (reverse)
+ Sort |= SORT_REVERSE;
+
+ return (Sort != method ? 0 : -1); /* no need to resort if it's the same */
+}
+
+/* invoke a command in a subshell */
+void mutt_shell_escape (void)
+{
+ char buf[LONG_STRING];
+
+ buf[0] = 0;
+ if (mutt_get_field ("Shell command: ", buf, sizeof (buf), M_CMD) == 0)
+ {
+ if (!buf[0])
+ strfcpy (buf, Shell, sizeof (buf));
+ CLEARLINE (LINES-1);
+ endwin ();
+ fflush (stdout);
+ if (mutt_system (buf) != 0 || option (OPTWAITKEY))
+ mutt_any_key_to_continue (NULL);
+ }
+}
+
+/* enter a mutt command */
+void mutt_enter_command (void)
+{
+ BUFFER err, token;
+ char buffer[LONG_STRING], errbuf[SHORT_STRING];
+ int r;
+ int old_strictthreads = option (OPTSTRICTTHREADS);
+ int old_sortre = option (OPTSORTRE);
+
+ buffer[0] = 0;
+ if (mutt_get_field (":", buffer, sizeof (buffer), 0) != 0 || !buffer[0])
+ return;
+ err.data = errbuf;
+ err.dsize = sizeof (errbuf);
+ memset (&token, 0, sizeof (token));
+ r = mutt_parse_rc_line (buffer, &token, &err);
+ FREE (&token.data);
+ if (errbuf[0])
+ {
+ /* since errbuf could potentially contain printf() sequences in it,
+ we must call mutt_error() in this fashion so that vsprintf()
+ doesn't expect more arguments that we passed */
+ if (r == 0)
+ mutt_message ("%s", errbuf);
+ else
+ mutt_error ("%s", errbuf);
+ }
+ if (option (OPTSTRICTTHREADS) != old_strictthreads ||
+ option (OPTSORTRE) != old_sortre)
+ set_option (OPTNEEDRESORT);
+}
+
+void mutt_display_address (ADDRESS *adr)
+{
+ char buf[SHORT_STRING];
+
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), adr);
+ mutt_message ("%s", buf);
+}
+
+/* returns 1 if OK to proceed, 0 to abort */
+static int save_confirm_func (const char *s, struct stat *st)
+{
+ char tmp[_POSIX_PATH_MAX];
+ int ret = 1;
+
+ if (stat (s, st) != -1)
+ {
+ if (mx_get_magic (s) == -1)
+ {
+ mutt_error ("%s is not a mailbox!", s);
+ return 0;
+ }
+
+ if (option (OPTCONFIRMAPPEND))
+ {
+ snprintf (tmp, sizeof (tmp), "Append messages to %s?", s);
+ if (mutt_yesorno (tmp, 1) < 1)
+ ret = 0;
+ }
+ }
+ else
+ {
+ st->st_mtime = 0;
+ st->st_atime = 0;
+
+ if (errno == ENOENT)
+ {
+ if (option (OPTCONFIRMCREATE))
+ {
+ snprintf (tmp, sizeof (tmp), "Create %s?", s);
+ if (mutt_yesorno (tmp, 1) < 1)
+ ret = 0;
+ }
+ }
+ else
+ {
+ mutt_perror (s);
+ return 0;
+ }
+ }
+
+ CLEARLINE (LINES-1);
+ return (ret);
+}
+
+/* returns 0 if the copy/save was successful, or -1 on error/abort */
+int mutt_save_message (HEADER *h, int delete, int decode, int *redraw)
+{
+ int i, need_buffy_cleanup;
+ int cmflags = decode ? M_CM_DECODE : 0;
+ int chflags = decode ? CH_XMIT | CH_MIME : CH_UPDATE_LEN;
+ char prompt[SHORT_STRING], buf[_POSIX_PATH_MAX];
+ CONTEXT ctx;
+ struct stat st;
+#ifdef BUFFY_SIZE
+ BUFFY *tmp = NULL;
+#else
+ struct utimbuf ut;
+#endif
+
+ *redraw = 0;
+
+ snprintf (prompt, sizeof (prompt), "%s%s to mailbox",
+ decode ? (delete ? "Decode-save" : "Decode-copy") :
+ (delete ? "Save" : "Copy"), h ? "" : " tagged");
+
+ if (h)
+ mutt_default_save (buf, sizeof (buf), h);
+ else
+ {
+ /* look for the first tagged message */
+
+ for (i = 0; i < Context->vcount; i++)
+ {
+ if (Context->hdrs[Context->v2r[i]]->tagged)
+ {
+ h = Context->hdrs[Context->v2r[i]];
+ break;
+ }
+ }
+
+ if (h)
+ {
+ mutt_default_save (buf, sizeof (buf), h);
+ h = NULL;
+ }
+ }
+
+ mutt_pretty_mailbox (buf);
+ if (mutt_enter_fname (prompt, buf, sizeof (buf), redraw, 0) == -1)
+ return (-1);
+
+ if (*redraw != REDRAW_FULL)
+ {
+ if (!h)
+ *redraw = REDRAW_INDEX | REDRAW_STATUS;
+ else
+ *redraw = REDRAW_STATUS;
+ }
+
+ if (!buf[0])
+ return (-1);
+
+ /* This is an undocumented feature of ELM pointed out to me by Felix von
+ * Leitner <leitner@prz.fu-berlin.de>
+ */
+ if (strcmp (buf, ".") == 0)
+ strfcpy (buf, LastSaveFolder, sizeof (buf));
+ else
+ strfcpy (LastSaveFolder, buf, sizeof (LastSaveFolder));
+
+ mutt_expand_path (buf, sizeof (buf));
+
+ /* check to make sure that this file is really the one the user wants */
+ if (!save_confirm_func (buf, &st))
+ {
+ CLEARLINE (LINES-1);
+ return (-1);
+ }
+
+ mutt_message ("Copying to %s...", buf);
+
+ if (mx_open_mailbox (buf, M_APPEND, &ctx) != NULL)
+ {
+ if (h)
+ {
+ if (decode)
+ mutt_parse_mime_message (Context, h);
+ if (mutt_append_message (&ctx, Context, h, cmflags, chflags) == 0 && delete)
+ {
+ mutt_set_flag (Context, h, M_DELETE, 1);
+ mutt_set_flag (Context, h, M_TAG, 0);
+ }
+ }
+ else
+ {
+ for (i = 0; i < Context->vcount; i++)
+ {
+ if (Context->hdrs[Context->v2r[i]]->tagged)
+ {
+ h = Context->hdrs[Context->v2r[i]];
+ if (decode)
+ mutt_parse_mime_message (Context, h);
+ mutt_append_message (&ctx, Context, h, cmflags, chflags);
+ if (delete)
+ {
+ mutt_set_flag (Context, h, M_DELETE, 1);
+ mutt_set_flag (Context, h, M_TAG, 0);
+ }
+ }
+ }
+ }
+
+ need_buffy_cleanup = (ctx.magic == M_MBOX || ctx.magic == M_MMDF);
+
+ mx_close_mailbox (&ctx);
+
+ if (need_buffy_cleanup)
+ {
+#ifdef BUFFY_SIZE
+ tmp = mutt_find_mailbox (buf);
+ if (tmp && !tmp->new)
+ mutt_update_mailbox (tmp);
+#else
+ /* fix up the times so buffy won't get confused */
+ if (st.st_mtime > st.st_atime)
+ {
+ ut.actime = st.st_atime;
+ ut.modtime = time (NULL);
+ utime (buf, &ut);
+ }
+ else
+ utime (buf, NULL);
+#endif
+ }
+
+ mutt_clear_error ();
+ }
+
+ return (0);
+}
+
+static void print_msg (FILE *fp, CONTEXT *ctx, HEADER *h)
+{
+
+
+
+#ifdef _PGPPATH
+ if (h->pgp & PGPENCRYPT)
+ {
+ if (!pgp_valid_passphrase ())
+ return;
+ endwin ();
+ }
+#endif
+
+
+
+ mutt_parse_mime_message (ctx, h);
+ mutt_copy_message (fp, ctx, h, M_CM_DECODE, CH_WEED | CH_DECODE);
+}
+
+void mutt_print_message (HEADER *h)
+{
+ int i, count = 0;
+ pid_t thepid;
+ FILE *fp;
+
+ if (query_quadoption (OPT_PRINT,
+ h ? "Print message?" : "Print tagged messages?") != M_YES)
+ return;
+ endwin ();
+ if ((thepid = mutt_create_filter (PrintCmd, &fp, NULL, NULL)) == -1)
+ return;
+ if (h)
+ {
+ print_msg (fp, Context, h);
+ count++;
+ }
+ else
+ {
+ for (i = 0 ; i < Context->vcount ; i++)
+ {
+ if (Context->hdrs[Context->v2r[i]]->tagged)
+ {
+ print_msg (fp, Context, Context->hdrs[Context->v2r[i]]);
+ /* add a formfeed */
+ fputc ('\f', fp);
+ count++;
+ }
+ }
+ }
+ fclose (fp);
+ if (mutt_wait_filter (thepid) || option (OPTWAITKEY))
+ mutt_any_key_to_continue (NULL);
+ mutt_message ("Message%s printed", (count > 1) ? "s" : "");
+}
+
+void mutt_version (void)
+{
+ mutt_message ("Mutt %s (%s)", VERSION, ReleaseDate);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+
+#include <dirent.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+/* given a partial pathname, this routine fills in as much of the rest of the
+ * path as is unique.
+ *
+ * return 0 if ok, -1 if no matches
+ */
+int mutt_complete (char *s)
+{
+ char *p;
+ DIR *dirp;
+ struct dirent *de;
+ int i ,init=0;
+ size_t len;
+ char dirpart[_POSIX_PATH_MAX], exp_dirpart[_POSIX_PATH_MAX];
+ char filepart[_POSIX_PATH_MAX];
+
+ if (*s == '=' || *s == '+')
+ {
+ dirpart[0] = *s;
+ dirpart[1] = 0;
+ strfcpy (exp_dirpart, NONULL (Maildir), sizeof (exp_dirpart));
+ if ((p = strrchr (s, '/')))
+ {
+ *p++ = 0;
+ sprintf (exp_dirpart + strlen (exp_dirpart), "/%s", s+1);
+ sprintf (dirpart + strlen (dirpart), "%s/", s+1);
+ strfcpy (filepart, p, sizeof (filepart));
+ }
+ else
+ strfcpy (filepart, s + 1, sizeof (filepart));
+ dirp = opendir (exp_dirpart);
+ }
+ else
+ {
+ if ((p = strrchr (s, '/')))
+ {
+ if (p == s) /* absolute path */
+ {
+ p = s + 1;
+ strfcpy (dirpart, "/", sizeof (dirpart));
+ exp_dirpart[0] = 0;
+ strfcpy (filepart, p, sizeof (filepart));
+ dirp = opendir (dirpart);
+ }
+ else
+ {
+ *p = 0;
+ len = (size_t)(p - s);
+ strncpy (dirpart, s, len);
+ dirpart[len]=0;
+ p++;
+ strfcpy (filepart, p, sizeof (filepart));
+ strfcpy (exp_dirpart, dirpart, sizeof (exp_dirpart));
+ mutt_expand_path (exp_dirpart, sizeof (exp_dirpart));
+ dirp = opendir (exp_dirpart);
+ }
+ }
+ else
+ {
+ /* no directory name, so assume current directory. */
+ dirpart[0] = 0;
+ strfcpy (filepart, s, sizeof (filepart));
+ dirp = opendir (".");
+ }
+ }
+
+ if (dirp == NULL)
+ {
+ dprint (1, (debugfile, "mutt_complete(): %s: %s (errno %d).\n", exp_dirpart, strerror (errno), errno));
+ return (-1);
+ }
+
+ /*
+ * special case to handle when there is no filepart yet. find the first
+ * file/directory which is not ``.'' or ``..''
+ */
+ if ((len = strlen (filepart)) == 0)
+ {
+ while ((de = readdir (dirp)) != NULL)
+ {
+ if (strcmp (".", de->d_name) != 0 && strcmp ("..", de->d_name) != 0)
+ {
+ strfcpy (filepart, de->d_name, sizeof (filepart));
+ init++;
+ break;
+ }
+ }
+ }
+
+ while ((de = readdir (dirp)) != NULL)
+ {
+ if (strncmp (de->d_name, filepart, len) == 0)
+ {
+ if (init)
+ {
+ for (i=0; filepart[i] && de->d_name[i]; i++)
+ {
+ if (filepart[i] != de->d_name[i])
+ {
+ filepart[i] = 0;
+ break;
+ }
+ }
+ filepart[i] = 0;
+ }
+ else
+ {
+ char buf[_POSIX_PATH_MAX];
+ struct stat st;
+
+ strfcpy (filepart, de->d_name, sizeof(filepart));
+
+ /* check to see if it is a directory */
+ if (dirpart[0])
+ {
+ strfcpy (buf, exp_dirpart, sizeof (buf));
+ strcat (buf, "/");
+ }
+ else
+ buf[0] = 0;
+ strcat (buf, filepart);
+ if (stat (buf, &st) != -1 && (st.st_mode & S_IFDIR))
+ strcat (filepart, "/");
+ init = 1;
+ }
+ }
+ }
+ closedir (dirp);
+
+ if (dirpart[0])
+ {
+ strcpy (s, dirpart);
+ if (strcmp ("/", dirpart) != 0 && dirpart[0] != '=' && dirpart[0] != '+')
+ strcat (s, "/");
+ strcat (s, filepart);
+ }
+ else
+ strcpy (s, filepart);
+
+ return (init ? 0 : -1);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mutt_menu.h"
+#include "rfc1524.h"
+#include "mime.h"
+#include "attach.h"
+#include "mapping.h"
+
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#define CHECK_COUNT if (idxlen == 0) { mutt_error ("There are no attachments."); break; }
+
+
+
+enum
+{
+ HDR_FROM = 1,
+ HDR_TO,
+ HDR_CC,
+ HDR_BCC,
+ HDR_SUBJECT,
+ HDR_REPLYTO,
+ HDR_FCC,
+
+
+#ifdef _PGPPATH
+ HDR_PGP,
+ HDR_PGPSIGINFO,
+#endif
+
+
+
+ HDR_ATTACH = (HDR_FCC + 5) /* where to start printing the attachments */
+};
+
+#define HDR_XOFFSET 10
+
+static struct mapping_t ComposeHelp[] = {
+ { "Send", OP_COMPOSE_SEND_MESSAGE },
+ { "Abort", OP_EXIT },
+ { "To", OP_COMPOSE_EDIT_TO },
+ { "CC", OP_COMPOSE_EDIT_CC },
+ { "Subj", OP_COMPOSE_EDIT_SUBJECT },
+ { "Attach", OP_COMPOSE_ATTACH_FILE },
+ { "Descrip", OP_COMPOSE_EDIT_DESCRIPTION },
+ { "Help", OP_HELP },
+ { NULL }
+};
+
+void snd_entry (char *b, size_t blen, MUTTMENU *menu, int num)
+{
+ char t[SHORT_STRING], size[SHORT_STRING];
+ char tmp[_POSIX_PATH_MAX];
+ BODY *m;
+ ATTACHPTR **idx = (ATTACHPTR **) menu->data;
+ struct stat finfo;
+
+ m = idx[num]->content;
+
+ if (m->filename && m->filename[0])
+ {
+ if (stat (m->filename, &finfo) != -1)
+ mutt_pretty_size (size, sizeof (size), finfo.st_size);
+ else
+ strcpy (size, "0K");
+ strfcpy (tmp, m->filename, sizeof (tmp));
+ }
+ else
+ {
+ strcpy (size, "0K");
+ strcpy (tmp, "<no file>");
+ }
+ mutt_pretty_mailbox (tmp);
+
+ snprintf (t, sizeof (t), "[%.7s/%.10s, %.6s, %s]",
+ TYPE (m->type), m->subtype, ENCODING (m->encoding), size);
+
+ snprintf (b, blen, "%c%c%2d %-34.34s %s%s <%s>",
+ m->unlink ? '-' : ' ',
+ m->tagged ? '*' : ' ',
+ num + 1,
+ t,
+ idx[num]->tree ? idx[num]->tree : "",
+ tmp,
+ m->description ? m->description : "no description");
+}
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+
+static int pgp_send_menu (int bits)
+{
+ int c;
+ char *p;
+ char *micalg = NULL;
+ char input_signas[SHORT_STRING];
+ char input_micalg[SHORT_STRING];
+
+ mvaddstr (LINES-1, 0, "(e)ncrypt, (s)ign, sign (a)s, (b)oth, select (m)ic algorithm, or (f)orget it? ");
+ clrtoeol ();
+ do
+ {
+ mutt_refresh ();
+ if ((c = mutt_getch ()) == ERR)
+ break;
+ if (c == 'a')
+ {
+ unset_option(OPTPGPCHECKTRUST);
+ if ((p = pgp_ask_for_key (pgp_secring(PGP_SIGN),
+ NULL, "Sign as: ", NULL, KEYFLAG_CANSIGN, &micalg)))
+ {
+ snprintf (input_signas, sizeof (input_signas), "0x%s", p);
+ safe_free((void **) &PgpSignAs);
+ PgpSignAs = safe_strdup(input_signas);
+ safe_free((void **) &PgpSignMicalg);
+ PgpSignMicalg = micalg; /* micalg is malloc()ed by pgp_ask_for_key */
+ pgp_void_passphrase (); /* probably need a different passphrase */
+ safe_free ((void **) &p);
+ bits |= PGPSIGN;
+ }
+ }
+ else if (c == 'm')
+ {
+ if(!(bits & PGPSIGN))
+ mutt_error("This doesn't make sense if you don't want to sign the message.");
+ else
+ {
+ if(mutt_get_field("MIC algorithm: ", input_micalg, sizeof(input_micalg), 0) == 0)
+ {
+ if(strcasecmp(input_micalg, "pgp-md5") && strcasecmp(input_micalg, "pgp-sha1")
+ && strcasecmp(input_micalg, "pgp-rmd160"))
+ {
+ mutt_error("Unknown MIC algorithm!");
+ strfcpy(input_micalg, "x-unknown", sizeof(input_micalg));
+ }
+ safe_free((void **) &PgpSignMicalg);
+ PgpSignMicalg = safe_strdup(input_micalg);
+ }
+ }
+ }
+ else if (c == 'e')
+ bits |= PGPENCRYPT;
+ else if (c == 's')
+ bits |= PGPSIGN;
+ else if (c == 'b')
+ bits = PGPENCRYPT | PGPSIGN;
+ else if (c == 'f')
+ bits = 0;
+ else
+ {
+ BEEP ();
+ c = 0;
+ }
+ }
+ while (c == 0);
+ CLEARLINE (LINES-1);
+ mutt_refresh ();
+ return (bits);
+}
+#endif /* _PGPPATH */
+
+
+
+
+
+
+
+static void draw_envelope (HEADER *msg, char *fcc)
+{
+ char buf[STRING];
+ int w = COLS - HDR_XOFFSET;
+
+ mvaddstr (HDR_FROM, 0, " From: ");
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), msg->env->from);
+ printw ("%-*.*s", w, w, buf);
+
+ mvaddstr (HDR_TO, 0, " To: ");
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), msg->env->to);
+ printw ("%-*.*s", w, w, buf);
+
+ mvaddstr (HDR_CC, 0, " Cc: ");
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), msg->env->cc);
+ printw ("%-*.*s", w, w, buf);
+
+ mvaddstr (HDR_BCC, 0, " Bcc: ");
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), msg->env->bcc);
+ printw ("%-*.*s", w, w, buf);
+
+ mvaddstr (HDR_SUBJECT, 0, " Subject: ");
+ if (msg->env->subject)
+ printw ("%-*.*s", w, w, msg->env->subject);
+ else
+ clrtoeol ();
+
+ mvaddstr (HDR_REPLYTO, 0, "Reply-To: ");
+ if (msg->env->reply_to)
+ {
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), msg->env->reply_to);
+ printw ("%-*.*s", w, w, buf);
+ }
+ else
+ clrtoeol ();
+
+ mvaddstr (HDR_FCC, 0, " Fcc: ");
+ addstr (fcc);
+
+
+
+#ifdef _PGPPATH
+ mvaddstr (HDR_PGP, 0, " PGP: ");
+ if ((msg->pgp & (PGPENCRYPT | PGPSIGN)) == (PGPENCRYPT | PGPSIGN))
+ addstr ("Sign, Encrypt");
+ else if (msg->pgp & PGPENCRYPT)
+ addstr ("Encrypt");
+ else if (msg->pgp & PGPSIGN)
+ addstr ("Sign");
+ else
+ addstr ("Clear");
+ clrtoeol ();
+
+ if (msg->pgp & PGPSIGN)
+ {
+ mvaddstr (HDR_PGPSIGINFO, 0, " sign as: ");
+ if (PgpSignAs)
+ printw ("%s", PgpSignAs);
+ else
+ printw ("%s", "<default>");
+ clrtoeol ();
+ mvaddstr (HDR_PGPSIGINFO, 40, "MIC algorithm: ");
+ printw ("%s", NONULL(PgpSignMicalg));
+ clrtoeol ();
+ }
+ else
+ {
+ mvaddstr(HDR_PGPSIGINFO, 0, "");
+ clrtoeol();
+ }
+
+#endif /* _PGPPATH */
+
+
+
+
+
+
+
+
+
+
+
+ mvaddstr (HDR_ATTACH - 1, 0, "===== Attachments =====");
+}
+
+static int edit_address_list (int line, ENVELOPE *env)
+{
+ char buf[HUGE_STRING] = ""; /* needs to be large for alias expansion */
+ ADDRESS **addr;
+ char *prompt;
+
+ switch (line)
+ {
+ case HDR_FROM:
+ prompt = "From: ";
+ addr = &env->from;
+ break;
+ case HDR_TO:
+ prompt = "To: ";
+ addr = &env->to;
+ break;
+ case HDR_CC:
+ prompt = "Cc: ";
+ addr = &env->cc;
+ break;
+ case HDR_BCC:
+ prompt = "Bcc: ";
+ addr = &env->bcc;
+ break;
+ case HDR_REPLYTO:
+ prompt = "Reply-To: ";
+ addr = &env->reply_to;
+ break;
+ default:
+ return 0;
+ }
+
+ rfc822_write_address (buf, sizeof (buf), *addr);
+ if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS) != 0)
+ return 0;
+
+ rfc822_free_address (addr);
+ *addr = mutt_parse_adrlist (*addr, buf);
+ *addr = mutt_expand_aliases (*addr);
+
+ if (option (OPTNEEDREDRAW))
+ {
+ unset_option (OPTNEEDREDRAW);
+ return (REDRAW_FULL);
+ }
+
+ /* redraw the expanded list so the user can see the result */
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), *addr);
+ move (line, HDR_XOFFSET);
+ printw ("%-*.*s", COLS - HDR_XOFFSET, COLS - HDR_XOFFSET, buf);
+
+ return 0;
+}
+
+static int delete_attachment (MUTTMENU *menu, short *idxlen, int x)
+{
+ ATTACHPTR **idx = (ATTACHPTR **) menu->data;
+ int y;
+
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+
+ if (x == 0 && menu->max == 1)
+ {
+ mutt_error ("You may not delete the only attachment.");
+ idx[x]->content->tagged = 0;
+ return (-1);
+ }
+
+ for (y = 0; y < *idxlen; y++)
+ {
+ if (idx[y]->content->next == idx[x]->content)
+ {
+ idx[y]->content->next = idx[x]->content->next;
+ break;
+ }
+ }
+
+ idx[x]->content->next = NULL;
+ idx[x]->content->parts = NULL;
+ mutt_free_body (&(idx[x]->content));
+ safe_free ((void **) &idx[x]->tree);
+ safe_free ((void **) &idx[x]);
+ for (; x < *idxlen - 1; x++)
+ idx[x] = idx[x+1];
+ menu->max = --(*idxlen);
+
+ return (0);
+}
+
+/* return values:
+ *
+ * 1 message should be postponed
+ * 0 normal exit
+ * -1 abort message
+ */
+int mutt_send_menu (HEADER *msg, /* structure for new message */
+ char *fcc, /* where to save a copy of the message */
+ size_t fcclen,
+ HEADER *cur) /* current message */
+{
+ char helpstr[SHORT_STRING];
+ char buf[LONG_STRING];
+ char fname[_POSIX_PATH_MAX];
+ MUTTMENU *menu;
+ ATTACHPTR **idx = NULL;
+ short idxlen = 0;
+ short idxmax = 0;
+ int i;
+ int r = -1; /* return value */
+ int op = 0;
+ int loop = 1;
+ int fccSet = 0; /* has the user edited the Fcc: field ? */
+
+ idx = mutt_gen_attach_list (msg->content, idx, &idxlen, &idxmax, 0, 1);
+
+ menu = mutt_new_menu ();
+ menu->menu = MENU_COMPOSE;
+ menu->offset = HDR_ATTACH;
+ menu->max = idxlen;
+ menu->make_entry = snd_entry;
+ menu->tag = mutt_tag_attach;
+ menu->title = "Compose";
+ menu->data = idx;
+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp);
+
+ while (loop)
+ {
+ switch (op = mutt_menuLoop (menu))
+ {
+ case OP_REDRAW:
+ draw_envelope (msg, fcc);
+ menu->offset = HDR_ATTACH;
+ menu->pagelen = LINES - HDR_ATTACH - 2;
+ break;
+ case OP_COMPOSE_EDIT_FROM:
+ menu->redraw = edit_address_list (HDR_FROM, msg->env);
+ break;
+ case OP_COMPOSE_EDIT_TO:
+ menu->redraw = edit_address_list (HDR_TO, msg->env);
+ break;
+ case OP_COMPOSE_EDIT_BCC:
+ menu->redraw = edit_address_list (HDR_BCC, msg->env);
+ break;
+ case OP_COMPOSE_EDIT_CC:
+ menu->redraw = edit_address_list (HDR_CC, msg->env);
+ break;
+ case OP_COMPOSE_EDIT_SUBJECT:
+ if (msg->env->subject)
+ strfcpy (buf, msg->env->subject, sizeof (buf));
+ else
+ buf[0] = 0;
+ if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0)
+ {
+ safe_free ((void **) &msg->env->subject);
+ msg->env->subject = safe_strdup (buf);
+ move (HDR_SUBJECT, HDR_XOFFSET);
+ clrtoeol ();
+ if (msg->env->subject)
+ printw ("%-*.*s", COLS-HDR_XOFFSET, COLS-HDR_XOFFSET,
+ msg->env->subject);
+ }
+ break;
+ case OP_COMPOSE_EDIT_REPLY_TO:
+ menu->redraw = edit_address_list (HDR_REPLYTO, msg->env);
+ break;
+ case OP_COMPOSE_EDIT_FCC:
+ strfcpy (buf, fcc, sizeof (buf));
+ if (mutt_get_field ("Fcc: ", buf, sizeof (buf), M_FILE | M_CLEAR) == 0)
+ {
+ strfcpy (fcc, buf, _POSIX_PATH_MAX);
+ mutt_pretty_mailbox (fcc);
+ mvprintw (HDR_FCC, HDR_XOFFSET, "%-*.*s",
+ COLS - HDR_XOFFSET, COLS - HDR_XOFFSET, fcc);
+ fccSet = 1;
+ }
+ MAYBE_REDRAW (menu->redraw);
+ break;
+ case OP_COMPOSE_EDIT_MESSAGE:
+ if (strcmp ("builtin", Editor) != 0 && !option (OPTEDITHDRS))
+ {
+ mutt_edit_file (Editor, msg->content->filename);
+ mutt_update_encoding (msg->content);
+ menu->redraw = REDRAW_CURRENT;
+ break;
+ }
+ /* fall through */
+ case OP_COMPOSE_EDIT_HEADERS:
+ if (op == OP_COMPOSE_EDIT_HEADERS ||
+ (op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS)))
+ {
+ mutt_edit_headers (strcmp ("builtin", Editor) == 0 ? Visual : Editor,
+ msg->content->filename, msg, fcc, fcclen);
+ }
+ else
+ {
+ /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the
+ attachment list could change if the user invokes ~v to edit
+ the message with headers, in which we need to execute the
+ code below to regenerate the index array */
+ mutt_builtin_editor (msg->content->filename, msg, cur);
+ }
+ mutt_update_encoding (msg->content);
+
+ /* attachments may have been added */
+ if (idxlen && idx[idxlen - 1]->content->next)
+ {
+ for (i = 0; i < idxlen; i++)
+ safe_free ((void **) &idx[i]);
+ idxlen = 0;
+ idx = mutt_gen_attach_list (msg->content, idx, &idxlen, &idxmax, 0, 1);
+ menu->max = idxlen;
+ }
+
+ menu->redraw = REDRAW_FULL;
+ break;
+
+
+
+#ifdef _PGPPATH
+ case OP_COMPOSE_ATTACH_KEY:
+
+ if (idxlen == idxmax)
+ {
+ safe_realloc ((void **) &idx, sizeof (ATTACHPTR *) * (idxmax += 5));
+ menu->data = idx;
+ }
+
+ idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
+ if ((idx[idxlen]->content = pgp_make_key_attachment(NULL)) != NULL)
+ {
+ idx[idxlen]->level = (idxlen > 0) ? idx[idxlen-1]->level : 0;
+
+ if(idxlen)
+ idx[idxlen - 1]->content->next = idx[idxlen]->content;
+
+ menu->current = idxlen++;
+ mutt_update_tree (idx, idxlen);
+ menu->max = idxlen;
+ menu->redraw |= REDRAW_INDEX;
+ }
+ else
+ safe_free ((void **) &idx[idxlen]);
+
+ menu->redraw |= REDRAW_STATUS;
+
+ if(option(OPTNEEDREDRAW))
+ {
+ menu->redraw = REDRAW_FULL;
+ unset_option(OPTNEEDREDRAW);
+ }
+
+ break;
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+ case OP_COMPOSE_ATTACH_FILE:
+ fname[0] = 0;
+ if (mutt_enter_fname ("Attach file", fname, sizeof (fname),
+ &menu->redraw, 0) == -1)
+ break;
+ if (!fname[0])
+ continue;
+ mutt_expand_path (fname, sizeof (fname));
+
+ /* check to make sure the file exists and is readable */
+ if (access (fname, R_OK) == -1)
+ {
+ mutt_perror (fname);
+ break;
+ }
+
+ if (idxlen == idxmax)
+ {
+ safe_realloc ((void **) &idx, sizeof (ATTACHPTR *) * (idxmax += 5));
+ menu->data = idx;
+ }
+
+ idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
+ if ((idx[idxlen]->content = mutt_make_attach (fname)) != NULL)
+ {
+ idx[idxlen]->level = (idxlen > 0) ? idx[idxlen-1]->level : 0;
+
+ if (idxlen)
+ idx[idxlen - 1]->content->next = idx[idxlen]->content;
+
+ menu->current = idxlen++;
+ mutt_update_tree (idx, idxlen);
+ menu->max = idxlen;
+ menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
+ }
+ else
+ {
+ mutt_error ("Unable to attach file!");
+ safe_free ((void **) &idx[idxlen]);
+ }
+ break;
+
+ case OP_DELETE:
+ CHECK_COUNT;
+ if (delete_attachment (menu, &idxlen, menu->current) == -1)
+ break;
+ mutt_update_tree (idx, idxlen);
+ if (idxlen)
+ {
+ if (menu->current > idxlen - 1)
+ menu->current = idxlen - 1;
+ }
+ else
+ menu->current = 0;
+
+ if (menu->current == 0)
+ msg->content = idx[0]->content;
+ break;
+
+ case OP_COMPOSE_EDIT_DESCRIPTION:
+ CHECK_COUNT;
+ strfcpy (buf,
+ idx[menu->current]->content->description ?
+ idx[menu->current]->content->description : "",
+ sizeof (buf));
+ if (mutt_get_field ("Description: ", buf, sizeof (buf), 0) == 0)
+ {
+ safe_free ((void **) &idx[menu->current]->content->description);
+ idx[menu->current]->content->description = safe_strdup (buf);
+ menu->redraw = REDRAW_CURRENT;
+ }
+ break;
+
+ case OP_COMPOSE_EDIT_TYPE:
+ CHECK_COUNT;
+ snprintf (buf, sizeof (buf), "%s/%s",
+ TYPE (idx[menu->current]->content->type),
+ idx[menu->current]->content->subtype);
+ if (mutt_get_field ("Content-Type: ", buf, sizeof (buf), 0) == 0 && buf[0])
+ {
+ char *p = strchr (buf, '/');
+
+ if (p)
+ {
+ *p++ = 0;
+ if ((i = mutt_check_mime_type (buf)) != TYPEOTHER)
+ {
+ idx[menu->current]->content->type = i;
+ safe_free ((void **) &idx[menu->current]->content->subtype);
+ idx[menu->current]->content->subtype = safe_strdup (p);
+ menu->redraw = REDRAW_CURRENT;
+ }
+ }
+ }
+ break;
+
+ case OP_COMPOSE_EDIT_ENCODING:
+ CHECK_COUNT;
+ strfcpy (buf, ENCODING (idx[menu->current]->content->encoding),
+ sizeof (buf));
+ if (mutt_get_field ("Content-Transfer-Encoding: ", buf,
+ sizeof (buf), 0) == 0 && buf[0])
+ {
+ if ((i = mutt_check_encoding (buf)) != ENCOTHER)
+ {
+ idx[menu->current]->content->encoding = i;
+ menu->redraw = REDRAW_CURRENT;
+ }
+ else
+ mutt_error ("Invalid encoding.");
+ }
+ break;
+
+ case OP_COMPOSE_SEND_MESSAGE:
+ if (!fccSet && *fcc)
+ {
+ if ((i = query_quadoption (OPT_COPY, "Save a copy of this message?"))
+ == -1)
+ break;
+ else if (i == M_NO)
+ *fcc = 0;
+ }
+
+ loop = 0;
+ r = 0;
+ break;
+
+ case OP_COMPOSE_EDIT_FILE:
+ CHECK_COUNT;
+ mutt_edit_file (strcmp ("builtin", Editor) == 0 ? Visual : Editor,
+ idx[menu->current]->content->filename);
+ mutt_update_encoding (idx[menu->current]->content);
+ menu->redraw = REDRAW_CURRENT;
+ break;
+
+ case OP_COMPOSE_TOGGLE_UNLINK:
+ CHECK_COUNT;
+ idx[menu->current]->content->unlink = !idx[menu->current]->content->unlink;
+ menu->redraw = REDRAW_INDEX;
+ break;
+
+ case OP_COMPOSE_RENAME_FILE:
+ CHECK_COUNT;
+ strfcpy (fname, idx[menu->current]->content->filename, sizeof (fname));
+ mutt_pretty_mailbox (fname);
+ if (mutt_get_field ("Rename to: ", fname, sizeof (fname), M_FILE) == 0
+ && fname[0])
+ {
+ mutt_expand_path (fname, sizeof (fname));
+ if (!mutt_rename_file (idx[menu->current]->content->filename, fname))
+ {
+ safe_free ((void **) &idx[menu->current]->content->filename);
+ idx[menu->current]->content->filename = safe_strdup (fname);
+ menu->redraw = REDRAW_CURRENT;
+ }
+ }
+ break;
+
+ case OP_COMPOSE_NEW_MIME:
+ {
+ char type[STRING];
+ char *p;
+ int itype;
+ FILE *fp;
+
+ CLEARLINE (LINES-1);
+ fname[0] = 0;
+ if (mutt_get_field ("New file: ", fname, sizeof (fname), M_FILE) != 0
+ || !fname[0])
+ continue;
+ mutt_expand_path (fname, sizeof (fname));
+
+ /* Call to lookup_mime_type () ? maybe later */
+ type[0] = 0;
+ if (mutt_get_field ("Content-Type: ", type, sizeof (type), 0) != 0
+ || !type[0])
+ continue;
+
+ if (!(p = strchr (type, '/')))
+ {
+ mutt_error ("Content-Type is of the form base/sub");
+ continue;
+ }
+ *p++ = 0;
+ if ((itype = mutt_check_mime_type (type)) == TYPEOTHER)
+ {
+ mutt_error ("Unknown Content-Type %s", type);
+ continue;
+ }
+ if (idxlen == idxmax)
+ {
+ safe_realloc ((void **) &idx, sizeof (ATTACHPTR *) * (idxmax += 5));
+ menu->data = idx;
+ }
+
+ idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
+ /* Touch the file */
+ if (!(fp = safe_fopen (fname, "w")))
+ {
+ mutt_error ("Can't create file %s", fname);
+ safe_free ((void **) &idx[idxlen]);
+ continue;
+ }
+ fclose (fp);
+
+ if ((idx[idxlen]->content = mutt_make_attach (fname)) == NULL)
+ {
+ mutt_error ("What we have here is a failure to make an attachment");
+ continue;
+ }
+
+ idx[idxlen]->level = (idxlen > 0) ? idx[idxlen-1]->level : 0;
+ if (idxlen)
+ idx[idxlen - 1]->content->next = idx[idxlen]->content;
+
+ menu->current = idxlen++;
+ mutt_update_tree (idx, idxlen);
+ menu->max = idxlen;
+
+ idx[menu->current]->content->type = itype;
+ safe_free ((void **) &idx[menu->current]->content->subtype);
+ idx[menu->current]->content->subtype = safe_strdup (p);
+ idx[menu->current]->content->unlink = 1;
+ menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
+
+ if (mutt_compose_attachment (idx[menu->current]->content))
+ {
+ mutt_update_encoding (idx[menu->current]->content);
+ menu->redraw = REDRAW_FULL;
+ }
+ }
+ break;
+
+
+
+
+
+
+ case OP_COMPOSE_EDIT_MIME:
+ CHECK_COUNT;
+ if (mutt_edit_attachment (idx[menu->current]->content, 0))
+ {
+ mutt_update_encoding (idx[menu->current]->content);
+ menu->redraw = REDRAW_FULL;
+ }
+ break;
+
+ case OP_VIEW_ATTACH:
+ case OP_DISPLAY_HEADERS:
+ CHECK_COUNT;
+ mutt_attach_display_loop (menu, op, NULL, idx);
+ menu->redraw = REDRAW_FULL;
+ mutt_clear_error ();
+ break;
+
+ case OP_SAVE:
+ CHECK_COUNT;
+ mutt_save_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content);
+ break;
+
+ case OP_PRINT:
+ CHECK_COUNT;
+ mutt_print_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content);
+ break;
+
+ case OP_PIPE:
+ case OP_FILTER:
+ CHECK_COUNT;
+ mutt_pipe_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content, op == OP_FILTER);
+ if (op == OP_FILTER)
+ menu->redraw = REDRAW_CURRENT; /* cte might have changed */
+ break;
+
+
+
+
+
+
+ case OP_EXIT:
+ if ((i = query_quadoption (OPT_POSTPONE, "Postpone this message?")) == M_NO)
+ {
+ while (idxlen-- > 0)
+ {
+ /* avoid freeing other attachments */
+ idx[idxlen]->content->next = NULL;
+ idx[idxlen]->content->parts = NULL;
+ mutt_free_body (&idx[idxlen]->content);
+ safe_free ((void **) &idx[idxlen]->tree);
+ safe_free ((void **) &idx[idxlen]);
+ }
+ safe_free ((void **) &idx);
+ idxlen = 0;
+ idxmax = 0;
+ r = -1;
+ loop = 0;
+ break;
+ }
+ else if (i == -1)
+ break; /* abort */
+
+ /* fall through to postpone! */
+
+ case OP_COMPOSE_POSTPONE_MESSAGE:
+ loop = 0;
+ r = 1;
+ break;
+
+ case OP_COMPOSE_ISPELL:
+ endwin ();
+ snprintf (buf, sizeof (buf), "%s -x %s", Ispell, msg->content->filename);
+ mutt_system (buf);
+ break;
+
+
+
+#ifdef _PGPPATH
+ case OP_COMPOSE_PGP_MENU:
+
+ msg->pgp = pgp_send_menu (msg->pgp);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_FORGET_PASSPHRASE:
+
+ mutt_forget_passphrase ();
+ break;
+
+#endif /* _PGPPATH */
+
+
+
+ }
+ }
+
+ mutt_menuDestroy (&menu);
+
+ if (idxlen)
+ {
+ msg->content = idx[0]->content;
+ for (i = 0; i < idxlen; i++)
+ safe_free ((void **) &idx[i]);
+ }
+ else
+ msg->content = NULL;
+
+ safe_free ((void **) &idx);
+
+ return (r);
+}
--- /dev/null
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Enable debugging info */
+#define DEBUG
+
+/* Does your version of PGP support the PGPPASSFD environment variable? */
+#define HAVE_PGPPASSFD
+
+/* Disable the X-Mailer header? */
+#undef NO_XMAILER
+
+/* What is your domain name? */
+#undef DOMAIN
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef pid_t
+
+/* Define as the return type of signal handlers (int or void). */
+#undef RETSIGTYPE
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define if `sys_siglist' is declared by <signal.h>. */
+#undef SYS_SIGLIST_DECLARED
+
+/* Mutt version info */
+#undef VERSION
+
+/* use dotlocking to lock mailboxes? */
+#undef USE_DOTLOCK
+
+/* use flock() to lock mailboxes? */
+#undef USE_FLOCK
+
+/* Use fcntl() to lock folders? */
+#undef USE_FCNTL
+
+/*
+ * Define if you have problems with mutt not detecting new/old mailboxes
+ * over NFS. Some NFS implementations incorrectly cache the attributes
+ * of small files.
+ */
+#undef NFS_ATTRIBUTE_HACK
+
+/* Do you want support for the POP3 protocol? (--enable-pop) */
+#undef USE_POP
+
+/*
+ * Is mail spooled to the user's home directory? If defined, MAILPATH should
+ * be set to the filename of the spool mailbox relative the the home
+ * directory.
+ * use: configure --with-homespool=FILE
+ */
+#undef HOMESPOOL
+
+/* Where new mail is spooled */
+#undef MAILPATH
+
+/* Should I just use the domain name? (--enable-hidden-host) */
+#undef HIDDEN_HOST
+
+/* Where to find sendmail on your system */
+#undef SENDMAIL
+
+/* Where is PGP located on your system? */
+#undef _PGPPATH
+
+/* Where is PGP 2.* located on your system? */
+#undef _PGPV2PATH
+
+/* Where is PGP 5 located on your system? */
+#undef _PGPV3PATH
+
+/* Do we have PGP 2.*? */
+#undef HAVE_PGP2
+
+/* Do we have PGP 5.0 or up? */
+#undef HAVE_PGP5
+
+/* Where to find ispell on your system? */
+#undef ISPELL
+
+/* Should Mutt run setgid "mail" ? */
+#undef USE_SETGID
+
+/* Does your curses library support color? */
+#undef HAVE_COLOR
+
+/* Are we using GNU rx? */
+#undef USE_GNU_RX
+
+/* Compiling with SLang instead of curses/ncurses? */
+#undef USE_SLANG_CURSES
+
+/* program to use for shell commands */
+#define EXECSHELL "/bin/sh"
+
+/* The "buffy_size" feature */
+#undef BUFFY_SIZE
+
+/* The result of isprint() is unreliable? */
+#undef LOCALES_HACK
+
+/* Enable exact regeneration of email addresses as parsed? NOTE: this requires
+ significant more memory when defined. */
+#undef EXACT_ADDRESS
+
+/* Does your system have the snprintf() call? */
+#undef HAVE_SNPRINTF
+
+/* Does your system have the vsnprintf() call? */
+#undef HAVE_VSNPRINTF
+
+/* The number of bytes in a long. */
+#undef SIZEOF_LONG
+
+/* Define if you have the bkgdset function. */
+#undef HAVE_BKGDSET
+
+/* Define if you have the curs_set function. */
+#undef HAVE_CURS_SET
+
+/* Define if you have the ftruncate function. */
+#undef HAVE_FTRUNCATE
+
+/* Define if you have the meta function. */
+#undef HAVE_META
+
+/* Define if you have the regcomp function. */
+#undef HAVE_REGCOMP
+
+/* Define if you have the resizeterm function. */
+#undef HAVE_RESIZETERM
+
+/* Define if you have the setegid function. */
+#undef HAVE_SETEGID
+
+/* Define if you have the srand48 function. */
+#undef HAVE_SRAND48
+
+/* Define if you have the strcasecmp function. */
+#undef HAVE_STRCASECMP
+
+/* Define if you have the strerror function. */
+#undef HAVE_STRERROR
+
+/* Define if you have the strftime function. */
+#undef HAVE_STRFTIME
+
+/* Define if you have the typeahead function. */
+#undef HAVE_TYPEAHEAD
+
+/* Define if you have the use_default_colors function. */
+#undef HAVE_USE_DEFAULT_COLORS
+
+/* Define if you have the <ncurses.h> header file. */
+#undef HAVE_NCURSES_H
+
+/* Define if you have the <stdarg.h> header file. */
+#undef HAVE_STDARG_H
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the intl library (-lintl). */
+#undef HAVE_LIBINTL
+
+/* Define if you have the nsl library (-lnsl). */
+#undef HAVE_LIBNSL
+
+/* Define if you have the socket library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Define if you have the x library (-lx). */
+#undef HAVE_LIBX
--- /dev/null
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.12
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --with-slang[=DIR] use S-Lang instead of ncurses"
+ac_help="$ac_help
+ --with-curses=DIR ncurses is installed in "
+ac_help="$ac_help
+ --with-rx[=DIR] Use GNU rx "
+ac_help="$ac_help
+ --with-homespool[=FILE] file in user's directory where new mail is spooled"
+ac_help="$ac_help
+ --with-mailpath=DIR directory where spool mailboxes are located"
+ac_help="$ac_help
+ --with-sharedir=PATH specify where to put arch independent files"
+ac_help="$ac_help
+ --with-domain=DOMAIN Specify your DNS domain name "
+ac_help="$ac_help
+ --enable-hidden-host Only use the domain name for local addresses"
+ac_help="$ac_help
+ --enable-pop Enable POP3 support"
+ac_help="$ac_help
+ --enable-flock Use flock() to lock files"
+ac_help="$ac_help
+ --disable-fcntl Do NOT use fcntl() to lock files "
+ac_help="$ac_help
+ --disable-warnings turn off compiler warnings (not recommended)"
+ac_help="$ac_help
+ --enable-nfs-fix Work around an NFS with broken attributes caching "
+ac_help="$ac_help
+ --enable-buffy-size Use file size attribute instead of access time "
+ac_help="$ac_help
+ --enable-locales-fix The result of isprint() is unreliable "
+ac_help="$ac_help
+ --with-exec-shell=SHELL Specify alternate shell (ONLY if /bin/sh is broken)"
+ac_help="$ac_help
+ --enable-exact-address enable regeneration of email addresses"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.12"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=mutt.h
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+VERSION=0.92.8
+SUBVERSION=''
+
+echo $ac_n "checking for prefix""... $ac_c" 1>&6
+echo "configure:561: checking for prefix" >&5
+if test x$prefix = xNONE; then
+ mutt_cv_prefix=$ac_default_prefix
+else
+ mutt_cv_prefix=$prefix
+fi
+echo "$ac_t""$mutt_cv_prefix" 1>&6
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:572: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:601: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ ac_prog_rejected=no
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:649: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext <<EOF
+#line 659 "configure"
+#include "confdefs.h"
+main(){return(0);}
+EOF
+if { (eval echo configure:663: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:683: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:688: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:697: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+ ac_test_CFLAGS="${CFLAGS+set}"
+ ac_save_CFLAGS="$CFLAGS"
+ CFLAGS=
+ echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:712: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+ if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+ elif test $ac_cv_prog_cc_g = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-O2"
+ fi
+else
+ GCC=
+ test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:740: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftestmake <<\EOF
+all:
+ @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+ eval ac_cv_prog_make_${ac_make}_set=yes
+else
+ eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ SET_MAKE=
+else
+ echo "$ac_t""no" 1>&6
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:796: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ for ac_prog in ginstall installbsd scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ # OSF/1 installbsd also uses dspmsg, but is usable.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+# Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:849: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_SENDMAIL'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$SENDMAIL" in
+ /*)
+ ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in `echo $PATH | sed "s/:/ /"` /usr/sbin /usr/lib$ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_SENDMAIL="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL="no"
+ ;;
+esac
+fi
+SENDMAIL="$ac_cv_path_SENDMAIL"
+if test -n "$SENDMAIL"; then
+ echo "$ac_t""$SENDMAIL" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+cat >> confdefs.h <<EOF
+#define SENDMAIL "$ac_cv_path_SENDMAIL"
+EOF
+
+
+OPS='$(srcdir)/OPS'
+if test -f $srcdir/pgp.c; then
+ SUBVERSION=i
+ PGPPATH=no
+ # Extract the first word of "pgpk", so it can be a program name with args.
+set dummy pgpk; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:890: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_PGPK'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$PGPK" in
+ /*)
+ ac_cv_path_PGPK="$PGPK" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_PGPK="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_PGPK" && ac_cv_path_PGPK="no"
+ ;;
+esac
+fi
+PGPK="$ac_cv_path_PGPK"
+if test -n "$PGPK"; then
+ echo "$ac_t""$PGPK" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $PGPK != no ; then
+ PGPK=`echo $PGPK | sed 's,.$,,'`
+ cat >> confdefs.h <<EOF
+#define _PGPV3PATH "$PGPK"
+EOF
+
+ PGPPATH="$PGPK"
+ cat >> confdefs.h <<\EOF
+#define HAVE_PGP5 1
+EOF
+
+ fi
+
+ # Extract the first word of "pgp", so it can be a program name with args.
+set dummy pgp; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:935: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_PGP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$PGP" in
+ /*)
+ ac_cv_path_PGP="$PGP" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_PGP="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_PGP" && ac_cv_path_PGP="no"
+ ;;
+esac
+fi
+PGP="$ac_cv_path_PGP"
+if test -n "$PGP"; then
+ echo "$ac_t""$PGP" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $PGP != no ; then
+ cat >> confdefs.h <<EOF
+#define _PGPV2PATH "$PGP"
+EOF
+
+ PGPPATH="$PGP"
+ cat >> confdefs.h <<\EOF
+#define HAVE_PGP2 1
+EOF
+
+ fi
+
+ if test $PGPPATH != no ; then
+ cat >> confdefs.h <<EOF
+#define _PGPPATH "$PGPPATH"
+EOF
+
+ fi
+
+ if test $PGP != no || test $PGPK != no ; then
+ LIBOBJS="$LIBOBJS pgp.o pgpinvoke.o pgpkey.o pgppubring.o sha1dgst.o"
+ OPS="$OPS \$(srcdir)/OPS.PGP"
+ fi
+fi
+
+
+cat >> confdefs.h <<EOF
+#define VERSION "$VERSION$SUBVERSION"
+EOF
+
+
+
+# Extract the first word of "ispell", so it can be a program name with args.
+set dummy ispell; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:999: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_ISPELL'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$ISPELL" in
+ /*)
+ ac_cv_path_ISPELL="$ISPELL" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_ISPELL="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_ISPELL" && ac_cv_path_ISPELL="no"
+ ;;
+esac
+fi
+ISPELL="$ac_cv_path_ISPELL"
+if test -n "$ISPELL"; then
+ echo "$ac_t""$ISPELL" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test $ISPELL != no; then
+ cat >> confdefs.h <<EOF
+#define ISPELL "$ISPELL"
+EOF
+
+fi
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1036: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 1051 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1057: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 1068 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1074: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+# Check whether --with-slang or --without-slang was given.
+if test "${with_slang+set}" = set; then
+ withval="$with_slang"
+ echo $ac_n "checking if -ltermlib is required""... $ac_c" 1>&6
+echo "configure:1100: checking if -ltermlib is required" >&5
+if eval "test \"`echo '$''{'mutt_cv_bsdish'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1108 "configure"
+#include "confdefs.h"
+#include <sys/param.h>
+
+main ()
+{
+#ifdef BSD
+ exit (0);
+#else
+ exit (1);
+#endif
+}
+EOF
+if { (eval echo configure:1121: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+ mutt_cv_bsdish=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ mutt_cv_bsdish=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$mutt_cv_bsdish" 1>&6
+
+ echo $ac_n "checking for S-Lang""... $ac_c" 1>&6
+echo "configure:1138: checking for S-Lang" >&5
+ if test $withval = yes; then
+ if test -d $srcdir/../slang; then
+ mutt_cv_slang=$srcdir/../slang/src
+ CPPFLAGS="$CPPFLAGS -I${mutt_cv_slang}"
+ LDFLAGS="$LDFLAGS -L${mutt_cv_slang}/objs"
+ else
+ if test -d $mutt_cv_prefix/include/slang; then
+ CPPFLAGS="$CPPFLAGS -I$mutt_cv_prefix/include/slang"
+ elif test -d /usr/include/slang; then
+ CPPFLAGS="$CPPFLAGS -I/usr/include/slang"
+ fi
+ mutt_cv_slang=yes
+ fi
+ else
+ if test -f $withval/src/slang.h; then
+ mutt_cv_slang=$withval/src
+ CPPFLAGS="$CPPFLAGS -I${mutt_cv_slang}"
+ LDFLAGS="$LDFLAGS -L${mutt_cv_slang}/objs"
+ else
+ mutt_cv_slang=$withval
+ if test -d $withval/include/slang; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include/slang"
+ elif test -d $withval/include; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ fi
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+ fi
+ echo "$ac_t""$mutt_cv_slang" 1>&6
+ LIBS="$LIBS -lslang -lm"
+ if test $mutt_cv_bsdish = yes; then
+ LIBS="$LIBS -ltermlib"
+ fi
+ cat >> confdefs.h <<\EOF
+#define USE_SLANG_CURSES 1
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define HAVE_COLOR 1
+EOF
+
+ LIBOBJS="$LIBOBJS resize.o"
+
+
+ echo $ac_n "checking if I can compile a test SLang program""... $ac_c" 1>&6
+echo "configure:1184: checking if I can compile a test SLang program" >&5
+ cat > conftest.$ac_ext <<EOF
+#line 1186 "configure"
+#include "confdefs.h"
+
+int main() {
+SLtt_get_terminfo ();
+; return 0; }
+EOF
+if { (eval echo configure:1193: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ { echo "configure: error: unable to compile. check config.log" 1>&2; exit 1; }
+fi
+rm -f conftest*
+
+
+else
+ mutt_cv_curses=/usr
+ # Check whether --with-curses or --without-curses was given.
+if test "${with_curses+set}" = set; then
+ withval="$with_curses"
+ if test $withval != yes; then
+ mutt_cv_curses=$withval
+ fi
+ if test x$mutt_cv_curses != x/usr; then
+ LDFLAGS="-L${mutt_cv_curses}/lib $LDFLAGS"
+ CPPFLAGS="$CPPFLAGS -I${mutt_cv_curses}/include"
+ fi
+fi
+
+
+ echo $ac_n "checking for initscr in -lncurses""... $ac_c" 1>&6
+echo "configure:1221: checking for initscr in -lncurses" >&5
+ac_lib_var=`echo ncurses'_'initscr | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lncurses $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1229 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char initscr();
+
+int main() {
+initscr()
+; return 0; }
+EOF
+if { (eval echo configure:1240: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="$LIBS -lncurses"
+ if test x$mutt_cv_curses = x/usr -a -d /usr/include/ncurses; then
+ CPPFLAGS="$CPPFLAGS -I/usr/include/ncurses"
+ fi
+ for ac_hdr in ncurses.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1263: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1268 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1273: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+else
+ echo "$ac_t""no" 1>&6
+LIBS="$LIBS -lcurses"
+ if test -f /usr/ccs/lib/libcurses.a; then
+ LDFLAGS="$LDFLAGS -L/usr/ccs/lib"
+ else
+ if test -f /usr/5lib/libcurses.a; then
+ LDFLAGS="$LDFLAGS -L/usr/5lib"
+ CPPFLAGS="$CPPFLAGS -I/usr/5include"
+ fi
+ fi
+fi
+
+
+ echo $ac_n "checking for start_color""... $ac_c" 1>&6
+echo "configure:1314: checking for start_color" >&5
+if eval "test \"`echo '$''{'ac_cv_func_start_color'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1319 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char start_color(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char start_color();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_start_color) || defined (__stub___start_color)
+choke me
+#else
+start_color();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1342: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_start_color=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_start_color=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'start_color`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_COLOR 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ for ac_func in typeahead bkgdset curs_set meta use_default_colors
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1367: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1372 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1395: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+ for ac_func in resizeterm
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1422: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1427 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1450: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ LIBOBJS="$LIBOBJS resize.o"
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+fi
+
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1479: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1484 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1492: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1509 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1527 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ :
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1548 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1559: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+ :
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+
+for ac_hdr in stdarg.h sys/ioctl.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1587: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1592 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1597: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:1625: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1630 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:1647: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_type_signal=void
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+
+echo $ac_n "checking for sys_siglist declaration in signal.h or unistd.h""... $ac_c" 1>&6
+echo "configure:1667: checking for sys_siglist declaration in signal.h or unistd.h" >&5
+if eval "test \"`echo '$''{'ac_cv_decl_sys_siglist'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1672 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+/* NetBSD declares sys_siglist in unistd.h. */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+int main() {
+char *msg = *(sys_siglist + 1);
+; return 0; }
+EOF
+if { (eval echo configure:1684: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_decl_sys_siglist=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_decl_sys_siglist=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_decl_sys_siglist" 1>&6
+if test $ac_cv_decl_sys_siglist = yes; then
+ cat >> confdefs.h <<\EOF
+#define SYS_SIGLIST_DECLARED 1
+EOF
+
+fi
+
+
+echo $ac_n "checking size of long""... $ac_c" 1>&6
+echo "configure:1706: checking size of long" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1714 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(long));
+ exit(0);
+}
+EOF
+if { (eval echo configure:1725: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_long=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_long=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_long" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+EOF
+
+
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+echo "configure:1746: checking for pid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1751 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_pid_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+ cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+
+
+for ac_func in setegid srand48 strerror
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1782: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1787 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1810: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+for ac_func in strcasecmp
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1838: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1843 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1866: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+LIBOBJS="$LIBOBJS ${ac_func}.o"
+fi
+done
+
+
+
+mutt_cv_snprintf=no
+echo $ac_n "checking for snprintf""... $ac_c" 1>&6
+echo "configure:1895: checking for snprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_snprintf'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1900 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char snprintf(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char snprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_snprintf) || defined (__stub___snprintf)
+choke me
+#else
+snprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1923: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_snprintf=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_snprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'snprintf`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_SNPRINTF 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+mutt_cv_snprintf=yes
+fi
+
+echo $ac_n "checking for vsnprintf""... $ac_c" 1>&6
+echo "configure:1947: checking for vsnprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vsnprintf'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1952 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char vsnprintf(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char vsnprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_vsnprintf) || defined (__stub___vsnprintf)
+choke me
+#else
+vsnprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1975: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_vsnprintf=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_vsnprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vsnprintf`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_VSNPRINTF 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+mutt_cv_snprintf=yes
+fi
+
+if test $mutt_cv_snprintf = yes; then
+ LIBOBJS="$LIBOBJS snprintf.o"
+fi
+
+for ac_func in ftruncate
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2005: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2010 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2033: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ break
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for chsize in -lx""... $ac_c" 1>&6
+echo "configure:2055: checking for chsize in -lx" >&5
+ac_lib_var=`echo x'_'chsize | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lx $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2063 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char chsize();
+
+int main() {
+chsize()
+; return 0; }
+EOF
+if { (eval echo configure:2074: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo x | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lx $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+done
+
+
+for ac_func in strftime
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2108: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2113 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2136: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ break
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for strftime in -lintl""... $ac_c" 1>&6
+echo "configure:2158: checking for strftime in -lintl" >&5
+ac_lib_var=`echo intl'_'strftime | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lintl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2166 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char strftime();
+
+int main() {
+strftime()
+; return 0; }
+EOF
+if { (eval echo configure:2177: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo intl | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lintl $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+done
+
+
+mutt_cv_regex=yes
+# Check whether --with-rx or --without-rx was given.
+if test "${with_rx+set}" = set; then
+ withval="$with_rx"
+ if test $withval != yes; then
+ if test -d $withval/lib; then
+ LIBS="$LIBS -L$withval/lib -lrx"
+ CPPFLAGS="-I$withval/include $CPPFLAGS"
+ else
+ LIBS="$LIBS -L$withval -lrx"
+ CPPFLAGS="-I$withval $CPPFLAGS"
+ fi
+ cat >> confdefs.h <<\EOF
+#define USE_GNU_RX 1
+EOF
+
+ mutt_cv_regex=no
+ fi
+else
+ for ac_func in regcomp
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2230: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2235 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2258: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ mutt_cv_regex=no
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+fi
+
+
+if test $mutt_cv_regex = yes; then
+ if test -d ../rx-1.5; then
+ LIBS="$LIBS ../rx-1.5/rx/librx.a"
+ CPPFLAGS="-I../rx-1.5/rx $CPPFLAGS"
+ else
+ LIBOBJS="$LIBOBJS rx/librx.a"
+ CPPFLAGS="-I\$(srcdir)/rx $CPPFLAGS"
+ fi
+ cat >> confdefs.h <<\EOF
+#define USE_GNU_RX 1
+EOF
+
+fi
+
+# Check whether --with-homespool or --without-homespool was given.
+if test "${with_homespool+set}" = set; then
+ withval="$with_homespool"
+ with_homespool=${withval}
+fi
+
+if test x$with_homespool != x; then
+ if test $with_homespool = yes; then
+ with_homespool=mailbox
+ fi
+ cat >> confdefs.h <<EOF
+#define MAILPATH "$with_homespool"
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define HOMESPOOL 1
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define USE_DOTLOCK 1
+EOF
+
+ mutt_cv_setgid=no
+else
+ # Check whether --with-mailpath or --without-mailpath was given.
+if test "${with_mailpath+set}" = set; then
+ withval="$with_mailpath"
+ mutt_cv_mailpath=$withval
+else
+ echo $ac_n "checking where new mail is stored""... $ac_c" 1>&6
+echo "configure:2329: checking where new mail is stored" >&5
+if eval "test \"`echo '$''{'mutt_cv_mailpath'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ mutt_cv_mailpath=no
+ if test -d /var/mail; then
+ mutt_cv_mailpath=/var/mail
+ elif test -d /var/spool/mail; then
+ mutt_cv_mailpath=/var/spool/mail
+ elif test -d /usr/spool/mail; then
+ mutt_cv_mailpath=/usr/spool/mail
+ elif test -d /usr/mail; then
+ mutt_cv_mailpath=/usr/mail
+ fi
+fi
+
+echo "$ac_t""$mutt_cv_mailpath" 1>&6
+
+fi
+
+ if test $mutt_cv_mailpath = no; then
+ { echo "configure: error: "Could not determine where new mail is stored."" 1>&2; exit 1; }
+ fi
+ cat >> confdefs.h <<EOF
+#define MAILPATH "$mutt_cv_mailpath"
+EOF
+
+
+ echo $ac_n "checking if $mutt_cv_mailpath is world writable""... $ac_c" 1>&6
+echo "configure:2358: checking if $mutt_cv_mailpath is world writable" >&5
+if eval "test \"`echo '$''{'mutt_cv_worldwrite'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2366 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+int main (int argc, char **argv)
+{
+ struct stat s;
+
+ stat ("$mutt_cv_mailpath", &s);
+ if (s.st_mode & S_IWOTH) exit (0);
+ exit (1);
+}
+EOF
+if { (eval echo configure:2380: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+ mutt_cv_worldwrite=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ mutt_cv_worldwrite=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$mutt_cv_worldwrite" 1>&6
+
+ mutt_cv_setgid=no
+ if test $mutt_cv_worldwrite = yes; then
+ cat >> confdefs.h <<\EOF
+#define USE_DOTLOCK 1
+EOF
+
+ else
+
+ echo $ac_n "checking if $mutt_cv_mailpath is group writable""... $ac_c" 1>&6
+echo "configure:2405: checking if $mutt_cv_mailpath is group writable" >&5
+if eval "test \"`echo '$''{'mutt_cv_groupwrite'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2413 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+int main (int argc, char **argv)
+{
+ struct stat s;
+
+ stat ("$mutt_cv_mailpath", &s);
+ if (s.st_mode & S_IWGRP) exit (0);
+ exit (1);
+}
+EOF
+if { (eval echo configure:2427: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null
+then
+ mutt_cv_groupwrite=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ mutt_cv_groupwrite=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$mutt_cv_groupwrite" 1>&6
+
+ if test $mutt_cv_groupwrite = yes; then
+ cat >> confdefs.h <<\EOF
+#define USE_DOTLOCK 1
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define USE_SETGID 1
+EOF
+
+ mutt_cv_setgid=yes
+ fi
+ fi
+fi
+
+# Check whether --with-sharedir or --without-sharedir was given.
+if test "${with_sharedir+set}" = set; then
+ withval="$with_sharedir"
+ mutt_cv_sharedir=$withval
+else
+ echo $ac_n "checking where to put architecture-independent data files""... $ac_c" 1>&6
+echo "configure:2463: checking where to put architecture-independent data files" >&5
+if eval "test \"`echo '$''{'mutt_cv_sharedir'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test x$prefix = xNONE; then
+ mutt_cv_prefix=$ac_default_prefix
+ else
+ mutt_cv_prefix=$prefix
+ fi
+ if test -d ${mutt_cv_prefix}/share; then
+ if test -d ${mutt_cv_prefix}/share/misc; then
+ mutt_cv_sharedir='${prefix}/share/misc'
+ else
+ mutt_cv_sharedir='${prefix}/share'
+ fi
+ else
+ mutt_cv_sharedir='${libdir}'
+ fi
+fi
+
+echo "$ac_t""$mutt_cv_sharedir" 1>&6
+
+fi
+
+
+sharedir=$mutt_cv_sharedir
+
+
+if test x$mutt_cv_setgid = xyes; then
+ MUTT_GROUP='-g mail'
+ MUTT_PERMISSION=2755
+else
+ MUTT_GROUP=''
+ MUTT_PERMISSION=755
+fi
+
+
+
+# Check whether --with-domain or --without-domain was given.
+if test "${with_domain+set}" = set; then
+ withval="$with_domain"
+ if test $withval != yes; then
+ cat >> confdefs.h <<EOF
+#define DOMAIN "$withval"
+EOF
+
+ fi
+fi
+
+
+# Check whether --enable-hidden-host or --disable-hidden-host was given.
+if test "${enable_hidden_host+set}" = set; then
+ enableval="$enable_hidden_host"
+ cat >> confdefs.h <<\EOF
+#define HIDDEN_HOST 1
+EOF
+
+fi
+
+
+# Check whether --enable-pop or --disable-pop was given.
+if test "${enable_pop+set}" = set; then
+ enableval="$enable_pop"
+ cat >> confdefs.h <<\EOF
+#define USE_POP 1
+EOF
+
+ echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
+echo "configure:2531: checking for socket in -lsocket" >&5
+ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lsocket $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2539 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:2550: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lsocket $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6
+echo "configure:2578: checking for gethostbyname in -lnsl" >&5
+ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lnsl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2586 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:2597: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lnsl $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ LIBOBJS="$LIBOBJS pop.o"
+
+fi
+
+
+# Check whether --enable-flock or --disable-flock was given.
+if test "${enable_flock+set}" = set; then
+ enableval="$enable_flock"
+ if test $enableval = yes; then
+ cat >> confdefs.h <<\EOF
+#define USE_FLOCK 1
+EOF
+
+ fi
+fi
+
+
+mutt_cv_fcntl=yes
+# Check whether --enable-fcntl or --disable-fcntl was given.
+if test "${enable_fcntl+set}" = set; then
+ enableval="$enable_fcntl"
+ if test $enableval = no; then mutt_cv_fcntl=no; fi
+fi
+
+
+if test $mutt_cv_fcntl = yes; then
+ cat >> confdefs.h <<\EOF
+#define USE_FCNTL 1
+EOF
+
+fi
+
+mutt_cv_warnings=yes
+# Check whether --enable-warnings or --disable-warnings was given.
+if test "${enable_warnings+set}" = set; then
+ enableval="$enable_warnings"
+ if test $enableval = no; then
+ mutt_cv_warnings=no
+fi
+fi
+
+
+if test "$ac_cv_prog_CC" = gcc -a $mutt_cv_warnings = yes; then
+ CFLAGS="-Wall -pedantic $CFLAGS"
+fi
+
+# Check whether --enable-nfs-fix or --disable-nfs-fix was given.
+if test "${enable_nfs_fix+set}" = set; then
+ enableval="$enable_nfs_fix"
+ if test x$enableval = xyes; then
+ cat >> confdefs.h <<\EOF
+#define NFS_ATTRIBUTE_HACK 1
+EOF
+
+ fi
+fi
+
+
+# Check whether --enable-buffy-size or --disable-buffy-size was given.
+if test "${enable_buffy_size+set}" = set; then
+ enableval="$enable_buffy_size"
+ if test x$enableval = xyes; then
+ cat >> confdefs.h <<\EOF
+#define BUFFY_SIZE 1
+EOF
+
+ fi
+fi
+
+
+# Check whether --enable-locales-fix or --disable-locales-fix was given.
+if test "${enable_locales_fix+set}" = set; then
+ enableval="$enable_locales_fix"
+ if test x$enableval = xyes; then
+ cat >> confdefs.h <<\EOF
+#define LOCALES_HACK 1
+EOF
+
+ fi
+fi
+
+
+# Check whether --with-exec-shell or --without-exec-shell was given.
+if test "${with_exec_shell+set}" = set; then
+ withval="$with_exec_shell"
+ if test $withval != yes; then
+ cat >> confdefs.h <<EOF
+#define EXECSHELL "$withval"
+EOF
+
+ fi
+fi
+
+
+# Check whether --enable-exact-address or --disable-exact-address was given.
+if test "${enable_exact_address+set}" = set; then
+ enableval="$enable_exact_address"
+ if test $enableval = yes; then
+ cat >> confdefs.h <<\EOF
+#define EXACT_ADDRESS 1
+EOF
+
+ fi
+fi
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.12"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile rx/Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@CC@%$CC%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@SENDMAIL@%$SENDMAIL%g
+s%@PGPK@%$PGPK%g
+s%@PGP@%$PGP%g
+s%@OPS@%$OPS%g
+s%@VERSION@%$VERSION%g
+s%@ISPELL@%$ISPELL%g
+s%@CPP@%$CPP%g
+s%@LIBOBJS@%$LIBOBJS%g
+s%@sharedir@%$sharedir%g
+s%@MUTT_GROUP@%$MUTT_GROUP%g
+s%@MUTT_PERMISSION@%$MUTT_PERMISSION%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile rx/Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+ CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ fi
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
--- /dev/null
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(mutt.h)
+AC_CONFIG_HEADER(config.h)
+VERSION=0.92.8
+SUBVERSION=''
+
+AC_MSG_CHECKING(for prefix)
+if test x$prefix = xNONE; then
+ mutt_cv_prefix=$ac_default_prefix
+else
+ mutt_cv_prefix=$prefix
+fi
+AC_MSG_RESULT($mutt_cv_prefix)
+
+AC_PROG_CC
+AC_PROG_MAKE_SET
+AC_PROG_INSTALL
+
+AC_PATH_PROG(SENDMAIL, sendmail, no, `echo $PATH | sed "s/:/ /"` /usr/sbin /usr/lib)
+AC_DEFINE_UNQUOTED(SENDMAIL, "$ac_cv_path_SENDMAIL")
+
+OPS='$(srcdir)/OPS'
+if test -f $srcdir/pgp.c; then
+ SUBVERSION=i
+ PGPPATH=no
+ AC_PATH_PROG(PGPK, pgpk, no)
+ if test $PGPK != no ; then
+ PGPK=`echo $PGPK | sed 's,.$,,'`
+ AC_DEFINE_UNQUOTED(_PGPV3PATH, "$PGPK")
+ PGPPATH="$PGPK"
+ AC_DEFINE(HAVE_PGP5)
+ fi
+
+ AC_PATH_PROG(PGP, pgp, no)
+ if test $PGP != no ; then
+ AC_DEFINE_UNQUOTED(_PGPV2PATH, "$PGP")
+ PGPPATH="$PGP"
+ AC_DEFINE(HAVE_PGP2)
+ fi
+
+ if test $PGPPATH != no ; then
+ AC_DEFINE_UNQUOTED(_PGPPATH, "$PGPPATH")
+ fi
+
+ if test $PGP != no || test $PGPK != no ; then
+ LIBOBJS="$LIBOBJS pgp.o pgpinvoke.o pgpkey.o pgppubring.o sha1dgst.o"
+ OPS="$OPS \$(srcdir)/OPS.PGP"
+ fi
+fi
+AC_SUBST(OPS)
+
+AC_DEFINE_UNQUOTED(VERSION, "$VERSION$SUBVERSION")
+AC_SUBST(VERSION)
+
+AC_PATH_PROG(ISPELL, ispell, no)
+if test $ISPELL != no; then
+ AC_DEFINE_UNQUOTED(ISPELL, "$ISPELL")
+fi
+
+AC_ARG_WITH(slang, [ --with-slang[=DIR] use S-Lang instead of ncurses],
+ [AC_CACHE_CHECK([if -ltermlib is required], mutt_cv_bsdish,
+ AC_TRY_RUN([#include <sys/param.h>
+
+main ()
+{
+#ifdef BSD
+ exit (0);
+#else
+ exit (1);
+#endif
+}],
+ [mutt_cv_bsdish=yes],
+ [mutt_cv_bsdish=no]))
+
+ AC_MSG_CHECKING(for S-Lang)
+ if test $withval = yes; then
+ if test -d $srcdir/../slang; then
+ mutt_cv_slang=$srcdir/../slang/src
+ CPPFLAGS="$CPPFLAGS -I${mutt_cv_slang}"
+ LDFLAGS="$LDFLAGS -L${mutt_cv_slang}/objs"
+ else
+ if test -d $mutt_cv_prefix/include/slang; then
+ CPPFLAGS="$CPPFLAGS -I$mutt_cv_prefix/include/slang"
+ elif test -d /usr/include/slang; then
+ CPPFLAGS="$CPPFLAGS -I/usr/include/slang"
+ fi
+ mutt_cv_slang=yes
+ fi
+ else
+ dnl ---Check to see if $withval is a source directory
+ if test -f $withval/src/slang.h; then
+ mutt_cv_slang=$withval/src
+ CPPFLAGS="$CPPFLAGS -I${mutt_cv_slang}"
+ LDFLAGS="$LDFLAGS -L${mutt_cv_slang}/objs"
+ else
+ dnl ---Must be installed somewhere
+ mutt_cv_slang=$withval
+ if test -d $withval/include/slang; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include/slang"
+ elif test -d $withval/include; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ fi
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+ fi
+ AC_MSG_RESULT($mutt_cv_slang)
+ LIBS="$LIBS -lslang -lm"
+ if test $mutt_cv_bsdish = yes; then
+ LIBS="$LIBS -ltermlib"
+ fi
+ AC_DEFINE(USE_SLANG_CURSES)
+ AC_DEFINE(HAVE_COLOR)
+ LIBOBJS="$LIBOBJS resize.o"
+
+ dnl --- try to link a sample program to check if we're ok
+
+ AC_MSG_CHECKING(if I can compile a test SLang program)
+ AC_TRY_LINK([], [SLtt_get_terminfo ();],
+ [AC_MSG_RESULT(yes)],
+ [AC_MSG_ERROR(unable to compile. check config.log)])
+
+ ],
+
+ [mutt_cv_curses=/usr
+ AC_ARG_WITH(curses, [ --with-curses=DIR ncurses is installed in ],
+ [if test $withval != yes; then
+ mutt_cv_curses=$withval
+ fi
+ if test x$mutt_cv_curses != x/usr; then
+ LDFLAGS="-L${mutt_cv_curses}/lib $LDFLAGS"
+ CPPFLAGS="$CPPFLAGS -I${mutt_cv_curses}/include"
+ fi])
+
+ AC_CHECK_LIB(ncurses, initscr,
+
+ [LIBS="$LIBS -lncurses"
+ if test x$mutt_cv_curses = x/usr -a -d /usr/include/ncurses; then
+ CPPFLAGS="$CPPFLAGS -I/usr/include/ncurses"
+ fi
+ AC_CHECK_HEADERS(ncurses.h)],
+
+ [LIBS="$LIBS -lcurses"
+ if test -f /usr/ccs/lib/libcurses.a; then
+ LDFLAGS="$LDFLAGS -L/usr/ccs/lib"
+ else
+ if test -f /usr/5lib/libcurses.a; then
+ LDFLAGS="$LDFLAGS -L/usr/5lib"
+ CPPFLAGS="$CPPFLAGS -I/usr/5include"
+ fi
+ fi])
+
+ AC_CHECK_FUNC(start_color, [AC_DEFINE(HAVE_COLOR)])
+ AC_CHECK_FUNCS(typeahead bkgdset curs_set meta use_default_colors)
+ AC_CHECK_FUNCS(resizeterm, [LIBOBJS="$LIBOBJS resize.o"])
+ ])
+
+AC_HEADER_STDC
+
+AC_CHECK_HEADERS(stdarg.h sys/ioctl.h)
+
+AC_TYPE_SIGNAL
+
+AC_DECL_SYS_SIGLIST
+
+dnl need this for DEC alpha
+AC_CHECK_SIZEOF(long)
+
+AC_TYPE_PID_T
+
+AC_CHECK_FUNCS(setegid srand48 strerror)
+
+AC_REPLACE_FUNCS(strcasecmp)
+
+mutt_cv_snprintf=no
+AC_CHECK_FUNC(snprintf, [AC_DEFINE(HAVE_SNPRINTF)], [mutt_cv_snprintf=yes])
+AC_CHECK_FUNC(vsnprintf, [AC_DEFINE(HAVE_VSNPRINTF)], [mutt_cv_snprintf=yes])
+if test $mutt_cv_snprintf = yes; then
+ LIBOBJS="$LIBOBJS snprintf.o"
+fi
+
+dnl SCO uses chsize() instead of ftruncate()
+AC_CHECK_FUNCS(ftruncate, break, [AC_CHECK_LIB(x, chsize)])
+
+dnl SCO has strftime() in libintl
+AC_CHECK_FUNCS(strftime, break, [AC_CHECK_LIB(intl, strftime)])
+
+mutt_cv_regex=yes
+AC_ARG_WITH(rx, [ --with-rx[=DIR] Use GNU rx ],
+ [if test $withval != yes; then
+ if test -d $withval/lib; then
+ LIBS="$LIBS -L$withval/lib -lrx"
+ CPPFLAGS="-I$withval/include $CPPFLAGS"
+ else
+ LIBS="$LIBS -L$withval -lrx"
+ CPPFLAGS="-I$withval $CPPFLAGS"
+ fi
+ AC_DEFINE(USE_GNU_RX)
+ mutt_cv_regex=no
+ fi],
+ [AC_CHECK_FUNCS(regcomp, mutt_cv_regex=no)])
+
+if test $mutt_cv_regex = yes; then
+ if test -d ../rx-1.5; then
+ LIBS="$LIBS ../rx-1.5/rx/librx.a"
+ CPPFLAGS="-I../rx-1.5/rx $CPPFLAGS"
+ else
+ LIBOBJS="$LIBOBJS rx/librx.a"
+ CPPFLAGS="-I\$(srcdir)/rx $CPPFLAGS"
+ fi
+ AC_DEFINE(USE_GNU_RX)
+fi
+
+AC_ARG_WITH(homespool, [ --with-homespool[=FILE] file in user's directory where new mail is spooled], with_homespool=${withval})
+if test x$with_homespool != x; then
+ if test $with_homespool = yes; then
+ with_homespool=mailbox
+ fi
+ AC_DEFINE_UNQUOTED(MAILPATH, "$with_homespool")
+ AC_DEFINE(HOMESPOOL)
+ AC_DEFINE(USE_DOTLOCK)
+ mutt_cv_setgid=no
+else
+ AC_ARG_WITH(mailpath, [ --with-mailpath=DIR directory where spool mailboxes are located],
+ [mutt_cv_mailpath=$withval],
+ [ AC_CACHE_CHECK(where new mail is stored, mutt_cv_mailpath,
+ [mutt_cv_mailpath=no
+ if test -d /var/mail; then
+ mutt_cv_mailpath=/var/mail
+ elif test -d /var/spool/mail; then
+ mutt_cv_mailpath=/var/spool/mail
+ elif test -d /usr/spool/mail; then
+ mutt_cv_mailpath=/usr/spool/mail
+ elif test -d /usr/mail; then
+ mutt_cv_mailpath=/usr/mail
+ fi])
+ ])
+ if test $mutt_cv_mailpath = no; then
+ AC_MSG_ERROR("Could not determine where new mail is stored.")
+ fi
+ AC_DEFINE_UNQUOTED(MAILPATH, "$mutt_cv_mailpath")
+
+ AC_CACHE_CHECK(if $mutt_cv_mailpath is world writable, mutt_cv_worldwrite, AC_TRY_RUN([#include <sys/types.h>
+#include <sys/stat.h>
+
+int main (int argc, char **argv)
+{
+ struct stat s;
+
+ stat ("$mutt_cv_mailpath", &s);
+ if (s.st_mode & S_IWOTH) exit (0);
+ exit (1);
+}], [mutt_cv_worldwrite=yes], [mutt_cv_worldwrite=no]))
+
+ mutt_cv_setgid=no
+ if test $mutt_cv_worldwrite = yes; then
+ AC_DEFINE(USE_DOTLOCK)
+ else
+
+ AC_CACHE_CHECK(if $mutt_cv_mailpath is group writable, mutt_cv_groupwrite, AC_TRY_RUN([#include <sys/types.h>
+#include <sys/stat.h>
+
+int main (int argc, char **argv)
+{
+ struct stat s;
+
+ stat ("$mutt_cv_mailpath", &s);
+ if (s.st_mode & S_IWGRP) exit (0);
+ exit (1);
+}], [mutt_cv_groupwrite=yes], [mutt_cv_groupwrite=no]))
+
+ if test $mutt_cv_groupwrite = yes; then
+ AC_DEFINE(USE_DOTLOCK)
+ AC_DEFINE(USE_SETGID)
+ mutt_cv_setgid=yes
+ fi
+ fi
+fi
+
+AC_ARG_WITH(sharedir, [ --with-sharedir=PATH specify where to put arch independent files],
+ [mutt_cv_sharedir=$withval],
+ [ AC_CACHE_CHECK(where to put architecture-independent data files,
+ mutt_cv_sharedir,
+ [if test x$prefix = xNONE; then
+ mutt_cv_prefix=$ac_default_prefix
+ else
+ mutt_cv_prefix=$prefix
+ fi
+ if test -d ${mutt_cv_prefix}/share; then
+ if test -d ${mutt_cv_prefix}/share/misc; then
+ mutt_cv_sharedir='${prefix}/share/misc'
+ else
+ mutt_cv_sharedir='${prefix}/share'
+ fi
+ else
+ mutt_cv_sharedir='${libdir}'
+ fi])
+ ])
+
+sharedir=$mutt_cv_sharedir
+AC_SUBST(sharedir)
+
+if test x$mutt_cv_setgid = xyes; then
+ MUTT_GROUP='-g mail'
+ MUTT_PERMISSION=2755
+else
+ MUTT_GROUP=''
+ MUTT_PERMISSION=755
+fi
+AC_SUBST(MUTT_GROUP)
+AC_SUBST(MUTT_PERMISSION)
+
+AC_ARG_WITH(domain, [ --with-domain=DOMAIN Specify your DNS domain name ],
+ [if test $withval != yes; then
+ AC_DEFINE_UNQUOTED(DOMAIN, "$withval")
+ fi])
+
+AC_ARG_ENABLE(hidden-host, [ --enable-hidden-host Only use the domain name for local addresses], AC_DEFINE(HIDDEN_HOST))
+
+AC_ARG_ENABLE(pop, [ --enable-pop Enable POP3 support],
+[ AC_DEFINE(USE_POP)
+ AC_CHECK_LIB(socket, socket)
+ AC_CHECK_LIB(nsl, gethostbyname)
+ LIBOBJS="$LIBOBJS pop.o"
+])
+
+AC_ARG_ENABLE(flock, [ --enable-flock Use flock() to lock files],
+ [if test $enableval = yes; then
+ AC_DEFINE(USE_FLOCK)
+ fi])
+
+mutt_cv_fcntl=yes
+AC_ARG_ENABLE(fcntl, [ --disable-fcntl Do NOT use fcntl() to lock files ],
+ [if test $enableval = no; then mutt_cv_fcntl=no; fi])
+
+if test $mutt_cv_fcntl = yes; then
+ AC_DEFINE(USE_FCNTL)
+fi
+
+mutt_cv_warnings=yes
+AC_ARG_ENABLE(warnings, [ --disable-warnings turn off compiler warnings (not recommended)],
+[if test $enableval = no; then
+ mutt_cv_warnings=no
+fi])
+
+if test "$ac_cv_prog_CC" = gcc -a $mutt_cv_warnings = yes; then
+ CFLAGS="-Wall -pedantic $CFLAGS"
+fi
+
+AC_ARG_ENABLE(nfs-fix, [ --enable-nfs-fix Work around an NFS with broken attributes caching ],
+ [if test x$enableval = xyes; then
+ AC_DEFINE(NFS_ATTRIBUTE_HACK)
+ fi])
+
+AC_ARG_ENABLE(buffy-size, [ --enable-buffy-size Use file size attribute instead of access time ],
+ [if test x$enableval = xyes; then
+ AC_DEFINE(BUFFY_SIZE)
+ fi])
+
+AC_ARG_ENABLE(locales-fix, [ --enable-locales-fix The result of isprint() is unreliable ],
+ [if test x$enableval = xyes; then
+ AC_DEFINE(LOCALES_HACK)
+ fi])
+
+AC_ARG_WITH(exec-shell, [ --with-exec-shell=SHELL Specify alternate shell (ONLY if /bin/sh is broken)],
+ [if test $withval != yes; then
+ AC_DEFINE_UNQUOTED(EXECSHELL, "$withval")
+ fi])
+
+AC_ARG_ENABLE(exact-address, [ --enable-exact-address enable regeneration of email addresses],
+ [if test $enableval = yes; then
+ AC_DEFINE(EXACT_ADDRESS)
+ fi])
+
+AC_OUTPUT(Makefile rx/Makefile)
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mailbox.h"
+#include "mx.h"
+#include "copy.h"
+#include "rfc2047.h"
+#include "parse.h"
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h> /* needed for SEEK_SET under SunOS 4.1.4 */
+
+static int copy_delete_attach(HEADER *h, HEADER *p, BODY *m, FILE *fpin,
+ FILE *fpout, int flags);
+
+/* Ok, the only reason for not merging this with mutt_copy_header()
+ * below is to avoid creating a HEADER structure in message_handler().
+ */
+int
+mutt_copy_hdr (FILE *in, FILE *out, long off_start, long off_end, int flags,
+ const char *prefix)
+{
+ int from = 0;
+ int ignore = 0;
+ char buf[STRING]; /* should be long enough to get most fields in one pass */
+ char *nl;
+ LIST *t;
+ char **headers;
+ int hdr_count;
+ int x;
+ int error;
+
+ if (ftell (in) != off_start)
+ fseek (in, off_start, 0);
+
+ buf[0] = '\n';
+ buf[1] = 0;
+
+ if ((flags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX)) == 0)
+ {
+ /* Without these flags to complicate things
+ * we can do a more efficient line to line copying
+ */
+ while (ftell (in) < off_end)
+ {
+ nl = strchr (buf, '\n');
+
+ if ((fgets (buf, sizeof (buf), in)) == NULL)
+ break;
+
+ /* Is it the begining of a header? */
+ if (nl && buf[0] != ' ' && buf[0] != '\t')
+ {
+ ignore = 1;
+ if (!from && strncmp ("From ", buf, 5) == 0)
+ {
+ if ((flags & CH_FROM) == 0)
+ continue;
+ from = 1;
+ }
+ else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
+ break; /* end of header */
+
+ if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
+ (strncasecmp ("Status:", buf, 7) == 0 ||
+ strncasecmp ("X-Status:", buf, 9) == 0))
+ continue;
+ if ((flags & (CH_UPDATE_LEN | CH_XMIT)) &&
+ (strncasecmp ("Content-Length:", buf, 15) == 0 ||
+ strncasecmp ("Lines:", buf, 6) == 0))
+ continue;
+ ignore = 0;
+ }
+
+ if (!ignore && fputs (buf, out) == EOF)
+ return (-1);
+ }
+ return 0;
+ }
+
+ hdr_count = 1;
+ x = 0;
+ error = FALSE;
+
+ /* We are going to read and collect the headers in an array
+ * so we are able to do re-ordering.
+ * First count the number of entries in the array
+ */
+ if (flags & CH_REORDER)
+ {
+ for (t = HeaderOrderList; t; t = t->next)
+ {
+ dprint(1, (debugfile, "Reorder list: %s\n", t->data));
+ hdr_count++;
+ }
+ }
+
+ dprint (1, (debugfile, "WEED is %s\n", (flags & CH_WEED) ? "Set" : "Not"));
+
+ headers = safe_calloc (hdr_count, sizeof (char *));
+
+ /* Read all the headers into the array */
+ while (ftell (in) < off_end)
+ {
+ nl = strchr (buf, '\n');
+
+ /* Read a line */
+ if ((fgets (buf, sizeof (buf), in)) == NULL)
+ break;
+
+ /* Is it the begining of a header? */
+ if (nl && buf[0] != ' ' && buf[0] != '\t')
+ {
+ ignore = 1;
+ if (!from && strncmp ("From ", buf, 5) == 0)
+ {
+ if ((flags & CH_FROM) == 0)
+ continue;
+ from = 1;
+ }
+ else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
+ break; /* end of header */
+
+ if ((flags & CH_WEED) &&
+ mutt_matches_ignore (buf, Ignore) &&
+ !mutt_matches_ignore (buf, UnIgnore))
+ continue;
+ if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
+ (strncasecmp ("Status:", buf, 7) == 0 ||
+ strncasecmp ("X-Status:", buf, 9) == 0))
+ continue;
+ if ((flags & (CH_UPDATE_LEN | CH_XMIT)) &&
+ (strncasecmp ("Content-Length:", buf, 15) == 0 ||
+ strncasecmp ("Lines:", buf, 6) == 0))
+ continue;
+ if ((flags & CH_MIME) &&
+ ((strncasecmp ("content-", buf, 8) == 0 &&
+ (strncasecmp ("transfer-encoding:", buf + 8, 18) == 0 ||
+ strncasecmp ("type:", buf + 8, 5) == 0)) ||
+ strncasecmp ("mime-version:", buf, 13) == 0))
+ continue;
+
+ /* Find x -- the array entry where this header is to be saved */
+ if (flags & CH_REORDER)
+ {
+ for (t = HeaderOrderList, x = 0 ; (t) ; t = t->next, x++)
+ {
+ if (!strncasecmp (buf, t->data, strlen (t->data)))
+ {
+ dprint(2, (debugfile, "Reorder: %s matches %s\n", t->data,
+ headers[x]));
+ break;
+ }
+ }
+ }
+
+ ignore = 0;
+ } /* If beginning of header */
+
+ if (!ignore)
+ {
+ /* Save the header in headers[x] */
+ if (!headers[x])
+ headers[x] = safe_strdup (buf);
+ else
+ {
+ safe_realloc ((void **) &headers[x],
+ strlen (headers[x]) + strlen (buf) + sizeof (char));
+ strcat (headers[x], buf);
+ }
+ }
+ } /* while (ftell (in) < off_end) */
+
+ /* Now output the headers in order */
+ for (x = 0; x < hdr_count; x++)
+ {
+ if (headers[x])
+ {
+ if (flags & CH_DECODE)
+ rfc2047_decode (headers[x], headers[x], strlen (headers[x]));
+
+ /* We couldn't do the prefixing when reading because RFC 2047
+ * decoding may have concatenated lines.
+ */
+ if (flags & CH_PREFIX)
+ {
+ char *ch = headers[x];
+ int print_prefix = 1;
+
+ while (*ch)
+ {
+ if (print_prefix)
+ {
+ if (fputs (prefix, out) == EOF)
+ {
+ error = TRUE;
+ break;
+ }
+ print_prefix = 0;
+ }
+
+ if (*ch == '\n' && ch[1])
+ print_prefix = 1;
+
+ if (putc (*ch++, out) == EOF)
+ {
+ error = TRUE;
+ break;
+ }
+ }
+ if (error)
+ break;
+ }
+ else
+ {
+ if (fputs (headers[x], out) == EOF)
+ {
+ error = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Free in a separate loop to be sure that all headers are freed
+ * in case of error. */
+ for (x = 0; x < hdr_count; x++)
+ safe_free ((void **) &headers[x]);
+ safe_free ((void **) &headers);
+
+ if (error)
+ return (-1);
+ return (0);
+}
+
+/* flags
+ CH_DECODE RFC2047 header decoding
+ CH_FROM retain the "From " message separator
+ CH_MIME ignore MIME fields
+ CH_NONEWLINE don't output a newline after the header
+ CH_NOSTATUS ignore the Status: and X-Status:
+ CH_PREFIX quote header with $indent_str
+ CH_REORDER output header in order specified by `hdr_order'
+ CH_TXTPLAIN generate text/plain MIME headers
+ CH_UPDATE write new Status: and X-Status:
+ CH_UPDATE_LEN write new Content-Length: and Lines:
+ CH_XMIT ignore Lines: and Content-Length:
+ CH_WEED do header weeding
+
+ prefix
+ string to use if CH_PREFIX is set
+ */
+
+int
+mutt_copy_header (FILE *in, HEADER *h, FILE *out, int flags, const char *prefix)
+{
+ if (mutt_copy_hdr (in, out, h->offset, h->content->offset, flags, prefix) == -1)
+ return (-1);
+
+ if (flags & CH_TXTPLAIN)
+ {
+ fputs ("Mime-Version: 1.0\n", out);
+ fputs ("Content-Type: text/plain\n", out);
+ fputs ("Content-Transfer-Encoding: 8bit\n", out);
+ }
+
+ if (flags & CH_UPDATE)
+ {
+ if ((flags & CH_NOSTATUS) == 0)
+ {
+ if (h->old || h->read)
+ {
+ if (fputs ("Status: ", out) == EOF)
+ return (-1);
+
+ if (h->read)
+ {
+ if (fputs ("RO", out) == EOF)
+ return (-1);
+ }
+ else if (h->old)
+ {
+ if (fputc ('O', out) == EOF)
+ return (-1);
+ }
+
+ if (fputc ('\n', out) == EOF)
+ return (-1);
+ }
+
+ if (h->flagged || h->replied)
+ {
+ if (fputs ("X-Status: ", out) == EOF)
+ return (-1);
+
+ if (h->replied)
+ {
+ if (fputc ('A', out) == EOF)
+ return (-1);
+ }
+
+ if (h->flagged)
+ {
+ if (fputc ('F', out) == EOF)
+ return (-1);
+ }
+
+ if (fputc ('\n', out) == EOF)
+ return (-1);
+ }
+ }
+ }
+
+ if (flags & CH_UPDATE_LEN)
+ {
+ fprintf (out, "Content-Length: %ld\n", h->content->length);
+ if (h->lines != 0 || h->content->length == 0)
+ fprintf (out, "Lines: %d\n", h->lines);
+ }
+
+ if ((flags & CH_NONEWLINE) == 0)
+ {
+ if (fputc ('\n', out) == EOF) /* add header terminator */
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* make a copy of a message
+
+ fpout where to write output
+ fpin where to get input
+ hdr header of message being copied
+ body structure of message being copied
+ flags
+ M_CM_NOHEADER don't copy header
+ M_CM_PREFIX quote header and body
+ M_CM_DECODE decode message body to text/plain
+ M_CM_DISPLAY displaying output to the user
+ M_CM_UPDATE update structures in memory after syncing
+ chflags flags to mutt_copy_header()
+ */
+
+int
+_mutt_copy_message (FILE *fpout, FILE *fpin, HEADER *hdr, BODY *body,
+ int flags, int chflags)
+{
+ char prefix[SHORT_STRING];
+ STATE s;
+
+ if (flags & M_CM_PREFIX)
+ _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), hdr, 0);
+
+ if ((flags & M_CM_NOHEADER) == 0)
+ {
+ if (flags & M_CM_PREFIX)
+ chflags |= CH_PREFIX;
+
+ /* eventually update Content-Length/Lines: count in HEADER,
+ * for now we punt (don't copy them in mutt_copy_header() */
+ if (hdr->attach_del)
+ {
+ dprint (1, (debugfile, "changing flags\n"));
+ chflags |= CH_XMIT;
+ chflags &= ~CH_UPDATE_LEN;
+ }
+
+ if (mutt_copy_header (fpin, hdr, fpout, chflags,
+ (chflags & CH_PREFIX) ? prefix : NULL) == -1)
+ return -1;
+ }
+
+ if (flags & M_CM_DECODE)
+ {
+ /* now make a text/plain version of the message */
+ memset (&s, 0, sizeof (STATE));
+ s.fpin = fpin;
+ s.fpout = fpout;
+ if (flags & M_CM_PREFIX)
+ s.prefix = prefix;
+ if (flags & M_CM_DISPLAY)
+ s.flags |= M_DISPLAY;
+
+
+
+#ifdef _PGPPATH
+ if (flags & M_CM_VERIFY)
+ s.flags |= M_VERIFY;
+#endif
+
+
+
+ mutt_body_handler (body, &s);
+ }
+ else
+ {
+ fseek (fpin, body->offset, 0);
+ if (flags & M_CM_PREFIX)
+ {
+ char buf[LONG_STRING];
+ size_t bytes = body->length;
+
+ buf[sizeof (buf) - 1] = 0;
+ while (bytes > 0)
+ {
+ if (fgets (buf, sizeof (buf) - 1, fpin) == NULL)
+ break;
+ bytes -= strlen (buf);
+ fputs (prefix, fpout);
+ fputs (buf, fpout);
+ }
+ }
+ else
+ {
+ if (hdr->attach_del)
+ {
+ if (copy_delete_attach (hdr, NULL, NULL, fpin, fpout, flags) == -1)
+ return -1;
+ }
+ else
+ {
+ if (mutt_copy_bytes (fpin, fpout, body->length) == -1)
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+int
+mutt_copy_message (FILE *fpout, CONTEXT *src, HEADER *hdr, int flags,
+ int chflags)
+{
+ MESSAGE *msg;
+ int r;
+
+ if ((msg = mx_open_message (src, hdr->msgno)) == NULL)
+ return -1;
+ r = _mutt_copy_message (fpout, msg->fp, hdr, hdr->content, flags, chflags);
+ mx_close_message (&msg);
+ return r;
+}
+
+/* appends a copy of the given message to a mailbox
+ *
+ * dest destination mailbox
+ * fpin where to get input
+ * src source mailbox
+ * hdr message being copied
+ * body structure of message being copied
+ * flags mutt_copy_message() flags
+ * chflags mutt_copy_header() flags
+ */
+
+int
+_mutt_append_message (CONTEXT *dest, FILE *fpin, CONTEXT *src, HEADER *hdr,
+ BODY *body, int flags, int chflags)
+{
+ MESSAGE *msg;
+ int r;
+
+ if ((msg = mx_open_new_message (dest, hdr, (src->magic == M_MBOX || src->magic == M_MMDF) ? 0 : M_ADD_FROM)) == NULL)
+ return -1;
+ if (dest->magic == M_MBOX || dest->magic == M_MMDF)
+ chflags |= CH_FROM;
+ chflags |= (dest->magic == M_MAILDIR ? CH_NOSTATUS : CH_UPDATE);
+ r = _mutt_copy_message (msg->fp, fpin, hdr, body, flags, chflags);
+ mx_close_message (&msg);
+ return r;
+}
+
+int
+mutt_append_message (CONTEXT *dest, CONTEXT *src, HEADER *hdr, int cmflags,
+ int chflags)
+{
+ MESSAGE *msg;
+ int r;
+
+ if ((msg = mx_open_message (src, hdr->msgno)) == NULL)
+ return -1;
+ r = _mutt_append_message (dest, msg->fp, src, hdr, hdr->content, cmflags, chflags);
+ mx_close_message (&msg);
+ return r;
+}
+
+/*
+ * copy_delete_attach()
+ *
+ * This function copies a message into an mbox folder and deletes
+ * any attachments which are marked for deletion
+ *
+ * A side effect of this is that any message copied using this function
+ * will not have content-length: and lines: headers, but these will be updated
+ * on the next sync of this mailbox
+ *
+ * This function will return 0 on success, -1 on failure.
+ */
+static int copy_delete_attach(HEADER *h, HEADER *p, BODY *m, FILE *fpin,
+ FILE *fpout, int flags)
+{
+ long offset = 0;
+ BODY *b;
+ char buf[STRING];
+ long orig_length = 0;
+ long orig_offset = 0;
+ long new_length = 0;
+ long new_offset = 0;
+ int x;
+
+ new_offset = ftell (fpout);
+ if (h == NULL)
+ {
+ if (m == NULL)
+ {
+ mutt_error ("Confused when attempting to delete attachment, h & m can't be NULL");
+ return -1;
+ }
+ b = m;
+ }
+ else
+ {
+ b = h->content;
+ }
+ orig_length = b->length;
+ orig_offset = b->offset;
+ dprint (1, (debugfile, "orig length: %ld orig offset: %ld\n", orig_length, orig_offset));
+ /* Find first deleted attachment */
+ if (b->parts == NULL)
+ {
+ mutt_error ("Deleting non-multipart messages not yet supported");
+ return -1;
+ }
+ b = b->parts;
+ while (b != NULL)
+ {
+ while (b && !b->deleted && !b->parts) b = b->next;
+
+ if (b)
+ {
+ /* Copy message from current to deleted attachment headers */
+ offset = ftell (fpin);
+ mutt_copy_bytes (fpin, fpout, b->hdr_offset - offset);
+ new_length += b->hdr_offset - offset;
+
+ if (!b->deleted && b->parts)
+ {
+ x = ftell (fpout);
+ if (mutt_copy_hdr (fpin, fpout, ftell (fpin), b->offset,
+ CH_UPDATE_LEN, NULL) == -1)
+ return -1;
+ new_length += (ftell (fpout) - x);
+ x = fprintf (fpout, "\n");
+ if (x > 0)
+ new_length += x;
+
+ if (copy_delete_attach(b->hdr, h, b, fpin, fpout, flags) == -1)
+ return -1;
+ new_length += b->parts->length;
+ }
+ else
+ {
+
+ if (h) h->lines = 0;
+ mutt_make_string (buf, sizeof (buf), NONULL (DeleteFmt), (p) ? p : h);
+
+ /* Go through deleted attachment headers, weed Content-Length,
+ * Content-Type and Content-Transfer-Encoding
+ * Also, keep track of what we write to update the length */
+ x = ftell (fpout);
+ if (mutt_copy_hdr (fpin, fpout, ftell (fpin), b->offset,
+ CH_UPDATE_LEN | CH_MIME , NULL) == -1)
+ return -1;
+ new_length += (ftell (fpout) - x);
+ x = fprintf (fpout, "\n%s\n", buf);
+ if (x > 0)
+ new_length += x;
+ /* Skip the deleted body */
+ fseek (fpin, (b->offset + b->length), SEEK_SET);
+ if (flags & M_CM_UPDATE)
+ b->deleted = 0;
+ }
+ b = b->next;
+ }
+ }
+ /* Copy til end of message */
+ offset = ftell(fpin);
+ mutt_copy_bytes (fpin, fpout, (orig_offset+orig_length) - offset);
+ new_length += (orig_offset+orig_length) - offset;
+
+ /*
+ * Update the content-length and offset. This offset will be wrong for
+ * mbox/mh type folders, but will be corrected in mbox_sync_mailbox()
+ *
+ */
+ if (flags & M_CM_UPDATE)
+ {
+ dprint (1, (debugfile, "new length: %ld new offset: %ld\n", new_length, new_offset));
+ if (h)
+ {
+ h->content->length = new_length;
+ h->content->offset = new_offset;
+ h->attach_del = 0;
+ }
+ else if (m)
+ {
+ m->parts->length = new_length;
+ m->parts->offset = new_offset;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* flags to _mutt_copy_message */
+#define M_CM_NOHEADER 1 /* don't copy the message header */
+#define M_CM_PREFIX (1<<1) /* quote the message */
+#define M_CM_DECODE (1<<2) /* decode the message body into text/plain */
+#define M_CM_DISPLAY (1<<3) /* output is displayed to the user */
+#define M_CM_UPDATE (1<<4) /* update structs on sync */
+
+
+
+#ifdef _PGPPATH
+#define M_CM_VERIFY (1<<4) /* do signature verification */
+#endif
+
+
+
+int mutt_copy_hdr (FILE *, FILE *, long, long, int, const char *);
+
+int mutt_copy_header (FILE *, HEADER *, FILE *, int, const char *);
+
+int _mutt_copy_message (FILE *fpout,
+ FILE *fpin,
+ HEADER *hdr,
+ BODY *body,
+ int flags,
+ int chflags);
+
+int mutt_copy_message (FILE *fpout,
+ CONTEXT *src,
+ HEADER *hdr,
+ int flags,
+ int chflags);
+
+int _mutt_append_message (CONTEXT *dest,
+ FILE *fpin,
+ CONTEXT *src,
+ HEADER *hdr,
+ BODY *body,
+ int flags,
+ int chflags);
+
+int mutt_append_message (CONTEXT *dest,
+ CONTEXT *src,
+ HEADER *hdr,
+ int cmflags,
+ int chflags);
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_menu.h"
+#include "mutt_curses.h"
+#include "pager.h"
+
+#include <termios.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+/* not possible to unget more than one char under some curses libs, and it
+ * is impossible to unget function keys in SLang, so roll our own input
+ * buffering routines.
+ */
+static short UngetCount = 0;
+
+#define UngetBufLen 128
+static int UngetBuf[UngetBufLen];
+
+void mutt_refresh (void)
+{
+ /* don't refresh in the middle of macros unless necessary */
+ if (!UngetCount || option (OPTFORCEREFRESH))
+ refresh ();
+}
+
+int mutt_getch (void)
+{
+ int ch;
+
+ if (UngetCount)
+ return (UngetBuf[--UngetCount]);
+
+ Signals &= ~S_INTERRUPT;
+
+#ifdef KEY_RESIZE
+ /* ncurses 4.2 sends this when the screen is resized */
+ ch = KEY_RESIZE;
+ while (ch == KEY_RESIZE)
+#endif /* KEY_RESIZE */
+ ch = getch ();
+
+ if (Signals & S_INTERRUPT)
+ mutt_query_exit ();
+
+ if ((ch & 0x80) && option (OPTMETAKEY))
+ {
+ /* send ALT-x as ESC-x */
+ ch &= ~0x80;
+ mutt_ungetch (ch);
+ return ('\033');
+ }
+
+ return (ch == ctrl ('G') ? ERR : ch);
+}
+
+int mutt_get_field (/* const */ char *field, char *buf, size_t buflen, int complete)
+{
+ int ret;
+ int len = strlen (field); /* in case field==buffer */
+
+ do
+ {
+ CLEARLINE (LINES-1);
+ addstr (field);
+ mutt_refresh ();
+ ret = mutt_enter_string ((unsigned char *) buf, buflen, LINES-1, len, complete);
+ }
+ while (ret == 1);
+ CLEARLINE (LINES-1);
+ return (ret);
+}
+
+int mutt_get_password (char *msg, char *buf, size_t buflen)
+{
+ int rc;
+
+ CLEARLINE (LINES-1);
+ addstr (msg);
+ rc = mutt_enter_string ((unsigned char *) buf, buflen, LINES - 1, strlen (msg), M_PASS);
+ CLEARLINE (LINES-1);
+ return (rc);
+}
+
+void mutt_clear_error (void)
+{
+ Errorbuf[0] = 0;
+ CLEARLINE (LINES-1);
+}
+
+void mutt_edit_file (const char *editor, const char *data)
+{
+ char cmd[LONG_STRING];
+
+ endwin ();
+ mutt_expand_fmt (cmd, sizeof (cmd), editor, data);
+ mutt_system (cmd);
+ keypad (stdscr, TRUE);
+ clearok (stdscr, TRUE);
+}
+
+int mutt_yesorno (const char *msg, int def)
+{
+ int ch;
+
+ CLEARLINE(LINES-1);
+ printw("%s: [%c] ", msg, def ? 'y' : 'n');
+ FOREVER
+ {
+ mutt_refresh ();
+ ch = mutt_getch ();
+ if (ch == ERR) return(-1);
+ if (CI_is_return (ch))
+ break;
+ else if (ch == 'y')
+ {
+ def = 1;
+ break;
+ }
+ else if (ch == 'n')
+ {
+ def = 0;
+ break;
+ }
+ else
+ {
+ BEEP();
+ }
+ }
+ addstr (def ? "Yes" : "No");
+ mutt_refresh ();
+ return (def);
+}
+
+/* this function is called when the user presses the abort key */
+void mutt_query_exit (void)
+{
+ mutt_flushinp ();
+ curs_set (1);
+ if (Timeout)
+ timeout (-1); /* restore blocking operation */
+ if (mutt_yesorno ("Exit Mutt?", 1) == 1)
+ {
+ endwin ();
+ exit (0);
+ }
+ mutt_curs_set (-1);
+ Signals &= ~S_INTERRUPT;
+}
+
+void mutt_curses_error (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vsnprintf (Errorbuf, sizeof (Errorbuf), fmt, ap);
+ va_end (ap);
+
+ Errorbuf[ (COLS < sizeof (Errorbuf) ? COLS : sizeof (Errorbuf)) - 2 ] = 0;
+
+ BEEP ();
+ SETCOLOR (MT_COLOR_ERROR);
+ mvaddstr (LINES-1, 0, Errorbuf);
+ clrtoeol ();
+ SETCOLOR (MT_COLOR_NORMAL);
+ mutt_refresh ();
+
+ set_option (OPTMSGERR);
+}
+
+void mutt_message (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vsnprintf (Errorbuf, sizeof (Errorbuf), fmt, ap);
+ va_end (ap);
+
+ Errorbuf[ (COLS < sizeof (Errorbuf) ? COLS : sizeof (Errorbuf)) - 2 ] = 0;
+
+ SETCOLOR (MT_COLOR_MESSAGE);
+ mvaddstr (LINES - 1, 0, Errorbuf);
+ clrtoeol ();
+ SETCOLOR (MT_COLOR_NORMAL);
+ mutt_refresh ();
+
+ unset_option (OPTMSGERR);
+}
+
+void mutt_show_error (void)
+{
+ SETCOLOR (option (OPTMSGERR) ? MT_COLOR_ERROR : MT_COLOR_MESSAGE);
+ CLEARLINE (LINES-1);
+ addstr (Errorbuf);
+ SETCOLOR (MT_COLOR_NORMAL);
+}
+
+void mutt_endwin (const char *msg)
+{
+ move (LINES - 1, COLS - 1);
+ attrset (A_NORMAL);
+ mutt_refresh ();
+ endwin ();
+ fputc ('\n', stdout);
+ if (msg)
+ puts (msg);
+}
+
+void mutt_perror (const char *s)
+{
+ char *p = strerror (errno);
+
+ mutt_error ("%s: %s (errno = %d)", s, p ? p : "unknown error", errno);
+}
+
+int mutt_any_key_to_continue (const char *s)
+{
+ struct termios t;
+ struct termios old;
+ int f, ch;
+
+ f = open ("/dev/tty", O_RDONLY);
+ tcgetattr (f, &t);
+ memcpy ((void *)&old, (void *)&t, sizeof(struct termios)); /* save original state */
+ t.c_lflag &= ~(ICANON | ECHO);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr (f, TCSADRAIN, &t);
+ fflush (stdout);
+ if (s)
+ fputs (s, stdout);
+ else
+ fputs ("Press any key to continue...", stdout);
+ fflush (stdout);
+ ch = fgetc (stdin);
+ fflush (stdin);
+ tcsetattr (f, TCSADRAIN, &old);
+ close (f);
+ fputs ("\r\n", stdout);
+ return (ch);
+}
+
+int mutt_do_pager (const char *banner,
+ const char *tempfile,
+ int do_color,
+ pager_t *info)
+{
+ int rc;
+
+ if (strcmp (Pager, "builtin") == 0)
+ rc = mutt_pager (banner, tempfile, do_color, info);
+ else
+ {
+ char cmd[STRING];
+
+ endwin ();
+ snprintf (cmd, sizeof (cmd), "%s %s", Pager, tempfile);
+ mutt_system (cmd);
+ mutt_unlink (tempfile);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int mutt_enter_fname (const char *prompt, char *buf, size_t blen, int *redraw, int buffy)
+{
+ int i;
+
+ mvaddstr (LINES-1, 0, (char *) prompt);
+ addstr (" ('?' for list): ");
+ if (buf[0])
+ addstr (buf);
+ clrtoeol ();
+ mutt_refresh ();
+
+ if ((i = mutt_getch ()) == ERR)
+ {
+ CLEARLINE (LINES-1);
+ return (-1);
+ }
+ else if (i == '?')
+ {
+ mutt_refresh ();
+ buf[0] = 0;
+ mutt_select_file (buf, blen, 0);
+ *redraw = REDRAW_FULL;
+ }
+ else
+ {
+ char *pc = safe_malloc (strlen (prompt) + 3);
+
+ sprintf (pc, "%s: ", prompt);
+ mutt_ungetch (i);
+ if (mutt_get_field (pc, buf, blen, (buffy ? M_EFILE : M_FILE) | M_CLEAR)
+ != 0)
+ buf[0] = 0;
+ MAYBE_REDRAW (*redraw);
+ free (pc);
+ }
+
+ return 0;
+}
+
+/* FOO - this could be made more efficient by allocating/deallocating memory
+ * instead of using a fixed array
+ */
+void mutt_ungetch (int ch)
+{
+ if (UngetCount < UngetBufLen) /* make sure not to overflow */
+ UngetBuf[UngetCount++] = ch;
+}
+
+void mutt_flushinp (void)
+{
+ UngetCount = 0;
+ flushinp ();
+}
+
+#if (defined(USE_SLANG_CURSES) || defined(HAVE_CURS_SET))
+/* The argument can take 3 values:
+ * -1: restore the value of the last call
+ * 0: make the cursor invisible
+ * 1: make the cursor visible
+ */
+void mutt_curs_set (int cursor)
+{
+ static int SavedCursor = 1;
+
+ if (cursor < 0)
+ cursor = SavedCursor;
+ else
+ SavedCursor = cursor;
+
+ curs_set (cursor);
+}
+#endif
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mutt_menu.h"
+#include "mailbox.h"
+#include "sort.h"
+#include "buffy.h"
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+
+
+
+
+
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#define CHECK_MSGCOUNT if (!Context) \
+ { \
+ mutt_flushinp (); \
+ mutt_error ("No mailbox is open."); \
+ break; \
+ } \
+ else if (!Context->msgcount) \
+ { \
+ mutt_flushinp (); \
+ mutt_error ("There are no messages."); \
+ break; \
+ }
+
+#define CHECK_READONLY if (Context->readonly) \
+ { \
+ mutt_flushinp (); \
+ mutt_error ("Mailbox is read-only."); \
+ break; \
+ }
+
+#define CURHDR Context->hdrs[Context->v2r[menu->current]]
+#define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]]
+
+extern const char *ReleaseDate;
+
+void index_make_entry (char *s, size_t l, MUTTMENU *menu, int num)
+{
+ format_flag flag = M_FORMAT_MAKEPRINT;
+ int reverse = Sort & SORT_REVERSE, edgemsgno;
+ HEADER *tmp, *h = Context->hdrs[Context->v2r[num]];
+
+ if ((Sort & SORT_MASK) == SORT_THREADS && h->tree)
+ {
+ flag |= M_FORMAT_TREE; /* display the thread tree */
+
+ if (h->display_subject)
+ flag |= M_FORMAT_FORCESUBJ;
+ else
+ {
+ edgemsgno = Context->v2r[menu->top + (reverse ? menu->pagelen - 1 : 0)];
+ for (tmp = h->parent; tmp; tmp = tmp->parent)
+ {
+ if ((reverse && tmp->msgno > edgemsgno)
+ || (!reverse && tmp->msgno < edgemsgno))
+ {
+ flag |= M_FORMAT_FORCESUBJ;
+ break;
+ }
+ else if (tmp->virtual >= 0)
+ break;
+ }
+ if ((flag & M_FORMAT_FORCESUBJ) && h->prev)
+ {
+ for (tmp = h->prev; tmp; tmp = tmp->prev)
+ {
+ if ((reverse && tmp->msgno > edgemsgno)
+ || (!reverse && tmp->msgno < edgemsgno))
+ break;
+ else if (tmp->virtual >= 0)
+ {
+ flag &= ~M_FORMAT_FORCESUBJ;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ _mutt_make_string (s, l, NONULL (HdrFmt), h, flag);
+}
+
+int index_color (int index_no)
+{
+ return Context->hdrs[Context->v2r[index_no]]->pair;
+}
+
+static int ci_next_undeleted (int msgno)
+{
+ int i;
+
+ for (i=msgno+1; i < Context->vcount; i++)
+ if (! Context->hdrs[Context->v2r[i]]->deleted)
+ return (i);
+ return (-1);
+}
+
+static int ci_previous_undeleted (int msgno)
+{
+ int i;
+
+ for (i=msgno-1; i>=0; i--)
+ if (! Context->hdrs[Context->v2r[i]]->deleted)
+ return (i);
+ return (-1);
+}
+
+/* Return the index of the first new message, or failing that, the first
+ * unread message.
+ */
+static int ci_first_message (void)
+{
+ int old = -1, i;
+
+ if (Context && Context->msgcount)
+ {
+ for (i=0; i < Context->vcount; i++)
+ {
+ if (! Context->hdrs[Context->v2r[i]]->read &&
+ ! Context->hdrs[Context->v2r[i]]->deleted)
+ {
+ if (! Context->hdrs[Context->v2r[i]]->old)
+ return (i);
+ else if (old == -1)
+ old = i;
+ }
+ }
+ if (old != -1)
+ return (old);
+
+ /* If Sort is reverse and not threaded, the latest message is first.
+ * If Sort is threaded, the latest message is first iff exactly one
+ * of Sort and SortAux are reverse.
+ */
+ if (((Sort & SORT_REVERSE) && (Sort & SORT_MASK) != SORT_THREADS) ||
+ ((Sort & SORT_MASK) == SORT_THREADS &&
+ ((Sort ^ SortAux) & SORT_REVERSE)))
+ return 0;
+ else
+ return (Context->vcount ? Context->vcount - 1 : 0);
+ }
+ return 0;
+}
+
+/* This should be in mx.c, but it only gets used here. */
+static int mx_toggle_write (CONTEXT *ctx)
+{
+ if (!ctx)
+ return -1;
+
+ if (ctx->readonly)
+ {
+ mutt_error ("Cannot toggle write on a readonly mailbox!");
+ return -1;
+ }
+
+ if (ctx->dontwrite)
+ {
+ ctx->dontwrite = 0;
+ mutt_message ("Changes to folder will be written on folder exit.");
+ }
+ else
+ {
+ ctx->dontwrite = 1;
+ mutt_message ("Changes to folder will not be written.");
+ }
+
+ return 0;
+}
+
+struct mapping_t IndexHelp[] = {
+ { "Quit", OP_QUIT },
+ { "Del", OP_DELETE },
+ { "Undel", OP_UNDELETE },
+ { "Save", OP_SAVE },
+ { "Mail", OP_MAIL },
+ { "Reply", OP_REPLY },
+ { "Group", OP_GROUP_REPLY },
+ { "Help", OP_HELP },
+ { NULL }
+};
+
+/* This function handles the message index window as well as commands returned
+ * from the pager (MENU_PAGER).
+ */
+void mutt_index_menu (void)
+{
+ char buf[LONG_STRING], helpstr[SHORT_STRING];
+ int op = OP_NULL; /* function to execute */
+ int done = 0; /* controls when to exit the "event" loop */
+ int i = 0, j;
+ int tag = 0; /* has the tag-prefix command been pressed? */
+ int newcount = -1;
+ int oldcount = -1;
+ int rc = -1;
+ MUTTMENU *menu;
+ char *cp; /* temporary variable. */
+ int index_hint; /* used to restore cursor position */
+ int do_buffy_notify = 1;
+
+ menu = mutt_new_menu ();
+ menu->menu = MENU_MAIN;
+ menu->offset = 1;
+ menu->pagelen = LINES - 3;
+ menu->make_entry = index_make_entry;
+ menu->color = index_color;
+ menu->current = ci_first_message ();
+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN, IndexHelp);
+
+ mutt_buffy_check(1); /* force the buffy check after we enter the folder */
+
+ FOREVER
+ {
+ tag = 0; /* clear the tag-prefix */
+
+ menu->max = Context ? Context->vcount : 0;
+ oldcount = Context ? Context->msgcount : 0;
+
+ if (Context)
+ {
+ /* check for new mail in the mailbox. If nonzero, then something has
+ * changed about the file (either we got new mail or the file was
+ * modified underneath us.)
+ */
+ index_hint = (Context->vcount) ? CURHDR->index : 0;
+
+ if ((i = mx_check_mailbox (Context, &index_hint)) < 0)
+ {
+ if (!Context->path)
+ {
+ /* fatal error occurred */
+ safe_free ((void **) &Context);
+ menu->redraw = REDRAW_FULL;
+ }
+
+ set_option (OPTSEARCHINVALID);
+ }
+ else if (i == M_NEW_MAIL || i == M_REOPENED)
+ {
+ /* take note of the current message */
+ if (oldcount)
+ {
+ if (menu->current < Context->vcount)
+ menu->oldcurrent = index_hint;
+ else
+ oldcount = 0; /* invalid message number! */
+ }
+
+ /* We are in a limited view. Check if the new message(s) satisfy
+ * the limit criteria. If they do, set their virtual msgno so that
+ * they will be visible in the limited view */
+ if (Context->pattern)
+ {
+ #define this_body Context->hdrs[i]->content
+ if (oldcount)
+ for (i = oldcount; i < Context->msgcount; i++)
+ {
+ if (mutt_pattern_exec (Context->limit_pattern,
+ M_MATCH_FULL_ADDRESS,
+ Context, Context->hdrs[i]))
+ {
+ Context->hdrs[i]->virtual = Context->vcount;
+ Context->v2r[Context->vcount] = i;
+ Context->vcount++;
+ Context->vsize+=this_body->length + this_body->offset -
+ this_body->hdr_offset;
+ }
+ }
+ #undef this_body
+ }
+
+ /* if the mailbox was reopened, need to rethread from scratch */
+ mutt_sort_headers (Context, (i == M_REOPENED));
+
+ menu->current = -1;
+ if (oldcount)
+ {
+ int j;
+
+ /* restore the current message to the message it was pointing to */
+ for (j = 0; j < Context->vcount; j++)
+ if (Context->hdrs[Context->v2r[j]]->index == menu->oldcurrent)
+ {
+ menu->current = j;
+ break;
+ }
+ }
+
+ if (menu->current < 0)
+ menu->current = ci_first_message ();
+
+ /* notify the user of new mail */
+ if (i == M_REOPENED)
+ mutt_error ("Mailbox was externally modified. Flags may be wrong.");
+ else
+ {
+ mutt_message ("New mail in this mailbox.");
+ if (option (OPTBEEPNEW))
+ beep ();
+ }
+ /* avoid the message being overwritten by buffy */
+ do_buffy_notify = 0;
+
+ menu->redraw = REDRAW_FULL;
+ menu->max = Context->vcount;
+
+ set_option (OPTSEARCHINVALID);
+ }
+ }
+
+ /* check for new mail in the incoming folders */
+ oldcount = newcount;
+ if ((newcount = mutt_buffy_check (0)) != oldcount)
+ menu->redraw |= REDRAW_STATUS;
+ if (do_buffy_notify)
+ {
+ if (mutt_buffy_notify () && option (OPTBEEPNEW))
+ beep ();
+ }
+ else
+ do_buffy_notify = 1;
+
+ mutt_curs_set (0);
+
+ if (menu->redraw & REDRAW_FULL)
+ {
+ menu_redraw_full (menu);
+ mutt_show_error ();
+ }
+
+ if (menu->menu == MENU_MAIN)
+ {
+ if (Context)
+ {
+ menu_check_recenter (menu);
+
+ if (menu->redraw & REDRAW_INDEX)
+ {
+ menu_redraw_index (menu);
+ menu->redraw |= REDRAW_STATUS;
+ }
+ else if (menu->redraw & (REDRAW_MOTION_RESYNCH | REDRAW_MOTION))
+ menu_redraw_motion (menu);
+ else if (menu->redraw & REDRAW_CURRENT)
+ menu_redraw_current (menu);
+ }
+
+ if (menu->redraw & REDRAW_STATUS)
+ {
+ menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
+ CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES-2);
+ SETCOLOR (MT_COLOR_STATUS);
+ printw ("%-*.*s", COLS, COLS, buf);
+ SETCOLOR (MT_COLOR_NORMAL);
+ menu->redraw &= ~REDRAW_STATUS;
+ }
+
+ menu->redraw = 0;
+ menu->oldcurrent = menu->current;
+
+ if (option (OPTARROWCURSOR))
+ move (menu->current - menu->top + menu->offset, 2);
+ else
+ move (menu->current - menu->top + menu->offset, COLS - 1);
+ mutt_refresh ();
+
+ if (Timeout > 0)
+ {
+ timeout (Timeout * 1000); /* milliseconds */
+ op = mutt_getch ();
+ timeout (-1); /* restore blocking operation */
+ if (op != -1)
+ {
+ mutt_ungetch (op);
+ op = km_dokey (MENU_MAIN);
+ }
+ }
+ else
+ op = km_dokey (MENU_MAIN);
+ mutt_curs_set (1);
+
+#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
+ if (Signals & S_SIGWINCH)
+ {
+ mutt_flushinp ();
+ mutt_resize_screen ();
+ menu->redraw = REDRAW_FULL;
+ menu->menu = MENU_MAIN;
+ Signals &= ~S_SIGWINCH;
+ menu->top = 0; /* so we scroll the right amount */
+ continue;
+ }
+#endif
+
+ if (op == -1)
+ continue; /* either user abort or timeout */
+
+ /* special handling for the tag-prefix function */
+ if (op == OP_TAG_PREFIX)
+ {
+ if (!Context)
+ {
+ mutt_error ("No mailbox is open.");
+ continue;
+ }
+
+ if (!Context->tagged)
+ {
+ mutt_error ("No tagged messages.");
+ continue;
+ }
+ tag = 1;
+
+ /* give visual indication that the next command is a tag- command */
+ mvaddstr (LINES - 1, 0, "tag-");
+ clrtoeol ();
+
+ /* get the real command */
+ if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX)
+ {
+ /* abort tag sequence */
+ CLEARLINE (LINES-1);
+ continue;
+ }
+ }
+ else if (option (OPTAUTOTAG) && Context && Context->tagged)
+ tag = 1;
+
+ mutt_clear_error ();
+ }
+ else
+ mutt_curs_set (1); /* fallback from the pager */
+
+ switch (op)
+ {
+
+ /* ----------------------------------------------------------------------
+ * movement commands
+ */
+
+ case OP_BOTTOM_PAGE:
+ menu_bottom_page (menu);
+ break;
+ case OP_FIRST_ENTRY:
+ menu_first_entry (menu);
+ break;
+ case OP_MIDDLE_PAGE:
+ menu_middle_page (menu);
+ break;
+ case OP_HALF_UP:
+ menu_half_up (menu);
+ break;
+ case OP_HALF_DOWN:
+ menu_half_down (menu);
+ break;
+ case OP_NEXT_LINE:
+ menu_next_line (menu);
+ break;
+ case OP_PREV_LINE:
+ menu_prev_line (menu);
+ break;
+ case OP_NEXT_PAGE:
+ menu_next_page (menu);
+ break;
+ case OP_PREV_PAGE:
+ menu_prev_page (menu);
+ break;
+ case OP_LAST_ENTRY:
+ menu_last_entry (menu);
+ break;
+ case OP_TOP_PAGE:
+ menu_top_page (menu);
+ break;
+ case OP_CURRENT_TOP:
+ menu_current_top (menu);
+ break;
+ case OP_CURRENT_MIDDLE:
+ menu_current_middle (menu);
+ break;
+ case OP_CURRENT_BOTTOM:
+ menu_current_bottom (menu);
+ break;
+
+ case OP_JUMP:
+
+ CHECK_MSGCOUNT;
+ mutt_ungetch (LastKey);
+ buf[0] = 0;
+ if (mutt_get_field ("Jump to message: ", buf, sizeof (buf), 0) != 0 ||
+ !buf[0])
+ break;
+
+ if (! isdigit (buf[0]))
+ {
+ mutt_error ("Argument must be a message number.");
+ break;
+ }
+
+ i = atoi (buf);
+ if (i > 0 && i <= Context->msgcount)
+ {
+ for (j = i-1; j < Context->msgcount; j++)
+ {
+ if (Context->hdrs[j]->virtual != -1)
+ break;
+ }
+ if (j >= Context->msgcount)
+ {
+ for (j = i-2; j >= 0; j--)
+ {
+ if (Context->hdrs[j]->virtual != -1)
+ break;
+ }
+ }
+
+ if (j >= 0)
+ {
+ menu->current = Context->hdrs[j]->virtual;
+ if (menu->menu == MENU_PAGER)
+ {
+ op = OP_DISPLAY_MESSAGE;
+ continue;
+ }
+ else
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("That message is not visible.");
+ }
+ else
+ mutt_error ("Invalid message number.");
+
+ break;
+
+ /* --------------------------------------------------------------------
+ * `index' specific commands
+ */
+
+ case OP_MAIN_DELETE_PATTERN:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+ mutt_pattern_func (M_DELETE, "Delete messages matching: ", CURHDR);
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ break;
+
+#ifdef USE_POP
+ case OP_MAIN_FETCH_MAIL:
+
+ mutt_fetchPopMail ();
+ menu->redraw = REDRAW_FULL;
+ break;
+#endif /* USE_POP */
+
+ case OP_HELP:
+
+ mutt_help (MENU_MAIN);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_MAIN_SHOW_LIMIT:
+ CHECK_MSGCOUNT;
+ if (!Context->pattern)
+ mutt_message ("No limit pattern is in effect.");
+ else
+ {
+ char buf[STRING];
+ snprintf (buf, sizeof(buf), "Limit: %s",Context->pattern);
+ mutt_message ("%s", buf);
+ }
+ break;
+
+ case OP_MAIN_LIMIT:
+
+ CHECK_MSGCOUNT;
+ menu->oldcurrent = Context->vcount ? CURHDR->index : -1;
+ if (mutt_pattern_func (M_LIMIT, "Limit to messages matching: ", CURHDR) == 0)
+ {
+ if (menu->oldcurrent >= 0)
+ {
+ /* try to find what used to be the current message */
+ menu->current = -1;
+ for (i = 0; i < Context->vcount; i++)
+ if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent)
+ {
+ menu->current = i;
+ break;
+ }
+ if (menu->current < 0) menu->current = 0;
+ }
+ else
+ menu->current = 0;
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ mutt_linearize_tree (Context, 0);
+ }
+ break;
+
+ case OP_QUIT:
+
+ if (query_quadoption (OPT_QUIT, "Quit Mutt?") == M_YES)
+ {
+ if (!Context || mx_close_mailbox (Context) == 0)
+ done = 1;
+ else
+ menu->redraw = REDRAW_FULL; /* new mail arrived? */
+ }
+ break;
+
+ case OP_REDRAW:
+
+ clearok (stdscr, TRUE);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_SEARCH:
+ case OP_SEARCH_REVERSE:
+ case OP_SEARCH_NEXT:
+ case OP_SEARCH_OPPOSITE:
+
+ CHECK_MSGCOUNT;
+ if ((menu->current = mutt_search_command (menu->current, op)) == -1)
+ menu->current = menu->oldcurrent;
+ else
+ menu->redraw = REDRAW_MOTION;
+ break;
+
+ case OP_SORT:
+ case OP_SORT_REVERSE:
+
+ if (mutt_select_sort ((op == OP_SORT_REVERSE)) == 0)
+ {
+ if (Context && Context->msgcount)
+ {
+ menu->oldcurrent = CURHDR->index;
+ mutt_sort_headers (Context, 0);
+
+ /* try to restore the current message */
+ for (i = 0; i < Context->vcount; i++)
+ {
+ if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent)
+ menu->current = i;
+ }
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ set_option (OPTSEARCHINVALID);
+ }
+ }
+ break;
+
+ case OP_MAIN_SYNC_FOLDER:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+ {
+ int oldvcount = Context->vcount;
+ int dcount = 0;
+
+ /* calculate the number of messages _above_ the cursor,
+ * so we can keep the cursor on the current message
+ */
+ for (j = 0; j < menu->current; j++)
+ {
+ if (Context->hdrs[Context->v2r[j]]->deleted)
+ dcount++;
+ }
+ if (mx_sync_mailbox (Context) == 0)
+ {
+ if (Context->vcount != oldvcount)
+ {
+ menu->current -= dcount;
+ if (menu->current < 0 || menu->current >= Context->vcount)
+ menu->current = ci_first_message ();
+ }
+ set_option (OPTSEARCHINVALID);
+ }
+ }
+
+ /* check for a fatal error, or all messages deleted */
+ if (!Context->path)
+ safe_free ((void **) &Context);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_TAG:
+
+ CHECK_MSGCOUNT;
+ if (tag && !option (OPTAUTOTAG))
+ {
+ for (j = 0; j < Context->vcount; j++)
+ mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_TAG, 0);
+ menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
+ }
+ else
+ {
+ mutt_set_flag (Context, CURHDR, M_TAG, !CURHDR->tagged);
+ menu->redraw = REDRAW_STATUS;
+ if (option (OPTRESOLVE) && menu->current < Context->vcount - 1)
+ {
+ menu->current++;
+ menu->redraw |= REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw |= REDRAW_CURRENT;
+ }
+ break;
+
+ case OP_MAIN_TAG_PATTERN:
+
+ CHECK_MSGCOUNT;
+ mutt_pattern_func (M_TAG, "Tag messages matching: ", CURHDR);
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ break;
+
+ case OP_MAIN_UNDELETE_PATTERN:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+ if (mutt_pattern_func (M_UNDELETE, "Undelete messages matching: ", CURHDR) == 0)
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ break;
+
+ case OP_MAIN_UNTAG_PATTERN:
+
+ CHECK_MSGCOUNT;
+ if (mutt_pattern_func (M_UNTAG, "Untag messages matching: ", CURHDR) == 0)
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ break;
+
+ /* --------------------------------------------------------------------
+ * The following operations can be performed inside of the pager.
+ */
+
+ case OP_MAIN_CHANGE_FOLDER:
+
+ if (option (OPTREADONLY))
+ op = OP_MAIN_CHANGE_FOLDER_READONLY;
+
+ /* fallback to the readonly case */
+
+ case OP_MAIN_CHANGE_FOLDER_READONLY:
+
+ if (op == OP_MAIN_CHANGE_FOLDER)
+ cp = "Open mailbox";
+ else
+ cp = "Open mailbox in read-only mode";
+
+ buf[0] = '\0';
+ mutt_buffy (buf);
+
+ if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1)
+ break;
+ if (!buf[0])
+ {
+ CLEARLINE (LINES-1);
+ break;
+ }
+
+ mutt_expand_path (buf, sizeof (buf));
+ if (mx_get_magic (buf) <= 0)
+ {
+ mutt_error ("%s is not a mailbox.", buf);
+ break;
+ }
+
+ if (Context)
+ {
+ FREE (&LastFolder);
+ LastFolder = safe_strdup (Context->path);
+ }
+
+ if (Context)
+ {
+ if (mx_close_mailbox (Context) != 0)
+ {
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ break;
+ }
+ safe_free ((void **) &Context);
+ }
+
+ sleep (1); /* give a second to read the mailbox status */
+
+ mutt_folder_hook (buf);
+
+ if ((Context = mx_open_mailbox (buf,
+ (option (OPTREADONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ?
+ M_READONLY : 0, NULL)) != NULL)
+ {
+ menu->current = ci_first_message ();
+ }
+ else
+ menu->current = 0;
+
+ mutt_clear_error ();
+ mutt_buffy_check(1); /* force the buffy check after we have changed
+ the folder */
+ menu->redraw = REDRAW_FULL;
+ set_option (OPTSEARCHINVALID);
+ break;
+
+ case OP_DISPLAY_MESSAGE:
+ case OP_DISPLAY_HEADERS: /* don't weed the headers */
+
+ CHECK_MSGCOUNT;
+ /*
+ * toggle the weeding of headers so that a user can press the key
+ * again while reading the message.
+ */
+ if (op == OP_DISPLAY_HEADERS)
+ toggle_option (OPTWEED);
+
+ unset_option (OPTNEEDRESORT);
+
+ if ((op = mutt_display_message (CURHDR)) == -1)
+ {
+ unset_option (OPTNEEDRESORT);
+ break;
+ }
+
+ if (option (OPTNEEDRESORT) && Context && Context->msgcount)
+ {
+ menu->oldcurrent = CURHDR->index;
+ mutt_sort_headers (Context, 0);
+
+ /* try to restore the current message */
+ for (i = 0; i < Context->vcount; i++)
+ {
+ if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent)
+ menu->current = i;
+ }
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ }
+
+ menu->menu = MENU_PAGER;
+ menu->oldcurrent = menu->current;
+ continue;
+
+ case OP_EXIT:
+
+ if ((menu->menu == MENU_MAIN)
+ && (query_quadoption (OPT_QUIT,
+ "Exit Mutt without saving?") == M_YES))
+ {
+ if (Context)
+ {
+ mx_fastclose_mailbox (Context);
+ safe_free ((void **)&Context);
+ }
+ done = 1;
+ }
+ break;
+
+ case OP_MAIN_NEXT_UNDELETED:
+
+ CHECK_MSGCOUNT;
+ if (menu->current >= Context->vcount - 1)
+ {
+ if (menu->menu == MENU_MAIN)
+ mutt_error ("You are on the last message.");
+ break;
+ }
+ if ((menu->current = ci_next_undeleted (menu->current)) == -1)
+ {
+ menu->current = menu->oldcurrent;
+ if (menu->menu == MENU_MAIN)
+ mutt_error ("No undeleted messages.");
+ }
+ else if (menu->menu == MENU_PAGER)
+ {
+ op = OP_DISPLAY_MESSAGE;
+ continue;
+ }
+ else
+ menu->redraw = REDRAW_MOTION;
+ break;
+
+ case OP_NEXT_ENTRY:
+
+ CHECK_MSGCOUNT;
+ if (menu->current >= Context->vcount - 1)
+ {
+ if (menu->menu == MENU_MAIN)
+ mutt_error ("You are on the last message.");
+ break;
+ }
+ menu->current++;
+ if (menu->menu == MENU_PAGER)
+ {
+ op = OP_DISPLAY_MESSAGE;
+ continue;
+ }
+ else
+ menu->redraw = REDRAW_MOTION;
+ break;
+
+ case OP_MAIN_PREV_UNDELETED:
+
+ CHECK_MSGCOUNT;
+ if (menu->current < 1)
+ {
+ mutt_error ("You are on the first message.");
+ break;
+ }
+ if ((menu->current = ci_previous_undeleted (menu->current)) == -1)
+ {
+ menu->current = menu->oldcurrent;
+ if (menu->menu == MENU_MAIN)
+ mutt_error ("No undeleted messages.");
+ }
+ else if (menu->menu == MENU_PAGER)
+ {
+ op = OP_DISPLAY_MESSAGE;
+ continue;
+ }
+ else
+ menu->redraw = REDRAW_MOTION;
+ break;
+
+ case OP_PREV_ENTRY:
+
+ CHECK_MSGCOUNT;
+ if (menu->current < 1)
+ {
+ if (menu->menu == MENU_MAIN) mutt_error ("You are on the first message.");
+ break;
+ }
+ menu->current--;
+ if (menu->menu == MENU_PAGER)
+ {
+ op = OP_DISPLAY_MESSAGE;
+ continue;
+ }
+ else
+ menu->redraw = REDRAW_MOTION;
+ break;
+
+ case OP_COPY_MESSAGE:
+ case OP_SAVE:
+ case OP_DECODE_COPY:
+ case OP_DECODE_SAVE:
+
+ CHECK_MSGCOUNT;
+ if (mutt_save_message (tag ? NULL : CURHDR,
+ (op == OP_SAVE || op == OP_DECODE_SAVE),
+ (op == OP_DECODE_SAVE || op == OP_DECODE_COPY),
+ &menu->redraw) == 0 &&
+ (op == OP_SAVE || op == OP_DECODE_SAVE))
+ {
+ if (tag)
+ menu->redraw |= REDRAW_INDEX;
+ else if (option (OPTRESOLVE))
+ {
+ if ((menu->current = ci_next_undeleted (menu->current)) == -1)
+ {
+ menu->current = menu->oldcurrent;
+ menu->redraw |= REDRAW_CURRENT;
+ }
+ else
+ menu->redraw |= REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw |= REDRAW_CURRENT;
+ }
+ break;
+
+ case OP_MAIN_NEXT_NEW:
+ case OP_MAIN_NEXT_UNREAD:
+ case OP_MAIN_PREV_NEW:
+ case OP_MAIN_PREV_UNREAD:
+
+ CHECK_MSGCOUNT;
+ i = menu->current;
+ menu->current = -1;
+ for (j = 0; j != Context->vcount; j++)
+ {
+ if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_NEXT_UNREAD)
+ {
+ i++;
+ if (i > Context->vcount - 1)
+ {
+ mutt_message ("Search wrapped to top.");
+ i = 0;
+ }
+ }
+ else
+ {
+ i--;
+ if (i < 0)
+ {
+ mutt_message ("Search wrapped to bottom.");
+ i = Context->vcount - 1;
+ }
+ }
+
+ if (! Context->hdrs[Context->v2r[i]]->deleted &&
+ ! Context->hdrs[Context->v2r[i]]->read)
+ {
+ if (op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD ||
+ ! Context->hdrs[Context->v2r[i]]->old)
+ {
+ menu->current = i;
+ break;
+ }
+ }
+ }
+ if (menu->current == -1)
+ {
+ menu->current = menu->oldcurrent;
+ mutt_error ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW) ?
+ "No new messages." : "No unread messages.");
+ }
+ else if (menu->menu == MENU_PAGER)
+ {
+ op = OP_DISPLAY_MESSAGE;
+ continue;
+ }
+ else
+ menu->redraw = REDRAW_MOTION;
+ break;
+
+ case OP_FLAG_MESSAGE:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+ mutt_set_flag (Context, CURHDR, M_FLAG, !CURHDR->flagged);
+
+ if (option (OPTRESOLVE))
+ {
+ if ((menu->current = ci_next_undeleted (menu->current)) == -1)
+ {
+ menu->current = menu->oldcurrent;
+ menu->redraw = REDRAW_CURRENT;
+ }
+ else
+ menu->redraw = REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw = REDRAW_CURRENT;
+ menu->redraw |= REDRAW_STATUS;
+ break;
+
+ case OP_TOGGLE_NEW:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+ if (tag)
+ {
+ for (j = 0; j < Context->vcount; j++)
+ {
+ if (Context->hdrs[Context->v2r[j]]->tagged)
+ {
+ if (Context->hdrs[Context->v2r[j]]->read ||
+ Context->hdrs[Context->v2r[j]]->old)
+ mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_NEW, 1);
+ else
+ mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_READ, 1);
+ }
+ }
+ menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
+ }
+ else
+ {
+ if (CURHDR->read || CURHDR->old)
+ mutt_set_flag (Context, CURHDR, M_NEW, 1);
+ else
+ mutt_set_flag (Context, CURHDR, M_READ, 1);
+
+ if (option (OPTRESOLVE))
+ {
+ if ((menu->current = ci_next_undeleted (menu->current)) == -1)
+ {
+ menu->current = menu->oldcurrent;
+ menu->redraw = REDRAW_CURRENT;
+ }
+ else
+ menu->redraw = REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw = REDRAW_CURRENT;
+ menu->redraw |= REDRAW_STATUS;
+ }
+ break;
+
+ case OP_TOGGLE_WRITE:
+
+ CHECK_MSGCOUNT;
+ if (mx_toggle_write (Context) == 0)
+ menu->redraw |= REDRAW_STATUS;
+ break;
+
+ case OP_MAIN_NEXT_THREAD:
+ case OP_MAIN_NEXT_SUBTHREAD:
+ case OP_MAIN_PREV_THREAD:
+ case OP_MAIN_PREV_SUBTHREAD:
+
+ CHECK_MSGCOUNT;
+ if (Context->msgcount != Context->vcount)
+ {
+ mutt_error ("No threads in limit mode.");
+ break;
+ }
+
+ switch (op)
+ {
+ case OP_MAIN_NEXT_THREAD:
+ menu->current = mutt_next_thread (CURHDR);
+ break;
+
+ case OP_MAIN_NEXT_SUBTHREAD:
+ menu->current = mutt_next_subthread (CURHDR);
+ break;
+
+ case OP_MAIN_PREV_THREAD:
+ menu->current = mutt_previous_thread (CURHDR);
+ break;
+
+ case OP_MAIN_PREV_SUBTHREAD:
+ menu->current = mutt_previous_subthread (CURHDR);
+ break;
+ }
+
+ if (menu->current < 0)
+ {
+ menu->current = menu->oldcurrent;
+ if (op == OP_MAIN_NEXT_THREAD || op == OP_MAIN_NEXT_SUBTHREAD)
+ mutt_error ("No more threads.");
+ else
+ mutt_error ("You are on the first thread.");
+ }
+ else if (menu->menu == MENU_PAGER)
+ {
+ op = OP_DISPLAY_MESSAGE;
+ continue;
+ }
+ else
+ menu->redraw = REDRAW_MOTION;
+ break;
+
+ case OP_MAIN_SET_FLAG:
+ case OP_MAIN_CLEAR_FLAG:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+ if (mutt_change_flag (tag ? NULL : CURHDR, (op == OP_MAIN_SET_FLAG)) == 0)
+ {
+ menu->redraw = REDRAW_STATUS;
+ if (tag)
+ menu->redraw |= REDRAW_INDEX;
+ else if (option (OPTRESOLVE))
+ {
+ if ((menu->current = ci_next_undeleted (menu->current)) == -1)
+ {
+ menu->current = menu->oldcurrent;
+ menu->redraw |= REDRAW_CURRENT;
+ }
+ else
+ menu->redraw |= REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw |= REDRAW_CURRENT;
+ }
+ break;
+
+ /* --------------------------------------------------------------------
+ * These functions are invoked directly from the internal-pager
+ */
+
+ case OP_BOUNCE_MESSAGE:
+
+ CHECK_MSGCOUNT;
+ ci_bounce_message (tag ? NULL : CURHDR, &menu->redraw);
+ break;
+
+ case OP_CREATE_ALIAS:
+
+ mutt_create_alias (Context && Context->vcount ? CURHDR->env : NULL, NULL);
+ MAYBE_REDRAW (menu->redraw);
+ break;
+
+ case OP_QUERY:
+ mutt_query_menu (NULL, 0);
+ MAYBE_REDRAW (menu->redraw);
+ break;
+
+ case OP_DELETE:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+ if (tag)
+ {
+ mutt_tag_set_flag (M_DELETE, 1);
+ menu->redraw = REDRAW_INDEX;
+ }
+ else
+ {
+ mutt_set_flag (Context, CURHDR, M_DELETE, 1);
+ if (option (OPTRESOLVE))
+ {
+ if ((menu->current = ci_next_undeleted (menu->current)) == -1)
+ {
+ menu->current = menu->oldcurrent;
+ menu->redraw = REDRAW_CURRENT;
+ }
+ else if (menu->menu == MENU_PAGER)
+ {
+ op = OP_DISPLAY_MESSAGE;
+ continue;
+ }
+ else
+ menu->redraw |= REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw = REDRAW_CURRENT;
+ }
+ menu->redraw |= REDRAW_STATUS;
+ break;
+
+ case OP_DELETE_THREAD:
+ case OP_DELETE_SUBTHREAD:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+
+ rc = mutt_thread_set_flag (CURHDR, M_DELETE, 1,
+ op == OP_DELETE_THREAD ? 0 : 1);
+
+ if (rc != -1)
+ {
+ if (option (OPTRESOLVE))
+ if ((menu->current = ci_next_undeleted (menu->current)) == -1)
+ menu->current = menu->oldcurrent;
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ }
+ break;
+
+ case OP_DISPLAY_ADDRESS:
+
+ CHECK_MSGCOUNT;
+ mutt_display_address (CURHDR->env->from);
+ break;
+
+ case OP_ENTER_COMMAND:
+
+ mutt_enter_command ();
+ mutt_check_rescore (Context);
+ if (option (OPTNEEDRESORT) && Context && Context->msgcount)
+ {
+ menu->oldcurrent = CURHDR->index;
+ mutt_sort_headers (Context, 0);
+
+ /* try to restore the current message */
+ for (i = 0; i < Context->vcount; i++)
+ {
+ if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent)
+ menu->current = i;
+ }
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ }
+ if (option (OPTFORCEREDRAWINDEX))
+ menu->redraw = REDRAW_FULL;
+ unset_option (OPTFORCEREDRAWINDEX);
+ unset_option (OPTFORCEREDRAWPAGER);
+ break;
+
+ case OP_FORWARD_MESSAGE:
+
+ CHECK_MSGCOUNT;
+ ci_send_message (SENDFORWARD, NULL, NULL, Context, tag ? NULL : CURHDR);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+
+
+#ifdef _PGPPATH
+ case OP_FORGET_PASSPHRASE:
+
+ mutt_forget_passphrase ();
+ break;
+#endif /* _PGPPATH */
+
+
+
+ case OP_GROUP_REPLY:
+
+ CHECK_MSGCOUNT;
+ ci_send_message (SENDREPLY|SENDGROUPREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_LIST_REPLY:
+
+ CHECK_MSGCOUNT;
+ ci_send_message (SENDREPLY|SENDLISTREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_MAIL:
+
+ ci_send_message (0, NULL, NULL, NULL, NULL);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+
+
+
+
+
+
+#ifdef _PGPPATH
+ case OP_MAIL_KEY:
+
+ ci_send_message (SENDKEY, NULL, NULL, NULL, NULL);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_EXTRACT_KEYS:
+
+ CHECK_MSGCOUNT;
+ pgp_extract_keys_from_messages(tag ? NULL : CURHDR);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+#endif /* _PGPPATH */
+
+
+
+
+
+
+
+ case OP_PIPE:
+
+ CHECK_MSGCOUNT;
+ mutt_pipe_message (tag ? NULL : CURHDR);
+ break;
+
+ case OP_PRINT:
+
+ CHECK_MSGCOUNT;
+ mutt_print_message (tag ? NULL : CURHDR);
+ break;
+
+ case OP_MAIN_READ_THREAD:
+ case OP_MAIN_READ_SUBTHREAD:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+
+ rc = mutt_thread_set_flag (CURHDR, M_READ, 1,
+ op == OP_MAIN_READ_THREAD ? 0 : 1);
+
+ if (rc != -1)
+ {
+ if (option (OPTRESOLVE))
+ {
+ if ((menu->oldcurrent = ci_next_undeleted (menu->current)) == -1)
+ menu->oldcurrent = menu->current;
+ }
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ }
+ break;
+
+ case OP_RECALL_MESSAGE:
+
+ ci_send_message (SENDPOSTPONED, NULL, NULL, Context, NULL);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_REPLY:
+
+ CHECK_MSGCOUNT;
+ ci_send_message (SENDREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_SHELL_ESCAPE:
+
+ mutt_shell_escape ();
+ MAYBE_REDRAW (menu->redraw);
+ break;
+
+ case OP_TAG_THREAD:
+ case OP_TAG_SUBTHREAD:
+
+ CHECK_MSGCOUNT;
+ rc = mutt_thread_set_flag (CURHDR, M_TAG, !CURHDR->tagged,
+ op == OP_TAG_THREAD ? 0 : 1);
+
+ if (rc != -1)
+ {
+ if (option (OPTRESOLVE))
+ {
+ menu->current = mutt_next_thread (CURHDR);
+
+ if (menu->current == -1)
+ menu->current = menu->oldcurrent;
+ }
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ }
+ break;
+
+ case OP_UNDELETE:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+ if (tag)
+ {
+ mutt_tag_set_flag (M_DELETE, 0);
+ menu->redraw = REDRAW_INDEX;
+ }
+ else
+ {
+ mutt_set_flag (Context, CURHDR, M_DELETE, 0);
+ if (option (OPTRESOLVE) && menu->current < Context->vcount - 1)
+ {
+ menu->current++;
+ menu->redraw = REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw = REDRAW_CURRENT;
+ }
+ menu->redraw |= REDRAW_STATUS;
+ break;
+
+ case OP_UNDELETE_THREAD:
+ case OP_UNDELETE_SUBTHREAD:
+
+ CHECK_MSGCOUNT;
+ CHECK_READONLY;
+
+ rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0,
+ op == OP_UNDELETE_THREAD ? 0 : 1);
+
+ if (rc != -1)
+ {
+ if (option (OPTRESOLVE))
+ {
+ if (op == OP_UNDELETE_THREAD)
+ menu->current = mutt_next_thread (CURHDR);
+ else
+ menu->current = mutt_next_subthread (CURHDR);
+
+ if (menu->current == -1)
+ menu->current = menu->oldcurrent;
+ }
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ }
+ break;
+
+ case OP_VERSION:
+ mutt_version ();
+ break;
+
+ case OP_VIEW_ATTACHMENTS:
+ CHECK_MSGCOUNT;
+ mutt_view_attachments (CURHDR);
+ if (CURHDR->attach_del)
+ Context->changed = 1;
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ default:
+ if (menu->menu == MENU_MAIN)
+ km_error_key (MENU_MAIN);
+ }
+
+ if (menu->menu == MENU_PAGER)
+ {
+ menu->menu = MENU_MAIN;
+ menu->redraw = REDRAW_FULL;
+ set_option (OPTWEED); /* turn header weeding back on. */
+ }
+
+ if (done) break;
+ }
+
+ mutt_menuDestroy (&menu);
+}
+
+void mutt_set_header_color (CONTEXT *ctx, HEADER *curhdr)
+{
+ COLOR_LINE *color;
+
+ if (!curhdr)
+ return;
+
+ for (color = ColorIndexList; color; color = color->next)
+ if (mutt_pattern_exec (color->color_pattern, M_MATCH_FULL_ADDRESS, ctx, curhdr))
+ {
+ curhdr->pair = color->pair;
+ return;
+ }
+ curhdr->pair = ColorDefs[MT_COLOR_NORMAL];
+}
+
+void mutt_cache_index_colors (CONTEXT *ctx)
+{
+ int i;
+
+ if (ctx)
+ for (i = 0; i < ctx->msgcount; i++)
+ mutt_set_header_color (ctx, ctx->hdrs[i]);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+
+/* returns the seconds west of UTC given `g' and its corresponding gmtime()
+ representation */
+static time_t mutt_compute_tz (time_t g, struct tm *utc)
+{
+ struct tm *lt = localtime (&g);
+ time_t t;
+
+ t = (((utc->tm_hour - lt->tm_hour) * 60) + (utc->tm_min - lt->tm_min)) * 60;
+ switch (utc->tm_yday - lt->tm_yday)
+ {
+ case 0:
+ break;
+
+ case 1:
+ case -364:
+ case -365:
+ t += 24 * 60 * 60;
+ break;
+
+ case -1:
+ case 364:
+ case 365:
+ t -= 24 * 60 * 60;
+ break;
+
+ default:
+ mutt_error ("Please report this program error in the function mutt_mktime.");
+ }
+
+ return t;
+}
+
+time_t mutt_local_tz (void)
+{
+ struct tm *ptm;
+ struct tm utc;
+ time_t now;
+
+ now = time (NULL);
+ ptm = gmtime (&now);
+ /* need to make a copy because gmtime/localtime return a pointer to
+ static memory (grr!) */
+ memcpy (&utc, ptm, sizeof (utc));
+ return (mutt_compute_tz (now, &utc));
+}
+
+/* converts struct tm to time_t, but does not take the local timezone into
+ account unless ``local'' is nonzero */
+time_t mutt_mktime (struct tm *t, int local)
+{
+ time_t g;
+
+ static int AccumDaysPerMonth[12] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+ };
+
+ /* Compute the number of days since January 1 in the same year */
+ g = AccumDaysPerMonth [t->tm_mon % 12];
+
+ /* The leap years are 1972 and every 4. year until 2096,
+ * but this algoritm will fail after year 2099 */
+ g += t->tm_mday;
+ if ((t->tm_year % 4) || t->tm_mon < 2)
+ g--;
+ t->tm_yday = g;
+
+ /* Compute the number of days since January 1, 1970 */
+ g += (t->tm_year - 70) * 365;
+ g += (t->tm_year - 69) / 4;
+
+ /* Compute the number of hours */
+ g *= 24;
+ g += t->tm_hour;
+
+ /* Compute the number of minutes */
+ g *= 60;
+ g += t->tm_min;
+
+ /* Compute the number of seconds */
+ g *= 60;
+ g += t->tm_sec;
+
+ if (local)
+ g += mutt_compute_tz (g, t);
+
+ return (g);
+}
+
+void mutt_normalize_time (struct tm *tm)
+{
+ static char DaysPerMonth[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+
+ while (tm->tm_sec < 0)
+ {
+ tm->tm_sec += 60;
+ tm->tm_min--;
+ }
+ while (tm->tm_min < 0)
+ {
+ tm->tm_min += 60;
+ tm->tm_hour--;
+ }
+ while (tm->tm_hour < 0)
+ {
+ tm->tm_hour += 24;
+ tm->tm_mday--;
+ }
+ while (tm->tm_mon < 0)
+ {
+ tm->tm_mon += 12;
+ tm->tm_year--;
+ }
+ while (tm->tm_mday < 0)
+ {
+ if (tm->tm_mon)
+ tm->tm_mon--;
+ else
+ {
+ tm->tm_mon = 11;
+ tm->tm_year--;
+ }
+ tm->tm_mday += DaysPerMonth[tm->tm_mon];
+ }
+}
--- /dev/null
+$0 ~ /^# DO NOT REMOVE THIS LINE/ { exit }
+{ print }
--- /dev/null
+# Translation file for PGP 2.6.3(i)n.
+
+# ------------------------------------------------------------------
+# Character set: ISO-Latin/1 (ISO 8859/1)
+# Date revised: 7 October 1997
+# ------------------------------------------------------------------
+# Language: German/Deutsch (de)
+# Translator: Frank Pruefer <F.PRUEFER@LINK-L.cl.sub.de>
+# (based on the German translation for PGP 2.3a by
+# Marc Aurel <4-tea-2@bong.saar.de>)
+# ------------------------------------------------------------------
+# Language: Spanish/Español (es)
+# Translator: Armando Ramos <armando@clerval.org>
+# ------------------------------------------------------------------
+# Language: French/Francais (fr)
+# Translator: Yanik Crépeau <yanik@mlink.net>
+# (based on the French translation for PGP 2.3a by
+# Jean-loup Gailly <jloup@chorus.fr>)
+# ------------------------------------------------------------------
+#
+# Additional language files may be obtained from:
+#
+# http://www.ifi.uio.no/pgp/modules.shtml
+# ftp://ftp.ifi.uio.no/pub/pgp/lang/
+#
+# ------------------------------------------------------------------
+
+"\nClear signature file: %s\n"
+de: "\nDateiname der Klartext-Unterschrift: %s\n"
+es: "\nFichero normal con firma: %s\n"
+fr: "\nFichier de signature en clair: %s\n"
+muttde: "\nDateiname der Klartext-Unterschrift: %s\n"
+
+"\nTransport armor file: %s\n"
+de: "\nDateiname der Versandhülle: %s\n"
+es: "\nFichero con armadura: %s\n"
+fr: "\nFichier de transport armure: %s\n"
+muttde: "\nDateiname der Versandhülle: %s\n"
+
+"\nTransport armor files: "
+de: "\nDateinamen der Versandhüllen: "
+es: "\nFicheros con armadura: "
+fr: "\nFichiers de transport armure: "
+muttde: "\nDateinamen der Versandhüllen: "
+
+"Invalid ASCII armor header line: \"%.40s\"\n\
+ASCII armor corrupted.\n"
+de: "\nUnzulässige Kopfzeile \"%.40s\"\n\
+in der ASCII-Versandhülle. Die Versandhülle ist deshalb ungültig.\n"
+es: "LÃnea incorrecta en la cabecera de la armadura ASCII:\n\
+\"%.40s\"\n\
+Armadura dañada\n"
+fr: "Entête enveloppe ASCII invalide: \"%.40s\"\n\
+l'enveloppe ASCII est corrompue"
+muttde: "\nUnzulässige Kopfzeile \"%.40s\"\n\
+in der ASCII-Versandhülle. Die Versandhülle ist deshalb ungültig.\n"
+
+"Warning: Unrecognized ASCII armor header label \"%.*s:\" ignored.\n"
+de: "\nWARNUNG: Der unbekannte Bezeichner \"%.*s:\"\n\
+in der ASCII-Versandhülle wurde überlesen.\n"
+es: "Advertencia: Se ignora la etiqueta \"%.*s:\"\n\
+No se reconoce como cabecera de armadura ASCII\n"
+fr: "Avertissement: Type d'entête de l'envloppe ASCII \"%.*s:\" ignoré\n"
+muttde: "\nWARNUNG: Der unbekannte Bezeichner \"%.*s:\"\n\
+in der ASCII-Versandhülle wurde überlesen.\n"
+
+"ERROR: Bad ASCII armor checksum in section %d.\n"
+de: "\nFEHLER: Falsche Prüfsumme im Abschnitt %d der Versandhülle.\n"
+es: "ERROR: Suma incorrecta de comprobación en armadura ASCII,\n\
+sección %d.\n"
+fr: "ERREUR: mauvaise vérification de l'armure ASCII dans la section %d.\n"
+muttde: "\nFEHLER: Falsche Prüfsumme im Abschnitt %d der Versandhülle.\n"
+
+"Can't find section %d.\n"
+de: "\nAbschnitt %d nicht gefunden.\n"
+es: "No se encuentra la sección %d.\n"
+fr: "Section %d introuvable.\n"
+muttde: "\nAbschnitt %d nicht gefunden.\n"
+
+"Badly formed section delimiter, part %d.\n"
+de: "\nFehlerhafter Abschnitts-Begrenzer im Teil %d.\n"
+es: "Delimitador de sección mal formado, parte %d.\n"
+fr: "Séparateurs de section mal formés"
+muttde: "\nFehlerhafter Abschnitts-Begrenzer im Teil %d.\n"
+
+"Sections out of order, expected part %d"
+de: "\nAbschnitte in falscher Reihenfolge.\nErwartet wurde Teil %d"
+es: "Las secciones están desordenadas: se esperaba la parte %d"
+fr: "Sections en désordre, partie %d attendue"
+muttde: "\nAbschnitte in falscher Reihenfolge.\nErwartet wurde Teil %d"
+
+", got part %d\n"
+de: ", angekommen ist Teil %d.\n"
+es: ", se encuentra la parte %d\n"
+fr: ", partie %d obtenue\n"
+muttde: ", angekommen ist Teil %d.\n"
+
+"ERROR: Hit EOF in header of section %d.\n"
+de: "\nFEHLER: EOF (Dateiende) im Header von Abschnitt %d.\n"
+es: "ERROR: Hay un EOF (fin de fichero) en la cabecera de\
+la sección %d.\n"
+fr: "ERREUR: find de fichier dans l'en-tête de la section %d.\n"
+muttde: "\nFEHLER: EOF (Dateiende) im Header von Abschnitt %d.\n"
+
+"ERROR: Badly formed ASCII armor checksum, line %d.\n"
+de: "\nFEHLER: Falsche Prüfsumme in Zeile %d der Versandhülle.\n"
+es: "ERROR: Suma de comprobación mal construida en la armadura ASCII,\n\
+lÃnea %d.\n"
+fr: "ERREUR: Verification de l'armure ASCII mal formée, ligne %d.\n"
+muttde: "\nFEHLER: Falsche Prüfsumme in Zeile %d der Versandhülle.\n"
+
+"WARNING: No ASCII armor `END' line.\n"
+de: "\nWARNUNG: Keine 'END'-Zeile in der Versandhülle.\n"
+es: "ADVERTENCIA: No hay lÃnea `END' en la armadura ASCII.\n"
+fr: "ATTENTION: Pas de ligne `END' dans l'armure ASCII.\n"
+muttde: "\nWARNUNG: Keine 'END'-Zeile in der Versandhülle.\n"
+
+"ERROR: Bad ASCII armor character, line %d.\n"
+de: "\nFEHLER: Unerlaubtes Zeichen in Zeile %d der Versandhülle.\n"
+es: "ERROR: Carácter incorrecto en la armadura ASCII, lÃnea %d.\n"
+fr: "ERREUR: Mauvais charactère dans l'armure ASCII, ligne %d.\n"
+muttde: "\nFEHLER: Unerlaubtes Zeichen in Zeile %d der Versandhülle.\n"
+
+"ERROR: Bad ASCII armor line length %d on line %d.\n"
+de: "\nFEHLER: Falsche Zeilenlänge (%d) in Zeile %d der Versandhülle.\n"
+es: "ERROR: Longitud incorrecta (%d) de lÃnea en la armadura ASCII,\n\
+lÃnea %d.\n"
+fr: "ERREUR: Mauvais longueur de ligne %d dans l'armure ASCII, ligne %d.\n"
+muttde: "\nFEHLER: Falsche Zeilenlänge (%d) in Zeile %d der Versandhülle.\n"
+
+"ERROR: Bad ASCII armor checksum"
+de: "\nFEHLER: Falsche Prüfsumme der Versandhülle"
+es: "ERROR: Suma incorrecta de comprobación en la armadura ASCII"
+fr: "ERREUR de vérification dans l'armure ASCII"
+muttde: "\nFEHLER: Falsche Prüfsumme der Versandhülle"
+
+" in section %d"
+de: " im Abschnitt %d.\n"
+es: " en la sección %d"
+fr: " dans la section %d"
+muttde: " im Abschnitt %d.\n"
+
+"Warning: Transport armor lacks a checksum.\n"
+de: "\nWARNUNG: Die Prüfsumme der Versandhülle fehlt.\n"
+es: "Advertencia: La armadura de transporte no lleva suma de\
+comprobación.\n"
+fr: "Attention: l'armure de transport n'a pas de vérification.\n"
+muttde: "\nWARNUNG: Die Prüfsumme der Versandhülle fehlt.\n"
+
+"ERROR: Can't find file %s\n"
+de: "\nFEHLER: Datei '%s' nicht gefunden.\n"
+es: "ERROR: No se encuentra el fichero %s\n"
+fr: "ERREUR: Fichier %s introuvable\n"
+muttde: "\nFEHLER: Datei '%s' nicht gefunden.\n"
+
+"ERROR: No ASCII armor `BEGIN' line!\n"
+de: "\nFEHLER: Keine 'BEGIN'-Zeile in der Versandhülle!\n"
+es: "ERROR: No hay lÃnea 'BEGIN' en la armadura ASCII\n"
+fr: "ERREUR: Pas de ligne `BEGIN' dans l'armure ASCII!\n"
+muttde: "\nFEHLER: Keine 'BEGIN'-Zeile in der Versandhülle!\n"
+
+"ERROR: ASCII armor decode input ended unexpectedly!\n"
+de: "\nFEHLER: Vorzeitiges Ende der Versandhülle!\n"
+es: "ERROR: La entrada con armadura ASCII termina antes de tiempo\n"
+fr: "ERREUR: fin prématurée du fichier armure ASCII!\n"
+muttde: "\nFEHLER: Vorzeitiges Ende der Versandhülle!\n"
+
+"ERROR: Header line added to ASCII armor: \"%s\"\n\
+ASCII armor corrupted.\n"
+de: "\nFEHLER: Eine Kopfzeile \"%s\" ist\n\
+in der ASCII-Versandhülle enthalten. Die Versandhülle ist deshalb ungültig.\n"
+es: "ERROR: LÃnea de cabecera añadida a la armadura ASCII:\n\
+\"%s\" Armadura dañada\n"
+fr: "Ligne d'entête ajoutée à l'enveloppe ASCII: \"%s\"\n\
+enveloppe ASCII corrompue"
+muttde: "\nFEHLER: Eine Kopfzeile \"%s\" ist\n\
+in der ASCII-Versandhülle enthalten. Die Versandhülle ist deshalb ungültig.\n"
+
+"\n\007Unable to write ciphertext output file '%s'.\n"
+de: "\n\007FEHLER beim Schreiben der verschlüsselten\nAusgabedatei '%s'.\n"
+es: "\n\007No puede escribirse el fichero de salida cifrado '%s'.\n"
+fr: "\n\007Ecriture impossible dans le fichier de sortie chiffré '%s'.\n"
+mutt: "\nUnable to write ciphertext output file '%s'.\n"
+muttde: "\nFEHLER beim Schreiben der verschlüsselten\nAusgabedatei '%s'.\n"
+
+"ERROR: Hit EOF in header.\n"
+de: "\nFEHLER: EOF (Dateiende) im Header.\n"
+es: "ERROR: Hay un EOF (fin de fichero) en la cabecera.\n"
+fr: "ERREUR: fin de fichier dans l'en-tête.\n"
+muttde: "\nFEHLER: EOF (Dateiende) im Header.\n"
+
+"Unsupported character set: '%s'\n"
+de: "\nKeine Unterstützung für Zeichensatz '%s'.\n"
+es: "Conjunto de caracteres no admitido: '%s'\n"
+fr: "Table de caractères non supportée: '%s'\n"
+muttde: "\nKeine Unterstützung für Zeichensatz '%s'.\n"
+
+"The legal_kludge cannot be disabled in US version.\n"
+de: "LEGAL_KLUDGE kann in der USA-Version nicht abgeschaltet werden!\n"
+es: "'legal_kludge' no puede desactivarse en la versión para los EE.UU.\n"
+fr: "Les embarras légaux ne peuvent pas être désactivés aux Etats-Unis.\n"
+muttde: "LEGAL_KLUDGE kann in der USA-Version nicht abgeschaltet werden!\n"
+
+"The multiple_recipients flag is unnecessary in this \
+version of MacPGP.\
+\nPlease remove this entry from your configuration file.\n"
+de: "Die Kennung \"multiple_recipients\" ist in dieser Version von MacPGP nicht\
+\nnötig. Bitte entferne diesen Eintrag aus Deiner Konfigurationsdatei.\n"
+es: "No se necesita la bandera 'multiple_recipients' en esta versión\n\
+de MacPGP.\
+\nElimina esa entrada del fichero de configuración.\n"
+fr: "L'indicateur de destinataires multiples n'est pas nécessaire dans \
+version de MacPGP. \
+\nS.V.P. supprimez cette entrée de votre fichier de configuration. \n"
+muttde: "Die Kennung \"multiple_recipients\" ist in dieser Version von MacPGP nicht\
+\nnötig. Bitte entferne diesen Eintrag aus Deiner Konfigurationsdatei.\n"
+
+"\007\nWARNING: This key has been revoked by its owner,\n\
+possibly because the secret key was compromised.\n"
+de: "\007\nWARNUNG: Dieser Schlüssel wurde von seinem Besitzer zurückgezogen,\n\
+möglicherweise, weil sein privater Schlüssel nicht mehr sicher ist.\n"
+es: "\007\nADVERTENCIA: Esta clave ha sido revocada por su propietario;\n\
+es posible que la clave secreta se haya visto comprometida.\n"
+fr: "\007\nATTENTION: cette clé a été révoquée par son propriétaire,\n\
+probablement parce que la clé secrète a été compromise.\n"
+mutt: "\nWARNING: This key has been revoked by its owner,\n\
+possibly because the secret key was compromised.\n"
+muttde: "\nWARNUNG: Dieser Schlüssel wurde von seinem Besitzer zurückgezogen,\n\
+möglicherweise, weil sein privater Schlüssel nicht mehr sicher ist.\n"
+
+"This could mean that this signature is a forgery.\n"
+de: "Dies könnte bedeuten, daß diese Unterschrift eine Fälschung ist.\n"
+es: "Puede significar que la firma está falsificada.\n"
+fr: "Ceci peut signifier que cette signature est un faux.\n"
+muttde: "Dies könnte bedeuten, daß diese Unterschrift eine Fälschung ist.\n"
+
+"You cannot use this revoked key.\n"
+de: "Du kannst diesen Schlüssel nicht benutzen, weil er zurückgezogen wurde.\n"
+es: "No puedes utilizar esta clave revocada.\n"
+fr: "Vous ne pouvez pas utiliser cette clé révoquée.\n"
+muttde: "Du kannst diesen Schlüssel nicht benutzen, weil er zurückgezogen wurde.\n"
+
+"\007\nWARNING: Because this public key is not certified with \
+a trusted\nsignature, it is not known with high confidence that this \
+public key\nactually belongs to: \"%s\".\n"
+de: "\007\nWARNUNG: Da dieser öffentliche Schlüssel nicht mit einer vertrauenswürdigen\n\
+Unterschrift beglaubigt ist, ist nicht sicher, daß er wirklich zu\n\"%s\" gehört.\n"
+es: "\nAVISO: Esta clave pública no está certificada con una firma\
+de confianza,\n\
+por lo que no se sabe con seguridad si realmente pertenece a:\n\
+ \"%s\".\n"
+fr: "\007\nATTENTION: Cette clé publique n'est pas certifiée avec une\n\
+signature fiable. Il n'est donc pas reconnu avec un haut degré de confiance\n\
+que cette signature appartient effectivement à : \"%s\".\n"
+mutt: "\nWARNING: Because this public key is not certified with \
+a trusted\nsignature, it is not known with high confidence that this \
+public key\nactually belongs to: \"%s\".\n"
+muttde: "\nWARNUNG: Da dieser öffentliche Schlüssel nicht mit einer vertrauenswürdigen\n\
+Unterschrift beglaubigt ist, ist nicht sicher, daß er wirklich zu\n\"%s\" gehört.\n"
+
+"\007\nWARNING: This public key is not trusted to actually belong \
+to:\n\"%s\".\n"
+de: "\007\nWARNUNG: Es ist nicht sicher, daß dieser öffentliche Schlüssel wirklich\n\
+zu \"%s\" gehört.\n"
+es: "\nADVERTENCIA: No se sabe con seguridad si esta clave pública\n\
+pertenece realmente a: \"%s\".\n"
+fr: "\007\nATTENTION: Cette clé publique n'est pas reconnue comme\n\
+appartenant à : \"%s\".\n"
+mutt: "\nWARNING: This public key is not trusted to actually belong \
+to:\n\"%s\".\n"
+muttde: "\nWARNUNG: Es ist nicht sicher, daß dieser öffentliche Schlüssel wirklich\n\
+zu \"%s\" gehört.\n"
+
+"\007\nWARNING: Because this public key is not certified with enough \
+trusted\nsignatures, it is not known with high confidence that this \
+public key\nactually belongs to: \"%s\".\n"
+de: "\007\nWARNUNG: Da dieser öffentliche Schlüssel nicht mit einer ausreichenden\n\
+Anzahl vertrauenswürdiger Unterschriften beglaubigt ist, ist nicht sicher,\n\
+daß er wirklich zu \"%s\" gehört.\n"
+es: "\nADVERTENCIA: Como esta clave no está certificada con suficientes\n\
+firmas fiables, no se sabe con seguridad si realmente pertenece a:\n\
+ \"%s\".\n"
+fr: "\007\nATTENTION: puisque cette clé publique n'est pas certifiée avec\n\
+suffisament de signatures, il n'est pas connu avec un haut niveau de confiance\
+\nque cette clé appartient effectivement à : \"%s\".\n"
+mutt: "\nWARNING: Because this public key is not certified with enough \
+trusted\nsignatures, it is not known with high confidence that this \
+public key\nactually belongs to: \"%s\".\n"
+muttde: "\nWARNUNG: Da dieser öffentliche Schlüssel nicht mit einer ausreichenden\n\
+Anzahl vertrauenswürdiger Unterschriften beglaubigt ist, ist nicht sicher,\n\
+daß er wirklich zu \"%s\" gehört.\n"
+
+"But you previously approved using this public key anyway.\n"
+de: "Aber Du hast diesen Schlüssel trotzdem bereits benutzt...\n"
+es: "Ya has permitido antes que se utilice esta clave p\372blica.\n"
+fr: "Mais vous avez déjà accepté l'usage de cette clé publique.\n"
+muttde: "Aber Du hast diesen Schlüssel trotzdem bereits benutzt...\n"
+
+"\nAre you sure you want to use this public key (y/N)? "
+de: "\nBist Du sicher, daß Du diesen Schlüssel benutzen willst? (j/N) "
+es: "\n¿Estás seguro de querer utilizar esta clave pública (s/N)? "
+fr: "\nEtes vous sûr(e) de vouloir utiliser cette clé publique (o/N)? "
+muttde: "\nBist Du sicher, daß Du diesen Schlüssel benutzen willst? (j/N) "
+
+"\n\007Unsupported packet format - you need a newer version of PGP \
+for this file.\n"
+de: "\n\007WARNUNG: nicht unterstütztes Datenformat!\n\
+Du brauchst eine neuere PGP-Version für diese Datei.\n"
+es: "\n\007Formato desconocido -\
+se necesita una versión más reciente de PGP"
+fr: "\n\007Format non-supporté - vous devez utiliser un version nouvelle de PGP
+pour ce fichier.\n"
+mutt: "\nUnsupported packet format - you need a newer version of PGP \
+for this file.\n"
+muttde: "\nWARNUNG: nicht unterstütztes Datenformat!\n\
+Du brauchst eine neuere PGP-Version für diese Datei.\n"
+
+"Preparing random session key..."
+de: "\nVorbereitung des zufälligen IDEA-Schlüssels..."
+es: "Preparando la clave aleatoria de la sesión..."
+fr: "Préparation de la clé aléatoire..."
+muttde: "\nVorbereitung des zufälligen IDEA-Schlüssels..."
+
+"\n\007Error: System clock/calendar is set wrong.\n"
+de: "\n\007FEHLER: Die System-Zeit und/oder das System-Datum sind falsch.\n"
+es: "\n\007Error: El reloj/calendario del sistema está equivocado.\n"
+fr: "\n\007Erreur: L'horloge du système est incorrecte.\n"
+mutt: "\nError: System clock/calendar is set wrong.\n"
+muttde: "\nFEHLER: Die System-Zeit und/oder das System-Datum sind falsch.\n"
+
+"Just a moment..."
+de: "\nEinen Augenblick, bitte..."
+es: "Un momento..."
+fr: "Un moment..."
+muttde: "\nEinen Augenblick, bitte..."
+
+"\n\007Can't open input plaintext file '%s'\n"
+de: "\n\007FEHLER beim Öffnen der Eingabedatei '%s'.\n"
+es: "\n\007No puede abrirse el fichero normal de entrada '%s'\n"
+fr: "\n\007Ouverture du fichier en clair '%s' impossible.\n"
+mutt: "\nCan't open input plaintext file '%s'\n"
+muttde: "\nFEHLER beim Öffnen der Eingabedatei '%s'.\n"
+
+"\n\007Can't open plaintext file '%s'\n"
+de: "\n\007FEHLER beim Öffnen der Klartextdatei '%s'.\n"
+es: "\n\007No puede abrirse el fichero normal '%s'\n"
+fr: "\n\007Ouverture du fichier en clair '%s' impossible\n"
+mutt: "\nCan't open plaintext file '%s'\n"
+muttde: "\nFEHLER beim Öffnen der Klartextdatei '%s'.\n"
+
+"\n\007Can't create signature file '%s'\n"
+de: "\n\007FEHLER beim Erzeugen der Unterschriftsdatei '%s'.\n"
+es: "\n\007No puede crearse el fichero de firma '%s'\n"
+fr: "\n\007Création du fichier de signature '%s' impossible\n"
+mutt: "\nCan't create signature file '%s'\n"
+muttde: "\nFEHLER beim Erzeugen der Unterschriftsdatei '%s'.\n"
+
+"\n\007Can't open key ring file '%s'\n"
+de: "\n\007FEHLER beim Öffnen des Schlüsselbunds '%s'.\n"
+es: "\n\007No puede abrirse el anillo de claves '%s'\n"
+fr: "\n\007Ouverture du fichier de clé '%s' impossible\n"
+mutt: "\nCan't open key ring file '%s'\n"
+muttde: "\nFEHLER beim Öffnen des Schlüsselbunds '%s'.\n"
+
+"This key has already been revoked.\n"
+de: "Dieser Schlüssel wurde bereits zurückgezogen.\n"
+es: "Esta clave ya se habÃa revocado.\n"
+fr: "Cette clé a déjà été révoquée.\n"
+muttde: "Dieser Schlüssel wurde bereits zurückgezogen.\n"
+
+"\n\007Can't create output file to update key ring.\n"
+de: "\n\007Dateifehler bei der Aktualisierung des Schlüsselbunds.\n"
+es: "\n\007No puede crearse el fichero de salida para actualizar\
+el anillo.\n"
+fr: "\n\007Impossible de créer le fichier de sortie pour modifier le\
+\nfichier de clés\n"
+mutt: "\nCan't create output file to update key ring.\n"
+muttde: "\nDateifehler bei der Aktualisierung des Schlüsselbunds.\n"
+
+"\nKey compromise certificate created.\n"
+de: "\nDie Urkunde zum Zurückziehen des Schlüssels wurde erzeugt.\n"
+es: "\nCreado el certificado de compromiso de clave.\n"
+fr: "\nCertificat de compromission de clé créé.\n"
+muttde: "\nDie Urkunde zum Zurückziehen des Schlüssels wurde erzeugt.\n"
+
+"\n\007Key is already signed by user '%s'.\n"
+de: "\n\007Der Schlüssel wurde von \"%s\"\nbereits unterschrieben.\n"
+es: "\n\007La clave ya ha sido firmada por '%s'.\n"
+fr: "\n\007La clé est déjà signée par l'utilisateur '%s'.\n"
+mutt: "\nKey is already signed by user '%s'.\n"
+muttde: "\nDer Schlüssel wurde von \"%s\"\nbereits unterschrieben.\n"
+
+"\n\nREAD CAREFULLY: Based on your own direct first-hand knowledge, \
+are\nyou absolutely certain that you are prepared to solemnly certify \
+that\nthe above public key actually belongs to the user specified by \
+the\nabove user ID (y/N)? "
+de: "\nSORGFÄLTIG LESEN: Bist Du, gestützt auf eigenes, direktes Wissen aus\n\
+erster Hand, absolut sicher, daß Du zuverlässig beglaubigen kannst, daß der\n\
+oben angezeigte öffentliche Schlüssel wirklich zu der oben genannten Person\n\
+gehört? (j/N) "
+es: "\n\nLEE ATENTAMENTE: Según tu conocimiento directo,\n\
+¿estás absolutamente seguro de poder certificar solemnemente que la\n\
+clave pública pertenece realmente al usuario especificado por\n\
+este identificador (s/N)? "
+fr: "\n\nLIRE ATTENTIVEMENT: Selon votre propre connaissance directe,\n\
+êtes vous absoluement certain(e) d'être prêt(e) à certifier\n\
+solennellement que la clé publique ci-dessus appartient effectivement à \n\
+la personne spécifiée par le nom d'utilisateur ci-dessus (o/N)? "
+muttde: "\nSORGFÄLTIG LESEN: Bist Du, gestützt auf eigenes, direktes Wissen aus\n\
+erster Hand, absolut sicher, daß Du zuverlässig beglaubigen kannst, daß der\n\
+oben angezeigte öffentliche Schlüssel wirklich zu der oben genannten Person\n\
+gehört? (j/N) "
+
+"\nKey signature certificate added.\n"
+de: "\n\nDer Schlüssel wurde mit Deiner Unterschrift beglaubigt.\n"
+es: "\nSe ha añadido el certificado de firma.\n"
+fr: "\nCertificat de signature de clé ajouté.\n"
+muttde: "\n\nDer Schlüssel wurde mit Deiner Unterschrift beglaubigt.\n"
+
+"\nError: Key for signing userid '%s'\n\
+does not appear in public keyring '%s'.\n\
+Thus, a signature made with this key cannot be checked on this keyring.\n"
+de: "\nFEHLER: Der Schlüssel für eine Unterschrift unter die Benutzer-ID\n\
+\"%s\" ist nicht im öffentlichen\n\
+Schlüsselbund '%s' enthalten. Deshalb ist eine mit diesem\n\
+Schlüssel erzeugte Unterschrift mit diesem Schlüsselbund nicht überprüfbar.\n"
+es: "\nError: La clave del firmante '%s'\n\
+no se encuentra en el anillo '%s'.\n\
+No puede comprobarse la firma realizada con esa clave.\n"
+fr: "\nErreur: La clef du signataire id '%s'\n\
+est absente du fichier de clefs publiques '%s'\n\
+en consequence, une signature faite avec cette clef ne peut etre verifiee.\n"
+muttde: "\nFEHLER: Der Schlüssel für eine Unterschrift unter die Benutzer-ID\n\
+\"%s\" ist nicht im öffentlichen\n\
+Schlüsselbund '%s' enthalten. Deshalb ist eine mit diesem\n\
+Schlüssel erzeugte Unterschrift mit diesem Schlüsselbund nicht überprüfbar.\n"
+
+"\nLooking for key for user '%s':\n"
+de: "\nSuche den Schlüssel für \"%s\":\n"
+es: "\nBuscando la clave del usuario '%s':\n"
+fr: "\nRecherche de la clé pour l'utilisateur '%s':\n"
+muttde: "\nSuche den Schlüssel für \"%s\":\n"
+
+"\n\007Can't open ciphertext file '%s'\n"
+de: "\n\007FEHLER beim Öffnen der verschlüsselten Datei '%s'.\n"
+es: "\n\007No puede abrirse el fichero cifrado '%s'\n"
+fr: "\n\007Ouverture du fichier chiffré '%s' impossible\n"
+mutt: "\nCan't open ciphertext file '%s'\n"
+muttde: "\nFEHLER beim Öffnen der verschlüsselten Datei '%s'.\n"
+
+"\nFile '%s' has signature, but with no text."
+de: "\nDie Datei '%s' enthält eine Unterschrift, aber keinen Text."
+es: "\nEl fichero '%s' tiene firma, pero no texto."
+fr: "\nLe fichier '%s' Ã une signature, mais pas de texte."
+mutt: " "
+muttde: " "
+
+"\nText is assumed to be in file '%s'.\n"
+de: "\nDer Text könnte sich in der Datei '%s' befinden.\n"
+es: "\nSe asume que el texto se encuentra en el fichero '%s'.\n"
+fr: "\nLe texte est supposé être dans le fichier '%s'.\n"
+mutt: " "
+muttde: " "
+
+"\nPlease enter filename of material that signature applies to: "
+de: "\nName der Datei, zu der die Unterschrift gehört: "
+es: "\nIntroduzca el nombre del fichero al que se aplica la firma: "
+fr: "SVP indiques le nom du fichier que vous voulez signer: "
+muttde: "\nName der Datei, zu der die Unterschrift gehört: "
+
+"File signature applies to?"
+de: "Die Unterschrift gehört zu welcher Datei?"
+es: "¿Dónde se aplica la firma?"
+fr: "Ce fichier signataire s'applique à quoi? "
+muttde: "Die Unterschrift gehört zu welcher Datei?"
+
+"\n\007Can't open file '%s'\n"
+de: "\n\007FEHLER beim Öffnen der Datei '%s'.\n"
+es: "\n\007No puede abrirse el fichero '%s'\n"
+fr: "\n\007Ouverture du fichier '%s' impossible\n"
+mutt: "\nCan't open file '%s'\n"
+muttde: "\nFEHLER beim Öffnen der Datei '%s'.\n"
+
+"File type: '%c'\n"
+de: "\nDateityp: '%c'\n"
+es: "\nTipo de fichero: '%c'\n"
+fr: "Type de fichier: '%c'\n"
+muttde: "\nDateityp: '%c'\n"
+
+"Original plaintext file name was: '%s'\n"
+de: "Der ursprüngliche Name der Klartextdatei war: '%s'.\n"
+es: "El nombre del fichero original era: '%s'\n"
+fr: "Le nom originel du fichier en clair était: '%s'\n"
+muttde: "Der ursprüngliche Name der Klartextdatei war: '%s'.\n"
+
+"\nWARNING: Can't find the right public key-- can't check signature \
+integrity.\n"
+de: "\nWARNUNG: Der passende öffentliche Schlüssel wurde nicht gefunden.\n\
+Eine Überprüfung der Unterschrift ist nicht möglich.\n"
+es: "\nAVISO: No se encuentra la clave pública necesaria para comprobar\n\
+la integridad de la firma.\n"
+fr: "\nATTENTION: impossible de trouver la clé publique adéquate et de\n\
+vérifier l'integrité de la signature.\n"
+muttde: "\nWARNUNG: Der passende öffentliche Schlüssel wurde nicht gefunden.\n\
+Eine Überprüfung der Unterschrift ist nicht möglich.\n"
+
+"\007\nUnrecognized message digest algorithm.\n\
+This may require a newer version of PGP.\n\
+Can't check signature integrity.\n"
+de: "\007\nDer Algorithmus für die Textprüfsumme ist unbekannt.\n\
+Du brauchst wahrscheinlich eine neuere Version von PGP.\n\
+Eine Überprüfung der Unterschrift ist nicht möglich.\n"
+es: "\007\nAlgoritmo desconocido de resumen de mensaje.\n\
+Puede necesitarse una nueva versión de PGP.\n\
+No puede comprobarse la integridad de la firma.\n"
+fr: "\007Algorithme de digest inconnu.\n\
+Ceci peut vouloir dire que vous avez besoin d'une version plus récente\n\
+de PGP. Incapable de vérifier la signature.\n"
+mutt: "\nUnrecognized message digest algorithm.\n\
+This may require a newer version of PGP.\n\
+Can't check signature integrity.\n"
+muttde: "\nDer Algorithmus für die Textprüfsumme ist unbekannt.\n\
+Du brauchst wahrscheinlich eine neuere Version von PGP.\n\
+Eine Überprüfung der Unterschrift ist nicht möglich.\n"
+
+"\a\nMalformed or obsolete signature. Can't check signature \
+integrity.\n"
+de: "\a\nFehlerhafte oder veraltete Unterschrift! Überprüfung nicht möglich.\n"
+es: "\a\nFirma incorrecta u obsoleta.\n\
+No puede comprobarse su integridad.\n"
+fr: "\a\nSignature déformée ou obsolète. Vérification impossible. \n"
+muttde: "\a\nFehlerhafte oder veraltete Unterschrift! Überprüfung nicht möglich.\n"
+
+"\a\nSigning key is too large. Can't check signature integrity.\n"
+de: "\a\nDer unterschreibende Schlüssel ist zu lang! Eine Überprüfung der\n\
+Unterschrift ist deshalb nicht möglich.\n"
+es: "\a\nLa clave para firmar es demasiado grande.\n\
+No puede comprobarse la integridad de la firma."
+fr: "La clef signataire est trop grande. Incapable d'en vérifier l'intégrité. \n"
+muttde: "\a\nDer unterschreibende Schlüssel ist zu lang! Eine Überprüfung der\n\
+Unterschrift ist deshalb nicht möglich.\n"
+
+"\n\007Error: RSA-decrypted block is corrupted.\n\
+This may be caused either by corrupted data or by using the wrong RSA key.\n\
+"
+de: "\n\007FEHLER: Die mit RSA entschlüsselten Daten sind fehlerhaft.\n\
+Ursache: beschädigte Daten oder ein falscher RSA-Schlüssel.\n"
+es: "\n\007Error: El bloque desencriptado RSA está dañado.\n\
+Puede deberse a un problema en los datos o a una clave RSA equivocada.\n"
+fr: "\n\007Erreur: le block dechiffré par RSA est endommagé.\n\
+Ceci est peut être causé par des données endommagées our par\n\
+l'utilisation d'une mauvaise clé RSA.\n"
+mutt: "\nError: RSA-decrypted block is corrupted.\n\
+This may be caused either by corrupted data or by using the wrong RSA key.\n\
+"
+muttde: "\nFEHLER: Die mit RSA entschlüsselten Daten sind fehlerhaft.\n\
+Ursache: beschädigte Daten oder ein falscher RSA-Schlüssel.\n"
+
+"WARNING: Bad signature, doesn't match file contents!"
+de: "WARNUNG: Die Unterschrift stimmt nicht mit dem Datei-Inhalt überein!"
+es: "ADVERTENCIA: Firma incorrecta, no coincide con el contenido\
+del fichero\n"
+fr: "ATTENTION: Mauvaise signature, ne correspond pas au contenu!"
+muttde: "WARNUNG: Die Unterschrift stimmt nicht mit dem Datei-Inhalt überein!"
+
+"\nBad signature from user \"%s\".\n"
+de: "\nFEHLERHAFTE Unterschrift von \"%s\",\n"
+es: "\nFirma incorrecta de \"%s\".\n"
+fr: "\nMauvaise signature de l'utilisateur \"%s\".\n"
+muttde: "\nFEHLERHAFTE Unterschrift von \"%s\",\n"
+
+"Signature made %s using %d-bit key, key ID %s\n"
+de: "Unterschrift erzeugt am %s mit %d-Bit-Schlüssel 0x%s.\n"
+es: "Firma realizada el %s con una clave de %d bits, identificador %s\n"
+fr: "Signature faite %s en utilisant un clef de %d bits. Id de la clef:%s\n"
+muttde: "Unterschrift erzeugt am %s mit %d-Bit-Schlüssel 0x%s.\n"
+
+"\nPress ENTER to continue..."
+de: "\nWeiter mit Return..."
+es: "\nPulse 'Enter' para continuar..."
+fr: "\nAppuyez sur la touche Retour ou Entrée pour continuer..."
+muttde: "\nWeiter mit Return..."
+
+"\nGood signature from user \"%s\".\n"
+de: "\nBESTÄTIGTE Unterschrift von \"%s\",\n"
+es: "\nFirma correcta de \"%s\".\n"
+fr: "\nBonne signature de l'utilisateur \"%s\".\n"
+muttde: "\nBESTÄTIGTE Unterschrift von \"%s\",\n"
+
+"\nSignature and text are separate. No output file produced. "
+de: "\nUnterschrift und Text sind getrennt. Es wurde keine Ausgabedatei erzeugt."
+es: "\nLa firma y el texto están separados.\n\
+No se produce fichero de salida. "
+fr: "\nLa signature et le texte sont séparés. Fichier de sortie non produit."
+muttde: "\nUnterschrift und Text sind getrennt. Es wurde keine Ausgabedatei erzeugt."
+
+"\n\007Can't create plaintext file '%s'\n"
+de: "\n\007FEHLER beim Erzeugen der Klartextdatei '%s'.\n"
+es: "\n\007No puede crearse el fichero normal '%s'\n"
+fr: "\n\007Creation du fichier en clair '%s' impossible.\n"
+mutt: "\nCan't create plaintext file '%s'\n"
+muttde: "\nFEHLER beim Erzeugen der Klartextdatei '%s'.\n"
+
+"\n\007Signature file '%s' already exists. Overwrite (y/N)? "
+de: "\n\007Die Unterschriftsdatei '%s'\nexistiert bereits. Überschreiben? (j/N) "
+es: "\n\007El fichero de firma '%s' ya existe.\n\
+¿Se sobreescribe (s/N)? "
+fr: "\n\007Le fichier de signature '%s' existe déjà . A écraser (o/N)? "
+mutt: "\nSignature file '%s' already exists. Overwrite (y/N)? "
+muttde: "\nDie Unterschriftsdatei '%s'\nexistiert bereits. Überschreiben? (j/N) "
+
+"\nWriting signature certificate to '%s'\n"
+de: "\nDie Unterschrift wird in die Datei '%s' geschrieben.\n"
+es: "\nEscribiendo el certificado de firma para '%s'\n"
+fr: "\nEcriture du certificat de signature dans '%s'\n"
+muttde: "\nDie Unterschrift wird in die Datei '%s' geschrieben.\n"
+
+"\n\007Error: Badly-formed or corrupted signature certificate.\n"
+de: "\n\007FEHLER: Format- oder Datenfehler in der Unterschrift.\n"
+es: "\n\007Error: Certificado de firma incorrecto o dañado.\n"
+fr: "\n\007Erreur: certificat de signature mal formé ou endommagé\n"
+mutt: "\nError: Badly-formed or corrupted signature certificate.\n"
+muttde: "\nFEHLER: Format- oder Datenfehler in der Unterschrift.\n"
+
+"File \"%s\" does not have a properly-formed signature.\n"
+de: "Die Datei '%s' hat keine formal korrekte Unterschrift.\n"
+es: "El fichero \"%s\" no tiene una firma construida correctamente.\n"
+fr: "Le fichier \"%s\" n'a pas une signature correctement formée.\n"
+muttde: "Die Datei '%s' hat keine formal korrekte Unterschrift.\n"
+
+"compressed. "
+de: "gepackt. "
+es: "comprimido. "
+fr: "comprimé. "
+muttde: "gepackt. "
+
+"\n\007Can't create compressed file '%s'\n"
+de: "\n\007FEHLER beim Erzeugen der gepackten Datei '%s'.\n"
+es: "\n\007No puede crearse el fichero comprimido '%s'\n"
+fr: "\n\007Création du fichier compressé '%s' impossible\n"
+mutt: "\nCan't create compressed file '%s'\n"
+muttde: "\nFEHLER beim Erzeugen der gepackten Datei '%s'.\n"
+
+"Compressing file..."
+de: "Packen der Datei..."
+es: "Comprimiendo el fichero..."
+fr: "Compression du fichier..."
+muttde: "Packen der Datei..."
+
+"\n\007Can't create ciphertext file '%s'\n"
+de: "\n\007FEHLER beim Erzeugen der verschlüsselten\nAusgabedatei '%s'.\n"
+es: "\n\007No puede crearse el fichero cifrado '%s'\n"
+fr: "\n\007Création du fichier chiffré '%s' impossible.\n"
+mutt: "\nCan't create ciphertext file '%s'\n"
+muttde: "\nFEHLER beim Erzeugen der verschlüsselten\nAusgabedatei '%s'.\n"
+
+"\nYou need a pass phrase to encrypt the file. "
+de: "\nDu brauchst ein Mantra zum Verschlüsseln der Datei."
+es: "\nSe necesita una contraseña para encriptar el fichero. "
+fr: "\nUn mot de passe est nécessaire pour chiffrer ce fichier. "
+muttde: "\nDu brauchst ein Mantra zum Verschlüsseln der Datei."
+
+"\n\007Cannot find the public key matching userid '%s'\n\
+This user will not be able to decrypt this message.\n"
+de: "\n\007Der öffentliche Schlüssel zu Benutzer-ID \"%s\"\n\
+ist nicht aufzufinden. Dieser Empfänger wird diese Nachricht nicht\n\
+entschlüsseln können.\n"
+es: "\n\007No puede encontrarse la clave pública de '%s'\n\
+Ese usuario no podrá descifrar el mensaje.\n"
+fr: "\n\007Impossible de trouver la clé publique pour l'utilisateur '%s'\n\
+Cet utilisateur ne pourra pas déchiffrer ce message.\n"
+mutt: "\nCannot find the public key matching userid '%s'\n\
+This user will not be able to decrypt this message.\n"
+muttde: "\nDer öffentliche Schlüssel zu Benutzer-ID \"%s\"\n\
+ist nicht aufzufinden. Dieser Empfänger wird diese Nachricht nicht\n\
+entschlüsseln können.\n"
+
+"Skipping userid %s\n"
+de: "Die Benutzer-ID \"%s\" wird übersprungen.\n"
+es: "Saltando el identificador %s\n"
+fr: "je passe l'utiliateur %s\n"
+muttde: "Die Benutzer-ID \"%s\" wird übersprungen.\n"
+
+"\n\007'%s' is not a cipher file.\n"
+de: "\n\007'%s' ist keine verschlüsselte Datei.\n"
+es: "\n\007'%s' no es un fichero cifrado.\n"
+fr: "\n\007'%s' n'est pas un fichier chiffré.\n"
+mutt: "\n'%s' is not a cipher file.\n"
+muttde: "\n'%s' ist keine verschlüsselte Datei.\n"
+
+"\n\007Error: RSA block is possibly malformed. Old format, maybe?\n"
+de: "\n\007FEHLER: RSA-Block möglicherweise fehlerhaft. Vielleicht altes Format?\n"
+es: "\n\007Error: El bloque RSA está mal formado.\n\
+Quizá se trate de un formato antiguo.\n"
+fr: "\n\007Erreur: Block RSA malformé, vieux format ???"
+mutt: "\nError: RSA block is possibly malformed. Old format, maybe?\n"
+muttde: "\nFEHLER: RSA-Block möglicherweise fehlerhaft. Vielleicht altes Format?\n"
+
+"\nThis message can only be read by:\n"
+de: "\nDiese Nachricht kann nur gelesen werden von:\n"
+es: "\nEste mensaje sólo puede leerlo:\n"
+fr: "\nCe message ne peut être lu que par:\n"
+muttde: "\nDiese Nachricht kann nur gelesen werden von:\n"
+
+" keyID: %s\n"
+de: " Schlüssel-ID: %s\n"
+es: " identificador: %s\n"
+fr: " Id de la clef: %s \n"
+muttde: " Schlüssel-ID: %s\n"
+
+"\n\007You do not have the secret key needed to decrypt this file.\n"
+de: "\n\007Dir fehlt der private Schlüssel zum Entschlüsseln dieser Datei.\n"
+es: "\n\007No tienes la clave secreta necesaria para descifrar\
+este fichero.\n"
+fr: "\n\007Vous n'avez pas la clé secrète requise pour déchiffrer\
+\nce fichier.\n"
+mutt: "\nYou do not have the secret key needed to decrypt this file.\n"
+muttde: "\nDir fehlt der private Schlüssel zum Entschlüsseln dieser Datei.\n"
+
+"\n\007Error: Decrypted plaintext is corrupted.\n"
+de: "\n\007FEHLER: Der entschlüsselte Klartext ist fehlerhaft.\n"
+es: "\n\007Error: El texto en claro desencriptado está dañado.\n"
+fr: "\n\007Erreur: le fichier déchiffré est endommagé.\n"
+mutt: "\nError: Decrypted plaintext is corrupted.\n"
+muttde: "\nFEHLER: Der entschlüsselte Klartext ist fehlerhaft.\n"
+
+"\nYou need a pass phrase to decrypt this file. "
+de: "\nDu brauchst ein Mantra zum Entschlüsseln dieser Datei."
+es: "\nSe necesita la contraseña para desencriptar este fichero. "
+fr: "\nUn mot de passe est nécessaire pour déchiffrer ce fichier. "
+muttde: "\nDu brauchst ein Mantra zum Entschlüsseln dieser Datei."
+
+"\n\007Error: Bad pass phrase.\n"
+de: "\n\007FEHLER: Falsches Mantra!\n"
+es: "\n\007Error: Contraseña incorrecta.\n"
+fr: "\n\007Erreur: Mauvais mot de passe.\n"
+mutt: "\nError: Bad pass phrase.\n"
+muttde: "\nFEHLER: Falsches Mantra!\n"
+
+"Pass phrase appears good. "
+de: "\nDas Mantra scheint zu stimmen.\n"
+es: "Parece correcta. "
+fr: "Le mot de passe semble correct. "
+muttde: "\nDas Mantra scheint zu stimmen.\n"
+
+"Decompressing plaintext..."
+de: "Entpacken des Klartextes..."
+es: "Descomprimiendo el texto normal..."
+fr: "Decompression du texte en clair..."
+muttde: "Entpacken des Klartextes..."
+
+"\n\007Can't open compressed file '%s'\n"
+de: "\n\007FEHLER beim Öffnen der gepackten Datei '%s'.\n"
+es: "\n\007No puede abrirse el fichero comprimido '%s'\n"
+fr: "\n\007Ouverture du fichier compressé '%s' impossible.\n"
+mutt: "\nCan't open compressed file '%s'\n"
+muttde: "\nFEHLER beim Öffnen der gepackten Datei '%s'.\n"
+
+"\007\nUnrecognized compression algorithm.\n\
+This may require a newer version of PGP.\n"
+de: "\007\nUnbekanntes Pack-Verfahren. Eine neuere Version von PGP könnte notwendig sein.\n"
+es: "\007\nAlgoritmo de compresión no reconocido.\n\
+Puede necesitarse una nueva versión de PGP.\n"
+fr: "\007\nAlgorithme de compression non reconnu.\n\
+Ceci peut nécessiter une nouvelle version de PGP.\n"
+mutt: "\nUnrecognized compression algorithm.\n\
+This may require a newer version of PGP.\n"
+muttde: "\nUnbekanntes Pack-Verfahren. Eine neuere Version von PGP könnte notwendig sein.\n"
+
+"\n\007Can't create decompressed file '%s'\n"
+de: "\n\007FEHLER beim Erzeugen der entpackten Datei '%s'.\n"
+es: "\n\007No puede crearse el fichero descomprimido '%s'\n"
+fr: "\n\007Création du fichier décompressé '%s' impossible.\n"
+mutt: "\nCan't create decompressed file '%s'\n"
+muttde: "\nFEHLER beim Erzeugen der entpackten Datei '%s'.\n"
+
+"\n\007Decompression error. Probable corrupted input.\n"
+de: "\n\007FEHLER beim Entpacken! Wahrscheinlich beschädigte Eingangsdaten.\n"
+es: "\n007Error en descompresión. Probable entrada dañada.\n"
+fr: "\n\007Erreur de Decompression, entrée probablement corrompue"
+mutt: "\nDecompression error. Probable corrupted input.\n"
+muttde: "\nFEHLER beim Entpacken! Wahrscheinlich beschädigte Eingangsdaten.\n"
+
+"done. "
+de: "fertig. "
+es: "finalizado. "
+fr: "terminé. "
+muttde: "fertig. "
+
+"Truncating filename '%s' "
+de: "Kürzung des Dateinamens '%s' "
+es: "Truncando el nombre de fichero '%s' "
+fr: "troncation du fichier '%s'"
+muttde: "Kürzung des Dateinamens '%s' "
+
+"y"
+de: "j"
+es: "s"
+fr: "o"
+muttde: "j"
+
+"n"
+de: "n"
+es: "n"
+fr: "n"
+muttde: "n"
+
+"\nShould '%s' be renamed to '%s' (Y/n)? "
+de: "\nSoll '%s' in '%s' umbenannt werden? (J/n) "
+es: "\n¿Renombrar '%s' como '%s' (S/n)? "
+fr: "\nEst-ce que '%s' doit être renommé '%s' (O/n)? "
+muttde: "\nSoll '%s' in '%s' umbenannt werden? (J/n) "
+
+"\nDisk full.\n"
+de: "\nDie Platte ist voll!\n"
+es: "\nDisco lleno.\n"
+fr: "\nDisque plein.\n"
+muttde: "\nDie Platte ist voll!\n"
+
+"\nFile write error.\n"
+de: "\nFEHLER beim Schreiben einer Datei.\n"
+es: "\nError de escritura del fichero.\n"
+fr: "\nErreur d'écriture sur fichier.\n"
+muttde: "\nFEHLER beim Schreiben einer Datei.\n"
+
+"\007Write error on stdout.\n"
+de: "\n\007FEHLER beim Schreiben auf stdout (Standard-Ausgabe).\n"
+es: "\007Error de escritura en la salida estándar (\"stdout\").\n"
+fr: "\007Erreur d'écriture sur la sortie standard.\n"
+mutt: "Write error on stdout.\n"
+muttde: "\nFEHLER beim Schreiben auf stdout (Standard-Ausgabe).\n"
+
+"\n\007Cannot create temporary file '%s'\n"
+de: "\n\007FEHLER beim Erzeugen der Temporärdatei '%s'.\n"
+es: "\n\007No puede crearse el fichero temporal '%s'\n"
+fr: "\n\007Création du fichier temporaire '%s' impossible\n"
+mutt: "\nCannot create temporary file '%s'\n"
+muttde: "\nFEHLER beim Erzeugen der Temporärdatei '%s'.\n"
+
+"Can't create output file '%s'\n"
+de: "\nFEHLER beim Erzeugen der Ausgabedatei '%s'.\n"
+es: "No puede crearse el fichero '%s'\n"
+fr: "Création du fichier '%s' impossible.\n"
+muttde: "\nFEHLER beim Erzeugen der Ausgabedatei '%s'.\n"
+
+"\n\007Output file '%s' already exists.\n"
+de: "\n\007Die Ausgabedatei '%s' existiert bereits.\n"
+es: "\n\007El fichero de salida '%s' ya existe.\n"
+fr: "\n\007Le ficher de sortie '%s' existe déjà .\n"
+mutt: "\nOutput file '%s' already exists.\n"
+muttde: "\nDie Ausgabedatei '%s' existiert bereits.\n"
+
+"\n\007Output file '%s' already exists. Overwrite (y/N)? "
+de: "\n\007Die Ausgabedatei '%s' existiert bereits. Überschreiben? (j/N) "
+es: "\n\007El fichero de salida '%s' ya existe. ¿Sobrescribir (s/N)? "
+fr: "\n\007Le fichier de sortie '%s' existe déjà . A écraser (o/N)? "
+mutt: "\nOutput file '%s' already exists. Overwrite (y/N)? "
+muttde: "\nDie Ausgabedatei '%s' existiert bereits. Überschreiben? (j/N) "
+
+"Enter new file name:"
+de: "Gib den neuen Dateinamen ein:"
+es: "Introduzca el nuevo nombre de fichero: "
+fr: "Donnez un nouveau nom de fichier:"
+muttde: "Gib den neuen Dateinamen ein:"
+
+"Replacing signature from keyID %s on userid \"%s\"\n"
+de: "Die Unterschrift von der Schlüssel-ID %s unter der\n\
+Benutzer-ID \"%s\" wird ersetzt.\n"
+es: "Sustituyendo la firma de la clave %s para el usuario\n\
+\"%s\"\n"
+fr: "Remplacement la signature de keyID %s de l'utilisateur '%s'\n"
+muttde: "Die Unterschrift von der Schlüssel-ID %s unter der\n\
+Benutzer-ID \"%s\" wird ersetzt.\n"
+
+"Verifying signature from %s\n"
+de: "Überprüfung der Unterschrift von \"%s\"\n"
+es: "Verificando la firma de %s\n"
+fr: "Vérification de la signature de %s\n"
+muttde: "Überprüfung der Unterschrift von \"%s\"\n"
+
+"on userid \"%s\"\n"
+de: "unter \"%s\".\n"
+es: "en el identificador \"%s\"\n"
+fr: "pour le nom d'utilisateur \"%s\"\n"
+muttde: "unter \"%s\".\n"
+
+"Replacing signature from %s\n"
+de: "Ersetzung der Unterschrift von \"%s\"\n"
+es: "Sustituyendo la firma de %s\n"
+fr: "Remplacement de la signature de %s"
+muttde: "Ersetzung der Unterschrift von \"%s\"\n"
+
+"Verification Failed\n"
+de: "\nDie Überprüfung ist fehlgeschlagen!\n"
+es: "Verificación fallida\n"
+fr: "Echec de la vérification"
+muttde: "\nDie Überprüfung ist fehlgeschlagen!\n"
+
+"New signature from keyID %s on userid \"%s\"\n"
+de: "Neue Unterschrift von %s unter \"%s\".\n"
+es: "Nueva firma de la clave %s para el usuario \"%s\"\n"
+fr: "Nouvelle signature de la clé %s sur l'utilisateur \"%s\"\n"
+muttde: "Neue Unterschrift von %s unter \"%s\".\n"
+
+"New signature from %s\n"
+de: "Neue Unterschrift von \"%s\"\n"
+es: "Nueva firma de %s\n"
+fr: "\nNouvelle signature de %s\n"
+muttde: "Neue Unterschrift von \"%s\"\n"
+
+"Key revocation certificate from \"%s\".\n"
+de: "Urkunde zum Zurückziehen eines Schlüssels\nvon \"%s\".\n"
+es: "Certificado de revocación de clave de \"%s\".\n"
+fr: "Certificat de révocation de clé de \"%s\".\n"
+muttde: "Urkunde zum Zurückziehen eines Schlüssels\nvon \"%s\".\n"
+
+"\n\007WARNING: File '%s' contains bad revocation certificate.\n"
+de: "\n\007WARNUNG: Die Datei '%s' enthält eine\n\
+fehlerhafte Urkunde zum Zurückziehen eines Schlüssels.\n"
+es: "\n\007AVISO: El fichero '%s' tiene un certificado de revocación\
+incorrecto.\n"
+fr: "\n\007ATTENTION: the fichier '%s' contient de mauvais certificats\n\
+de révocation.\n"
+mutt: "\nWARNING: File '%s' contains bad revocation certificate.\n"
+muttde: "\nWARNUNG: Die Datei '%s' enthält eine\n\
+fehlerhafte Urkunde zum Zurückziehen eines Schlüssels.\n"
+
+"New userid: \"%s\".\n"
+de: "Neue Benutzer-ID: \"%s\".\n"
+es: "Nuevo identificador: \"%s\".\n"
+fr: "Nouveau nom d'utilisateur: \"%s\".\n"
+muttde: "Neue Benutzer-ID: \"%s\".\n"
+
+"\nWill be added to the following key:\n"
+de: "Sie wird zu dem folgenden Schlüssel hinzugefügt:\n"
+es: "\nSe añadirá a la clave siguiente:\n"
+fr: "\nSera ajouté(e) à la clé suivante:\n"
+muttde: "Sie wird zu dem folgenden Schlüssel hinzugefügt:\n"
+
+"\nAdd this userid (y/N)? "
+de: "\nSoll diese Benutzer-ID hinzugefügt werden? (j/N) "
+es: "\n¿Añadir este identificador (s/N)? "
+fr: "\nAjouter ce nom d'utilisateur (o/N)? "
+muttde: "\nSoll diese Benutzer-ID hinzugefügt werden? (j/N) "
+
+"\n\007Can't open key file '%s'\n"
+de: "\n\007FEHLER beim Öffnen der Schlüsseldatei '%s'.\n"
+es: "\n\007No puede abrirse el fichero de claves '%s'\n"
+fr: "\n\007Ouverture du fichier de clé '%s' impossible\n"
+mutt: "\nCan't open key file '%s'\n"
+muttde: "\nFEHLER beim Öffnen der Schlüsseldatei '%s'.\n"
+
+"\nKey ring file '%s' cannot be created.\n"
+de: "\nFEHLER beim Anlegen des Schlüsselbunds '%s'.\n"
+es: "\nNo se puede crear el anillo de claves '%s'.\n"
+fr: "\nCréation du fichier de clés '%s' impossible.\n"
+muttde: "\nFEHLER beim Anlegen des Schlüsselbunds '%s'.\n"
+
+"\nLooking for new keys...\n"
+de: "\nSuche nach neuen Schlüsseln...\n"
+es: "\nBuscando nuevas claves...\n"
+fr: "\nRecherche des nouvelles clés...\n"
+muttde: "\nSuche nach neuen Schlüsseln...\n"
+
+"\n\007Could not read key from file '%s'.\n"
+de: "\n\007FEHLER beim Lesen des Schlüssels aus Datei '%s'.\n"
+es: "\n\007No ha podido leerse la clave en el fichero '%s'.\n"
+fr: "\n\007Lecture impossible de la clé dans le fichier '%s'.\n"
+mutt: "\nCould not read key from file '%s'.\n"
+muttde: "\nFEHLER beim Lesen des Schlüssels aus Datei '%s'.\n"
+
+"\n\007Warning: Key ID %s matches key ID of key already on \n\
+key ring '%s', but the keys themselves differ.\n\
+This is highly suspicious. This key will not be added to ring.\n\
+Acknowledge by pressing return: "
+de: "\n\007WARNUNG: Die Schlüssel-ID %s stimmt mit einem schon\n\
+im Schlüsselbund '%s' vorhandenen Schlüssel\n\
+überein, aber die Schlüssel selbst sind nicht identisch.\n\
+Das ist höchst verdächtig! Dieser Schlüssel wird nicht zum\n\
+Schlüsselbund hinzugefügt. Mit Return bestätigen: "
+es: "\n\007Advertencia: El identificador de clave %s coincide con otro\n\
+en el anillo '%s', pero las claves son distintas.\n\
+Es muy sospechoso. Esta clave no se incluirá en el anillo.\n\
+Confirmar pulsando 'retorno': "
+fr: "\n\007Attention: l'identificateur de clé %s correspond à une clé\
+\ndéjà dans le fichier de clés '%s', mais les clés sont différentes.\
+\nCeci est très suspect. Cette clé ne sera pas ajoutée au fichier de clés.\
+\nAppuyez sur la touche Retour ou Entrée: "
+mutt: "\nWarning: Key ID %s matches key ID of key already on \n\
+key ring '%s', but the keys themselves differ.\n\
+This is highly suspicious. This key will not be added to ring.\n\
+Acknowledge by pressing return: "
+muttde: "\nWARNUNG: Die Schlüssel-ID %s stimmt mit einem schon\n\
+im Schlüsselbund '%s' vorhandenen Schlüssel\n\
+überein, aber die Schlüssel selbst sind nicht identisch.\n\
+Das ist höchst verdächtig! Dieser Schlüssel wird nicht zum\n\
+Schlüsselbund hinzugefügt. Mit Return bestätigen: "
+
+"\nDo you want to add this key to keyring '%s' (y/N)? "
+de: "\nMöchtest Du diesen Schlüssel zum Schlüsselbund\n'%s' hinzufügen? (j/N) "
+es: "\n¿Quieres añadir esta clave al anillo '%s' (s/N)? "
+fr: "\nVoulez vous ajouter cette clé au fichier de clés '%s' (o/N)? "
+muttde: "\nMöchtest Du diesen Schlüssel zum Schlüsselbund\n'%s' hinzufügen? (j/N) "
+
+"Key has been revoked.\n"
+de: "Der Schlüssel wurde zurückgezogen.\n"
+es: "La clave se ha revocado.\n"
+fr: "La clé a été révoquée.\n"
+muttde: "Der Schlüssel wurde zurückgezogen.\n"
+
+"\n\007Key file contains duplicate keys: cannot be added to keyring\n"
+de: "\n\007Die Datei enthält doppelte Schlüssel, die nicht zum Schlüsselbund\n\
+hinzugefügt werden.\n"
+es: "\n\007El fichero contiene claves duplicadas: \
+no puede añadirse al anillo\n"
+fr: "\n\007Le fichier contient des clés dupliquées: impossible\n\
+de l'ajouter au fichier de clés\n"
+mutt: "\nKey file contains duplicate keys: cannot be added to keyring\n"
+muttde: "\nDie Datei enthält doppelte Schlüssel, die nicht zum Schlüsselbund\n\
+hinzugefügt werden.\n"
+
+"No new keys or signatures in keyfile.\n"
+de: "Keine neuen Schlüssel oder Unterschriften in der Datei.\n"
+es: "No hay nuevas claves ni nuevas firmas en el fichero.\n"
+fr: "Pas de nouvelles clés ou signatures dans le fichier de clés.\n"
+muttde: "Keine neuen Schlüssel oder Unterschriften in der Datei.\n"
+
+"\nKeyfile contains:\n"
+de: "\nDie Datei enthält folgende Schlüssel:\n"
+es: "\nEl fichero de claves contiene:\n"
+fr: "\nLe fichier de clés contient:\n"
+muttde: "\nDie Datei enthält folgende Schlüssel:\n"
+
+"%4d new key(s)\n"
+de: "%4d neue(n) Schlüssel\n"
+es: "%4d nueva(s) clave(s)\n"
+fr: "%4d nouvelle(s) clé(s)\n"
+muttde: "%4d neue(n) Schlüssel\n"
+
+"%4d new signatures(s)\n"
+de: "%4d neue Unterschrift(en)\n"
+es: "%4d nueva(s) firma(s)\n"
+fr: "%4d nouvelle(s) signatures(s)\n"
+muttde: "%4d neue Unterschrift(en)\n"
+
+"%4d new user ID(s)\n"
+de: "%4d neue Benutzer-ID(s)\n"
+es: "%4d nuevo(s) identificador(es) de usuario\n"
+fr: "%4d nouveau(x) nom(s) d'utilisateur\n"
+muttde: "%4d neue Benutzer-ID(s)\n"
+
+"%4d new revocation(s)\n"
+de: "%4d neue Urkunde(n) zum Zurückziehen von Schlüsseln\n"
+es: "%4d nueva(s) revocacion(es)\n"
+fr: "%4d nouvelle(s) révocation(s)\n"
+muttde: "%4d neue Urkunde(n) zum Zurückziehen von Schlüsseln\n"
+
+"\nNo keys found in '%s'.\n"
+de: "\nKeine Schlüssel in '%s' gefunden.\n"
+es: "\nNo se encuentran claves en '%s'.\n"
+fr: "\nPas de clés trouvées dans '%s'.\n"
+muttde: "\nKeine Schlüssel in '%s' gefunden.\n"
+
+"\nOne or more of the new keys are not fully certified.\n\
+Do you want to certify any of these keys yourself (y/N)? "
+de: "\nEin oder mehrere neue Schlüssel sind nicht ausreichend beglaubigt.\n\
+Willst Du sie selbst beglaubigen? (j/N) "
+es: "\nUna o más de las nuevas claves no están completamente certificadas.\n\
+¿Quieres certificar alguna de ellas tú mismo (s/N)? "
+fr: "\nUne ou plusieurs des nouvelles clés ne sont pas complètement\
+\ncertifiées. Voulez vous certifier ces clés vous même (o/N)? "
+muttde: "\nEin oder mehrere neue Schlüssel sind nicht ausreichend beglaubigt.\n\
+Willst Du sie selbst beglaubigen? (j/N) "
+
+"\nDo you want to certify this key yourself (y/N)? "
+de: "\nWillst Du diesen Schlüssel selbst beglaubigen? (j/N) "
+es: "\n¿Quieres certificar esta clave tú mismo (s/N)? "
+fr: "\nVoulez vous certifier cette clé vous même (o/N)? "
+muttde: "\nWillst Du diesen Schlüssel selbst beglaubigen? (j/N) "
+
+"undefined"
+de: "undefin."
+es: "sin definir"
+fr: "indéfinie"
+muttde: "undefin."
+
+"unknown"
+de: "unbekannt"
+es: "desconocida"
+fr: "inconnu"
+muttde: "unbekannt"
+
+"untrusted"
+de: "kein"
+es: "no fiable"
+fr: "non sûr"
+muttde: "kein"
+
+"marginal"
+de: "teilweise"
+es: "relativa"
+fr: "marginal"
+muttde: "teilweise"
+
+"complete"
+de: "voll"
+es: "completa"
+fr: "complète"
+muttde: "voll"
+
+"ultimate"
+de: "absolut"
+es: "fundamental"
+fr: "ultime"
+muttde: "absolut"
+
+"\nCan't open backup key ring file '%s'\n"
+de: "\nFEHLER beim Öffnen der Schlüsselbund-Kopie '%s'.\n"
+es: "\nNo se puede abrir la copia de seguridad del anillo '%s'\n"
+fr: "\nImpossible d'ouvrir le fichier de clé de sauvegarde '%s'\n"
+muttde: "\nFEHLER beim Öffnen der Schlüsselbund-Kopie '%s'.\n"
+
+"\n%d \"trust parameter(s)\" need to be changed.\n"
+de: "\n%d 'Vertrauens-Einstellung(en)' muß/müssen geändert werden.\n"
+es: "\n%d \"parámetro(s) de confianza\" debe(n) cambiarse.\n"
+fr: "\n%d \"paramètre(s) de confiance\" doi(ven)t être changé(s).\n"
+muttde: "\n%d 'Vertrauens-Einstellung(en)' muß/müssen geändert werden.\n"
+
+"Continue with '%s' (Y/n)? "
+de: "Weiter mit '%s'? (J/n) "
+es: "¿Seguir con '%s' (S/n)? "
+fr: "Continuer avec '%s' (O/n)? "
+muttde: "Weiter mit '%s'? (J/n) "
+
+"\n%d \"trust parameter(s)\" changed.\n"
+de: "\n%d 'Vertrauens-Einstellung(en)' geändert.\n"
+es: "\nCambiados %d \"parámetro(s) de confianza.\n"
+fr: "\n%d \"paramètre(s) de confiance\" changé(s).\n"
+muttde: "\n%d 'Vertrauens-Einstellung(en)' geändert.\n"
+
+"Update public keyring '%s' (Y/n)? "
+de: "Öffentlichen Schlüsselbund '%s' aktualisieren? (J/n) "
+es: "¿Actualizar el anillo de claves públicas '%s' (S/n)? "
+fr: "Modifier le fichier de clés publiques '%s' (O/n)? "
+muttde: "Öffentlichen Schlüsselbund '%s' aktualisieren? (J/n) "
+
+"\nCan't open secret key ring file '%s'\n"
+de: "\nFEHLER beim Öffnen des privaten Schlüsselbunds '%s'.\n"
+es: "\nNo puede abrirse el anillo de claves secretas '%s'\n"
+fr: "\nOuverture du fichier de clés secrètes '%s' impossible.\n"
+muttde: "\nFEHLER beim Öffnen des privaten Schlüsselbunds '%s'.\n"
+
+"\nPass 1: Looking for the \"ultimately-trusted\" keys...\n"
+de: "\nDurchlauf 1: Suche nach 'absolut vertrauenswürdigen' Schlüsseln...\n"
+es: "\nProceso 1: Búsqueda de las claves \"fundamentalmente fiables\" ...\n"
+fr: "\nPasse 1: Recherche des clés \"de confiance ultime\"...\n"
+muttde: "\nDurchlauf 1: Suche nach 'absolut vertrauenswürdigen' Schlüsseln...\n"
+
+"\nPass 2: Tracing signature chains...\n"
+de: "\nDurchlauf 2: Überprüfung von verketteten Unterschriften...\n"
+es: "\nProceso 2: Seguimiento de las cadenas de firmas...\n"
+fr: "\nPasse 2: Vérification des chaines de signatures...\n"
+muttde: "\nDurchlauf 2: Überprüfung von verketteten Unterschriften...\n"
+
+"Keyring contains duplicate key: %s\n"
+de: "\nDer Schlüsselbund enthält den Schlüssel %s doppelt.\n"
+es: "El anillo contiene una clave duplicada: %s\n"
+fr: "Le fichier de clés contient des clés dupliquées: %s\n"
+muttde: "\nDer Schlüsselbund enthält den Schlüssel %s doppelt.\n"
+
+"No ultimately-trusted keys.\n"
+de: "Keine absolut vertrauenswürdigen Schlüssel gefunden.\n"
+es: "No hay ninguna clave fundamentalmente fiable.\n"
+fr: "Pas de clés de confiance ultime.\n"
+muttde: "Keine absolut vertrauenswürdigen Schlüssel gefunden.\n"
+
+" KeyID Trust Validity User ID\n"
+de: "\n ID Vertrauen Gültigk. Benutzer\n"
+es: " Clave Confianza Validez Identificador\n"
+fr: " IDcle Conf. Validité Utilisateur\n"
+muttde: "\n ID Vertrauen Gültigk. Benutzer\n"
+
+"(KeyID: %s)\n"
+de: "(Schlüssel-ID: %s)\n"
+es: "(Identificador: %s)\n"
+fr: "(IDclef: %s)\n"
+muttde: "(Schlüssel-ID: %s)\n"
+
+"\nAn \"axiomatic\" key is one which does not need certifying by\n\
+anyone else. Usually this special status is reserved only for your\n\
+own keys, which should also appear on your secret keyring. The owner\n\
+of an axiomatic key (who is typically yourself) is \"ultimately trusted\"\n\
+by you to certify any or all other keys.\n"
+de: "\nEin 'definitionsgemäß vertrauenswürdiger' Schlüssel braucht nicht von einer\n\
+anderen Person beglaubigt zu werden. Normalerweise ist dieser spezielle Status\n\
+nur für Deine eigenen Schlüssel reserviert, die sich auch in Deinem privaten\n\
+Schlüsselbund befinden sollten. Der Besitzer eines 'definitionsgemäß vertrau-\n\
+enswürdigen' Schlüssels (das bist in der Regel Du selbst) wird von Dir als\n\
+'absolut vertrauenswürdig' betrachtet, beliebige oder sogar alle anderen\n\
+Schlüssel Dir gegenüber zu beglaubigen.\n"
+es: "\nUna clave \"axiomatica\" es aquella que no necesita certificación.\n\
+Normalmente este estado se reserva para tus propias claves, que deben\n\
+aparecer también en tu anillo de claves secretas. El propietario de una\n\
+clave axiomática (normalmente tú mismo) es \"fundamentalmente fiable\"\n\
+para certificar cualquier clave.\n"
+fr: "\n Une clef 'axiomatique' est un clef qui n'a pas besoin d'être\n\
+signé par personne. Habituellement ce statut est reservé à vos propres \n\
+clefs, celles qui apparaissent dans votre tousseau de clefs secrètes. \n\
+Le propriétaire de ces clefs (vous-même) possède votre 'confiance ultime'\n\
+pour accorder un certificat à l'une au l'autre des autres clefs.\n"
+muttde: "\nEin 'definitionsgemäß vertrauenswürdiger' Schlüssel braucht nicht von einer\n\
+anderen Person beglaubigt zu werden. Normalerweise ist dieser spezielle Status\n\
+nur für Deine eigenen Schlüssel reserviert, die sich auch in Deinem privaten\n\
+Schlüsselbund befinden sollten. Der Besitzer eines 'definitionsgemäß vertrau-\n\
+enswürdigen' Schlüssels (das bist in der Regel Du selbst) wird von Dir als\n\
+'absolut vertrauenswürdig' betrachtet, beliebige oder sogar alle anderen\n\
+Schlüssel Dir gegenüber zu beglaubigen.\n"
+
+"\nKey for user ID \"%s\"\n\
+is designated as an \"ultimately-trusted\" introducer, but the key\n\
+does not appear in the secret keyring.\n\
+Use this key as an ultimately-trusted introducer (y/N)? "
+de: "\nDer Schlüssel von \"%s\"\n\
+soll ein 'absolut vertrauenswürdiger Einführer' werden, aber er befindet\n\
+sich nicht im privaten Schlüsselbund. Soll ich diesen Schlüssel trotzdem\n\
+als 'absolut vertrauenswürdigen Einführer' behandeln? (j/N) "
+es: "\nLa clave del usuario \"%s\"\n\
+está designada como referencia \"fundamentalmente fiable\", pero
+la clave no aparece en el anillo de claves secretas.\n\
+¿Se utiliza como referencia fundamentalmente fiable (s/N)? "
+fr: "\nLa clef de l'utilisateur '%s' \n\
+a été désigné comme ayant la 'confiance ulime', mais cette clef \n\
+n'apparraît pas dans le trousseau des clefs secrètes. \n\
+Voulez-vous utiliser cette clef comme ayant la 'confiance ultime'? (o/N) "
+muttde: "\nDer Schlüssel von \"%s\"\n\
+soll ein 'absolut vertrauenswürdiger Einführer' werden, aber er befindet\n\
+sich nicht im privaten Schlüsselbund. Soll ich diesen Schlüssel trotzdem\n\
+als 'absolut vertrauenswürdigen Einführer' behandeln? (j/N) "
+
+"\n\007Cannot read from secret keyring.\n"
+de: "\n\007FEHLER beim Lesen des privaten Schlüsselbunds.\n"
+es: "\n\007No puede leerse el anillo de claves secretas.\n"
+fr: "\n\007Lecture du fichier de clés secrètes impossible.\n"
+mutt: "\nCannot read from secret keyring.\n"
+muttde: "\nFEHLER beim Lesen des privaten Schlüsselbunds.\n"
+
+"\n\007WARNING: Public key for user ID: \"%s\"\n\
+does not match the corresponding key in the secret keyring.\n"
+de: "\n\007WARNUNG: Der öffentliche Schlüssel\nvon \"%s\" stimmt nicht\n\
+mit dem Gegenstück im privaten Schlüsselbund überein.\n"
+es: "\n\007AVISO: La clave pública de \"%s\"\n\
+no coincide con la clave correspondiente en el anillo de claves secretas.\n"
+fr: "\n\007ATTENTION: la clé publique pour l'utilisateur: \"%s\"\n\
+ne correspond pas avec la clé respective dans le fichier de clés\
+secrètes.\n"
+mutt: "\nWARNING: Public key for user ID: \"%s\"\n\
+does not match the corresponding key in the secret keyring.\n"
+muttde: "\nWARNUNG: Der öffentliche Schlüssel\nvon \"%s\" stimmt nicht\n\
+mit dem Gegenstück im privaten Schlüsselbund überein.\n"
+
+"This is a serious condition, indicating possible keyring tampering.\n"
+de: "Dies könnte bedeuten, daß die Schlüsselbunde manipuliert wurden!\n"
+es: "Es una situación grave: posible manipulación de anillos.\n"
+fr: "Ceci est une condition sérieuse, indiquant une possible manipulation\n\
+du fichier de clés.\n"
+muttde: "Dies könnte bedeuten, daß die Schlüsselbunde manipuliert wurden!\n"
+
+"\nKey for user ID \"%s\"\n\
+also appears in the secret key ring."
+de: "\nDer Schlüssel für die Benutzer-ID \"%s\"\n\
+ist auch im privaten Schlüsselbund vorhanden."
+es: "\nLa clave del identificador \"%s\"\n\
+también aparece en el anillo de claves secretas."
+fr: "\nLa clef pour l'utilisateur avec id: \"%s\"\n\
+apparait egalement dans le repertoire des clefs secretes."
+muttde: "\nDer Schlüssel für die Benutzer-ID \"%s\"\n\
+ist auch im privaten Schlüsselbund vorhanden."
+
+"\nUse this key as an ultimately-trusted introducer (y/N)? "
+de: "\nSoll dieser Schlüssel als 'absolut vertrauenswürdigen Einführer'\n\
+behandelt werden? (j/N) "
+es: "\n¿Se utiliza esta clave como referencia fundamentalmente fiable (s/N)? "
+fr: "\nUtiliser cette clé comme introducteur de confiance ultime (o/N)? "
+muttde: "\nSoll dieser Schlüssel als 'absolut vertrauenswürdigen Einführer'\n\
+behandelt werden? (j/N) "
+
+"Public key for: \"%s\"\n\
+is not present in the backup keyring '%s'.\n"
+de: "Der öffentliche Schlüssel von \"%s\"\n\
+ist nicht in der Schlüsselbund-Kopie '%s' enthalten.\n"
+es: "La clave pública de: \"%s\"\n\
+no se encuentra en la copia de seguridad del anillo '%s'.\n"
+fr: "La clé publique pour: \"%s\"\n\
+n'est pas présente dans le fichier de clés de sauvegarde '%s'.\n"
+muttde: "Der öffentliche Schlüssel von \"%s\"\n\
+ist nicht in der Schlüsselbund-Kopie '%s' enthalten.\n"
+
+"\n\007WARNING: Secret key for: \"%s\"\n\
+does not match the key in the backup keyring '%s'.\n"
+de: "\n\007WARNUNG: Der private Schlüssel von \"%s\"\n\
+entspricht nicht der Kopie im Schlüsselbund '%s'.\n"
+es: "\n\007AVISO: La clave secreta de: \"%s\"\n\
+no coincide con la clave en la copia de seguridad del anillo '%s'.\n"
+fr: "\n\007ATTENTION: la clé secrète pour: \"%s\"\n\
+ne correspond pas avec la clé dans le fichier de clés de sauvegarde.\n"
+mutt: "\nWARNING: Secret key for: \"%s\"\n\
+does not match the key in the backup keyring '%s'.\n"
+muttde: "\nWARNUNG: Der private Schlüssel von \"%s\"\n\
+entspricht nicht der Kopie im Schlüsselbund '%s'.\n"
+
+"\nMake a determination in your own mind whether this key actually\n\
+belongs to the person whom you think it belongs to, based on available\n\
+evidence. If you think it does, then based on your estimate of\n\
+that person's integrity and competence in key management, answer\n\
+the following question:\n"
+de: "\nEntscheide für Dich, ob dieser Schlüssel tatsächlich zu dieser Person gehört.\n\
+Triff diese Entscheidung unter Berücksichtigung der zur Verfügung stehenden\n\
+Informationen. Wenn Du glaubst, daß der Schlüssel echt ist, dann beantworte\n\
+folgende Frage aufgrund Deiner Einschätzung der Vertrauenswürdigkeit der\n\
+Person und ihrer Kompetenz beim Umgang mit PGP-Schlüsseln:\n"
+es: "\nDecide tú mismo si esta clave realmente pertenece, según la\n\
+evidencia a tu alcance, a la persona que crees. Si es asÃ,\n\
+contesta a la siguiente pregunta, basándote en tu estimación de la\n\
+integridad de esa persona y de su conocimiento sobre gestión de claves:\n"
+fr: "\nDeterminez vous même si cette clé appartient vraiment à la personne\
+\nà qui vous croyez qu'elle appartient, selon les informations disponibles.\
+\nSi vous le croyez, alors selon votre estimation de l'intégrité de cette\
+\npersonne et de sa compétence dans la gestion de clés, répondez à la\
+\nquestion suivante:\n"
+muttde: "\nEntscheide für Dich, ob dieser Schlüssel tatsächlich zu dieser Person gehört.\n\
+Triff diese Entscheidung unter Berücksichtigung der zur Verfügung stehenden\n\
+Informationen. Wenn Du glaubst, daß der Schlüssel echt ist, dann beantworte\n\
+folgende Frage aufgrund Deiner Einschätzung der Vertrauenswürdigkeit der\n\
+Person und ihrer Kompetenz beim Umgang mit PGP-Schlüsseln:\n"
+
+"\nWould you trust \"%s\"\n\
+to act as an introducer and certify other people's public keys to you?\n\
+(1=I don't know. 2=No. 3=Usually. 4=Yes, always.) ? "
+de: "\nWürdest Du \"%s\" als 'Einführer'\n\
+und 'Beglaubiger' für die öffentlichen Schlüssel Dritter vertrauen?\n\
+(1=Ich weiß nicht; 2=Nein; 3=In der Regel; 4=Ja, immer) : "
+es: "\n¿ConfiarÃas en \"%s\"\n\
+como referencia y para certificar ante ti otras claves públicas?\n\
+(1=No sé. 2=No. 3=Normalmente. 4=SÃ, siempre.) ? "
+fr: "\nAuriez vous confiance en \"%s\"\n\
+pour servir d'introducteur et certifier pour vous les clés publiques d'autres\
+\npersonnes? (1=Ne sais pas. 2=Non. 3=Généralement. 4=Oui, toujours.) ? "
+muttde: "\nWürdest Du \"%s\" als 'Einführer'\n\
+und 'Beglaubiger' für die öffentlichen Schlüssel Dritter vertrauen?\n\
+(1=Ich weiß nicht; 2=Nein; 3=In der Regel; 4=Ja, immer) : "
+
+"This user is untrusted to certify other keys.\n"
+de: "Dieser Benutzer ist nicht vertrauenswürdig genug,\n\
+um andere Schlüssel zu beglaubigen.\n"
+es: "Este usuario no es fiable para certificar otras claves.\n"
+fr: "Cet utilisateur n'est pas de confiance pour certifier d'autres clés.\n"
+muttde: "Dieser Benutzer ist nicht vertrauenswürdig genug,\n\
+um andere Schlüssel zu beglaubigen.\n"
+
+"This user is generally trusted to certify other keys.\n"
+de: "Dieser Benutzer ist in der Regel vertrauenswürdig genug,\n\
+um andere Schlüssel zu beglaubigen.\n"
+es: "Este usuario es de relativa confianza para certificar otras claves.\n"
+fr: "Cet utilisateur est généralement de confiance pour certifier d'autres\
+ clés.\n"
+muttde: "Dieser Benutzer ist in der Regel vertrauenswürdig genug,\n\
+um andere Schlüssel zu beglaubigen.\n"
+
+"This user is completely trusted to certify other keys.\n"
+de: "Dieser Benutzer ist immer vertrauenswürdig genug,\n\
+um andere Schlüssel zu beglaubigen.\n"
+es: "Este usuario es de completa confianza para certificar otras claves.\n"
+fr: "Cet utilisateur est de confiance totale pour certifier d'autres clés.\n"
+muttde: "Dieser Benutzer ist immer vertrauenswürdig genug,\n\
+um andere Schlüssel zu beglaubigen.\n"
+
+"This axiomatic key is ultimately trusted to certify other keys.\n"
+de: "Dieser Schlüssel ist definitionsgemäß vertrauenswürdig genug,\n\
+um andere Schlüssel zu beglaubigen.\n"
+es: "Esta clave axiomática es absolutamente fiable para certificar otras.\n"
+fr: "Cette clé axiomatique est de confiance ultime pour certifier\n\
+d'autres clés\n"
+muttde: "Dieser Schlüssel ist definitionsgemäß vertrauenswürdig genug,\n\
+um andere Schlüssel zu beglaubigen.\n"
+
+"This key/userID association is not certified.\n"
+de: "Diese Schlüssel-/Benutzer-Zuordnung ist nicht bestätigt.\n"
+es: "Esta asociación clave/usuario no está certificada.\n"
+fr: "Cette association clé/utilisateur n'est pas certifiée.\n"
+muttde: "Diese Schlüssel-/Benutzer-Zuordnung ist nicht bestätigt.\n"
+
+"This key/userID association is marginally certified.\n"
+de: "Diese Schlüssel-/Benutzer-Zuordnung ist teilweise bestätigt.\n"
+es: "Esta asociación clave/usuario está relativamente certificada.\n"
+fr: "Cette association clé/utilisateur est marginalement certifiée.\n"
+muttde: "Diese Schlüssel-/Benutzer-Zuordnung ist teilweise bestätigt.\n"
+
+"This key/userID association is fully certified.\n"
+de: "Diese Schlüssel-/Benutzer-Zuordnung ist voll bestätigt.\n"
+es: "Esta asociación clave/usuario está completamente certificada.\n"
+fr: "Cette association clé/utilisateur est complètement certifiée.\n"
+muttde: "Diese Schlüssel-/Benutzer-Zuordnung ist voll bestätigt.\n"
+
+" Questionable certification from:\n "
+de: " Fragwürdige Beglaubigung von:\n "
+es: " Certificación cuestionable de:\n "
+fr: " Certificat de confiance douteux de:\n "
+muttde: " Fragwürdige Beglaubigung von:\n "
+
+" Untrusted certification from:\n "
+de: " Unglaubwürdige Beglaubigung von:\n "
+es: " Certificación no fiable de:\n "
+fr: " Certificat non sûr de:\n "
+muttde: " Unglaubwürdige Beglaubigung von:\n "
+
+" Generally trusted certification from:\n "
+de: " Glaubwürdige Beglaubigung von:\n "
+es: " Certificación relativamente fiable de:\n "
+fr: " Certificat de confiance relatif de:\n "
+muttde: " Glaubwürdige Beglaubigung von:\n "
+
+" Completely trusted certification from:\n "
+de: " Voll glaubwürdige Beglaubigung von:\n "
+es: " Certificación completamente fiable de:\n "
+fr: " Certificat de confiance complet de:\n "
+muttde: " Voll glaubwürdige Beglaubigung von:\n "
+
+" Axiomatically trusted certification from:\n "
+de: " Definitionsgemäß glaubwürdige Beglaubigung von:\n "
+es: " Certificación axiomáticamente fiable de:\n "
+fr: " Certificat de confiance par axiome de:\n "
+muttde: " Definitionsgemäß glaubwürdige Beglaubigung von:\n "
+
+"\nKey for user ID: %s\n"
+de: "\nSchlüssel für Benutzer-ID \"%s\",\n"
+es: "\nClave del usuario: %s\n"
+fr: "\nClé pour le nom d'utilisateur: %s\n"
+muttde: "\nSchlüssel für Benutzer-ID \"%s\",\n"
+
+"%d-bit key, key ID %s, created %s\n"
+de: "%d-Bit-Schlüssel, Schlüssel-ID: %s, erzeugt am: %s.\n"
+es: "Clave de %d bits, con identificador %s, creada el %\n"
+fr: "Clef de %d bits. Id clef %s créé %s\n"
+muttde: "%d-Bit-Schlüssel, Schlüssel-ID: %s, erzeugt am: %s.\n"
+
+"Bad key format.\n"
+de: "Falsches Schlüssel-Format.\n"
+es: "Formato incorrecto de clave.\n"
+fr: "Mauvais format de clé\n"
+muttde: "Falsches Schlüssel-Format.\n"
+
+"Unrecognized version.\n"
+de: "Unbekannte Version.\n"
+es: "Versión no reconocida.\n"
+fr: "Version non reconnue.\n"
+muttde: "Unbekannte Version.\n"
+
+"Key is disabled.\n"
+de: "Der Schlüssel ist gesperrt.\n"
+es: "La clave está desactivada.\n"
+fr: "La clé est inactivée.\n"
+muttde: "Der Schlüssel ist gesperrt.\n"
+
+"Also known as: %s\n"
+de: "Alternative Benutzer-ID: %s\n"
+es: "También conocido como: %s\n"
+fr: "Egalement connu(e) en tant que: %s\n"
+muttde: "Alternative Benutzer-ID: %s\n"
+
+" Certified by: "
+de: " Beglaubigt von: "
+es: " Certificado por: "
+fr: " Certifiée par: "
+muttde: " Beglaubigt von: "
+
+"\nWarning: keyid %4d/%s %s has no user id!\n"
+de: "\nWARNUNG: Der Schlüssel '%4d/%s %s' hat keine Benutzer-ID!\n"
+es: "\nAdvertencia: la clave %4d/%s %s no tiene identificador\n"
+fr: "\nAttention: IDclef %4d/%s %s n'est pas associé à un utilisateur!\n"
+muttde: "\nWARNUNG: Der Schlüssel '%4d/%s %s' hat keine Benutzer-ID!\n"
+
+"Updated keyID: 0x%s\n"
+de: "Die Schlüssel-ID 0x%s wurde aktualisiert.\n"
+es: "Identificador actualizado: 0x%s\n"
+fr: "Mise à jour de la clef ID: 0x%s\n"
+muttde: "Die Schlüssel-ID 0x%s wurde aktualisiert.\n"
+
+"\n\007Unable to create key file '%s'.\n"
+de: "\n\007FEHLER beim Erzeugen der Schlüsseldatei '%s'.\n"
+es: "\n\007No puede crearse el fichero de claves '%s'.\n"
+fr: "\n\007Impossible de créer le fichier de clés '%s'.\n"
+mutt: "\nUnable to create key file '%s'.\n"
+muttde: "\nFEHLER beim Erzeugen der Schlüsseldatei '%s'.\n"
+
+"\n\007Keyring file '%s' does not exist. "
+de: "\n\007Der Schlüsselbund '%s' existiert nicht.\n"
+es: "\n\007El anillo '%s' no existe. "
+fr: "\n\007Le fichier de clés '%s' n'existe pas. "
+mutt: "\nKeyring file '%s' does not exist. "
+muttde: "\nDer Schlüsselbund '%s' existiert nicht.\n"
+
+"\n\007Sorry, this key has been revoked by its owner.\n"
+de: "\n\007Dieser Schlüssel wurde von seinem Besitzer zurückgezogen.\n"
+es: "\n\007Esa clave ha sido revocada por su propietario.\n"
+fr: "\n\007Désolé, cette clé a été révoquée par son propriétaire.\n"
+mutt: "\nSorry, this key has been revoked by its owner.\n"
+muttde: "\nDieser Schlüssel wurde von seinem Besitzer zurückgezogen.\n"
+
+"\nKey for user ID \"%s\"\n\
+has been revoked. You cannot use this key.\n"
+de: "\nDer Schlüssel von \"%s\"\n\
+wurde zurückgezogen und kann nicht verwendet werden.\n"
+es: "\nLa clave del usuario \"%s\"\n\
+ha sido revocada. No puede utilizarse.\n"
+fr: "\nLa clé pour l'utilisateur \"%s\"\n\
+a été révoquée. Vous ne pouvez utiliser cette clé.\n"
+muttde: "\nDer Schlüssel von \"%s\"\n\
+wurde zurückgezogen und kann nicht verwendet werden.\n"
+
+"\n\007Key matching expected Key ID %s not found in file '%s'.\n"
+de: "\n\007Der zur erwarteten Schlüssel-ID %s passende Schlüssel\n\
+ist nicht in der Datei '%s' enthalten.\n"
+es: "\n\007Se esperaba una clave %s que no se encuentra en '%s'.\n"
+fr: "\n\007Clé correspondant à l'identificateur %s non trouvée\
+\ndans le fichier '%s'.\n"
+mutt: "\nKey matching expected Key ID %s not found in file \n\
+'%s'.\n"
+muttde: "\nDer zur erwarteten Schlüssel-ID %s passende Schlüssel\n\
+ist nicht in der Datei '%s' enthalten.\n"
+
+"\n\007Key matching userid '%s' not found in file '%s'.\n"
+de: "\n\007Der zur Benutzer-ID \"%s\" passende Schlüssel\n\
+ist nicht in der Datei '%s' enthalten.\n"
+es: "\n\007La clave del usuario '%s' no se encuentra en el fichero '%s'.\n"
+fr: "\n\007Clé correspondant à l'utilisateur '%s' introuvable\n\
+dans le fichier '%s'.\n"
+mutt: "\nKey matching userid '%s' not found in file '%s'.\n"
+muttde: "\nDer zur Benutzer-ID \"%s\" passende Schlüssel\n\
+ist nicht in der Datei '%s' enthalten.\n"
+
+"Enter secret key filename: "
+de: "Dateiname des privaten Schlüssels: "
+es: "Introduzca el nombre del anillo de claves secretas: "
+fr: "Entrez le nom du fichier de clés secrètes: "
+muttde: "Dateiname des privaten Schlüssels: "
+
+"Enter public key filename: "
+de: "Dateiname des öffentlichen Schlüssels: "
+es: "Introduzca el nombre del anillo de claves públicas: "
+fr: "Entrez le nom du fichier de clés publiques: "
+muttde: "Dateiname des öffentlichen Schlüssels: "
+
+"\nYou need a pass phrase to unlock your RSA secret key. "
+de: "\nDu brauchst ein Mantra, um Deinen privaten RSA-Schlüssel zu benutzen."
+es: "\nSe necesita la contraseña para abrir la clave secreta RSA. "
+fr: "\nVous devez avoir un mot de passe pour utiliser votre clé secrète RSA."
+muttde: "\nDu brauchst ein Mantra, um Deinen privaten RSA-Schlüssel zu benutzen."
+
+"No passphrase; secret key unavailable.\n"
+de: "\nKein Mantra; der private Schlüssel kann nicht benutzt werden.\n"
+es: "\nSin contraseña; la clave secreta no está disponible.\n"
+fr: "\nSans la phrase secrete, pas de clef secrete disponible.\n"
+muttde: "\nKein Mantra; der private Schlüssel kann nicht benutzt werden.\n"
+
+"\nAdvisory warning: This RSA secret key is not protected by a \
+passphrase.\n"
+de: "\nHinweis: Dieser private Schlüssel ist nicht durch ein Mantra geschützt.\n"
+es: "\nAdvertencia: Esta clave secreta RSA no tiene contraseña.\n"
+fr: "\nAttention: cette clé secrète RSA n'est pas protégée par un mot \
+de passe"
+muttde: "\nHinweis: Dieser private Schlüssel ist nicht durch ein Mantra geschützt.\n"
+
+"Pass phrase is good. "
+de: "\nDas Mantra ist richtig.\n"
+es: "La contraseña es correcta. "
+fr: "Le mot de passe est correct. "
+muttde: "\nDas Mantra ist richtig.\n"
+
+"\n\007Key file '%s' is not a secret key file.\n"
+de: "\n\007Die Datei '%s' ist keine private Schlüsseldatei.\n"
+es: "\n\007El fichero '%s' no tiene claves secretas.\n"
+fr: "\n\007Le fichier de clé '%s' n'est pas un fichier de clés secrètes.\n"
+mutt: "\nKey file '%s' is not a secret key file.\n"
+muttde: "\nDie Datei '%s' ist keine private Schlüsseldatei.\n"
+
+"Key fingerprint ="
+de: "Fingerabdruck des Schlüssels:"
+es: "Huella dactilar ="
+fr: "Empreinte de la clé ="
+muttde: "Fingerabdruck des Schlüssels:"
+
+"\nKey ring: '%s'"
+de: "\nSchlüsselbund '%s':\n"
+es: "\nAnillo de claves: '%s',\n"
+fr: "\nFichier de clé: '%s'"
+muttde: "\nSchlüsselbund '%s':\n"
+
+", looking for user ID \"%s\"."
+de: "Suche nach Benutzer-ID \"%s\":\n"
+es: "buscando el usuario \"%s\"\n"
+fr: ", recherche du nom d'utilisateur \"%s\"."
+muttde: "Suche nach Benutzer-ID \"%s\":\n"
+
+"1 matching key found.\n"
+de: "Es wurde ein passender Schlüssel gefunden.\n"
+es: "Se ha encontrado una clave.\n"
+fr: "1 clef trouvée.\n"
+muttde: "Es wurde ein passender Schlüssel gefunden.\n"
+
+"%d matching keys found.\n"
+de: "Es wurden %d passende Schlüssel gefunden.\n"
+es: "Se han encontrado %d claves.\n"
+fr: "%d clefs trouvées. \n"
+muttde: "Es wurden %d passende Schlüssel gefunden.\n"
+
+"\nChecking signatures...\n"
+de: "\nÜberprüfung der Unterschriften...\n"
+es: "\nComprobando las firmas...\n"
+fr: "\nVérification des signatures...\n"
+muttde: "\nÜberprüfung der Unterschriften...\n"
+
+"*** KEY REVOKED ***\n"
+de: "*** ZURÜCKGEZOGEN ***\n"
+es: "*** CLAVE REVOCADA ***\n"
+fr: "*** CLEF REVOQUÉE ***\n"
+muttde: "*** ZURÜCKGEZOGEN ***\n"
+
+"(Unknown signator, can't be checked)"
+de: "(Unterschreibender unbekannt, keine Prüfung)"
+es: "(Firmante desconocido, no puede comprobarse)"
+fr: "(Signataire inconnu, ne peut être vérifié)"
+muttde: "(Unterschreibender unbekannt, keine Prüfung)"
+
+"(Key too long, can't be checked)"
+de: "(Schlüssel zu lang, keine Prüfung)"
+es: "(Clave demasiado larga, no puede comprobarse)"
+fr: "(Clef trop longue, ne peut etre verifiée)"
+muttde: "(Schlüssel zu lang, keine Prüfung)"
+
+"(Malformed or obsolete signature format)"
+de: "(Unterschriftsformat fehlerhaft oder veraltet)"
+es: "(Formato de firma obsoleto o incorrecto)"
+fr: "(Signature malformee ou obsolete)"
+muttde: "(Unterschriftsformat fehlerhaft oder veraltet)"
+
+"(Unknown public-key algorithm)"
+de: "(unbek. Algorithmus des öffentl. Schlüssels)"
+es: "(Algoritmo desconocido de clave pública)"
+fr: "(Algorithme de clef publique inconnu)"
+muttde: "(unbek. Algorithmus des öffentl. Schlüssels)"
+
+"(Unknown hash algorithm)"
+de: "(unbekannter Prüfsummen-Algorithmus)"
+es: "(Algoritmo desconocido de distribución [hash])"
+fr: "(Algorithme de hash inconnu)"
+muttde: "(unbekannter Prüfsummen-Algorithmus)"
+
+"(Unknown signature packet version)"
+de: "(unbekannte Version des Unterschriftsblocks)"
+es: "(Versión desconocida de firma)"
+fr: "Version de paquet de signature inconnue"
+muttde: "(unbekannte Version des Unterschriftsblocks)"
+
+"(Malformed signature)"
+de: "(fehlerhafte Unterschrift)"
+es: "(Firma mal formada)"
+fr: "(Signature deformée)"
+muttde: "(fehlerhafte Unterschrift)"
+
+"(Corrupted signature packet)"
+de: "(beschädigter Unterschriftsblock)"
+es: "(Firma dañada)"
+fr: "(Signature corrompue)"
+muttde: "(beschädigter Unterschriftsblock)"
+
+"\007**** BAD SIGNATURE! ****"
+de: "\007**** FALSCHE UNTERSCHRIFT! ****"
+es: "\007**** FIRMA INCORRECTA ****"
+fr: "\007**** MAUVAISE SIGNATURE! ****"
+mutt: "**** BAD SIGNATURE! ****"
+muttde: "**** FALSCHE UNTERSCHRIFT! ****"
+
+"Remove bad signatures (Y/n)? "
+de: "Falsche Unterschriften löschen? (J/n) "
+es: "¿Suprimir las firmas incorrectas (S/n)? "
+fr: "Supprimer les mauvaises signatures (O/n)? "
+muttde: "Falsche Unterschriften löschen? (J/n) "
+
+"\nRemoving signatures from userid '%s' in key ring '%s'\n"
+de: "\nLöschen der Unterschriften unter ID \"%s\"\naus dem Schlüsselbund '%s'.\n"
+es: "\nSuprimiendo las firmas del usuario '%s' del\n\
+anillo de claves '%s'\n"
+fr: "\nSuppression des signatures de l'utilisateur '%s'\n\
+dans le fichier de clés '%s'\n"
+muttde: "\nLöschen der Unterschriften unter ID \"%s\"\naus dem Schlüsselbund '%s'.\n"
+
+"\n\007Key not found in key ring '%s'.\n"
+de: "\n\007Der Schlüssel ist nicht im Schlüsselbund\n'%s' enthalten.\n"
+es: "\n\007No se ha encontrado la clave en el anillo '%s'.\n"
+fr: "\n\007Clé introuvable dans le fichier de clés '%s'.\n"
+mutt: "\nKey not found in key ring '%s'.\n"
+muttde: "\nDer Schlüssel ist nicht im Schlüsselbund\n'%s' enthalten.\n"
+
+"\nKey has no signatures to remove.\n"
+de: "\nDer Schlüssel trägt keine Unterschriften, die gelöscht werden könnten.\n"
+es: "\nLa clave no tiene ninguna firma por borrar.\n"
+fr: "\nLa clé n'a pas de signatures à supprimer.\n"
+muttde: "\nDer Schlüssel trägt keine Unterschriften, die gelöscht werden könnten.\n"
+
+"\nKey has %d signature(s):\n"
+de: "\nDer Schlüssel trägt %d Unterschrift(en):\n"
+es: "\nLa clave tiene %d firma(s):\n"
+fr: "\nLa clé a %d signature(s):\n"
+muttde: "\nDer Schlüssel trägt %d Unterschrift(en):\n"
+
+"(Unknown signator, can't be checked)\n"
+de: "(Unterschreibender unbekannt, keine Prüfung)\n"
+es: "(Firmante desconocido, no puede comprobarse)\n"
+fr: "(Signataire inconnu, ne peut être vérifié)\n"
+muttde: "(Unterschreibender unbekannt, keine Prüfung)\n"
+
+"Remove this signature (y/N)? "
+de: "Diese Unterschrift löschen? (j/N) "
+es: "\277Suprimir esta firma (s/N)? "
+fr: "Suppression de cette signature (o/N)? "
+muttde: "Diese Unterschrift löschen? (j/N) "
+
+"\nNo key signatures removed.\n"
+de: "\nKeine Unterschriften gelöscht.\n"
+es: "\nNo se ha suprimido ninguna firma.\n"
+fr: "\nPas de supression de signature de clé.\n"
+muttde: "\nKeine Unterschriften gelöscht.\n"
+
+"\n%d key signature(s) removed.\n"
+de: "\n%d Unterschrift(en) gelöscht.\n"
+es: "\nSuprimidas %d firma(s) de clave.\n"
+fr: "\n%d signature(s) de clé supprimée(s).\n"
+muttde: "\n%d Unterschrift(en) gelöscht.\n"
+
+"\nRemoving from key ring: '%s'"
+de: "\nLöschen aus Schlüsselbund '%s'\n"
+es: "\nSuprimiendo del anillo: '%s'"
+fr: "\nSuppression du ficher de clés: '%s'"
+muttde: "\nLöschen aus Schlüsselbund '%s'\n"
+
+", userid \"%s\".\n"
+de: "Benutzer-ID \"%s\".\n"
+es: ", identificador \"%s\".\n"
+fr: ", utilisateur \"%s\".\n"
+muttde: "Benutzer-ID \"%s\".\n"
+
+"\nKey has more than one user ID.\n\
+Do you want to remove the whole key (y/N)? "
+de: "\nDer Schlüssel hat mehr als eine Benutzer-ID.\n\
+Soll der ganze Schlüssel vollständig gelöscht werden? (j/N) "
+es: "\nLa clave tiene más de un identificador de usuario.\n\
+¿Quieres suprimirla por completo (s/N)? "
+fr: "\nLa clé a plus d'un nom d'utilisateur.\n\
+Voulez vous supprimier toute la clé (o/N)? "
+muttde: "\nDer Schlüssel hat mehr als eine Benutzer-ID.\n\
+Soll der ganze Schlüssel vollständig gelöscht werden? (j/N) "
+
+"\nNo more user ID's\n"
+de: "\nKeine weiteren Benutzer-IDs.\n"
+es: "\nNo hay más identificadores de usuario\n"
+fr: "\nPlus de noms d'utilisateur\n"
+muttde: "\nKeine weiteren Benutzer-IDs.\n"
+
+"Remove \"%s\" (y/N)? "
+de: "\"%s\" löschen? (j/N) "
+es: "¿Suprimir \"%s\" (s/N)? "
+fr: "Supprimer \"%s\" (o/N)? "
+muttde: "\"%s\" löschen? (j/N) "
+
+"\nAre you sure you want this key removed (y/N)? "
+de: "\nBist Du sicher, daß Du diesen Schlüssel löschen willst? (j/N) "
+es: "\n¿Estás seguro de querer suprimir esta clave (s/N)? "
+fr: "\nEtes vous sûr(e) de vouloir supprimer cette clé (o/N)? "
+muttde: "\nBist Du sicher, daß Du diesen Schlüssel löschen willst? (j/N) "
+
+"\nUser ID removed from key ring.\n"
+de: "\nDie Benutzer-ID wurde aus dem Schlüsselbund gelöscht.\n"
+es: "\nIdentificador suprimido del anillo.\n"
+fr: "\nNom d'utilisateur supprimé du fichier de clés.\n"
+muttde: "\nDie Benutzer-ID wurde aus dem Schlüsselbund gelöscht.\n"
+
+"\nKey removed from key ring.\n"
+de: "\nDer Schlüssel wurde aus dem Schlüsselbund gelöscht.\n"
+es: "\nClave suprimida del anillo.\n"
+fr: "\nClé supprimée du fichier de clés.\n"
+muttde: "\nDer Schlüssel wurde aus dem Schlüsselbund gelöscht.\n"
+
+"\nKey or user ID is also present in secret keyring.\n\
+Do you also want to remove it from the secret keyring (y/N)? "
+de: "\nDer Schlüssel oder die Benutzer-ID sind auch im privaten Schlüsselbund\n\
+enthalten. Sollen sie dort ebenfalls gelöscht werden? (j/N) "
+es: "\nEl identificador se encuentra además en el anillo de\n\
+claves secretas. ¿Quieres borrarlo también de ahà (s/N)? "
+fr: "\nLa clé ou le nom d'utilisateur est aussi dans le fichier de clés\n\
+secrètes. Voulez vous l'enlever du ficher de clés secrètes (o/N)? "
+muttde: "\nDer Schlüssel oder die Benutzer-ID sind auch im privaten Schlüsselbund\n\
+enthalten. Sollen sie dort ebenfalls gelöscht werden? (j/N) "
+
+"\nExtracting from key ring: '%s'"
+de: "\nExtrahieren aus dem Schlüsselbund: '%s'\n"
+es: "\nExtrayendo del anillo de claves: '%s'"
+fr: "\nExtraction du fichier de clés: '%s'"
+muttde: "\nExtrahieren aus dem Schlüsselbund: '%s'\n"
+
+"Extract the above key into which file?"
+de: "Dateiname dieses extrahierten Schlüssels?"
+es: "¿En qué fichero se extrae la clave anterior?"
+fr: "Extraire la clef suivant de quel fichier?"
+muttde: "Dateiname dieses extrahierten Schlüssels?"
+
+"Key ID %s is already included in key ring '%s'.\n"
+de: "Die Schlüssel-ID %s ist bereits im Schlüsselbund\n'%s' enthalten.\n"
+es: "La clave %s ya está en el anillo '%s'.\n"
+fr: "L'identificateur de clé %s est déjà présent\
+\ndans le fichier de clés '%s'.\n"
+muttde: "Die Schlüssel-ID %s ist bereits im Schlüsselbund\n'%s' enthalten.\n"
+
+"\nKey extracted to file '%s'.\n"
+de: "\nSchlüssel extrahiert in Datei '%s'.\n"
+es: "\nClave extraÃda en el fichero '%s'.\n"
+fr: "\nClé mise dans le fichier '%s'.\n"
+muttde: "\nSchlüssel extrahiert in Datei '%s'.\n"
+
+"\nThis operation may not be performed on a secret keyring.\n\
+Defaulting to public keyring."
+de: "\nDiese Operation kann mit einem privaten Schlüsselbund nicht ausgeführt\n\
+werden. Der öffentliche Schlüsselbund wird versucht."
+es: "\nEsta operacion no puede realizarse sobre el anillo de \
+claves secretas.\nSe pasa al anillo de claves públicas."
+fr: "\nCette operation ne peut pas être effectuée sur un fichier de clés\n\
+secrètes. Le fichier de clés publiques sera utilisé à la place."
+muttde: "\nDiese Operation kann mit einem privaten Schlüsselbund nicht ausgeführt\n\
+werden. Der öffentliche Schlüsselbund wird versucht."
+
+"\nEditing userid \"%s\" in key ring: '%s'.\n"
+de: "\nBearbeitung der Benutzer-ID \"%s\"\nim Schlüsselbund '%s'.\n"
+es: "\nModificación del identificador \"%s\"\n\
+en el anillo: '%s'.\n"
+fr: "\nModification du nom d'utilsateur \"%s\"\n\
+dans le fichier de clé: '%s'.\n"
+muttde: "\nBearbeitung der Benutzer-ID \"%s\"\nim Schlüsselbund '%s'.\n"
+
+"\nCan't open public key ring file '%s'\n"
+de: "\nFEHLER beim Öffnen des öffentlichen Schlüsselbunds '%s'.\n"
+es: "\nNo puede abrirse el anillo de claves públicas '%s'\n"
+fr: "\nOuverture du fichier de clés publiques '%s' impossible.\n"
+muttde: "\nFEHLER beim Öffnen des öffentlichen Schlüsselbunds '%s'.\n"
+
+"\n\007File '%s' is not a public keyring.\n"
+de: "\n\007Die Datei '%s' ist kein öffentlicher Schlüsselbund.\n"
+es: "\n\007El fichero '%s' no es un anillo de claves públicas.\n"
+fr: "\n\007Le fichier '%s' n'est pas un fichier de clés publiques.\n"
+mutt: "\nFile '%s' is not a public keyring.\n"
+muttde: "\nDie Datei '%s' ist kein öffentlicher Schlüsselbund.\n"
+
+"\n\007This key has been revoked by its owner.\n"
+de: "\n\007Dieser Schlüssel wurde von seinem Besitzer zurückgezogen.\n"
+es: "\n\007Esta clave ha sido revocada por su propietario.\n"
+fr: "\n\007Cette clé a été révoquée par son propriétaire.\n"
+mutt: "\nThis key has been revoked by its owner.\n"
+muttde: "\nDieser Schlüssel wurde von seinem Besitzer zurückgezogen.\n"
+
+"\nNo secret key available. Editing public key trust parameter.\n"
+de: "\nKein privater Schlüssel vorhanden. Die 'Vertrauens-Einstellungen' des\n\
+öffentlichen Schlüssels werden bearbeitet.\n"
+es: "\nNo hay clave secreta disponible. Modificando el parámetro \
+de confianza\n\
+de la clave pública.\n"
+fr: "\nPas de clé secrète disponible. Modification du paramètre de\n\
+confiance de la clé publique.\n"
+muttde: "\nKein privater Schlüssel vorhanden. Die 'Vertrauens-Einstellungen' des\n\
+öffentlichen Schlüssels werden bearbeitet.\n"
+
+"Current trust for this key's owner is: %s\n"
+de: "\nAktuelles 'Vertrauen' zum Besitzer dieses Schlüssels: %s\n"
+es: "La confianza actual en el propietario de esta clave es: %s\n"
+fr: "Le niveau de confiance courant pour le propriétaire de\n\
+cette clé est: %s\n"
+muttde: "\nAktuelles 'Vertrauen' zum Besitzer dieses Schlüssels: %s\n"
+
+"Public key ring updated.\n"
+de: "\nDer öffentliche Schlüsselbund wurde aktualisiert.\n"
+es: "Actualizado el anillo de claves públicas.\n"
+fr: "Fichier de clés publiques modifié...\n"
+muttde: "\nDer öffentliche Schlüsselbund wurde aktualisiert.\n"
+
+"\nCurrent user ID: %s"
+de: "\nAktuelle Benutzer-ID: %s"
+es: "\nIdentificador actual de usuario: %s"
+fr: "\nNom d'utilisateur courant: %s"
+muttde: "\nAktuelle Benutzer-ID: %s"
+
+"\nDo you want to add a new user ID (y/N)? "
+de: "\nWillst Du eine neue Benutzer-ID hinzufügen? (j/N) "
+es: "\n¿Quieres añadir un nuevo identificador de usuario (s/N)? "
+fr: "\nVoulez-vous ajouter ce nouvel ID (o/N)?"
+muttde: "\nWillst Du eine neue Benutzer-ID hinzufügen? (j/N) "
+
+"\nEnter the new user ID: "
+de: "\nGib die neue Benutzer-ID ein: "
+es: "\nIntroduzca el nuevo identificador: "
+fr: "\nEntrez le nouveau nom d'utilisateur: "
+muttde: "\nGib die neue Benutzer-ID ein: "
+
+"\nMake this user ID the primary user ID for this key (y/N)? "
+de: "\nSoll dies die vorrangige Benutzer-ID für diesen Schlüssel werden? (j/N) "
+es: "\n¿Se establece este identificador como primario para esta clave (s/N)? "
+fr: "\nEtablir ce nom d'utilisateur comme nom principal pour cette clé \
+(o/N)? "
+muttde: "\nSoll dies die vorrangige Benutzer-ID für diesen Schlüssel werden? (j/N) "
+
+"\nDo you want to change your pass phrase (y/N)? "
+de: "\nWillst Du Dein Mantra ändern? (j/N) "
+es: "\n¿Quieres cambiar la contraseña (s/N)? "
+fr: "\nVoulez vous changer votre mot de passe (o/N)? "
+muttde: "\nWillst Du Dein Mantra ändern? (j/N) "
+
+"(No changes will be made.)\n"
+de: "\n(Es werden keine Änderungen vorgenommen.)\n"
+es: "(No se efectuará ningún cambio.)\n"
+fr: "(Aucun changement ne sera effectué.)\n"
+muttde: "\n(Es werden keine Änderungen vorgenommen.)\n"
+
+"\n\007Unable to update secret key ring.\n"
+de: "\n\007FEHLER beim Aktualisieren des privaten Schlüsselbunds.\n"
+es: "\n\007No puede actualizarse el anillo de claves secretas.\n"
+fr: "\n\007Impossible de modifier le fichier de clés secrètes.\n"
+mutt: "\nUnable to update secret key ring.\n"
+muttde: "\nFEHLER beim Aktualisieren des privaten Schlüsselbunds.\n"
+
+"\nSecret key ring updated...\n"
+de: "\n\nDer private Schlüsselbund wurde aktualisiert.\n"
+es: "\nActualizado el anillo de claves secretas ...\n"
+fr: "\nFichier de clés secrètes modifié...\n"
+muttde: "\n\nDer private Schlüsselbund wurde aktualisiert.\n"
+
+"\n\007Unable to update public key ring.\n"
+de: "\n\007FEHLER beim Aktualisieren des öffentlichen Schlüsselbunds.\n"
+es: "\n\007No puede actualizarse el anillo de claves públicas.\n"
+fr: "\n\007Impossible de modifier le fichier de clés publiques.\n"
+mutt: "\nUnable to update public key ring.\n"
+muttde: "\nFEHLER beim Aktualisieren des öffentlichen Schlüsselbunds.\n"
+
+"(No need to update public key ring)\n"
+de: "\n(Keine Änderung am öffentlichen Schlüsselbund notwendig.)\n"
+es: "(No es necesario actualizar el anillo de claves públicas)\n"
+fr: "(Pas besoin de modifier le fichier de clés publiques)\n"
+muttde: "\n(Keine Änderung am öffentlichen Schlüsselbund notwendig.)\n"
+
+"\nDo you want to permanently revoke your public key\n\
+by issuing a secret key compromise certificate\n\
+for \"%s\" (y/N)? "
+de: "\nWillst Du Deinen öffentlichen Schlüssel wirklich durch das Versenden\n\
+einer Widerrufs-Urkunde für \"%s\"\nzurückziehen, d.h. für ungültig erklären? (j/N) "
+es: "\n¿Quieres revocar permanentemente tu clave pública\n\
+emitiendo un certificado de compromiso de clave secreta\n\
+para \"%s\" (s/N)? "
+fr: "\nVoulez vous révoquer de façon permanente votre clé publique\n\
+en émettant un certificat de compromission de clé secrète\n\
+pour \"%s\" (o/N)? "
+muttde: "\nWillst Du Deinen öffentlichen Schlüssel wirklich durch das Versenden\n\
+einer Widerrufs-Urkunde für \"%s\"\nzurückziehen, d.h. für ungültig erklären? (j/N) "
+
+"You can only disable keys on your public keyring.\n"
+de: "Du kannst nur Schlüssel aus Deinem öffentlichen Schlüsselbund sperren.\n"
+es: "Sólo puedes desactivar claves en el anillo de claves públicas.\n"
+fr: "Vous ne pouvez inactiver des clés que sur votre fichier de clés\
+\npubliques.\n"
+muttde: "Du kannst nur Schlüssel aus Deinem öffentlichen Schlüsselbund sperren.\n"
+
+"\nKey is already disabled.\n\
+Do you want to enable this key again (y/N)? "
+de: "\nDieser Schlüssel ist schon gesperrt.\nMöchtest Du ihn wieder freigeben? (j/N) "
+es: "\nLa clave ya está desactivada.\n\
+¿Quieres activarla otra vez (s/N)? "
+fr: "\nLa clé est déjà inactivée.\n\
+Voulez vous réactiver cette clé (o/N)? "
+muttde: "\nDieser Schlüssel ist schon gesperrt.\nMöchtest Du ihn wieder freigeben? (j/N) "
+
+"\nDisable this key (y/N)? "
+de: "\nSoll dieser Schlüssel gesperrt werden? (j/N) "
+es: "\n¿Desactivar esta clave (s/N)? "
+fr: "Désactiver cette clé (o/N)? "
+muttde: "\nSoll dieser Schlüssel gesperrt werden? (j/N) "
+
+"Pick your RSA key size:\n\
+ 1) 1024 bits- User grade, fast but less secure\n\
+ 2) 1535 bits- Regional CA grade, medium speed, good security\n\
+ 3) 2048 bits- Root CA grade, slow, high security\n\
+Choose 1, 2, or 3, or enter desired number of bits (384...8192): "
+de: "Wähle die Länge Deines RSA-Schlüssels aus:\
+\n 1) 1024 Bits: für Nutzer: schnell, aber nicht ganz so sicher\
+\n 2) 1535 Bits: für regionale CAs: mittelmäßig schnell, recht sicher\
+\n 3) 2048 Bits: für Root CAs: langsam, jedoch sehr sicher\
+\nAuswahl (1, 2, 3 oder die Länge des Schlüssels in Bits (384...8192)): "
+es: "Elija un tamaño de clave RSA:\n\
+ 1) 1024 bits- Nivel comercial bajo, r\0341pido pero menos seguro\n\
+ 2) 1535 bits- Nivel comercial alto, velocidad media con buena seguridad\n\
+ 3) 2048 bits- Nivel \"militar\", lento con alta seguridad\n\
+Escoge 1, 2, 3, o el número requerido de bits (384...8192): "
+fr: "Choisissez la taille de votre clef RSA:\n\
+ 1) 1024 bits- Niveau de base, rapide mais moins securitaire\n\
+ 2) 1535 bits- Niveau de securité eleve - vitesse moyenne \n\
+ 3) 2048 bits- Pour les militaires, les diplomates... les paranoiaques \n\
+Choisissez 1, 2, ou 3, ou entrez le nombre de bits desires (384...8192): "
+muttde: "Wähle die Länge Deines RSA-Schlüssels aus:\
+\n 1) 1024 Bits: für Nutzer: schnell, aber nicht ganz so sicher\
+\n 2) 1535 Bits: für regionale CAs: mittelmäßig schnell, recht sicher\
+\n 3) 2048 Bits: für Root CAs: langsam, jedoch sehr sicher\
+\nAuswahl (1, 2, 3 oder die Länge des Schlüssels in Bits (384...8192)): "
+
+"Generating an RSA key with a %d-bit modulus.\n"
+de: "Erzeugung eines RSA-Schlüssels mit einem %d-Bit-Modulus.\n"
+es: "Generando una clave RSA con módulo de %d bits.\n"
+fr: "Generation d'une clé RSA avec un module de %d bits.\n"
+muttde: "Erzeugung eines RSA-Schlüssels mit einem %d-Bit-Modulus.\n"
+
+"\nYou need a user ID for your public key. The desired form for this\n\
+user ID is your name, followed by your E-mail address enclosed in\n\
+<angle brackets>, if you have an E-mail address.\n\
+Form: Real Name (comment) <email> (options)\n\
+ Optional options: ENCR, SIGN, EXPIRE:yyyy-mm-dd\n\
+Enter a user ID for your public key: \n"
+de: "\nDu brauchst eine Benutzer-ID für Deinen öffentlichen Schlüssel. Das übliche\n\
+Format für diese Benutzer-ID ist Dein Realname, gefolgt von Deinem Usernamen\n\
+in <spitzen Klammern>, falls Du per E-Mail erreichbar bist.\n\
+Format: Bürgerlicher Name (Kommentar) <email> (Optionen)\n\
+ Freiwillige Optionen: ENCR, SIGN, EXPIRE:yyyy-mm-dd\n\
+Gib die Benutzer-ID für Deinen öffentlichen Schlüssel ein:\n"
+es: "\nNecesitas un identificador para tu clave pública. El formato preferido\n\
+consiste en tu nombre, seguido de tu dirección de correo electrónico,\n\
+si tienes, entre <ángulos>.\n\
+Form: Real Name (comment) <email> (options)\n\
+ Optional options: ENCR, SIGN, EXPIRE:yyyy-mm-dd\n\
+Introduce un identificador de usuario para tu clave pública: \n"
+fr: "\nIl vous faut un nom d'utilisateur pour votre clé publique. La forme\n\
+désirée pour ce nom d'utilisateur est votre nom, suivi de votre addresse\n\
+de courrier électronique entre <crochets>, si vous en avez une.\n\
+Form: Real Name (comment) <email> (options)\n\
+ Optional options: ENCR, SIGN, EXPIRE:yyyy-mm-dd\n\
+Entrez un nom d'utilisateur pour votre clé publique\n\
+(votre nom): "
+muttde: "\nDu brauchst eine Benutzer-ID für Deinen öffentlichen Schlüssel. Das übliche\n\
+Format für diese Benutzer-ID ist Dein Realname, gefolgt von Deinem Usernamen\n\
+in <spitzen Klammern>, falls Du per E-Mail erreichbar bist.\n\
+Format: Bürgerlicher Name (Kommentar) <email> (Optionen)\n\
+ Freiwillige Optionen: ENCR, SIGN, EXPIRE:yyyy-mm-dd\n\
+Gib die Benutzer-ID für Deinen öffentlichen Schlüssel ein:\n"
+
+"Generating RSA key-pair with UserID \"%s\".\n"
+de: "Erzeugung eines RSA-Schlüsselpaares mit der\nBenutzer-ID \"%s\".\n"
+es: "Generando el par de claves RSA con identificador \"%s\".\n"
+fr: "Génération d'une paire de clefs RSA de l'utilisateur '%s'.\n"
+muttde: "Erzeugung eines RSA-Schlüsselpaares mit der\nBenutzer-ID \"%s\".\n"
+
+"\nYou need a pass phrase to protect your RSA secret key.\n\
+Your pass phrase can be any sentence or phrase and may have many\n\
+words, spaces, punctuation, or any other printable characters.\n"
+de: "\nDu brauchst ein Mantra, um Deinen privaten RSA-Schlüssel zu schützen.\n\
+Dein Mantra kann jeder beliebige Satz oder Zeichenfolge sein und darf aus\n\
+vielen Worten, Leerzeichen oder anderen druckbaren Zeichen bestehen.\n"
+es: "\nNecesitas una contraseña para proteger tu clave secreta RSA.\n\
+Puede ser cualquier expresión formada por varias palabras, espacios,\n\
+signos de puntuación o cualquier otro carácter imprimible.\n"
+fr: "\nVous devez avoir un mot de passe pour protéger votre clé RSA \n\
+secrète. Votre mot de passe peut être n'importe quelle phrase ou portion\n\
+de phrase et peut avoir plusieurs mots, espaces, caractères de ponctuation\n\
+ou tout autre caractère imprimable.\n"
+muttde: "\nDu brauchst ein Mantra, um Deinen privaten RSA-Schlüssel zu schützen.\n\
+Dein Mantra kann jeder beliebige Satz oder Zeichenfolge sein und darf aus\n\
+vielen Worten, Leerzeichen oder anderen druckbaren Zeichen bestehen.\n"
+
+"\nNote that key generation is a lengthy process.\n"
+de: "\n\nBeachte, daß die Schlüsselerzeugung eine zeitaufwendige Sache ist.\n"
+es: "\nTen en cuenta que la generación de claves es un proceso lento.\n"
+fr: "\nNotez que la génération de clé est une procédure lente.\n"
+muttde: "\n\nBeachte, daß die Schlüsselerzeugung eine zeitaufwendige Sache ist.\n"
+
+"Key generation stopped at user request.\n"
+de: "Die Schlüsselerzeugung wurde durch den Benutzer abgebrochen!\n"
+es: "Generación de claves interrumpida a petición del usuario.\n"
+fr: "Le génération de la clef a été stoppée à la demande de l'utilisateur.\n"
+muttde: "Die Schlüsselerzeugung wurde durch den Benutzer abgebrochen!\n"
+
+"\n\007Keygen failed!\n"
+de: "\n\007FEHLER bei der Schlüssel-Erzeugung!\n"
+es: "\n\007Error en la generación de claves\n"
+fr: "\n\007Generation de clé non réussie!\n"
+mutt: "\nKeygen failed!\n"
+muttde: "\nFEHLER bei der Schlüssel-Erzeugung!\n"
+
+"Key ID %s\n"
+de: "Schlüssel-ID: %s\n"
+es: "Identificador de clave %s\n"
+fr: "Identificateur de clé %s\n"
+muttde: "Schlüssel-ID: %s\n"
+
+"Display secret components (y/N)?"
+de: "Geheime Bestandteile anzeigen? (j/N) "
+es: "¿Mostrar los componentes secretos (s/N)?"
+fr: "Affichier les composantes secretes (o/N)?"
+muttde: "Geheime Bestandteile anzeigen? (j/N) "
+
+"\007Key generation completed.\n"
+de: "\n\007Die Erzeugung des Schlüssels ist beendet.\n"
+es: "\007Finalizada la generación de claves.\n"
+fr: "\007Génération de clé terminée.\n"
+mutt: "Key generation completed.\n"
+muttde: "\nDie Erzeugung des Schlüssels ist beendet.\n"
+
+"Type Bits/KeyID Date User ID\n"
+de: "Typ Bits/KeyID Datum NutzerID\n"
+es: "Tipo Bits/Clave Fecha Identificador\n"
+fr: "Type Bits/Clef Date ID utilisateur\n"
+muttde: "Typ Bits/KeyID Datum NutzerID\n"
+
+"\n\007File '%s' is not a text file; cannot display.\n"
+de: "\n\007Die Datei '%s' ist keine Textdatei\n\
+und kann deshalb nicht angezeigt werden!\n"
+es: "\n\007El fichero '%s' no es de texto; no puede mostrarse.\n"
+fr: "\n\007Le fichier '%s' n'est pas un fichier de texte et ne peut être \
+visualisé\n"
+mutt: "\nFile '%s' is not a text file; cannot display.\n"
+muttde: "\nDie Datei '%s' ist keine Textdatei\n\
+und kann deshalb nicht angezeigt werden!\n"
+
+"\nDone...hit any key\r"
+de: "\nFertig... Bitte drücke eine Taste.\r"
+es: "\nFinalizado...pulse cualquier tecla\r"
+fr: "\nTerminé... appuyez sur n'importe quelle touche \r"
+muttde: "\nFertig... Bitte drücke eine Taste.\r"
+
+"-- More -- Space: next screen, Enter: next line\
+, 'B': back, 'Q': quit --\r"
+de: "-- Weiter -- Vorwärts: Leertaste oder Return; Rückwärts: 'B'; Ende: 'Q' --\r"
+es: "-- más -- espacio: otra pantalla, enter: otra lÃnea,\
+ B: atrás, Q: salir\r"
+fr: "- Plus - Espace: procahin écran , Chariot: prochaine ligne\
+, 'B': retour, 'Q': quitter - \r"
+muttde: "-- Weiter -- Vorwärts: Leertaste oder Return; Rückwärts: 'B'; Ende: 'Q' --\r"
+
+"More -- %d%% -- Space: next screen, Enter: next line\
+, 'B': back, 'Q': quit --\r"
+de: "Weiter -- %d%% -- Vorwärts: Leertaste oder Return; Rückwärts: 'B'; Ende: 'Q' --\r"
+es: "Más -- %d%% -- espacio: otra pantalla, enter: otra lÃnea,\
+ B: atrás, Q: salir\r"
+fr: "- Plus - %D%% - Espace: procahin écran , Chariot: prochaine ligne\
+, 'B': retour, 'Q': quitter - \r"
+muttde: "Weiter -- %d%% -- Vorwärts: Leertaste oder Return; Rückwärts: 'B'; Ende: 'Q' --\r"
+
+"\nEnter pass phrase: "
+de: "\nGib das Mantra ein: "
+es: "\nIntroduce la contraseña: "
+fr: "\nEntrez votre mot de passe: "
+muttde: "\nGib das Mantra ein: "
+
+"\nEnter same pass phrase again: "
+de: "\nWiederhole das Mantra: "
+es: "\nEscrÃbela otra vez: "
+fr: "\nEntrez le même mot de passe de nouveau: "
+muttde: "\nWiederhole das Mantra: "
+
+"\n\007Error: Pass phrases were different. Try again."
+de: "\n\007FEHLER: Die beiden Eingaben waren unterschiedlich! Bitte noch einmal."
+es: "\n\007Error: Las contraseñas son diferentes. Prueba otra vez."
+fr: "\n\007Erreur: Les mots de passe étaient différents. Essayez encore."
+mutt: "\nError: Pass phrases were different. Try again."
+muttde: "\nFEHLER: Die beiden Eingaben waren unterschiedlich! Bitte noch einmal."
+
+"\nStopped at user request\n"
+de: "\nAbbruch durch Benutzer\n"
+es: "\nInterrupción por petición del usuario\n"
+fr: "\nArrêt par demande de l'utilisateur\n"
+muttde: "\nAbbruch durch Benutzer\n"
+
+"Pretty Good Privacy(tm) %s - Public-key encryption for the masses.\n"
+de: "Pretty Good Privacy(tm) %s - Public-key-Verschlüsselung für die Massen.\n"
+es: "Pretty Good Privacy(tm) %s - CriptografÃa de clave pública para todos.\n"
+fr: "Pretty Good Privacy(tm) %s - Cryptographie à clé publique pour tous.\n"
+muttde: "Pretty Good Privacy(tm) %s - Public-key-Verschlüsselung für die Massen.\n"
+
+"(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software."
+de: "(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software."
+es: "(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software."
+fr: "( c ) 1990-96 Philip Zimmermann, Phil's Pretty Good Software. "
+muttde: "(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software."
+
+"Export of this software may be restricted by the U.S. government.\n"
+de: "Der Export dieser Software aus den USA kann Beschränkungen unterliegen.\n"
+es: "La exportación de este programa puede estar restringida por\n\
+el gobierno de los EE.UU."
+fr: "L'exportation de ce logiciel peut être restreint par le \
+gouvernement des États-Unis"
+muttde: "Der Export dieser Software aus den USA kann Beschränkungen unterliegen.\n"
+
+"International version - not for use in the USA. Does not use RSAREF.\n"
+de: "Internationale Version - nicht in den USA verwenden! Benutzt nicht RSAREF.\n"
+es: "Versión internacional - no apta para los EE.UU. No utiliza RSAREF.\n"
+fr: "Version internationale - ne pas utiliser aux Etats-Unis. N'utilise pas le
+RSAREF.\n"
+muttde: "Internationale Version - nicht in den USA verwenden! Benutzt nicht RSAREF.\n"
+
+"Current time: %s\n"
+de: "Aktuelles Datum und Uhrzeit: %s\n"
+es: "Hora actual: %s\n"
+fr: "Heure actuelle: %s\n"
+muttde: "Aktuelles Datum und Uhrzeit: %s\n"
+
+"\007No configuration file found.\n"
+de: "\007Keine Konfigurationsdatei gefunden!\n"
+es: "\007No se encuentra el fichero de configuración.\n"
+fr: "\007Fichier de configuration introuvable.\n"
+mutt: "No configuration file found.\n"
+muttde: "Keine Konfigurationsdatei gefunden!\n"
+
+"\007WARNING: Environmental variable TZ is not \
+defined, so GMT timestamps\n\
+may be wrong. See the PGP User's Guide to properly define TZ\n\
+in AUTOEXEC.BAT file.\n"
+de: "\007WARNUNG: Die Umgebungsvariable TZ ist nicht definiert, daher könnten\n\
+die GMT-Zeitangaben falsch sein. Beachte den Abschnitt in der PGP-Anleitung\n\
+über das richtige Setzen von TZ in AUTOEXEC.BAT.\n\n"
+es: "\007ADVERTENCIA: La variable TZ no está definida, por lo que\n\
+los sellos de fecha GMT pueden estar equivocados. Consulta la GuÃa del\n\
+usuario de PGP para definir adecuadamente TZ en AUTOEXEC.BAT.\n"
+fr: "\007ATTENTION: La variable d'environnement TZ n'est pas définie, les\n\
+temps GMT peuvent donc êtres faussés. Voir le guide de l'utilisateur PGP pour\n\
+définir correctement TZ dans le fichier AUTOEXEC.BAT.\n"
+mutt: "WARNING: Environmental variable TZ is not \
+defined, so GMT timestamps\n\
+may be wrong. See the PGP User's Guide to properly define TZ\n\
+in AUTOEXEC.BAT file.\n"
+muttde: "WARNUNG: Die Umgebungsvariable TZ ist nicht definiert, daher könnten\n\
+die GMT-Zeitangaben falsch sein. Beachte den Abschnitt in der PGP-Anleitung\n\
+über das richtige Setzen von TZ in AUTOEXEC.BAT.\n\n"
+
+"\nFile %s wiped and deleted. "
+de: "\nDie Datei '%s' wurde überschrieben und gelöscht."
+es: "\nEl fichero %s ha sido borrado y destruido. "
+fr: "\nFichier %s effacé et détruit. "
+muttde: "\nDie Datei '%s' wurde überschrieben und gelöscht."
+
+"\n\007Error: Can't wipe out file '%s' - read only, maybe?\n"
+de: "\n\007FEHLER: Die Datei '%s' kann nicht überschrieben werden.\n\
+Ist sie vielleicht schreibgeschützt?\n"
+es: "\n\007Error: No puede eliminarse el fichero '%s' - \
+quizá sea sólo de lectura\n"
+fr: "\n\007Erreur: Incapable de détruire le fichier '%s' - lectu4re seulement peut être?\n"
+mutt: "\nError: Can't wipe out file '%s' - read only, maybe?\n"
+muttde: "\nFEHLER: Die Datei '%s' kann nicht überschrieben werden.\n\
+Ist sie vielleicht schreibgeschützt?\n"
+
+"\n\007File '%s' does not exist.\n"
+de: "\n\007Die Datei '%s' existiert nicht!\n"
+es: "\n\007El fichero '%s' no existe.\n"
+fr: "Le fichier '%s' n'existe pas. \n"
+mutt: "\nFile '%s' does not exist.\n"
+muttde: "\nDie Datei '%s' existiert nicht!\n"
+
+"\nFor details on licensing and distribution, see the PGP User's Guide.\
+\nFor other cryptography products and custom development services, contact:\
+\nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, \
+phone +1 303 541-0140\n"
+de: "\nInformationen über Lizenzen und Verteilung finden sich in der PGP-Anleitung.\
+\nInformationen über Verschlüsselungs-Produkte und Auftrags-Entwicklungen:\
+\nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, Tel. +1-(303)-541-0140\n"
+es: "\nInformación sobre licencia y distribución en la GuÃa del usuario de PGP.\
+\nMás información sobre otros productos y servicios criptográficos a medida:\
+\nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, tel +1 303 541-0140\n"
+fr: "\nConsulter le guide de l'utilisateur de PGP pour les détails de\n\
+license et de distribution. Pour d'autres produits de cryptographie\n\
+et services de développement personalisés, contacter: Philip Zimmermann,\n\
+3021 11th St, Boulder CO 80304 USA, téléphone +1 303 541-0140\n"
+muttde: "\nInformationen über Lizenzen und Verteilung finden sich in der PGP-Anleitung.\
+\nInformationen über Verschlüsselungs-Produkte und Auftrags-Entwicklungen:\
+\nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, Tel. +1-(303)-541-0140\n"
+
+"@translator@"
+de: "\nÜbersetzer:\
+\n Frank Pruefer <F.PRUEFER@LINK-L.cl.sub.de>; Stand: 07.10.1997\
+\n (basierend auf der deutschen Übersetzung der LANGUAGE.TXT von\
+\n Marc Aurel <4-tea-2@bong.saar.de> vom 19.01.1994)\n"
+es: "\nTraducido al castellano por Armando Ramos <armando@clerval.org>.\n"
+fr: "\nTraduction française de Jean-loup Gailly <jloup@chorus.fr> et Yanik \
+Crépeau <yanik@mlink.net>\n"
+muttde: "\nÜbersetzer:\
+\n Frank Pruefer <F.PRUEFER@LINK-L.cl.sub.de>; Stand: 07.10.1997\
+\n (basierend auf der deutschen Übersetzung der LANGUAGE.TXT von\
+\n Marc Aurel <4-tea-2@bong.saar.de> vom 19.01.1994)\n"
+
+"\nFor a usage summary, type: pgp -h\n"
+de: "\nEine Übersicht der PGP-Befehle erhältst Du mit: pgp -h\n"
+es: "\nPara ver un resumen de las instrucciones, escribe: pgp -h\n"
+fr: "\nPour un sommaire d'utilisation, tapez: pgp -h\n"
+mutt: " "
+muttde: " "
+
+"File %s created containing %d random bytes.\n"
+de: "\nDie Datei '%s', die %d Bytes Zufallszahlen enthält,\n\
+wurde erzeugt.\n"
+es: "Generado el fichero %s con %d bytes aleatorios.\n"
+fr: "Le fichier %s est créé et contient %d octets aléatoires"
+muttde: "\nDie Datei '%s', die %d Bytes Zufallszahlen enthält,\n\
+wurde erzeugt.\n"
+
+"\007Invalid filename: '%s' too long\n"
+de: "\007Ungültiger Dateiname: '%s' ist zu lang.\n"
+es: "\007Nombre incorrecto: '%s' es demasiado largo\n"
+fr: "\007Nom incalide: '%s' trop long\n"
+mutt: "Invalid filename: '%s' too long\n"
+muttde: "Ungültiger Dateiname: '%s' ist zu lang.\n"
+
+"\n\007Input file '%s' looks like it may have been created by PGP. "
+de: "\n\007Die Eingabedatei '%s' könnte von PGP erzeugt worden sein."
+es: "\n\007El fichero de entrada '%s' parece haber sido creado por PGP. "
+fr: "\n\007Le fichier d'entrée '%s' semble avoir été créé par PGP. "
+mutt: "\nInput file '%s' looks like it may have been created by PGP. "
+muttde: "\nDie Eingabedatei '%s' könnte von PGP erzeugt worden sein."
+
+"\nIs it safe to assume that it was created by PGP (y/N)? "
+de: "\nWurde diese von PGP erzeugt? (j/N) "
+es: "\n¿Puede asumirse con seguridad que ha sido asà (s/N)? "
+fr: "\nEtes vous sûr qu'il a été créé par PGP (o/N)? "
+muttde: "\nWurde diese von PGP erzeugt? (j/N) "
+
+"\nNote: '%s' is not a pure text file.\n\
+File will be treated as binary data.\n"
+de: "\nHinweis: '%s' ist keine reine Textdatei.\n\
+Die Datei wird als Binärdatei behandelt.\n"
+es: "\nNota: '%s' no es un fichero de texto puro.\n\
+Se tratará como datos binarios.\n"
+fr: "Note: '%s' n'est pas un fichier texte. \n\
+Il sera traité comme données binaires"
+muttde: "\nHinweis: '%s' ist keine reine Textdatei.\n\
+Die Datei wird als Binärdatei behandelt.\n"
+
+"\n\007Error: Only text files may be sent as display-only.\n"
+de: "\n\007FEHLER: Nur Textdateien können \"nur zur Ansicht\" verschickt werden.\n"
+es: "\n\007Error: Sólo los ficheros de texto pueden enviarse para mostrar.\n"
+fr: "\n\007Erreur: seuls les fichiers de texte peuvent être envoyés\n\
+pour affichage exclusivement.\n"
+mutt: "\nError: Only text files may be sent as display-only.\n"
+muttde: "\nFEHLER: Nur Textdateien können \"nur zur Ansicht\" verschickt werden.\n"
+
+"\n\007Error: MacBinary failed!\n"
+de: "\n\007FEHLER: MacBinary fehlgeschlagen!\n"
+es: "\n\007Error: ha fallado MacBinary\n"
+fr:"\n\007Erreur:MacBinary a échoué\n"
+mutt: "\nError: MacBinary failed!\n"
+muttde: "\nFEHLER: MacBinary fehlgeschlagen!\n"
+
+"\nA secret key is required to make a signature. "
+de: "\nFür eine Unterschrift wird ein privater Schlüssel benötigt."
+es: "\nSe necesita una clave secreta para generar la firma. "
+fr: "\nUne clé secrète est nécessaire pour faire une signature. "
+muttde: "\nFür eine Unterschrift wird ein privater Schlüssel benötigt."
+
+"\nYou specified no user ID to select your secret key,\n\
+so the default user ID and key will be the most recently\n\
+added key on your secret keyring.\n"
+de: "\nDa Du keine Benutzer-ID für Deinen privaten Schlüssel angegeben hast,\n\
+wird der letzte zum privaten Schlüsselbund hinzugefügte Schlüssel benutzt.\n"
+es: "\nNo has indicado ningún identificador para escoger la clave secreta,\n\
+por lo que el identificador y la clave por omisión serán los últimos\n\
+añadidos al anillo.\n"
+fr: "\nVous n'avez pas spécifié de nom d'utilisateur pour sélectionner\n\
+votre clé secrète, donc le nom et la clé par défaut seront ceux les\n\
+plus récemment ajoutés à votre fichier de clés secrètes.\n"
+muttde: "\nDa Du keine Benutzer-ID für Deinen privaten Schlüssel angegeben hast,\n\
+wird der letzte zum privaten Schlüsselbund hinzugefügte Schlüssel benutzt.\n"
+
+"\007Signature error\n"
+de: "\n\007FEHLER beim Unterschreiben!\n"
+es: "\007Error de firma\n"
+fr: "\007Erreur de signature\n"
+mutt: "Signature error\n"
+muttde: "\nFEHLER beim Unterschreiben!\n"
+
+"\n\nRecipients' public key(s) will be used to encrypt. "
+de: "\n\nVerschlüsselung mit Empfänger-Schlüssel(n).\n"
+es: "\n\nSe utilizan las claves públicas de los destinatarios para encriptar. "
+fr: "\n\nLa ou les clé(s) publique(s) du destinataire seront utilisées\
+ pour chiffrer. "
+muttde: "\n\nVerschlüsselung mit Empfänger-Schlüssel(n).\n"
+
+"\nA user ID is required to select the recipient's public key. "
+de: "\nZur Auswahl des Empfänger-Schlüssels wird eine Benutzer-ID benötigt."
+es: "\nSe necesita un identificador para encontrar la clave pública\n\
+del destinatario. "
+fr: "\nUn nom d'utilisateur est nécessaire pour sélectionner la clé\n\
+publique du destinataire. "
+muttde: "\nZur Auswahl des Empfänger-Schlüssels wird eine Benutzer-ID benötigt."
+
+"\nEnter the recipient's user ID: "
+de: "\nBenutzer-ID des Empfängers: "
+es: "\nIntroduzca el identificador del destinatario: "
+fr: "\nEntrez le nom d'utilisateur du destinataire: "
+muttde: "\nBenutzer-ID des Empfängers: "
+
+"\007Encryption error\n"
+de: "\n\007FEHLER beim Verschlüsseln!\n"
+es: "\007Error en la encriptación\n"
+fr: "\007Erreur de chiffrage\n"
+mutt: "Encryption error\n"
+muttde: "\nFEHLER beim Verschlüsseln!\n"
+
+"\nCiphertext file: %s\n"
+de: "\nVerschlüsselte Datei: %s\n"
+es: "\nFichero cifrado: %s\n"
+fr: "\nFichier chiffré: %s\n"
+muttde: "\nVerschlüsselte Datei: %s\n"
+
+"\nSignature file: %s\n"
+de: "\nUnterschriftsdatei: %s\n"
+es: "\nFichero de firma: %s\n"
+fr: "\nFichier de signature: %s\n"
+muttde: "\nUnterschriftsdatei: %s\n"
+
+"\n\007Error: Transport armor stripping failed for file %s\n"
+de: "\n\007FEHLER beim Entfernen der Versandhülle von Datei '%s'.\n"
+es: "\n\007Error: No se ha podido quitar la armadura de %s\n"
+fr: "\n\007Erreur dans la suppression de la protection de transport pour\n\
+le fichier %s\n"
+mutt: "\nError: Transport armor stripping failed for file %s\n"
+muttde: "\nFEHLER beim Entfernen der Versandhülle von Datei '%s'.\n"
+
+"Stripped transport armor from '%s', producing '%s'.\n"
+de: "\nDie Versandhülle von Datei '%s' wurde entfernt.\nAusgabedatei: %s\n"
+es: "Quitada la armadura de '%s', produciendo '%s'.\n"
+fr: "Protection de transport supprimée pour '%s', produisant '%s'.\n"
+muttde: "\nDie Versandhülle von Datei '%s' wurde entfernt.\nAusgabedatei: %s\n"
+
+"\nLooking for next packet in '%s'...\n"
+de: "\nSuche nach dem nächsten Paket in '%s'...\n"
+es: "\nBuscando el siguiente paquete en '%s'...\n"
+fr: "\nRecherche du prochain paquet dans '%s'...\n"
+muttde: "\nSuche nach dem nächsten Paket in '%s'...\n"
+
+"\nFile is encrypted. Secret key is required to read it. "
+de: "\nDie Datei ist verschlüsselt. Zum Lesen wird der private Schlüssel benötigt.\n"
+es: "\nEl fichero está encriptado. Para leerlo se necesita la clave secreta. "
+fr: "\nLe fichier est chiffré. La clé secrète est nécessaire pour le lire."
+muttde: "\nDie Datei ist verschlüsselt. Zum Lesen wird der private Schlüssel benötigt.\n"
+
+"\nThis file has a signature, which will be left in place.\n"
+de: "\nDiese Datei trägt eine Unterschrift, die nicht entfernt wird.\n"
+es: "\nEste fichero tiene firma, que se deja en su sitio.\n"
+fr: "\nCe fichier a une signature, qui sera gardée.\n"
+muttde: "\nDiese Datei trägt eine Unterschrift, die nicht entfernt wird.\n"
+
+"\nFile has signature. Public key is required to check signature.\n"
+de: "\nDiese Datei trägt eine Unterschrift.\n\
+Zur Überprüfung wird der öffentliche Schlüssel benötigt.\n"
+es: "\nEl fichero tiene firma. Se necesita la clave pública para comprobarla.\n"
+fr: "Ce fichier est signé. Une clef publique est nécessaire pour sa vérification.\n"
+muttde: "\nDiese Datei trägt eine Unterschrift.\n\
+Zur Überprüfung wird der öffentliche Schlüssel benötigt.\n"
+
+"\nFile is conventionally encrypted. "
+de: "\nDiese Datei ist konventionell verschlüsselt.\n"
+es: "\nEl fichero ha sido encriptado convencionalmente. "
+fr: "\nLe fichier est chiffré de manière conventionnelle. "
+muttde: "\nDiese Datei ist konventionell verschlüsselt.\n"
+
+"\nFile contains key(s). Contents follow..."
+de: "\nDiese Datei enthält einen oder mehrere Schlüssel. Hier kommt die Liste:\n"
+es: "\nEl fichero contiene claves. Se muestran a continuación ..."
+fr: "\nLe fichier contient une ou plusieurs clés. Le contenu suit..."
+muttde: "\nDiese Datei enthält einen oder mehrere Schlüssel. Hier kommt die Liste:\n"
+
+"\nDo you want to add this keyfile to keyring '%s' (y/N)? "
+de: "\nWillst Du die Schlüssel dieser Datei zum Schlüsselbund\n'%s' hinzufügen? (j/N) "
+es: "\n¿Quieres añadir este fichero de claves al anillo '%s' (s/N)? "
+fr: "\nVoulez vous ajouter ce fichier de clé au fichier '%s' (o/N)? "
+muttde: "\nWillst Du die Schlüssel dieser Datei zum Schlüsselbund\n'%s' hinzufügen? (j/N) "
+
+"\007Keyring add error. "
+de: "\n\007FEHLER beim Hinzufügen zum Schlüsselbund.\n"
+es: "\007Error al añadir en el anillo. "
+fr: "\007Erreur dans l'addition au fichier de clés. "
+mutt: "Keyring add error. "
+muttde: "\nFEHLER beim Hinzufügen zum Schlüsselbund.\n"
+
+"\007\nError: '%s' is not a ciphertext, signature, or key file.\n"
+de: "\007\nFEHLER: Die Datei '%s' ist nicht verschlüsselt\n\
+und enthält weder eine Unterschrift noch einen oder mehrere Schlüssel.\n"
+es: "\007\nError: '%s' no es un texto cifrado, una firma ni una clave.\n"
+fr: "\007\nErreur: '%s' n'est pas un fichier chiffré, de signatures\
+\nou de clés.\n"
+mutt: "\nError: '%s' is not a ciphertext, signature, or key file.\n"
+muttde: "\nFEHLER: Die Datei '%s' ist nicht verschlüsselt\n\
+und enthält weder eine Unterschrift noch einen oder mehrere Schlüssel.\n"
+
+"\n\nThis message is marked \"For your eyes only\". Display now \
+(Y/n)? "
+de: "\n\nDiese Nachricht ist als VERTRAULICH markiert! Jetzt anzeigen? (J/n) "
+es: "\n\nEste mensaje está marcado como \"Sólo para tus ojos\".\
+¿Mostrar ahora (S/n)? "
+fr: "\n\nCe message est marqué \"Pour vos yeux seulement\".\n\
+Afficher maintenant (O/n)? "
+muttde: "\n\nDiese Nachricht ist als VERTRAULICH markiert! Jetzt anzeigen? (J/n) "
+
+"\n\nPlaintext message follows...\n"
+de: "\n\nHier kommt die Nachricht im Klartext:\n"
+es: "\n\nMensaje en claro a continuación...\n"
+fr: "\n\nLe message en clair suit...\n"
+muttde: "\n\nHier kommt die Nachricht im Klartext:\n"
+
+"Save this file permanently (y/N)? "
+de: "Klartext sichern? (j/N) "
+es: "¿Grabar este fichero de forma permanente (s/N)? "
+fr: "Sauvegarde de ce fichier de manière permanente (o/N)? "
+muttde: "Klartext sichern? (j/N) "
+
+"Enter filename to save file as: "
+de: "Klartext sichern als: "
+es: "Introduzca el nombre para el fichero: "
+fr: "Entrez le nom du fichier de sauvegarde: "
+muttde: "Klartext sichern als: "
+
+"Enter filename to save file as:"
+de: "Gib den Namen ein, unter dem die Datei zu sichern ist:"
+es: "Introduzca el nombre para el fichero:"
+fr: "Donner le nom de fichier pour sauvegarder sous:"
+muttde: "Gib den Namen ein, unter dem die Datei zu sichern ist:"
+
+"\nPlaintext filename: %s"
+de: "\nDateiname des Klartextes: %s"
+es: "\nNombre del fichero normal: %s"
+fr: "\nNom du fichier en clair: %s"
+muttde: "\nDateiname des Klartextes: %s"
+
+"\nPlaintext file '%s' looks like it contains a public key."
+de: "\nDie Klartextdatei '%s' scheint einen\nöffentlichen Schlüssel zu enthalten."
+es: "\nEl fichero normal '%s' parece contener una clave pública."
+fr: "\nLe ficher en clair '%s' semble contenir une clé publique."
+muttde: "\nDie Klartextdatei '%s' scheint einen\nöffentlichen Schlüssel zu enthalten."
+
+"\nPlaintext file '%s' looks like a %s file."
+de: "\nDie Klartextdatei '%s' scheint eine %s-Datei zu sein."
+es: "\nEl fichero normal '%s' parece un fichero %s."
+fr: "\nLe fichier en clair '%s' semble être un fichier %s."
+muttde: "\nDie Klartextdatei '%s' scheint eine %s-Datei zu sein."
+
+"\n\007Output file '%s' may contain more ciphertext or signature."
+de: "\n\007Die Ausgabedatei '%s' könnte weiteren\n\
+verschlüsselten Text oder eine Unterschrift enthalten."
+es: "\n\007El fichero de salida '%s' puede contener más texto \
+cifrado o una firma."
+fr: "\n\007Le fichier de sortie '%s' peut contenir d'autre texte chiffré\n\
+ou signature."
+mutt: "\nOutput file '%s' may contain more ciphertext or signature."
+muttde: "\nDie Ausgabedatei '%s' könnte weiteren\n\
+verschlüsselten Text oder eine Unterschrift enthalten."
+
+"\a\nError: PGP User's Guide not found.\n\
+PGP looked for it in the following directories:\n"
+de: "\a\nFEHLER: PGP-Benutzerhandbuch nicht gefunden!\n\
+PGP hat danach in den folgenden Verzeichnissen gesucht:\n"
+es: "\a\nError: No se encuentra la GuÃa del usuario.\n\
+Se ha buscado en estos directorios:\n"
+fr: "\n\aErreur: Le manuel d'utilisation de PGP est introuvable.\n\
+PGP a examiné les repertoires suivants:\n"
+muttde: "\a\nFEHLER: PGP-Benutzerhandbuch nicht gefunden!\n\
+PGP hat danach in den folgenden Verzeichnissen gesucht:\n"
+
+"and the doc subdirectory of each of the above. Please put a copy of\n\
+both volumes of the User's Guide in one of these directories.\n\
+\n\
+Under NO CIRCUMSTANCES should PGP ever be distributed without the PGP\n\
+User's Guide, which is included in the standard distribution package.\n\
+If you got a copy of PGP without the manual, please inform whomever you\n\
+got it from that this is an incomplete package that should not be\n\
+distributed further.\n\
+\n\
+PGP will not generate a key without finding the User's Guide.\n\
+There is a simple way to override this restriction. See the\n\
+PGP User's Guide for details on how to do it.\n\
+\n"
+de: "sowie jeweils im Unterverzeichnis 'DOC' der oben genannten Verzeichnisse.\n\
+Bitte lege eine Kopie beider Teile des Benutzerhandbuches (Dateien PGPDOC1.TXT\n\
+und PGPDOC2.TXT) in eines der genannten Verzeichnisse.\n\
+Unter GAR KEINEN UMSTÄNDEN sollte PGP jemals ohne das PGP-Benutzerhandbuch\n\
+ausgeliefert werden, das sich im Standard-Auslieferungspaket befindet. Wenn\n\
+Du eine Kopie von PGP ohne das Handbuch erhalten hast, dann informiere bitte\n\
+denjenigen, von dem Du sie bekommen hast, daß es sich um ein unvollständiges\n\
+Paket handelt, das künftig nicht mehr ausgeliefert werden sollte.\n\
+PGP generiert keine Schlüssel, ohne das Handbuch gefunden zu haben!\n\
+Im PGP-Handbuch steht übrigens auch, wie diese Einschränkung zu umgehen ist...\n"
+es: "y el subdirectorio doc de cada uno de ellos. Pon una copia de\n\
+ambos volúmenes en uno de esos directorios.\n\
+\n\
+Bajo *ninguna circunstancia* debe distribuirse PGP sin la GuÃa del usuario,\n\
+incluida con la distribución habitual.\n\
+Si tienes una copia de PGP sin manual, informa a quien te la suministró de\n\
+que es un paquete incompleto que no debe seguir distribuyéndose.\n\
+\n\
+PGP no genera ninguna clave si no encuentra la GuÃa del usuario.\n\
+Hay una forma sencilla de saltarse esta restricción. Consulta la\n\
+GuÃa para ver cómo hacerlo.\n\
+\n"
+fr: "et leur(s) sous-repertoire(s). Veuillez placer une copie des\n\
+deux (2) documents constituant le manuel d'utilisation dans l'un de\n\
+ces repertoires.\n\
+En aucune cinconstances PGP ne devrait etre distribué sans que les\n\
+documents constituant le manuel ne soit inclus avec le reste.\n\
+Si vous avez obtenu PGP sans les documents constituant le manuel\n\
+veuillez SVP en aviser votre fournisseur que PGP est incomplet et\n\
+que cette maniere de distribuer PGP doit cesser.\n\
+\n\
+PGP ne fonctionnera pas sans la presence des deux documents constituant le
+manuel \n\
+La facon de circonvenir a cette restriction est de consulter le manuel\n\
+\n"
+muttde: "sowie jeweils im Unterverzeichnis 'DOC' der oben genannten Verzeichnisse.\n\
+Bitte lege eine Kopie beider Teile des Benutzerhandbuches (Dateien PGPDOC1.TXT\n\
+und PGPDOC2.TXT) in eines der genannten Verzeichnisse.\n\
+Unter GAR KEINEN UMSTÄNDEN sollte PGP jemals ohne das PGP-Benutzerhandbuch\n\
+ausgeliefert werden, das sich im Standard-Auslieferungspaket befindet. Wenn\n\
+Du eine Kopie von PGP ohne das Handbuch erhalten hast, dann informiere bitte\n\
+denjenigen, von dem Du sie bekommen hast, daß es sich um ein unvollständiges\n\
+Paket handelt, das künftig nicht mehr ausgeliefert werden sollte.\n\
+PGP generiert keine Schlüssel, ohne das Handbuch gefunden zu haben!\n\
+Im PGP-Handbuch steht übrigens auch, wie diese Einschränkung zu umgehen ist...\n"
+
+"\007Keygen error. "
+de: "\n\007FEHLER bei der Erzeugung des Schlüssels.\n"
+es: "\007Error en la generación de claves. "
+fr: "\007Erreur dans la génération de clé. "
+mutt: "Keygen error. "
+muttde: "\nFEHLER bei der Erzeugung des Schlüssels.\n"
+
+"\007Keyring check error.\n"
+de: "\n\007FEHLER bei der Überprüfung des Schlüsselbunds.\n"
+es: "\007Error en la comprobación del anillo.\n"
+fr: "Erreur dans la vérification du trousseau de clef."
+mutt: "Keyring check error.\n"
+muttde: "\nFEHLER bei der Überprüfung des Schlüsselbunds.\n"
+
+"\007Maintenance pass error. "
+de: "\n\007FEHLER beim Verwaltungsdurchgang.\n"
+es: "\007Error en el proceso de mantenimiento. "
+fr: "\007Erreur dans la passe de maintenance. "
+mutt: "Maintenance pass error. "
+muttde: "\nFEHLER beim Verwaltungsdurchgang.\n"
+
+"File '%s' is not a public keyring\n"
+de: "Die Datei '%s' ist kein öffentlicher Schlüsselbund.\n"
+es: "El fichero '%s' no es un anillo de claves públicas\n"
+fr: "Le fichier '%s' n'est pas un fichier de clés publiques\n"
+muttde: "Die Datei '%s' ist kein öffentlicher Schlüsselbund.\n"
+
+"\nA user ID is required to select the public key you want to sign. "
+de: "\nZur Auswahl des zu unterschreibenden Schlüssels wird\n\
+eine Benutzer-ID benötigt."
+es: "\nSe necesita un identificador para elegir\n\
+la clave pública por firmar. "
+fr: "\nUn nom d'utilisateur est nécessaire pour sélectionner la clé\n\
+publique que vous voulez signer. "
+muttde: "\nZur Auswahl des zu unterschreibenden Schlüssels wird\n\
+eine Benutzer-ID benötigt."
+
+"\nEnter the public key's user ID: "
+de: "\nBenutzer-ID des öffentlichen Schlüssels: "
+es: "\nIntroduzca el identificador de la clave pública: "
+fr: "\nEntrez le nom d'utilisateur pour la clé publique: "
+muttde: "\nBenutzer-ID des öffentlichen Schlüssels: "
+
+"\007Key signature error. "
+de: "\n\007FEHLER beim Unterschreiben des Schlüssels.\n"
+es: "\007Error en firma de clave. "
+fr: "\007Erreur dans la signature de clé. "
+mutt: "Key signature error. "
+muttde: "\nFEHLER beim Unterschreiben des Schlüssels.\n"
+
+"\nA user ID is required to select the key you want to revoke or \
+disable. "
+de: "\nZur Auswahl des zurückzuziehenden oder zu sperrenden Schlüssels wird\n\
+eine Benutzer-ID benötigt."
+es: "\nSe necesita un identificador de usuario para elegir la clave \
+que quieras\n\revocar o desactivar. "
+fr: "\nUn nom d'utilisateur est requis pour sélectionner la clé que vous\
+voulez révoquer ou inactiver. "
+muttde: "\nZur Auswahl des zurückzuziehenden oder zu sperrenden Schlüssels wird\n\
+eine Benutzer-ID benötigt."
+
+"\nEnter user ID: "
+de: "\nBenutzer-ID: "
+es: "\nIntroduce el identificador: "
+fr: "\nEntrez le nom d'utilisateur: "
+muttde: "\nBenutzer-ID: "
+
+"\nA user ID is required to select the key you want to edit. "
+de: "\nZur Auswahl des zu bearbeitenden Schlüssels wird eine Benutzer-ID benötigt."
+es: "\nSe necesita el identificador de usuario para elegir la clave que \
+quieras\nmodificar. "
+fr: "\nUn nom d'utilisateur est nécessaire pour sélectionner la clé que\n\
+vous voulez modifier. "
+muttde: "\nZur Auswahl des zu bearbeitenden Schlüssels wird eine Benutzer-ID benötigt."
+
+"\nEnter the key's user ID: "
+de: "\nBenutzer-ID des Schlüssels: "
+es: "\nIntroduce el identificador: "
+fr: "\nEntrez le nom d'utilisateur pour la clé: "
+muttde: "\nBenutzer-ID des Schlüssels: "
+
+"\007Keyring edit error. "
+de: "\n\007FEHLER beim Bearbeiten des Schlüsselbunds.\n"
+es: "\007Error en la modificación del anillo. "
+fr: "\007Erreur dans la modification du fichier de clés. "
+mutt: "Keyring edit error. "
+muttde: "\nFEHLER beim Bearbeiten des Schlüsselbunds.\n"
+
+"\n\007Key file '%s' does not exist.\n"
+de: "\n\007Die Datei '%s' existiert nicht.\n"
+es: "\n\007No existe el anillo de claves '%s.\n"
+fr: "\n\007Le fichier de clés '%s' n'existe pas.\n"
+mutt: "\nKey file '%s' does not exist.\n"
+muttde: "\nDie Datei '%s' existiert nicht.\n"
+
+"\nA user ID is required to select the key you want to extract. "
+de: "\nZur Auswahl des zu extrahierenden Schlüssels wird eine Benutzer-ID benötigt."
+es: "\nSe necesita el identificador de usuario para elegir la clave que \
+quieras\n\extraer. "
+fr: "\nUn nom d'utilisateur est nécessaire pour sélectionner la clé que\n\
+vous voulez extraire. "
+muttde: "\nZur Auswahl des zu extrahierenden Schlüssels wird eine Benutzer-ID benötigt."
+
+"\007Keyring extract error. "
+de: "\n\007FEHLER beim Extrahieren aus dem Schlüsselbund.\n"
+es: "\007Error al extraer del anillo. "
+fr: "\007Erreur dans l'extraction du fichier de clés. "
+mutt: "Keyring extract error. "
+muttde: "\nFEHLER beim Extrahieren aus dem Schlüsselbund.\n"
+
+"\nA user ID is required to select the public key you want to\n\
+remove certifying signatures from. "
+de: "\nZur Auswahl des Schlüssels, von dem Beglaubigungen entfernt werden sollen,\n\
+wird eine Benutzer-ID benötigt."
+es: "\nSe necesita el identificador de usuario para elegir la clave pública\n\
+de la que suprimir firmas. "
+fr: "\nUn nom d'utilisateur est nécessaire pour sélectionner la clé\
+ publique\n\
+pour laquelle vous voulez supprimer des signatures de certification. "
+muttde: "\nZur Auswahl des Schlüssels, von dem Beglaubigungen entfernt werden sollen,\n\
+wird eine Benutzer-ID benötigt."
+
+"\nA user ID is required to select the key you want to remove. "
+de: "\nZur Auswahl des zu löschenden Schlüssels wird eine Benutzer-ID benötigt."
+es: "\nSe necesita el identificador de usuario para elegir la clave que \
+quieras\nsuprimir . "
+fr: "\nUn nom d'utilisateur est nécessaire pour sélectionner la clé que\n\
+vous voulez supprimer. "
+muttde: "\nZur Auswahl des zu löschenden Schlüssels wird eine Benutzer-ID benötigt."
+
+"\007Key signature remove error. "
+de: "\n\007FEHLER beim Entfernen der Beglaubigung.\n"
+es: "\007Error en la supresión de la firma de una clave. "
+fr: "\007Erreur dans la suppression de signature d'une clé. "
+mutt: "Key signature remove error. "
+muttde: "\nFEHLER beim Entfernen der Beglaubigung.\n"
+
+"\007Keyring remove error. "
+de: "\n\007FEHLER beim Löschen aus dem Schlüsselbund.\n"
+es: "\007Error al suprimir del anillo. "
+fr: "\007Erreur dans la suppression du fichier de clés. "
+mutt: "Keyring remove error. "
+muttde: "\nFEHLER beim Löschen aus dem Schlüsselbund.\n"
+
+"\007Keyring view error. "
+de: "\n\007FEHLER beim Anzeigen des Schlüsselbunds.\n"
+es: "\007Error al visualizar el anillo. "
+fr: "\007Erreur dans la visualisation du fichier de clés. "
+mutt: "Keyring view error. "
+muttde: "\nFEHLER beim Anzeigen des Schlüsselbunds.\n"
+
+"For more detailed help, consult the PGP User's Guide.\n"
+de: "Ausführlichere Hilfe findet sich in der PGP-Anleitung.\n"
+es: "Para obtener más ayuda, consulta la GuÃa del usuario de PGP.\n"
+fr: "Pour une aide plus détaillée, consultez le guide de l'utilisateur de
+PGP.\n"
+mutt: "\n"
+muttde: "\n"
+
+"\nInvalid arguments.\n"
+de: "\nUngültige Argumente!\n"
+es: "\nArgumentos incorrectos.\n"
+fr: "\nArguments invalides.\n"
+muttde: "\nUngültige Argumente!\n"
+
+"\nUsage summary:\
+\nTo encrypt a plaintext file with recipent's public key, type:\
+\n pgp -e textfile her_userid [other userids] (produces textfile.pgp)\
+\nTo sign a plaintext file with your secret key:\
+\n pgp -s textfile [-u your_userid] (produces textfile.pgp)\
+\nTo sign a plaintext file with your secret key, and then encrypt it\
+\n with recipent's public key, producing a .pgp file:\
+\n pgp -es textfile her_userid [other userids] [-u your_userid]\
+\nTo encrypt with conventional encryption only:\
+\n pgp -c textfile\
+\nTo decrypt or check a signature for a ciphertext (.pgp) file:\
+\n pgp ciphertextfile [-o plaintextfile]\
+\nTo produce output in ASCII for email, add the -a option to other options.\
+\nTo generate your own unique public/secret key pair: pgp -kg\
+\nFor help on other key management functions, type: pgp -k\n"
+de: "\nÜbersicht der PGP-Befehle:\
+\nVerschlüsseln eines Textes mit dem öffentlichen Schlüssel des Empfängers:\
+\n pgp -e {Text} {Benutzer-ID des Empfängers} (Ergebnis: {Text}.pgp)\
+\nUnterschreiben eines Textes mit Deinem privaten Schlüssel:\
+\n pgp -s {Text} [-u {Deine Benutzer-ID}] (Ergebnis: {Text}.pgp)\
+\nUnterschreiben eines Textes mit Deinem privaten Schlüssel und anschließend\
+\nVerschlüsseln mit dem öffentlichen Schlüssel des Empfängers:\
+\n pgp -es {Text} {Benutzer-ID des Empfängers} [weitere Benutzer-IDs]\
+\n [-u {Deine Benutzer-ID}] (Ergebnis: {Text}.pgp)\
+\nVerschlüsseln mit konventioneller Verschlüsselung:\
+\n pgp -c {Text}\
+\nEntschlüsseln oder Überprüfen der Unterschrift:\
+\n pgp {verschlüsselter Text} [-o {Klartext}]\
+\nVerpacken der Ausgabe in ASCII (für E-Mail): pgp {...} -a {...}\
+\nErzeugen eines eigenen Schlüssel-Paares: pgp -kg\
+\nHilfe zur Schlüsselverwaltung: pgp -k\n"
+es: "\nResumen de las instrucciones:\
+\nEncriptar fichero normal con la clave pública del destinatario:\
+\n pgp -e ftexto su_identificador (produce ftexto.pgp)\
+\nFirmar un fichero de texto normal con tu clave secreta:\
+\n pgp -s ftexto [-u tu_identificador] (produce ftexto.pgp)\
+\nFirmar un fichero normal con tu clave secreta y después encriptarlo\
+\n con la clave pública del destinatario, produciendo un fichero .pgp:\
+\n pgp -es ftexto su_identificador [otros] [-u tu_identificador]\
+\nEncriptar sólo con cifrado convencional:\
+\n pgp -c ftexto\
+\nDesencriptar o comprobar la firma en un fichero cifrado (.pgp):\
+\n pgp fcifrado [-o fnormal]\
+\nProducir resultado en ASCII para correo electrónico: añadir la opción -a.\
+\nGenerar tu propio par único de claves pública/secreta: pgp -kg\
+\nAyuda sobre otras funciones de gestión de claves: pgp -k\n"
+fr: "\nSommaire:\
+\nPour chiffrer un fichier en clair avec la clé publique du destinataire, \
+tapez:\
+\n pgp -e fichier son_nom [autres noms] (produit fichier.pgp)\
+\nPour signer un texte en clair avec votre clé secrète:\
+\n pgp -s fichier [-u votre_nom] (produit fichier.pgp)\
+\nPour signer un texte en clair avec votre clé secrète, puis le chiffrer\
+\n avec la clé publique du destinataire, produisant un fichier .pgp:\
+\n pgp -es fichier son_nom [autres noms] [-u votre_nom]\
+\nPour chiffrer de manière conventionelle seulement:\
+\n pgp -c fichier\
+\nPour déchiffrer ou vérifier une signature pour un fichier chiffré (.pgp):\
+\n pgp fichier_chiffré [-o fichier_en_clair]\
+\nPour produire une sortie en ASCII pour courrier électronique, ajouter\
+\nl'option -a aux autres options.\
+\nPour générer votre propre paire de clés publique/secrète:\
+\n pgp -kg\
+\nPour de l'aide sur les autres fonctions de gestion de clé, tapez: pgp -k\n"
+muttde: "\nÜbersicht der PGP-Befehle:\
+\nVerschlüsseln eines Textes mit dem öffentlichen Schlüssel des Empfängers:\
+\n pgp -e {Text} {Benutzer-ID des Empfängers} (Ergebnis: {Text}.pgp)\
+\nUnterschreiben eines Textes mit Deinem privaten Schlüssel:\
+\n pgp -s {Text} [-u {Deine Benutzer-ID}] (Ergebnis: {Text}.pgp)\
+\nUnterschreiben eines Textes mit Deinem privaten Schlüssel und anschließend\
+\nVerschlüsseln mit dem öffentlichen Schlüssel des Empfängers:\
+\n pgp -es {Text} {Benutzer-ID des Empfängers} [weitere Benutzer-IDs]\
+\n [-u {Deine Benutzer-ID}] (Ergebnis: {Text}.pgp)\
+\nVerschlüsseln mit konventioneller Verschlüsselung:\
+\n pgp -c {Text}\
+\nEntschlüsseln oder Überprüfen der Unterschrift:\
+\n pgp {verschlüsselter Text} [-o {Klartext}]\
+\nVerpacken der Ausgabe in ASCII (für E-Mail): pgp {...} -a {...}\
+\nErzeugen eines eigenen Schlüssel-Paares: pgp -kg\
+\nHilfe zur Schlüsselverwaltung: pgp -k\n"
+
+"\nKey management functions:\
+\nTo generate your own unique public/secret key pair:\
+\n pgp -kg\
+\nTo add a key file's contents to your public or secret key ring:\
+\n pgp -ka keyfile [keyring]\
+\nTo remove a key or a user ID from your public or secret key ring:\
+\n pgp -kr userid [keyring]\
+\nTo edit your user ID or pass phrase:\
+\n pgp -ke your_userid [keyring]\
+\nTo extract (copy) a key from your public or secret key ring:\
+\n pgp -kx userid keyfile [keyring]\
+\nTo view the contents of your public key ring:\
+\n pgp -kv[v] [userid] [keyring]\
+\nTo check signatures on your public key ring:\
+\n pgp -kc [userid] [keyring]\
+\nTo sign someone else's public key on your public key ring:\
+\n pgp -ks her_userid [-u your_userid] [keyring]\
+\nTo remove selected signatures from a userid on a keyring:\
+\n pgp -krs userid [keyring]\
+\n"
+de: "\nSchlüssel-Verwaltung:\
+\nErzeugen eines eigenen, eindeutigen Schlüssel-Paares (privat/öffentlich):\
+\n pgp -kg\
+\nHinzufügen von Schlüsseln zum privaten oder öffentlichen Schlüsselbund:\
+\n pgp -ka {Datei mit Schlüsseln} [{Schlüsselbund}]\
+\nLöschen eines Schlüssels oder einer Benutzer-ID aus einem Schlüsselbund:\
+\n pgp -kr {Benutzer-ID} [{Schlüsselbund}]\
+\nÄndern Deiner Benutzer-ID oder Deines Mantras:\
+\n pgp -ke {Deine Benutzer-ID} [{Schlüsselbund}]\
+\nHerauskopieren eines Schlüssels aus einem Schlüsselbund:\
+\n pgp -kx {Benutzer-ID} {Ausgabe-Datei} [{Schlüsselbund}]\
+\nAnzeigen des Inhaltes des öffentlichen Schlüsselbunds:\
+\n pgp -kv[v] [{Benutzer-ID}] [{Schlüsselbund}]\
+\nÜberprüfen der Beglaubigungen im öffentlichen Schlüsselbund:\
+\n pgp -kc [{Benutzer-ID}] [{Schlüsselbund}]\
+\nBeglaubigen eines öffentlichen Schlüssels eines anderen Benutzers:\
+\n pgp -ks {seine Benutzer-ID} [-u {Deine Benutzer-ID}] [{Schlüsselbund}]\
+\nEntfernen ausgewählter Beglaubigungen von einem Schlüssel:\
+\n pgp -krs {Benutzer-ID} [{Schlüsselbund}]\n"
+es: "\nFunciones para la gestión de claves:\
+\nGenerar tu propio par único de claves pública/secreta:\
+\n pgp -kg\
+\nAñadir contenido de fichero de clave al anillo de claves públicas o secretas:\
+\n pgp -ka fdclaves [anillo]\
+\nSuprimir una clave o identificador de usuario de un anillo de claves:\
+\n pgp -kr identificador [anillo]\
+\nModificar tu identificador de usuario o tu contraseña:\
+\n pgp -ke tu_identificador [anillo]\
+\nExtraer (copiar) una clave del anillo de claves públicas o secretas:\
+\n pgp -kx identificador fdclaves [anillo]\
+\nVisualizar el contenido del anillo de claves públicas:\
+\n pgp -kv[v] [identificador] [anillo]\
+\nComprobar las firmas del anillo de claves públicas:\
+\n pgp -kc [identificador] [anillo]\
+\nFirmar la clave pública de alguien en el anillo de claves correspondiente:\
+\n pgp -ks otro_identificador [-u tu_identificador] [anillo]\
+\nSuprimir ciertas firmas de un idusuario en un anillo:\
+\n pgp -krs identificador [anillo]\
+\n"
+fr: "Fonctions de gestion des clés:\
+\nPour générer votre propre paire de clés publique/secrète:\
+\n pgp -kg\
+\nPour ajouter le contenu d'un fichier de clés à votre fichier de clés\
+\n public ou secret:\
+\n pgp -ka fichier_de_clés [votre_fichier_de_clés]\
+\nPour retirer une clé de votre fichier de clés public ou secret:\
+\n pgp -kr nom_d_utilisateur [fichier_de_clés]\
+\nPour extraire (copier) une clé de votre fichier de clés public ou secret:\
+\n pgp -kx nom_d_utilisateur fichier_de_la_clé [fichier_de_clés]\
+\nPour visualiser le contenu de votre fichier de clés:\
+\n pgp -kv[v] [nom_d_utilisateur] [ficher_de_clés]\
+\nPour vérifier les signatures sur votre fichier de clés publiques:\
+\n pgp -kc [nom_d_utilisateur] [ficher_de_clés]\
+\nPour signer la clé publique de quelqu'un d'autre sur votre fichier de\
+\n clés publiques:\
+\n pgp -ks son_nom votre_nom [fichier_de_clés]\
+\nPour enlever certaines signatures d'une personne sur un fichier de clés:\
+\n pgp -krs son_nom [fichier_de_clés]\n"
+muttde: "\nSchlüssel-Verwaltung:\
+\nErzeugen eines eigenen, eindeutigen Schlüssel-Paares (privat/öffentlich):\
+\n pgp -kg\
+\nHinzufügen von Schlüsseln zum privaten oder öffentlichen Schlüsselbund:\
+\n pgp -ka {Datei mit Schlüsseln} [{Schlüsselbund}]\
+\nLöschen eines Schlüssels oder einer Benutzer-ID aus einem Schlüsselbund:\
+\n pgp -kr {Benutzer-ID} [{Schlüsselbund}]\
+\nÄndern Deiner Benutzer-ID oder Deines Mantras:\
+\n pgp -ke {Deine Benutzer-ID} [{Schlüsselbund}]\
+\nHerauskopieren eines Schlüssels aus einem Schlüsselbund:\
+\n pgp -kx {Benutzer-ID} {Ausgabe-Datei} [{Schlüsselbund}]\
+\nAnzeigen des Inhaltes des öffentlichen Schlüsselbunds:\
+\n pgp -kv[v] [{Benutzer-ID}] [{Schlüsselbund}]\
+\nÜberprüfen der Beglaubigungen im öffentlichen Schlüsselbund:\
+\n pgp -kc [{Benutzer-ID}] [{Schlüsselbund}]\
+\nBeglaubigen eines öffentlichen Schlüssels eines anderen Benutzers:\
+\n pgp -ks {seine Benutzer-ID} [-u {Deine Benutzer-ID}] [{Schlüsselbund}]\
+\nEntfernen ausgewählter Beglaubigungen von einem Schlüssel:\
+\n pgp -krs {Benutzer-ID} [{Schlüsselbund}]\n"
+
+"\nIncluding \"%s\"...\n"
+de: "\nEmpfängerliste aus der Datei '%s' wird eingelesen...\n"
+es: "\nIncluyendo \"%s\"...\n"
+fr: "\nIncluant '%s'...\n"
+muttde: "\nEmpfängerliste aus der Datei '%s' wird eingelesen...\n"
+
+"\nWe need to generate %u random bits. This is done by measuring the\
+\ntime intervals between your keystrokes. Please enter some random text\
+\non your keyboard until you hear the beep:\n"
+de: "\nWir müssen %u zufällige Bits erzeugen. Dies wird durch Messung\
+\nder Abstände zwischen Deinen Anschlägen bewerkstelligt. Bitte gib\
+\nirgendwelchen beliebigen Text auf der Tastatur ein, bis es piepst:\n"
+es: "\nNecesitamos generar %d bits aleatorios. Se hace midiendo los\
+\nintervalos de tiempo entre pulsaciones de tecla. Escribe\
+\ntexto al azar en el teclado hasta que oigas un pitido:\n"
+fr: "\nNous devons générer %d bits aléatoires. Ceci est fait en mesurant\
+\nl'intervalle de temps entre les frappes de touches. Veuillez tapper du\
+\ntexte aléatoire sur votre clavier jusqu'à ce que vous entendiez le\
+\nsignal sonore:\n"
+muttde: "\nWir müssen %u zufällige Bits erzeugen. Dies wird durch Messung\
+\nder Abstände zwischen Deinen Anschlägen bewerkstelligt. Bitte gib\
+\nirgendwelchen beliebigen Text auf der Tastatur ein, bis es piepst:\n"
+
+"\007 -Enough, thank you.\n"
+de: "\007 -Danke, das genügt!\n"
+es: "\007 -Es suficiente.\n"
+fr: "\007 -Assez, merci.\n"
+mutt: " -Enough, thank you.\n"
+muttde: " -Danke, das genügt!\n"
+
+"\
+Uses the RSAREF(tm) Toolkit, which is copyright RSA Data Security, Inc.\n\
+Distributed by the Massachusetts Institute of Technology.\n"
+de: "Benutzt das RSAREF(tm) Toolkit, (c) RSA Data Security, Inc.\n\
+Ausgeliefert vom Massachusetts Institute of Technology.\n"
+es: "\
+Utiliza RSAREF(tm), copyright de RSA Data Security, Inc.\n\
+Distribuido por el Massachusetts Institute of Technology.\n"
+fr: "Ce logiciel utilise RSAREF(tm) Toolkit, Copyright RSA Data Security, Inc\n\
+Distribué par le Massachusetts Institute of Technology.\n"
+muttde: "Benutzt das RSAREF(tm) Toolkit, (c) RSA Data Security, Inc.\n\
+Ausgeliefert vom Massachusetts Institute of Technology.\n"
+
+"Out of memory"
+de: "Zu wenig Speicher!"
+es: "No queda memoria"
+fr: "Mémoire insufisante"
+muttde: "Zu wenig Speicher!"
+
+"\nOut of memory\n"
+de: "\nZu wenig Speicher!\n"
+es: "\nNo queda memoria\n"
+fr: "\nMémoire insuffisante\n"
+muttde: "\nZu wenig Speicher!\n"
+
+"\n\007Out of memory.\n"
+de: "\n\007Zu wenig Speicher!\n"
+es: "\n\007No queda memoria.\n"
+fr: "\n\007Mémoire insuffisante.\n"
+mutt: "\nOut of memory.\n"
+muttde: "\nZu wenig Speicher!\n"
+
+"\nCompression/decompression error\n"
+de: "\nFEHLER beim Packen/Entpacken!\n"
+es: "\nError en compresión/descompresión\n"
+fr: "\nErreur de compression/decompression\n"
+muttde: "\nFEHLER beim Packen/Entpacken!\n"
+
+"\nERROR: unexpected end of compressed data input.\n"
+de: "\nFEHLER: vorzeitiges Ende der ZIP-gepackten Eingangsdaten.\n"
+es: "\nERROR: los datos comprimidos terminan antes de tiempo.\n"
+fr: "\nERREUR: fin innopinée des données d'entrée compressées.\n"
+muttde: "\nFEHLER: vorzeitiges Ende der ZIP-gepackten Eingangsdaten.\n"
+
+# The following 4 translations MUST be exactly 3 characters long!
+
+"pub"
+de: "öff"
+muttde: "öff"
+
+"sec"
+de: "prv"
+muttde: "prv"
+
+"sig"
+de: "Unt"
+es: "fir"
+muttde: "Unt"
+
+"com"
+de: "Wid"
+muttde: "Wid"
+
+# IN-CH extentions. <lutz@iks-jena.de>
+
+# translation must not change the string length
+"rev"
+de: "zur"
+muttde: "zur"
+
+# translation must not change the string length
+" Expire: %s%s"
+de: " Verfall: %s%s"
+muttde: " Verfall: %s%s"
+
+# translation must not change the string length
+" no expire "
+de: " kein Verfall "
+muttde: " kein Verfall "
+
+"ENCRyption only\n"
+de: "*** nur Verschlüsselung! ***\n"
+muttde: "*** nur Verschlüsselung! ***\n"
+
+"Key ID %s is SIGN only. Decryption avoided.\n"
+de: "Schlüssel ID %s ist nur zum Unterschreiben. Entschlüsselung verhindert.\n"
+muttde: "Schlüssel ID %s ist nur zum Unterschreiben. Entschlüsselung verhindert.\n"
+
+"Key ID %s is not valid.\n"
+de: "Schlüssel ID %s ist ungültig.\n"
+muttde: "Schlüssel ID %s ist ungültig.\n"
+
+"Key is an ENCRyption only key.\n"
+de: "Der Schlüssel ist nur zur Verschlüsselung.\n"
+muttde: "Der Schlüssel ist nur zur Verschlüsselung.\n"
+
+"Key is a SIGNature only key.\n"
+de: "Der Schlüssel ist nur für Unterschriften.\n"
+muttde: "Der Schlüssel ist nur für Unterschriften.\n"
+
+"Key is out of use.\n"
+de: "Der Schlüssel ist nicht mehr in Benutzung.\n"
+muttde: "Der Schlüssel ist nicht mehr in Benutzung.\n"
+
+"SIGNature only\n"
+de: "*** nur Unterschriften! ***\n"
+muttde: "*** nur Unterschriften! ***\n"
+
+" Revoked by: "
+de: " Zurückgezogen von: "
+muttde: " Zurückgezogen von: "
+
+" Low Cert by: "
+de: "Niedrige Beglaubigung von: "
+muttde: "Niedrige Beglaubigung von: "
+
+"Medium Cert by: "
+de: "Mittlere Beglaubigung von: "
+muttde: "Mittlere Beglaubigung von: "
+
+" High Cert by: "
+de: " Hohe Beglaubigung von: "
+muttde: " Hohe Beglaubigung von: "
+
+" Unknown type: "
+de: " Unbekannter Typ: "
+muttde: " Unbekannter Typ: "
+
+"Try to obtain the corresponding SIGN key.\n"
+de: "Es wird versucht, den zugehörigen Unterschriftsschlüssel zu verwenden.\n"
+muttde: "Es wird versucht, den zugehörigen Unterschriftsschlüssel zu verwenden.\n"
+
+"\n\007Key cert is already revoked by user '%s'.\n"
+de: "\n\007Die Unterschrift unter dem Schlüssel wurde bereits\n\
+von '%s' zurückgezogen.\n"
+mutt: "\nKey cert is already revoked by user '%s'.\n"
+muttde: "\nDie Unterschrift unter dem Schlüssel wurde bereits\n\
+von '%s' zurückgezogen.\n"
+
+"\n\007Key is not signed by user '%s'.\n"
+de: "\n\007Der Schlüssel ist nicht von '%s' unterschrieben.\n"
+mutt: "\nKey is not signed by user '%s'.\n"
+muttde: "\nDer Schlüssel ist nicht von '%s' unterschrieben.\n"
+
+"\nKey signature certificate revoked.\n"
+de: "\nDie Unterschrift unter dem Schlüssel wurde zurückgezogen.\n"
+muttde: "\nDie Unterschrift unter dem Schlüssel wurde zurückgezogen.\n"
+
+"This key is already out of use.\n"
+de: "Dieser Schlüssel ist bereits nicht mehr in Benutzung.\n"
+muttde: "Dieser Schlüssel ist bereits nicht mehr in Benutzung.\n"
+
+"\007 Key ID %s is SIGN only. (protest against decryption)\n"
+de: "\007Die Schlüssel-ID %s ist nur für Unterschriften.\n\
+Keine Entschlüsselung möglich!\n"
+mutt: " Key ID %s is SIGN only. (protest against decryption)\n"
+muttde: "Die Schlüssel-ID %s ist nur für Unterschriften.\n\
+Keine Entschlüsselung möglich!\n"
+
+"Signature was made after the key ID %s was expired.\n"
+de: "Unterschrift wurde mit dem verfallen Schlüssel ID %s erstellt.\n"
+muttde: "Unterschrift wurde mit dem verfallen Schlüssel ID %s erstellt.\n"
+
+"Signature was made before the key ID %s was valid.\n"
+de: "Unterschrift wurde mit dem noch ungültigen Schlüssel ID %s erstellt.\n"
+muttde: "Unterschrift wurde mit dem noch ungültigen Schlüssel ID %s erstellt.\n"
+
+"Signature was made using the ENCR key ID %s.\n"
+de: "Unterschrift wurde mit dem Verschlüsselungsschlüssel ID %s erstellt.\n"
+muttde: "Unterschrift wurde mit dem Verschlüsselungsschlüssel ID %s erstellt.\n"
+
+"Something goes wrong, may be an unlucky User ID?\n"
+de: "Irgendwas ist schiefgegangen. Liegt's an der NutzerID?\n"
+muttde: "Irgendwas ist schiefgegangen. Liegt's an der NutzerID?\n"
+
+"Something goes wrong, may be this userid '%s' is unlucky?\n"
+de: "Irgendwas ist schiefgegangen. Liegt's an der NutzerID '%s'?\n"
+muttde: "Irgendwas ist schiefgegangen. Liegt's an der NutzerID '%s'?\n"
+
+"The compromise certificate might not be accepted on newer versions.\n\
+Revoke anyway? (y/N)"
+de: "Das Widerrufszertifikat kann von neueren Versionen zurückgewiesen\
+werden.\nTrotzdem widerrufen? (j/N)"
+muttde: "Das Widerrufszertifikat kann von neueren Versionen zurückgewiesen\
+werden.\nTrotzdem widerrufen? (j/N)"
+
+"The key for the userid '%s' is SIGN only.\nI'll try the corresponding ENCR key.\n"
+de: "Schlüssel ID '%s' ist nur zum Unterschreiben.\n\
+Der zugehörige Verschlüsselungsschlüssel wird versucht.\n"
+muttde: "Schlüssel ID '%s' ist nur zum Unterschreiben.\n\
+Der zugehörige Verschlüsselungsschlüssel wird versucht.\n"
+
+"The key for the userid '%s' is not valid, skipped.\n"
+de: "Schlüssel ID '%s' ist ungültig.\nÜbersprungen.\n"
+muttde: "Schlüssel ID '%s' ist ungültig.\nÜbersprungen.\n"
+
+"The user ID typed in has garbled options!\n"
+de: "Die NutzerID enthält fehlerhafte Optionseinträge!\n"
+muttde: "Die NutzerID enthält fehlerhafte Optionseinträge!\n"
+
+"This key is an ENCRyption only key.\n"
+de: "Das ist ein Verschlüsselungsschlüssel.\n"
+muttde: "Das ist ein Verschlüsselungsschlüssel.\n"
+
+"This key is too old to be revoked.\n"
+de: "Der Schlüssel ist zu alt, um zurückgezogen zu werden.\n"
+muttde: "Der Schlüssel ist zu alt, um zurückgezogen zu werden.\n"
+
+"This key is too old to be signed.\n"
+de: "Der Schlüssel ist zu alt, um unterschrieben zu werden.\n"
+muttde: "Der Schlüssel ist zu alt, um unterschrieben zu werden.\n"
+
+"This key will be generated in future. *oops*\n"
+de: "Der Schlüssel wird in der Zukunft erstellt. *huch*\n"
+muttde: "Der Schlüssel wird in der Zukunft erstellt. *huch*\n"
+
+"Your key is too old to sign with.\n"
+de: "Der Schlüssel ist zu alt zum unterschreiben.\n"
+muttde: "Der Schlüssel ist zu alt zum unterschreiben.\n"
+
+"Your key will be generated in future. *oops*\n"
+de: "Dein Schlüssel wird in der Zukunft erstellt. *huch*\n"
+muttde: "Dein Schlüssel wird in der Zukunft erstellt. *huch*\n"
+
+"\007**** Signed with an ENCRyption only key ****"
+de: "\007**** Unterschr. mit einem Verschl.-Schlüssel ****"
+mutt: "**** Signed with an ENCRyption only key ****"
+muttde: "**** Unterschr. mit einem Verschl.-Schlüssel ****"
+
+"\007**** Signed with an invalid key ****"
+de: "\007**** Unterschr. mit einem ungültigen Schlüssel ****"
+mutt: "**** Signed with an invalid key ****"
+muttde: "**** Unterschr. mit einem ungültigen Schlüssel ****"
+
+"\007**** Signed with a revoked key ****"
+de: "\007**** Unterschr. mit einem zurückgez. Schlüssel ****"
+mutt: "**** Signed with a revoked key ****"
+muttde: "**** Unterschr. mit einem zurückgez. Schlüssel ****"
+
+"\n\nREAD CAREFULLY: How did you prove the users real identity ?\n\
+ 0) What? I do not understand this question.\n\
+ 1) No attempt made at all to identify the user with a real name.\n\
+ 2) Some casual attempt made to identify user with his name.\n\
+ 3) Heavy-duty identification efforts, photo ID, direct contact...\n"
+de: "\n\nSORGFÄLTIG LESEN: Wie hast Du die Identität des Nutzers geprüft ?\n\
+ 0) Bitte? Ich verstehe die Frage nicht.\n\
+ 1) Keine Überprüfung des wirklichen Namens des Nutzers.\n\
+ 2) Einige Bemühungen, die Person mit diesem Namen zu identifizieren.\n\
+ 3) Große Identifizierungsaufwendungen, Ausweis, direkter Kontakt...\n"
+muttde: "\n\nSORGFÄLTIG LESEN: Wie hast Du die Identität des Nutzers geprüft ?\n\
+ 0) Bitte? Ich verstehe die Frage nicht.\n\
+ 1) Keine Überprüfung des wirklichen Namens des Nutzers.\n\
+ 2) Einige Bemühungen, die Person mit diesem Namen zu identifizieren.\n\
+ 3) Große Identifizierungsaufwendungen, Ausweis, direkter Kontakt...\n"
--- /dev/null
+#This file contains the strings used by PGP.
+
+[DIFFERENT_EXES]
+us=\
+PGP is now invoked from different executables for different operations:\n\n\
+pgpe Encrypt (including Encrypt/Sign)\n\
+pgps Sign\n\
+pgpv Verify/Decrypt\n\
+pgpk Key management\n\
+pgpo PGP 2.6.2 command-line simulator (not yet implemented)\n\n\
+See each application's respective man page or the general PGP documentation\n\
+for more information.\n
+
+[CREATING_OUTPUT_FILE]
+us=Creating output file %s\n
+
+#Untested
+[COPYING_KEYFILE_AND_RUNNING_PGPK]
+us=Copying key file to \"%s\", running pgpk to process it...\n\n
+mutt=
+
+#Untested
+[NEED_PASSPHRASE]
+us=You need a passphrase to encrypt the file\n
+
+[MUST_SPECIFY_A_RECIPIENT]
+us=You must specify at least one recipient for encryption!\n
+
+#Untested
+[NEED_PASSPHRASE_AGAIN]
+us=Enter same passphrase again\n
+
+#Untested
+[PASSPHRASES_DIFFERENT]
+us=Error: Passphrases were different. Try again.\n
+
+#Untested
+[ZERO_LEN_PASSPHRASE]
+us=Encryption error\n
+
+#Untested
+[TREAT_AS_PGP]
+us=This is a PGP File. Treat it as such? [y/N]\n
+
+#Untested
+[PRIVATE_KEY_MISSING]
+us=Cannot find a private key for signing: %s\n
+
+#Untested
+[CANNOT_CONVERT_TO_PRIVATE_KEY]
+us=Cannot convert to private key\n
+
+#Untested
+[PRIVATE_KEY_CANNOT_SIGN]
+us=Private Key cannot sign\n
+
+#Untested
+[CANNOT_UNLOCK_PRIVATE_KEY]
+us=Cannot unlock private key\n
+
+#Untested
+[NO_KEYRINGS]
+us=No keyrings to use
+
+#Untested
+[NO_ENCRYPTION_KEYS_FOUND_FOR]
+us=No encryption keys found for: %s\n
+
+#Untested
+[CANNOT_FIND_KEY]
+us=Cannot find key: %s\n
+
+#Untested
+[CANNOT_ADD_MY_KEY]
+us=Cannot add my key to set\n
+
+#Untested
+[NO_VALID_RECIPIENTS]
+us=No valid keys found for any recipients, exiting...\n
+
+#Untested
+[USING_STDIN]
+us=No files specified. Using stdin.\n\n
+mutt=
+
+#Untested
+[CANNOT_OPEN_INPUT_FILE]
+us=Cannot open input file %s\n
+
+#Untested
+[CANNOT_SETUP_PROCESSING_PIPELINE]
+us=Cannot Setup Processing Pipeline\n
+
+#Untested
+[UNRECOGNIZED_OPTION_STRING]
+us=Unrecognized option %s\n
+
+#Untested
+[UNRECOGNIZED_OPTION_STRING_DASH]
+us=Unrecognized option -%s\n
+
+#Untested
+[UNRECOGNIZED_OPTION_CHAR]
+us=Unrecognized option -%c\n
+
+#Untested
+[ARGS_INCOMPATABLE]
+us="Cannot use -%c and -%c together\n"
+
+#Untested
+[ONLY_ONE_OUTPUT_FILE]
+us="Only one -o option allowed\n"
+
+#Untested
+[ONLY_ONE_USERNAME]
+us="Only one -u option allowed\n"
+
+#Untested
+[NO_OUTPUT_FILENAME]
+us=-o option requires an output file name argument\n
+
+[NO_RECIPIENT_SPECIFIED]
+us=-r option requires a recipient name argument\n
+
+#Untested
+[NO_USERID_SPECIFIED]
+us=-u option requires a userid argument\n
+
+#Untested, and probably going away
+[NO_PASSPHRASE_SPECIFIED_IN_BATCHMODE]
+us=-z option requires a passphrase argument\n
+
+#Untested
+[CANNOT_COMBINE_CONVENTIONAL_AND_PK]
+us=Cannot combine -c and -r arguments\n
+
+#Untested
+[PGPK_IS_SEPERATE]
+us=pgpk is a seperate program, not a symlink to pgp!\n
+
+#Untested
+[UNKNOWN_SYMLINK]
+us=Invoked with unknown symlink\n
+
+#Untested
+[PRIVATE_KEY_NEEDED_FOR_SIGNATURE]
+us=A private key is required to make a signature.\n
+
+[ENTER_Y_OR_N]
+us="Invalid response. Please enter Y or N [default %c]: \n"
+
+#Untested
+[GENERIC_KEYRING_ERROR]
+us="Error on keyring \"%s\": "
+
+#Untested
+[UNABLE_TO_OPEN_DEFAULT_KEYRINGS]
+us="Unable to open default keyrings: "
+
+#Untested
+[UNABLE_TO_OPEN_KEYRING]
+us="Unable to open keyring: "
+
+#Untested
+[KEY_CORRUPTED]
+us="Key Corrupted (%s): "
+
+#Untested
+[NEED_SIG_FILE]
+us="File to check signature against [%s]: "
+
+#untested
+[GOOD_SIGNATURE]
+us="Good signature made %s by key:\n"
+
+#untested
+[BAD_SIGNATURE]
+us="BAD signature made %s by key:\n"
+
+#untested
+[ERROR_SIGNATURE]
+us="Error %s checking signature: %s\n"
+
+#Untested
+[UNKNOWN_SIGNATURE]
+us="Signature by unknown keyid: "
+
+#untested
+[ENTER_PASSPHRASE]
+us="Enter pass phrase: "
+
+#Untested
+[RANDOM_BITS_FROM_DEVICE]
+us="\n\
+We need to generate %u random bits. This is done by reading\n\
+%s. Depending on your system, you may be able\n\
+to speed this process by typing on your keyboard and/or moving your mouse.\n"
+
+#Untested
+[RANDOM_BITS_FROM_DEVICE_OLD_KERNEL]
+us="\n\
+/dev/random detected; however, on Linux kernel versions < 1.3.33, it is not\n\
+cryptographically usable. If you wish to use /dev/random as an entropy\n\
+source, it is recommended that you upgrade your kernel version. If you feel\n\
+that you received this message in error, add ForceRandomDevice=1 to your\n\
+pgp.cfg file, but be warned that by doing so without know what you are\n\
+doing, you could compromise the security of your key.\n"
+
+#Untested
+[RANDOM_BITS_FROM_KEYBOARD]
+us="\n\
+We need to generate %u random bits. This is done by measuring the\n\
+time intervals between your keystrokes. Please enter some random text\n\
+on your keyboard until you hear the beep:\n"
+
+#Untested
+[NO_INPUT_FILE_IN_BATCHMODE]
+us="Cannot request input file in batchmode\n"
+
+#Untested
+[UNABLE_TO_OPEN_FILE]
+us="Unable to open file \"%s\"\n"
+
+#Untested
+[UNABLE_TO_CREATE_READ_MODULE]
+us="Unable to create file read module.\n"
+
+#Untested
+[UNKNOWN_FILE_TYPE]
+us="Unknown file type (clearsigned?). Assuming text\n"
+
+#Untested
+[OPENING_FILE_WITH_TYPE]
+us="Opening file \"%s\" type %s.\n"
+mutt=
+
+#Untested
+[ERROR_CLOSING_OLD_FILE]
+us="Error closing old file: %d\n"
+
+#Untested
+[NEED_PASSPHRASE_TO_DECRYPT_KEY]
+us="Need a pass phrase to decrypt private key:\n"
+
+#Untested
+[GOOD_PASSPHRASE]
+us="Pass phrase is good.\n"
+
+#Untested
+[BAD_PASSPHRASE]
+us="Error: Bad pass phrase.\n\n"
+
+#Untested
+[PASSPHRASE_INCORRECT]
+us="Password Incorrect."
+
+#Untested
+[TRY_AGAIN]
+us=" Try Again."
+
+#Untested
+[UNKNOWN_ESK]
+us="Unknown ESK type: %d\n"
+
+#Untested
+[CANNOT_DECRYPT]
+us="Cannot decrypt message. It can only be decrypted by:\n"
+
+#Untested
+[A_PASSPHRASE]
+us=" A Pass Phrase\n"
+
+#Untested
+[KEY_ID]
+us=" Key ID "
+
+#Untested
+[FORCE_OVERWRITE]
+us="File \"%s\" already exists. Overwrite? [y/N] "
+
+#Untested
+[UNABLE_TO_OVERWRITE_FILE]
+us="Unable to overwrite file \"%s\"\n"
+
+#Untested
+[RANDOM_DEVICE_NOT_DEFAULT]
+us="Warning! Random device is something other than %s!\n\
+This MAY be a security hole.\n"
+
+#Untested
+[RANDOM_DEVICE_WRITABLE]
+us="Warning! %s is writable by users other than root!\n\
+This is probably OK, but you should have your sysadmin fix it.\n\
+Proceeding.\n"
+
+#Untested
+[RANDOM_DEVICE_UNREADABLE]
+us="\
+Warning! Random device %s found, but you can't read it!\n"
+
+#Untested
+[BITS_AND_KEYID]
+us="%6u bits, Key ID "
+
+#Untested
+[KEY_NOT_FOUND]
+us=Key not found: \"%s\"\n
+
+#Untested
+[PGPERR_TROUBLE_BADTRUST_LONG]
+us="Trust packet too long: %lu bytes long"
+
+#Untested
+[PGPERR_TROUBLE_UNKPKTBYTE_LONG]
+us="Unknown packet byte: %02X"
+
+#Untested
+[PGPERR_TROUBLE_KEY2BIG_LONG]
+us="Key grossly oversized: %lu bytes long"
+
+#Untested
+[PGPERR_TROUBLE_NAME2BIG_LONG]
+us="User ID too long: %lu bytes long"
+
+#Untested
+[PGPERR_TROUBLE_SIG2BIG_LONG]
+us="Signature grossly oversized: %lu bytes long"
+
+#Untested
+[PGPERR_TROUBLE_DUPKEYID_LONG]
+us="Duplicate keyID found. Two keys have the same keyID,\n\
+but they are different. This is highly suspicious. The first key
+is:"
+
+#Untested
+[PGPERR_TROUBLE_DUPKEY_LONG]:
+us="A key was found twice in one keyring file. It is a duplicate of:\n"
+
+#Untested
+[PGPERR_TROUBLE_DUPNAME_LONG]
+us="A name was found twice in one keyring file. It is a duplicate of:\n"
+
+#Untested
+[PGPERR_TROUBLE_BAREKEY_LONG]
+us="A key was found twice in one keyring file. It is a duplicate of: "
+
+#Untested
+[PGPERR_TROUBLE_VERSION_BUG_CUR_LONG]
+us="This private key's version number appears to be incorrect.\n\
+PGP version 2.6 had a bug wherein it would improperly change the\n\
+version number of a private key generated by older versions of PGP\n\
+when it was edited. The private key in this key file has a version\n\
+byte that is different from a copy in another key file, and appears\n\
+to be improper. PGP will fix this by changing the version byte in\n\
+the private key to the previous value. The key with the problem
+is:\n"
+
+#Untested
+[PGPERR_TROUBLE_VERSION_BUG_PREV_LONG]
+us="A previously seen private key's version number appears to be\n\
+incorrect. PGP version 2.6 had a bug wherein it would improperly\n\
+change the version byte of a private key generated by older versions\n\
+of PGP when it was edited. The public key in this key file has\n\
+a version byte that is different from a private key elsewhere,\n\
+which appears to be improper. PGP will fix this by changing the\n\
+version byte in the private key to the previous value. The key\n\
+with the problem is:\n"
+
+#Untested
+[PGPERR_KEYIO_READING_LONG]
+us="I/O error reading file: %s"
+
+#Untested
+[PGPERR_KEYIO_FTELL_LONG]
+us="I/O error during call to ftell(): %s"
+
+#Untested
+[PGPERR_PRECEDING_ASSOCIATED_WITH]
+us="The preceeding error was associated with: "
+
+#Untested
+[NOT_PGP_KEYFILE]
+us="File is not a PGP key file. Aborting.\n"
+
+#Untested
+[FOLLOWING_KEYRING_PROBLEMS]
+us="The following problems were encountered while reading the keyring:\n"
+
+#Untested
+[OFFSET_DESCRIPTION]
+us="Offset Description\n"
+
+#Untested
+[UNKNOWN_SIGNATOR]
+us=" (Unknown signator, can't be checked)\n"
+
+#Untested
+[OPEN_PAREN_KEYID]
+us=" (KeyID:"
+
+#Untested
+[REVOKED]
+us="*REVOKED*"
+
+#Untested
+[ABOVE_KEY_REVOKED]
+us="\
+WARNING: The above key has been revoked by its owner,\n\
+possibly because the private key was compromised.\n\
+You cannot use a revoked key for encryption.\n"
+
+#Untested
+[ABOVE_KEY_DISABLED]
+us="\
+WARNING: The above key has been disabled on your keyring. If you\n\
+wish to use it, use \"pgpk -d\" to reenable it.\n"
+
+[ABOVE_KEY_EXPIRED]
+us="\
+WARNING: The above key is not valid for use after %s.\n"
+
+#Untested
+[STILL_USE_EXPIRED_KEY]
+us="\
+WARNING: This key is not valid for use after %s.\n\
+Do you still want to use it? [y/N] "
+
+#Untested
+[PGP_NAMETRUST_UNKNOWN]
+us="\
+WARNING: Because the following name has not been certified\n\
+by a trusted signature, it is not known with a high\n\
+degree of confidence that the above key belongs to:\n"
+
+#Untested
+[PGP_NAMETRUST_UNTRUSTED]
+us="WARNING: The above key is not trusted to belong to:\n"
+
+#Untested
+[PGP_NAMETRUST_MARGINAL]
+us="\
+WARNING: Because the following name is not certified with sufficient\n\
+trusted signatures, it is not known with high confidence that the\n\
+above key actually belongs to:\n"
+
+#Untested
+[PGP_NEWTRUST_NOT_TRUSTED]
+us="\n\
+WARNING: The above key is not trusted to belong to:\n"
+
+#Untested
+[PGP_NEWTRUST_PARTIAL_TRUST]
+us="\n\
+WARNING: Because the following name is not certified with sufficient\n\
+trusted signatures, there is an estimated 1/%-ld probability that\n\
+the above key may not belong to:\n"
+
+#Untested
+[PGP_NEWTRUST_NOT_TRUSTED_SIGNING_KET]
+us="\n\
+WARNING: The signing key is not trusted to belong to:\n"
+
+#Untested
+[PREVIOUSLY_APPROVED_KEY]
+us="\nBut you previously approved using the key with this name.\n"
+
+#Untested
+[DO_YOU_WISH_TO_USE_UNTRUSTED_KEY]
+us="\nDo you want to use the key with this name? [y/N] "
+
+#Untested
+[DONT_TRUST_SIGS_FROM_REVOKED_KEYS]
+us="\
+WARNING: The signing key has been revoked by its owner,\n\
+possibly because the private key was compromised.\n\
+A signature made by this key should not be trusted.\n"
+
+#Untested
+[YOU_HAVE_DISABLED_SIGNING_KEY]
+us="WARNING: You have disabled the signing key\n"
+
+#Untested
+[KEY_HAS_EXPIRED]
+us="WARNING: This key is not valid for use after %s.\n"
+
+#Untested
+[PGP_NAMETRUST_UNTRUSTED_SIGNING_KEY]
+us="\nWARNING: The signing key is not trusted to belong to:\n"
+
+[MESSAGE_IS_ENCRYPTED]
+us="Message is encrypted.\n"
+
+[GETTING_KEY_FOR]
+us="Getting key for %s.\n"
+
+[LOOKING_UP_HOST]
+us="Looking up host %s\n"
+
+[ESTABLISHING_CONNECTION]
+us="Establishing connection\n"
+
+[SENDING_REQUEST]
+us="Sending request\n"
+
+[RECEIVING_DATA]
+us="Receiving data\n"
+
+[CLEANING_UP]
+us="Cleaning up\n"
+
+[COMPLETE]
+us="Complete.\n"
+
+[ONE_KEY_RECEIVED]
+us="One key received. Adding it to your keyring...\n"
+
+[MANY_KEYS_RECEIVED]
+us="%li keys received. Adding them to your keyring...\n"
+
+[UNKNOWN_PROTOCOL]
+us="Unknown protocol %s.\n"
+
+[SENDING_KEY]
+us="Sending key \r"
+
+[RECEIVING_RESPONSE]
+us="Receiving response \r"
+
+#Untested
+[NO_KEYFILE_SPECIFIED]
+us="-a argument requires a key file or URL to add to your keyring."
+
+#Untested
+[UNABLE_TO_IMPORT_KEYFILE]
+us="Unable to import keyfile \"%s\".\n"
+
+#Untested
+[ADDING_KEYS]
+us="Adding keys:\n\n"
+
+#Untested
+[UNABLE_TO_CREATE_KEYLIST]
+us="Unable to create keylist\n"
+
+#Untested
+[NO_KEYS_TO_ADD]
+us="No keys to add \n"
+
+#Untested
+[KEYS_ADDED_SUCCESSFULLY]
+us="Keys added successfully.\n"
+
+#Untested
+[INVALID_SELECTION]
+us="Invalid Selection. Please try again.\n"
+
+[TOO_MANY_MATCHES]
+us="Too many matches; aborting!\n"
+
+[CHOOSE_ONE_ABOVE]
+us="Choose one of the above: "
+
+[PLEASE_SELECT_A_USER_ID]
+us="Please select a user ID %s:\n"
+
+[PLEASE_SELECT_A_USER_ID_WITH_SIG]
+us="Please select a user ID with a signature %s:\n"
+
+[PLEASE_SELECT_A_KEY_WITH_USERID]
+us="Please select a key with a userid %s:"
+
+[PLEASE_SELECT_A_KEY_WITH_SIG]
+us="Please select a key with a signature %s:"
+
+[NO_USER_IDS_SELECTED]
+us="No user IDs selected %s.\n"
+
+[PLEASE_SELECT_A_SIGNATURE]
+us="Please select a signature %s:"
+
+[NO_SIGNATURES_SELECTED]
+us="No signatures selected %s.\n"
+
+[NO_KEYS_SELECTED]
+us="No keys selected %s.\n"
+
+[A_USERID_IS_REQUIRED]
+us="A user ID is required to select the key you want %s.\n\
+Enter the key's user ID: "
+
+[UNABLE_TO_ORDER_KEYSET]
+us="Unable to order keyset\n"
+
+[PLEASE_SELECT_A_KEY]
+us="Please select a key %s:"
+
+[UNABLE_TO_CREATE_ITER]
+us="Unable to create key iterator\n"
+
+[NO_HTTP_SEND]
+us="HTTP cannot be used as a sending protocol at this time.\n"
+
+[UNKNOWN_PROTOCOL]
+us="Unknown protocol %s.\n"
+
+[NO_KEYS_SELECTED_FOR_EXTRACTION]
+us="No keys were selected for extraction.\n"
+
+[ENABLE_THIS_KEY]
+us="\nEnable this key? [y/N] "
+
+[DISABLE_THIS_KEY]
+us="\nDisable this key? [y/N] "
+
+[KEY_ENABLED]
+us="\nKey enabled.\n"
+
+[KEY_DISABLED]
+us="\nKey disabled.\n"
+
+[CANNOT_TRUST_INVALID_KEYS]
+us="This key is not valid, and cannot be assigned trust\n"
+
+[DO_YOU_WISH_TO_CHANGE_INTRODUCER_RELIABITY]
+us="Do you want to change your estimate of this key owner's reliability\n\
+as an introducer of other keys [y/N]? "
+
+[NO_CHANGES_MADE]
+us="No changes made.\n"
+
+[DETERMINE_IN_YOUR_MIND]
+us="\n"\
+"Make a determination in your own mind whether this key actually\n"\
+"belongs to the person whom you think it belongs to, based on available\n"\
+"evidence. If you think it does, then based on your estimate of\n"\
+"that person's integrity and competence in key management, answer\n"\
+"the following question:\n"
+
+[WOULD_YOU_TRUST_THIS_KEY_AND_OWNER]
+us="\nWould you trust this key owner to act as an introducer and\n\
+certify other people's public keys to you?\n\
+(1=I don't know. 2=No. 3=Usually. 4=Yes, always? "
+
+[UNRECOGNIZED_RESPONSE]
+us="Unrecognized response.\n"
+
+[UNABLE_TO_SET_TRUST]
+us="Unable to set trust\n"
+
+[DESCRIBE_CONFIDENCE_AS_INTRODUCER]
+us="\nDescribe the confidence you have in this person as an introducer.\n\
+What are the odds that this key owner is going to be wrong about\n\
+a key which she has signed as an introducer?\n"
+
+[CURRENTLY_INFINITE_TRUST]
+us="(Currently she is listed as having essentially zero chance\
+ of being wrong.)\n"
+
+[CURRENTLY_ZERO_TRUST]
+us="(Currently he is listed as not having any confidence as an\
+ introducer.)\n"
+
+[CURRENTLY_HAS_PERCENT_TRUST_START]
+us="(Currently she is listed as having a one in "
+
+[CURRENTLY_HAS_PERCENT_TRUST_END]
+us=" chance of being wrong.)\n"
+
+[ENTER_A_TRUST_RANGE]
+us="Enter a number from 1 to 2 million"
+
+[OR_HIT_RETURN_TO_LEAVE_UNCHANGED]
+us=", or hit return to leave unchanged."
+
+[WILL_BE_WRONG_TIME_TIME_IN]
+us="\nShe will be wrong one time in: "
+
+[DO_YOU_WANT_THIS_KEY_AXIOMATIC]
+us="\nDo you want to set this key as axiomatic [y/N]? "
+
+[PUBLIC_KEYRING_UPDATED]
+us="Public keyring updated.\n"
+
+[NEED_OLD_PASSPHRASE]
+us="Need old passphrase. "
+
+[NEED_NEW_PASSPHRASE]
+us="Need new passphrase. "
+
+[ENTER_IT_A_SECOND_TIME]
+us="Enter it a second time. "
+
+[PASSPHRASES_ARE_DIFFERENT]
+us="Passphrases are different\n"
+
+[CHANGING_MASTER_KEY_PASSPHRASE]
+us="Changing master key passphrase...\n"
+
+[PASSPHRASE_CHANGE_FAILED_MASTER]
+us="Passphrase change failed for master key.\n"
+
+[CHANGING_SUBKEY_PASSPHRASE]
+us="Changing subkey passphrase...\n"
+
+[PASSPHRASE_CHANGE_FAILED_SUBKEY]
+us="Passphrase change failed for subkey.\n"
+
+[CONFIRM_NON_AXIOMATIC]
+us="\nDo you want to unset this key as axiomatic [y/N]? "
+
+[CONFIRM_ADD_NEW_USERID]
+us="\nDo you want to add a new user ID [y/N]? "
+
+[ENTER_NEW_USERID]
+us="Enter the new user ID: "
+
+[NO_NAME_ENTERED]
+us="No name entered.\n"
+
+[UNABLE_TO_ADD_NEW_USERID]
+us="Unable to add new User ID (%d)\n"
+
+[CONFIRM_CHANGE_PASSPHRASE]
+us="\nDo you want to change your pass phrase (y/N)? "
+
+[CHANGE_PASSPHRASE_FAILED]
+us="Change passphrase failed (%d)\n"
+
+[CONFIRM_SET_DEFAULT_KEY]
+us="\nDo want to set this as your default key [y/N]? "
+
+[KEYRINGS_UPDATED]
+us="Keyrings updated.\n"
+
+[TO_BE_REMOVED_FRAGMENT]
+us="to be removed"
+
+[SIGNATURE_FRAGMENT]
+us="signature"
+
+[USERID_FRAGMENT]
+us="user ID"
+
+[KEY_FRAGMENT]
+us="key"
+
+[SELECTED_KEY_HAS_ONLY_ONE_USERID"]
+us="Selected key has only one user ID; can't be selected %s\n"
+
+[FOLLOWING_OBJECT_HAS_BEEN_SELECTED]
+us="\nThe following %s has been selected %s:\n"
+
+[UNABLE_TO_REMOVE_OBJECT]
+us="Unable to remove object\n"
+
+[TO_BE_SIGNED_FRAGMENT]
+us="to be signed"
+
+[VALIDITY_CERTIFICATION_WARNING]
+us="\n\n\
+READ CAREFULLY: Based on your own direct first-hand knowledge, are\n\
+you absolutely certain that you are prepared to solemnly certify that\n\
+the above public key actually belongs to the user specified by the\n\
+above user ID [y/N]? "
+
+[KEY_SIGNING_CANCELED]
+us="Key sign operation cancelled.\n"
+
+[KEY_SELECTED_FOR_SIGNING_IS]
+us="Key selected for signing is:\n"
+
+[KEY_SIGN_OPERATION_FAILED]
+us="Key sign operation failed\n"
+
+[KEY_SIG_CERT_ADDED]
+us="Key signature certificate added.\n"
+
+[TO_BE_REVOKED_FRAGMENT]
+us="to be revoked"
+
+[YOU_DONT_HAVE_THE_PRIVATE_KEY]
+us="You don't have the private key corresponding to that key\n"
+
+[SIG_ALREADY_REVOKED]
+us="That signature has already been revoked.\n\
+Are you sure you want to add another revocation certificate [y/N]? "
+
+[SIG_REVOCATION_CANCELLED]
+us="Signature revocation cancelled.\n"
+
+[CONFIRM_REVOKE_KEY]
+us="Do you want to permanently revoke your public key\n\
+by issuing a secret key compromise certificate on this key [y/N]? "
+
+[CONFIRM_REVOKE_SIG]
+us="Do you want to revoke this signature [y/N]? "
+
+[REVOKE_CANCELLED]
+us="Revoke cancelled.\n"
+
+[UNABLE_TO_GENERATE_REVOCATION_SIGNATURE]
+us="Unable to generate revocation signature\n"
+
+[KEY_REVOCATION_CERT_ADDED]
+us="Key revocation certificate added.\n"
+
+[SELECT_SIGNING_KEY]
+us="Please select a key to sign with:"
+
+[UNABLE_TO_OPEN_KEYRING]
+us="Unable to open keyring\n"
+
+[PGPINITAPP_FAILED]
+us="pgpInitApp failed\n"
+
+[KEY_IS_ALREADY_REVOKED]
+us="That key has already been revoked\n"
+
+[USE_FORCE_TO_ALLOW_OVERWRITING]
+us="In batchmode, use +force to allow overwriting of output files\n"
+
+[INCONSISTENT_RECIPIENT_SET]
+us="No algorithm available that all keys support.\n"
+
+[UNKNOWN_ERROR]
+us="Unknown error code %i!\n"
+
+[VERIFY_REMOVE_KEY_PUBLIC_PRIVATE]
+us="\nDo you wish to remove this key from your public and private \
+keyrings?\n[y/N]? "
+
+[UNABLE_TO_ITERATE_KEY]
+us="Unable to iterate key!\n";
+
+[CANCELED]
+us="Canceled.\n"
+
+[REMOVED]
+us="Removed.\n"
+
+[NEED_FILE_TO_SAVE]
+us="Save file as [%s] "
+
+[PGP_NEWTRUST_NOT_TRUSTED_SIGNING_KEY]
+us="WARNING: The signing key is not trusted to belong to:\n"
+
+[TO_DISABLE_OR_ENABLE]
+us="to disable or enable"
+
+[TO_EDIT]
+us="to edit"
+
+[SELECTED_KEY_HAS_ONLY_ONE_USERID]
+us="Selected key has only one user ID, can't be selected %s\n"
+
+[NO_DEFAULT_PRIVATE_KEY]
+us="No default private key\n"
+
+[MULTIPLE_RECIPIENTS_MATCHED]
+us="WARNING: %i matches were found for recipient %s.\n\
+This may not be what you intended.\n"
+
+[ENOUGH_THANK_YOU]
+us="\a -Enough, thank you.\n"
+
+[SEEDING_RANDPOOL_FROM_DEVICE]
+us="Seeding entropy pool with up to %u bits from %s...\n"
+
+[COMPLETE_READ_NUM_BITS]
+us="Complete. Read %u bits.\n"
+
+[RSA_AND_DH_RECIPS]
+us="WARNING: You are encrypting to both RSA and Diffie-Hellman keys.\n\
+If the RSA user is still using PGP version 2.6.3 or earlier; 4.0; or 4.5,\n\
+she will not be able to decrypt this message.\n"
+
+[ONLY_ONE_USER_ALLOWED]
+us=Specified operation may only be performed on one argument per execution.\n
+
+[CANNOT_DISABLE_AXIOMATIC_KEYS]
+us=You cannot disable an axiomatic key. Use pgpk -e to change your\n\
+trust of this key, first.\n
+
+[RETRIEVING_URL]
+us="Retreiving %s:/%s:%i%s\n"
+
+[ADD_THESE_KEYS]
+us="\nAdd these keys to your keyring? [Y/n] "
+
+[ABORTED]
+us="\nAborted.\n"
+
+[WARNING_NO_MRK]
+us="A requested Message Recovery Key (MRK) for this key was not\
+found.\n"
+
+[MRK_FOUND]
+us="Message Recovery Key (MRK) found. Will also encrypt this message\n\
+to Key ID %s.\n"
+
+#Everything from here down is automatically generated.
+
+[PGPERR_OK]
+us="No errors\n"
+
+[PGPERR_GENERIC]
+us="Generic error (should be changed)\n"
+
+[PGPERR_NOMEM]
+us="Out of Memory\n"
+
+[PGPERR_BADPARAM]
+us="Invalid Parameter\n"
+
+[PGPERR_NO_FILE]
+us="Cannot open file\n"
+
+[PGPERR_NO_KEYBITS]
+us="Internal keyring bits exhausted\n"
+
+[PGPERR_BAD_HASHNUM]
+us="Unknown hash number\n"
+
+[PGPERR_BAD_CIPHERNUM]
+us="Unknown cipher number\n"
+
+[PGPERR_BAD_KEYLEN]
+us="Illegal key length for cipher\n"
+
+[PGPERR_SIZEADVISE]
+us="SizeAdvise promise not kept\n"
+
+[PGPERR_CONFIG]
+us="Error parsing configuration\n"
+
+[PGPERR_CONFIG_BADFUNC]
+us="Invalid configuration function\n"
+
+[PGPERR_CONFIG_BADOPT]
+us="Unknown configuration option\n"
+
+[PGPERR_STRING_NOT_FOUND]
+us="Requested string not found\n"
+
+[PGPERR_STRING_NOT_IN_LANGUAGE]
+us="Requested string not in language\n"
+
+[PGPERR_KEY_ISLOCKED]
+us="Key requires passphrase to unlock\n"
+
+[PGPERR_KEY_UNUNLOCKABLE]
+us="Key requires passphrase each time\n"
+
+[PGPERR_SIG_ERROR]
+us="Error while processing signature\n"
+
+[PGPERR_ADDSIG_ERROR]
+us="Cannot add signature\n"
+
+[PGPERR_CANNOT_DECRYPT]
+us="Cannot decrypt message\n"
+
+[PGPERR_ADDESK_ERROR]
+us="Cannot add encrypted session key\n"
+
+[PGPERR_UNK_STRING2KEY]
+us="Don't know how to convert pass\n"
+
+[PGPERR_BAD_STRING2KEY]
+us="Invalid conversion from pass\n"
+
+[PGPERR_ESK_BADTYPE]
+us="Unknown encrypted session key type\n"
+
+[PGPERR_ESK_TOOSHORT]
+us="Encrypted session key too short\n"
+
+[PGPERR_ESK_TOOLONG]
+us="Encrypted session key too long\n"
+
+[PGPERR_ESK_BADVERSION]
+us="Encrypted session key version\n"
+
+[PGPERR_ESK_BADALGORITHM]
+us="Encrypted session key algorithm\n"
+
+[PGPERR_ESK_BITSWRONG]
+us="Wrong number of bits in ESK\n"
+
+[PGPERR_ESK_NOKEY]
+us="Can't find key to decrypt session key\n"
+
+[PGPERR_ESK_NODECRYPT]
+us="Can't decrypt this session key\n"
+
+[PGPERR_ESK_BADPASS]
+us="Passphrase incorrect\n"
+
+[PGPERR_SIG_BADTYPE]
+us="Unknown signature type\n"
+
+[PGPERR_SIG_TOOSHORT]
+us="Signature too short\n"
+
+[PGPERR_SIG_TOOLONG]
+us="Signature too long\n"
+
+[PGPERR_SIG_BADVERSION]
+us="Signature version unknown\n"
+
+[PGPERR_SIG_BADALGORITHM]
+us="Signature algorithm unknown\n"
+
+[PGPERR_SIG_BITSWRONG]
+us="Wrong number of bits in signature\n"
+
+[PGPERR_SIG_NOKEY]
+us="Can't find necessary key to check sig\n"
+
+[PGPERR_SIG_BADEXTRA]
+us="Invalid Extra Data for Signature\n"
+
+[PGPERR_NO_PUBKEY]
+us="No public key found\n"
+
+[PGPERR_NO_SECKEY]
+us="No secret key found\n"
+
+[PGPERR_UNKNOWN_KEYID]
+us="No matching keyid found\n"
+
+[PGPERR_NO_RECOVERYKEY]
+us="Requested message recovery key\n"
+
+[PGPERR_COMMIT_INVALID]
+us="Invalid commit response\n"
+
+[PGPERR_CANNOT_HASH]
+us="Cannot hash message\n"
+
+[PGPERR_UNBALANCED_SCOPE]
+us="Unbalanced scope\n"
+
+[PGPERR_WRONG_SCOPE]
+us="Data sent in wrong scope\n"
+
+[PGPERR_UI_INVALID]
+us="Invalid UI Callback Object\n"
+
+[PGPERR_CB_INVALID]
+us="Invalid Parser Callback\n"
+
+[PGPERR_INTERRUPTED]
+us="Interrupted encrypt/decrypt\n"
+
+[PGPERR_PUBKEY_TOOSMALL]
+us="Public Key too small for data\n"
+
+[PGPERR_PUBKEY_TOOBIG]
+us="Public key is too big for this version\n"
+
+[PGPERR_PUBKEY_UNIMP]
+us="Unimplemented public key operation\n"
+
+[PGPERR_RSA_CORRUPT]
+us="Corrupt data decrypting RSA block\n"
+
+[PGPERR_PK_CORRUPT]
+us="Corrupt data decrypting public\n"
+
+[PGPERR_CMD_TOOBIG]
+us="Command to Buffer too big\n"
+
+[PGPERR_FIFO_READ]
+us="Incomplete read from Fifo\n"
+
+[PGPERR_VRFYSIG_WRITE]
+us="Data illegally written into\n"
+
+[PGPERR_VRFYSIG_BADANN]
+us="Invalid annotation to signature\n"
+
+[PGPERR_ADDHDR_FLUSH]
+us="Cannot flush buffer until size\n"
+
+[PGPERR_JOIN_BADANN]
+us="Invalid annotation to join module\n"
+
+[PGPERR_RANDSEED_TOOSMALL]
+us="Not enough data in randseed file\n"
+
+[PGPERR_ENV_LOWPRI]
+us="Env Var not set: priority too low\n"
+
+[PGPERR_ENV_BADVAR]
+us="Invalid environment variable\n"
+
+[PGPERR_CHARMAP_UNKNOWN]
+us="Unknown Charset\n"
+
+[PGPERR_FILE_PERMISSIONS]
+us="Unsufficient file permissions\n"
+
+[PGPERR_FILE_WRITELOCKED]
+us="File already open for writing\n"
+
+[PGPERR_FILE_BADOP]
+us="Invalid PgpFile Operation\n"
+
+[PGPERR_FILE_OPFAIL]
+us="PgpFile Operation Failed\n"
+
+[PGPERR_IMMUTABLE]
+us="Attempt to change an\n"
+
+[PGPERR_PARSEASC_INCOMPLETE]
+us="Ascii Armor Input Incomplete\n"
+
+[PGPERR_PARSEASC_BADINPUT]
+us="PGP text input is corrupted\n"
+
+[PGPERR_FILEFIFO_SEEK]
+us="Temp-File Seek Error\n"
+
+[PGPERR_FILEFIFO_WRITE]
+us="Temp-File Write Error\n"
+
+[PGPERR_FILEFIFO_READ]
+us="Temp-File Read Error\n"
+
+[PGPERR_FILEIO_BADPKT]
+us="Corrupted or bad packet in\n"
+
+[PGPERR_SYSTEM_PGPK]
+us="Error Executing PGPK Program\n"
+
+[PGPERR_KEYIO_READING]
+us="I/O error reading keyring\n"
+
+[PGPERR_KEYIO_WRITING]
+us="I/O error writing keyring\n"
+
+[PGPERR_KEYIO_FTELL]
+us="I/O error finding keyring position\n"
+
+[PGPERR_KEYIO_SEEKING]
+us="I/O error seeking keyring\n"
+
+[PGPERR_KEYIO_OPENING]
+us="I/O error opening keyring\n"
+
+[PGPERR_KEYIO_CLOSING]
+us="I/O error closing keyring\n"
+
+[PGPERR_KEYIO_FLUSHING]
+us="I/O error flushing keyring\n"
+
+[PGPERR_KEYIO_EOF]
+us="Unexpected EOF fetching key packet\n"
+
+[PGPERR_KEYIO_BADPKT]
+us="Bad data found where key\n"
+
+[PGPERR_KEYIO_BADFILE]
+us="Not a keyring file\n"
+
+[PGPERR_TROUBLE_KEYSUBKEY]
+us="Key matches subkey\n"
+
+[PGPERR_TROUBLE_SIGSUBKEY]
+us="Signature by subkey\n"
+
+[PGPERR_TROUBLE_BADTRUST]
+us="Trust packet malformed\n"
+
+[PGPERR_TROUBLE_UNKPKTBYTE]
+us="Unknown packet byte in keyring\n"
+
+[PGPERR_TROUBLE_UNXSUBKEY]
+us="Unexpected subkey (before key)\n"
+
+[PGPERR_TROUBLE_UNXNAME]
+us="Unexpected name (before key)\n"
+
+[PGPERR_TROUBLE_UNXSIG]
+us="Unexpected sig (before key)\n"
+
+[PGPERR_TROUBLE_UNXUNK]
+us="Packet of unknown type in unexpected\n"
+
+[PGPERR_TROUBLE_UNXTRUST]
+us="Unexpected trust packet\n"
+
+[PGPERR_TROUBLE_KEY2BIG]
+us="Key grossly oversized\n"
+
+[PGPERR_TROUBLE_SEC2BIG]
+us="Secret key grossly oversized\n"
+
+[PGPERR_TROUBLE_NAME2BIG]
+us="Name grossly oversized\n"
+
+[PGPERR_TROUBLE_SIG2BIG]
+us="Sig grossly oversized\n"
+
+[PGPERR_TROUBLE_UNK2BIG]
+us="Packet of unknown type too large\n"
+
+[PGPERR_TROUBLE_DUPKEYID]
+us="Duplicate KeyID, different keys\n"
+
+[PGPERR_TROUBLE_DUPKEY]
+us="Duplicate key (in same keyring)\n"
+
+[PGPERR_TROUBLE_DUPSEC]
+us="Duplicate secret (in same keyring)\n"
+
+[PGPERR_TROUBLE_DUPNAME]
+us="Duplicate name (in same keyring)\n"
+
+[PGPERR_TROUBLE_DUPSIG]
+us="Duplicate signature (in same keyring)\n"
+
+[PGPERR_TROUBLE_DUPUNK]
+us="Duplicate unknown \"thing\" in keyring\n"
+
+[PGPERR_TROUBLE_BAREKEY]
+us="Key found with no names\n"
+
+[PGPERR_TROUBLE_VERSION_BUG_PREV]
+us="Bug introduced by legal_kludge\n"
+
+[PGPERR_TROUBLE_VERSION_BUG_CUR]
+us="Bug introduced by legal_kludge\n"
+
+[PGPERR_TROUBLE_OLDSEC]
+us="Passphrase is out of date\n"
+
+[PGPERR_TROUBLE_NEWSEC]
+us="Passphrase is newer than another\n"
+
+[PGPERR_KEY_NO_RSA_ENCRYPT]
+us="No RSA Encryption/Signature support\n"
+
+[PGPERR_KEY_NO_RSA_DECRYPT]
+us="No RSA Decryption/Verification support\n"
+
+[PGPERR_KEY_NO_RSA]
+us="No RSA key support\n"
+
+[PGPERR_KEY_LONG]
+us="Key packet has trailing junk\n"
+
+[PGPERR_KEY_SHORT]
+us="Key packet truncated\n"
+
+[PGPERR_KEY_VERSION]
+us="Key version unknown\n"
+
+[PGPERR_KEY_PKALG]
+us="Key algorithm unknown\n"
+
+[PGPERR_KEY_MODMPI]
+us="Key modulus mis-formatted\n"
+
+[PGPERR_KEY_EXPMPI]
+us="Key exponent mis-formatted\n"
+
+[PGPERR_KEY_MODEVEN]
+us="RSA public modulus is even\n"
+
+[PGPERR_KEY_EXPEVEN]
+us="RSA public exponent is even\n"
+
+[PGPERR_KEY_MPI]
+us="Key component mis-formatted\n"
+
+[PGPERR_KEY_UNSUPP]
+us="Key is not supported by this version of PGP\n"
+
+[PGPERR_SIG_LONG]
+us="Signature packet has trailing junk\n"
+
+[PGPERR_SIG_SHORT]
+us="Signature truncated\n"
+
+[PGPERR_SIG_MPI]
+us="Signature integer mis-formatted\n"
+
+[PGPERR_SIG_PKALG]
+us="Signature algorithm unknown\n"
+
+[PGPERR_SIG_EXTRALEN]
+us="Bad signature extra material (not 5)\n"
+
+[PGPERR_SIG_VERSION]
+us="Signature version unknown\n"
+
+[PGPERR_KEYDB_BADPASSPHRASE]
+us="Bad passphrase\n"
+
+[PGPERR_KEYDB_KEYDBREADONLY]
+us="Key database is read-only\n"
+
+[PGPERR_KEYDB_NEEDMOREBITS]
+us="Insufficient random bits\n"
+
+[PGPERR_KEYDB_OBJECTREADONLY]
+us="Object is read-only\n"
+
+[PGPERR_KEYDB_INVALIDPROPERTY]
+us="Invalid property name\n"
+
+[PGPERR_KEYDB_BUFFERTOOSHORT]
+us="Buffer too short\n"
+
+[PGPERR_KEYDB_CORRUPT]
+us="Key database is corrupt\n"
+
+[PGPERR_KEYDB_VERSIONTOONEW]
+us="Data is too new to be read\n"
+
+[PGPERR_KEYDB_IOERROR]
+us="Input/output error\n"
+
+[PGPERR_KEYDB_VALUETOOLONG]
+us="Value too long\n"
+
+[PGPERR_KEYDB_DUPLICATE_CERT]
+us="Duplicate certification\n"
+
+[PGPERR_KEYDB_DUPLICATE_USERID]
+us="Duplicate UserID\n"
+
+[PGPERR_KEYDB_CERTIFYINGKEY_DEAD]
+us="Certifying key no longer\n"
+
+[PGPERR_KEYDB_OBJECT_DELETED]
+us="Object has been deleted\n"
--- /dev/null
+all: manual.txt mutt.man manual.html
+
+manual.txt: manual.sgml
+ sgml2txt manual
+
+manual.html: manual.sgml
+ sgml2html manual
+
+mutt.man: mutt.sgml
+ sgml2txt -man mutt
+
+clean clean-real distclean:
+ rm -f *~ *.html *.orig *.rej
--- /dev/null
+<!doctype linuxdoc system>
+
+<article>
+
+<title>The Mutt E-Mail Client
+<author>by Michael Elkins <htmlurl url="mailto:me@cs.hmc.edu" name="<me@cs.hmc.edu>">
+<date>v0.92.2, 23 April 1998
+<abstract>
+``All mail clients suck. This one just sucks less.'' -me, circa 1995
+</abstract>
+
+<sect>Introduction
+<p>
+<bf/Mutt/ is a small but very powerful text-based MIME mail client. Mutt is
+highly configurable, and is well suited to the mail power user with advanced
+features like key bindings, keyboard macros, mail threading, regular
+expression searches and a powerful pattern matching language for selecting
+groups of messages.
+
+<sect1>Mutt Home Page
+<p>
+<htmlurl url="http://www.cs.hmc.edu/~me/mutt/index.html"
+name="http://www.cs.hmc.edu/~me/mutt/index.html">
+
+<sect1>Mailing Lists
+<p>
+To subscribe to one of the following mailing lists, send a message with the
+word <em/subscribe/ in the subject to
+<tt/list-name/<em/-request/<tt/@cs.hmc.edu/.
+
+<itemize>
+<item><htmlurl url="mailto:mutt-announce-request@cs.hmc.edu"
+name="mutt-announce@cs.hmc.edu"> -- low traffic list for announcements
+<item><htmlurl url="mailto:mutt-users-request@cs.hmc.edu"
+name="mutt-users@cs.hmc.edu"> -- help, bug reports and feature requests
+<item><htmlurl url="mailto:mutt-dev-request@cs.hmc.edu" name="mutt-dev@cs.hmc.edu"> -- development mailing list
+</itemize>
+
+<bf/Note:/ all messages posted to <em/mutt-announce/ are automatically
+forwarded to <em/mutt-users/, so you do not need to be subscribed to both
+lists.
+
+<sect1>Software Distribution Sites
+<p>
+<itemize>
+<item><htmlurl url="ftp://ftp.cs.hmc.edu/pub/me/mutt/"
+name="ftp://ftp.cs.hmc.edu/pub/me/mutt/">
+</itemize>
+
+<sect1>IRC
+<p>
+Visit channel <em/#mutt/ on <htmlurl url="http://www.dal.net" name="DALnet
+(www.dal.net)"> to chat with other people interested in Mutt.
+
+<sect1>USENET
+<p>
+See the newsgroup <htmlurl url="news:comp.mail.mutt" name="comp.mail.mutt">.
+
+<sect1>Copyright
+<p>
+Mutt is Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+<sect>Getting Started
+<p>
+
+This section is intended as a brief overview of how to use Mutt. There are
+many other features which are described elsewhere in the manual. There
+is even more information available in the Mutt FAQ and various web
+pages. See the <htmlurl url="http://www.cs.hmc.edu/~me/mutt/"
+name="Mutt Page"> for more details.
+
+The keybindings described in this section are the defaults as distributed.
+Your local system administrator may have altered the defaults for your site.
+You can always type ``?'' in any menu to display the current bindings.
+
+The first thing you need to do is invoke mutt, simply by typing mutt
+at the command line. There are various command-line options, see
+either the mutt man page or the <ref id="commandline" name="reference">.
+
+<sect1>Moving Around in Menus
+<p>
+
+Information is presented in menus, very similar to ELM. Here is a table
+showing the common keys used to navigate menus in Mutt.
+
+<tscreen><verb>
+j or Down next-entry move to the next entry
+k or Up previous-entry move to the previous entry
+z or PageDn page-down go to the next page
+Z or PageUp page-up go to the previous page
+= or Home first-entry jump to the first entry
+* or End last-entry jump to the last entry
+q quit exit the current menu
+? help list all keybindings for the current menu
+</verb></tscreen>
+
+<sect1>Editing Input Fields<label id="editing">
+<p>
+Mutt has a builtin line editor which is used as the primary way to input
+textual data such as email addresses or filenames. The keys used to move
+around while editing are very similar to those of Emacs.
+
+<tscreen><verb>
+^A or <Home> bol move to the start of the line
+^B or <Left> backward-char move back one char
+^D or <Delete> delete-char delete the char under the cursor
+^E or <End> eol move to the end of the line
+^F or <Right> forward-char move forward one char
+^K kill-eol delete to the end of the line
+^U kill-line delete entire line
+^W kill-word kill the word in front of the cursor
+<Up> history-up recall previous string from history
+<Down> history-down recall next string from history
+<BackSpace> backspace kill the char in front of the cursor
+^G n/a abort
+<Tab> n/a complete filename (only when prompting for a file)
+<Return> n/a finish editing
+</verb></tscreen>
+
+You can remap the <em/editor/ functions using the <ref id="bind" name="bind">
+command. For example, to make the <em/Delete/ key delete the character in
+front of the cursor rather than under, you could use
+
+<tt/bind editor delete backspace/
+
+<sect1>Reading Mail - The Index and Pager
+<p>
+
+Similar to many other mail clients, there are two modes in which mail is
+read in Mutt. The first is the index of messages in the mailbox, which is
+called the ``index'' in Mutt. The second mode is the display of the
+message contents. This is called the ``pager.''
+
+The next few sections describe the functions provided in each of these
+modes.
+
+<sect2>The Message Index
+<p>
+
+<tscreen><verb>
+c change to a different mailbox
+ESC c change to a folder in read-only mode
+C copy the current message to another mailbox
+ESC C decode a message and copy it to a folder
+ESC s decode a message and save it to a folder
+D delete messages matching a pattern
+d delete the current message
+F mark as important
+l show messages matching a pattern
+N mark message as new
+o change the current sort method
+O reverse sort the mailbox
+q save changes and exit
+s save-message
+t toggle the tag on a message
+ESC t toggle tag on entire message thread
+u undelete-message
+v view-attachments
+x abort changes and exit
+<Return> display-message
+<Tab> jump to the next new message
+@ show the author's full e-mail address
+$ save changes to mailbox
+/ search
+ESC / search-reverse
+^L clear and redraw the screen
+^T tag messages matching a pattern
+^U undelete messages matching a pattern
+</verb></tscreen>
+
+<sect3>Status Flags
+<p>
+
+In addition to who sent the message and the subject, a short summary of
+the disposition of each message is printed beside the message number.
+Zero or more of the following ``flags'' may appear, which mean:
+
+<p>
+<tscreen><verb>
+D message is deleted
+K contains a PGP public key
+M requires mailcap to view
+N message is new
+O message is old
+P message is PGP encrypted
+r message has been replied to
+S message is PGP signed
+! message is flagged
+* message is tagged
+</verb></tscreen>
+
+Some of the status flags can be turned on or off using
+<itemize>
+<item><bf/set-flag/ (default: w)
+<item><bf/clear-flag/ (default: W)
+</itemize>
+
+<p>
+Furthermore, the following flags reflect who the message is addressed
+to. They can be customized with the
+<ref id="to_chars" name="$to_chars"> variable.
+
+<p>
+<tscreen><verb>
++ message is to you and you only
+T message is to you, but also to or cc'ed to others
+C message is cc'ed to you
+F message is from you
+</verb></tscreen>
+
+<sect2>The Pager
+<p>
+
+By default, Mutt uses its builtin pager to display the body of messages.
+The pager is very similar to the Unix program <em/less/ though not nearly as
+featureful.
+
+<tscreen><verb>
+<Return> go down one line
+<Space> display the next page (or next message if at the end of a message)
+- go back to the previous page
+n display the next message
+? show keybindings
+/ search for a regular expression (pattern)
+\ toggle search pattern coloring
+</verb></tscreen>
+
+In addition, many of the functions from the <em/index/ are available in
+the pager, such as <em/delete-message/ or <em/copy-message/ (this is one
+advantage over using an external pager to view messages).
+
+Also, the internal pager supports a couple other advanced features. For
+one, it will accept and translate the ``standard'' nroff sequences for
+bold and underline. These sequences are a series of either the letter,
+backspace (^H), the letter again for bold or the letter, backspace,
+``_'' for denoting underline. Mutt will attempt to display these
+in bold and underline respectively if your terminal supports them. If
+not, you can use the bold and underline <ref id="color" name="color">
+objects to specify a color or mono attribute for them.
+
+Additionally, the internal pager supports the ANSI escape sequences for
+character attributes. Mutt translates them into the correct color and
+character settings. The sequences Mutt supports are:
+
+<p>
+<tscreen><verb>
+ESC [ Ps;Ps;Ps;...;Ps m
+where Ps =
+0 All Attributes Off
+1 Bold on
+4 Underline on
+5 Blink on
+7 Reverse video on
+3x Foreground color is x
+4x Background color is x
+
+Colors are
+0 black
+1 red
+2 green
+3 yellow
+4 blue
+5 magenta
+6 cyan
+7 white
+</verb></tscreen>
+
+Mutt uses these attributes for handling text/enriched messages, and they
+can also be used by an external <ref id="auto_view" name="autoview">
+script for highlighting purposes. <bf/Note:/ If you change the colors for your
+display, for example by changing the color associated with color2 for
+your xterm, then that color will be used instead of green.
+
+<sect2>Threaded Mode<label id="threads">
+<p>
+When the mailbox is <ref id="sort" name="sorted"> by <em/threads/, there are
+a few additional functions available in the <em/index/ and <em/pager/ modes.
+
+<tscreen><verb>
+^D delete-thread delete all messages in the current thread
+^U undelete-thread undelete all messages in the current thread
+^N next-thread jump to the start of the next thread
+^P previous-thread jump to the start of the previous thread
+^R read-thread mark the current thread as read
+ESC d delete-subthread delete all messages in the current subthread
+ESC u undelete-subthread undelete all messages in the current subthread
+ESC n next-subthread jump to the start of the next subthread
+ESC p previous-subthread jump to the start of the previous subthread
+ESC r read-subthread mark the current subthread as read
+ESC t tag-thread toggle the tag on the current thread
+</verb></tscreen>
+
+See also: <ref id="strict_threads" name="$strict_threads">.
+
+<sect2>Miscellaneous Functions
+<p><bf/create-alias/<label id="create-alias"> (default: a)<newline>
+
+Creates a new alias based upon the current message (or prompts for a
+new one). Once editing is complete, an <ref id="alias" name="alias">
+command is added to the file specified by the <ref id="alias_file"
+name="$alias_file"> variable for future use. <bf/Note:/
+Specifying an <ref id="alias_file" name="$alias_file">
+does not add the aliases specified there-in, you must also <ref
+id="source" name="source"> the file.
+
+<p><bf/display-headers/<label id="display-headers"> (default: h)<newline>
+
+Toggles the weeding of message header fields specified by <ref id="ignore"
+name="ignore"> commands.
+
+<p><bf/enter-command/<label id="enter-command"> (default: ``:'')<newline>
+
+This command is used to execute any command you would normally put in a
+configuration file. A common use is to check the settings of variables, or
+in conjunction with <ref id="macro" name="macros"> to change settings on the
+fly.
+
+<p><bf/extract-keys/<label id="extract-keys"> (default: ESC k)<newline>
+
+This command extracts PGP public keys from the current or tagged
+message(s) and adds them to your <ref id="pgp_v2_pubring"
+name="$pgp_v2_pubring"> or <ref
+id="pgp_v5_pubring" name="$pgp_v5_pubring">
+depending on <ref id="pgp_key_version"
+name="$pgp_key_version">.
+
+<p><bf/forget-passphrase/<label id="forget-passphrase"> (default:
+^F)<newline>
+
+This command wipes the PGP passphrase from memory. It is useful, if
+you misspelled the passphrase.
+
+<p><bf/list-reply/<label id="list-reply"> (default: L)<newline>
+
+Reply to the current or tagged message(s) by extracting any addresses which
+match the addresses given by the <ref id="lists" name="lists"> command.
+Using this when replying to messages posted to mailing lists help avoid
+duplicate copies being sent to the author of the message you are replying
+to.
+
+<bf/pipe-message/<label id="pipe-message"> (default: |)<newline>
+
+Asks for an external Unix command and pipes the current or
+tagged message(s) to it. The variables <ref id="pipe_decode"
+name="$pipe_decode">, <ref id="pipe_split"
+name="$pipe_split">, <ref id="pipe_sep"
+name="$pipe_sep"> and <ref id="wait_key"
+name="$wait_key"> control the exact behaviour of this
+function.
+
+<bf/shell-escape/<label id="shell-escape"> (default: !)<newline>
+
+Asks for an external Unix command and executes it. The <ref
+id="wait_key" name="$wait_key"> can be used to control
+whether Mutt will wait for a key to be pressed when the command returns
+(presumably to let the user read the output of the command), based on
+the return status of the named command.
+
+<bf/toggle-quoted/<label id="toggle-quoted"> (default: T)<newline>
+
+The <em/pager/ uses the <ref id="quote_regexp"
+name="$quote_regexp"> variable to detect quoted text when
+displaying the body of the message. This function toggles the display
+of the quoted material in the message. It is particularly useful when
+are interested in just the response and there is a large amount of
+quoted text in the way.
+
+<bf/skip-quoted/<label id="skip-quoted"> (default: S)<newline>
+
+This function will go to the next line of non-quoted text which come
+after a line of quoted text in the internal pager.
+
+<sect1>Sending Mail
+<p>
+
+The following bindings are available in the <em/index/ for sending
+messages.
+
+<tscreen><verb>
+m compose compose a new message
+r reply reply to sender
+g group-reply reply to all recipients
+L list-reply reply to mailing list address
+f forward forward message
+b bounce bounce (remail) message
+ESC k mail-key mail a PGP public key to someone
+</verb></tscreen>
+
+Bouncing a message sends the message as is to the recipient you
+specify. Forwarding a message allows you to add comments or
+modify the message you are forwarding. Bouncing a message uses
+the <ref id="sendmail" name="sendmail">
+command to send a copy of a message to recipients as if they were
+original recipients of the message. See also <ref id="mime_forward"
+name="$mime_forward">.
+
+Mutt will then enter the <em/compose/ menu and prompt you for the
+recipients to place on the ``To:'' header field. Next, it will ask
+you for the ``Subject:'' field for the message, providing a default if
+you are replying to or forwarding a message. See also <ref id="askcc"
+name="$askcc">, <ref id="askbcc" name="$askbcc">, <ref
+id="autoedit" name="$autoedit">, and <ref id="fast_reply"
+name="$fast_reply"> for changing how Mutt asks these
+questions.
+
+Mutt will then automatically start your <ref id="editor"
+name="$editor"> on the message body. If the <ref id="edit_headers"
+name="$edit_headers"> variable is set, the headers will be at
+the top of the message in your editor. Any messages you are replying
+to will be added in sort order to the message, with appropriate <ref
+id="attribution" name="$attribution">, <ref id="indent_string"
+name="$indent_string"> and <ref id="post_indent_string"
+name="$post_indent_string">. When forwarding a
+message, if the <ref id="mime_forward" name="$mime_forward">
+variable is unset, a copy of the forwarded message will be included. If
+you have specified a <ref id="signature" name="$signature">, it
+will be appended to the message.
+
+Once you have finished editing the body of your mail message, you are
+returned to the <em/compose/ menu. The following options are available:
+
+<tscreen><verb>
+a attach-file attach a file
+ESC k attach-key attach a PGP public key
+d edit-description edit description on attachment
+D detach-file detach a file
+T edit-to edit the To field
+c edit-cc edit the Cc field
+b edit-bcc edit the Bcc field
+y send-message send the message
+s edit-subject edit the Subject
+f edit-fcc specify an ``Fcc'' mailbox
+p pgp-menu select PGP options (``i'' version only)
+P postpone-message postpone this message until later
+q quit quit (abort) sending the message
+i ispell check spelling (if available on your system)
+^F forget-passphrase whipe PGP passphrase from memory
+</verb></tscreen>
+
+<sect2>Editing the message header<label id="edit_headers">
+<p>
+When editing the header of your outgoing message, there are a couple of
+special features available.
+
+If you specify<newline>
+<tt/Fcc:/ <em/filename/<newline>
+Mutt will pick up <em/filename/
+just as if you had used the <em/edit-fcc/ function in the <em/compose/ menu.
+
+You can also attach files to your message by specifying<newline>
+<tt/Attach:/ <em/filename/ [ <em/description/ ]<newline>
+where <em/filename/ is the file to attach and <em/description/ is an
+optional string to use as the description of the attached file.
+
+When replying to messages, if you remove the <em/In-Reply-To:/ field from
+the header field, Mutt will not generate a <em/References:/ field, which
+allows you to create a new message thread.
+
+If you want to use PGP, you can specify
+
+<tt/Pgp:/ [ <tt/E/ | <tt/S/ | <tt/S<id/> ] <newline>
+
+``E'' encrypts, ``S'' signs and
+``S<id>'' signs with the given key, setting <ref
+id="pgp_sign_as" name="$pgp_sign_as"> permanently.
+
+Also see <ref id="edit_headers" name="edit_headers">.
+
+<sect1>Postponing Mail<label id="postponing_mail">
+<p>
+
+At times it is desirable to delay sending a message that you have
+already begun to compose. When the <em/postpone-message/ function is
+used in the <em/compose/ menu, the body of your message and attachments
+are stored in the mailbox specified by the <ref id="postponed"
+name="$postponed"> variable. This means that you can recall the
+message even if you exit Mutt and then restart it at a later time.
+
+Once a message is postponed, there are several ways to resume it. From the
+command line you can use the ``-p'' option, or if you <em/compose/ a new
+message from the <em/index/ or <em/pager/ you will be prompted if postponed
+messages exist. If multiple messages are currently postponed, the
+<em/postponed/ menu will pop up and you can select which message you would
+like to resume.
+
+<bf/Note:/ If you postpone a reply to a message, the reply setting of
+the message is only updated when you actually finish the message and
+send it. Also, you must be in the same folder with the message you
+replied to for the status of the message to be updated.
+
+See also the <ref id="postpone" name="$postpone"> quad-option.
+
+<sect>Configuration
+<p>
+
+While the default configuration (or ``preferences'') make Mutt usable right
+out of the box, it is often desirable to tailor Mutt to suit your own tastes.
+When Mutt is first invoked, it will attempt to read the ``system''
+configuration file (defaults set by your local system administrator), unless
+the ``-n'' <ref id="commandline" name="command line"> option is
+specified. This file is typically
+<tt>/usr/local/share/Muttrc</tt> or <tt>/usr/local/lib/Muttrc</tt>. Next,
+it looks for a file in your home directory named <tt/.muttrc/. In this file
+is where you place <ref id="commands" name="commands"> to configure Mutt.
+
+In addition, mutt supports version specific configuration files that are
+parsed instead of the default files as explained above. For instance, if
+your system has a <tt/Muttrc-0.88/ file in the system configuration
+directory, and you are running version 0.88 of mutt, this file will be
+sourced instead of the <tt/Muttrc/ file. The same is true of the user
+configuration file, if you have a file <tt/.muttrc-0.88.6/ in your home
+directory, when you run mutt version 0.88.6, it will source this file
+instead of the default <tt/.muttrc/ file. The version number is the
+same which is visible using the ``-v'' <ref id="commandline"
+name="command line"> switch or using the <tt/show-version/ key (default:
+V) from the index menu.
+
+<sect1>Syntax of Initialization Files
+<p>
+
+An initialization file consists of a series of <ref id="commands"
+name="commands">. Each line of the file may contain one or more commands.
+When multiple commands are used, they must be separated by a semicolon (;).
+<tscreen><verb>
+ set realname='Mutt user' ; ignore x-
+</verb><tscreen>
+The hash mark, or pound sign
+(``#''), is used as a ``comment'' character. You can use it to
+annotate your initialization file. All text after the comment character
+to the end of the line is ignored. For example,
+
+<tscreen><verb>
+my_hdr X-Disclaimer: Why are you listening to me? # This is a comment
+</verb></tscreen>
+
+Single quotes (') and double quotes (&dquot;) can be used to quote strings
+which contain spaces or other special characters. The difference between
+the two types of quotes is similar to that of many popular shell programs,
+namely that a single quote is used to specify a literal string (one that is
+not interpreted for shell variables or quoting with a backslash [see
+next paragraph]), while double quotes indicate a string for which
+should be evaluated. For example, backtics are evaluated inside of double
+quotes, but <bf/not/ for single quotes.
+
+\ quotes the next character, just as in shells such as bash and zsh.
+For example, if want to put quotes ``&dquot;'' inside of a string, you can use
+``\'' to force the next character to be a literal instead of interpreted
+character.
+<tscreen><verb>
+set realname="Michael \"MuttDude\" Elkins"
+</verb></tscreen>
+
+``\\'' means to insert a literal ``\'' into the line.
+``\n'' and ``\r'' have their usual C meanings of linefeed and
+carriage-return, respectively.
+
+A \ at the end of a line can be used to split commands over
+multiple lines, provided that the split points don't appear in the
+middle of command names.
+
+It is also possible to substitute the output of a Unix command in an
+initialization file. This is accomplished by enclosing the command in
+backquotes (``). For example,
+<tscreen><verb>
+my_hdr X-Operating-System: `uname -a`
+</verb></tscreen>
+The output of the Unix command ``uname -a'' will be substituted before the
+line is parsed. Note that since initialization files are line oriented, only
+the first line of output from the Unix command will be substituted.
+
+For a complete list of the commands understood by mutt, see the
+<ref id="commands" name="command reference">.
+
+<sect1>Defining/Using aliases<label id="alias">
+<p>
+
+Usage: <tt/alias/ <em/key/ <em/address/ [ , <em/address/, ... ]
+
+It's usually very cumbersome to remember or type out the address of someone
+you are communicating with. Mutt allows you to create ``aliases'' which map
+a short string to a full address.
+
+<bf/Note:/ if you want to create an alias for a group (by specifying more than
+one address), you <bf/must/ separate the addresses with a comma (``,'').
+
+To remove an alias or aliases:
+
+<tt/unalias/ <em/addr/ [ <em/addr/ <em/.../ ]
+
+<tscreen><verb>
+alias muttdude me@cs.hmc.edu (Michael Elkins)
+alias theguys manny, moe, jack
+</verb></tscreen>
+
+Unlike other mailers, Mutt doesn't require aliases to be defined
+in a special file. The <tt/alias/ command can appear anywhere in
+a configuration file, as long as this file is <ref id="source"
+name="sourced">. Consequently, you can have multiple alias files, or
+you can have all aliases defined in your muttrc.
+
+On the other hand, the <ref id="create-alias" name="create-alias">
+function can use only one file, the one pointed to by the <ref
+id="alias_file" name="$alias_file"> variable (which is
+<tt>˜/.muttrc</tt> by default). This file is not special either,
+in the sense that Mutt will happily append aliases to any file, but in
+order for the new aliases to take effect you need to explicitly <ref
+id="source" name="source"> this file too.
+
+For example:
+
+<tscreen><verb>
+source /usr/local/share/Mutt.aliases
+source ~/.mail_aliases
+set alias_file=~/.mail_aliases
+</verb></tscreen>
+
+To use aliases, you merely use the alias at any place in mutt where mutt
+prompts for addresses, such as the <em/To:/ or <em/Cc:/ prompt. You can
+also enter aliases in your editor at the appropriate headers if you have the
+<ref id="edit_headers" name="$edit_headers"> variable set.
+
+In addition, at the various address prompts, you can use the tab character
+to expand a partial alias to the full alias. If there are multiple matches,
+mutt will bring up a menu with the matching aliases. In order to be
+presented with the full list of aliases, you must hit tab with out a partial
+alias, such as at the beginning of the prompt or after a comma denoting
+multiple addresses.
+
+In the alias menu, you can select as many aliases as you want with the
+<em/select-entry/ key (default: RET), and use the <em/exit/ key
+(default: q) to return to the address prompt.
+
+<sect1>Changing the default key bindings<label id="bind">
+<p>
+Usage: <tt/bind/ <em/map/ <em/key/ <em/function/
+
+This command allows you to change the default key bindings (operation
+invoked when pressing a key).
+
+<em/map/ specifies in which menu the binding belongs. The currently
+defined maps are:
+
+<itemize>
+<item>generic
+<item>alias
+<item>attach
+<item>browser
+<item>editor
+<item>index
+<item>compose
+<item>pager
+<item>pgp
+<item>url
+</itemize>
+
+<em/key/ is the key (or key sequence) you wish to bind. To specify a
+control character, use the sequence <em/\Cx/, where <em/x/ is the
+letter of the control character (for example, to specify control-A use
+``\Ca''). Note that the case of <em/x/ as well as <em/\C/ is
+ignored, so that <em/\CA, \Ca, \cA/ and <em/\ca/ are all
+equivalent. An alternative form is to specify the key as a three digit
+octal number prefixed with a ``\'' (for example <em/\177/ is
+equivalent to <em/\c?/).
+
+In addition, <em/key/ may consist of:
+
+<tscreen><verb>
+\t tab
+\r carriage return
+\n newline
+\e escape
+up up arrow
+down down arrow
+left left arrow
+right right arrow
+pageup Page Up
+pagedown Page Down
+backspace Backspace
+delete Delete
+insert Insert
+enter Enter
+home Home
+end End
+f1 function key 1
+f10 function key 10
+</verb></tscreen>
+
+<em/key/ does not need to be enclosed in quotes unless it contains a
+space (`` '').
+
+<em/function/ specifies which action to take when <em/key/ is pressed.
+For a complete list of functions, see the <ref id="functions"
+name="reference">. The special function <tt/noop/ unbinds the specify key
+sequence.
+
+<sect1>Setting variables based upon mailbox<label id="folder-hook">
+<p>
+Usage: <tt/folder-hook/ [!]<em/pattern/ <em/command/
+
+It is often desirable to change settings based on which mailbox you are
+reading. The folder-hook command provides a method by which you can execute
+any configuration command. <em/pattern/ is a regular expression specifying
+in which mailboxes to execute <em/command/ before loading. If a mailbox
+matches multiple folder-hook's, they are executed in the order given in the
+muttrc.
+
+<bf/Note:/ if you use the ``!'' shortcut for <ref id="spoolfile"
+name="$spoolfile"> at the beginning of the pattern, you must place it
+inside of double or single quotes in order to distinguish it from the
+logical <em/not/ operator for the expression.
+
+Note that the settings are <em/not/ restored when you leave the mailbox.
+For example, a command action to perform is to change the sorting method
+based upon the mailbox being read:
+
+<tscreen><verb>
+folder-hook mutt set sort=threads
+</verb></tscreen>
+
+However, the sorting method is not restored to its previous value when
+reading a different mailbox. To specify a <em/default/ command, use the
+pattern ``.'':
+
+<p>
+<tscreen><verb>
+folder-hook . set sort=date-sent
+</verb></tscreen>
+
+<sect1>Keyboard macros<label id="macro">
+<p>
+Usage: <tt/macro/ <em/menu/ <em/key/ <em/sequence/
+
+Macros are useful when you would like a single key to perform a series of
+actions. When you press <em/key/ in menu <em/menu/, Mutt will behave as if
+you had typed <em/sequence/. So if you have a common sequence of commands
+you type, you can create a macro to execute those commands with a single
+key.
+
+<em/key/ and <em/sequence/ are expanded by the same rules as the <ref
+id="bind" name="key bindings">, with the addition that control
+characters in <em/sequence/ can also be specified as <em/ˆx/. In
+order to get a caret (``ˆ'') you need to use <em/ˆˆ/.
+
+<bf/Note:/ Macro definitions (if any) listed in the help screen(s), are
+silently truncated at the screen width, and are not wrapped.
+
+<sect1>Using color and mono video attributes<label id="color">
+<p>
+Usage: <tt/color/ <em/object/ <em/foreground/ <em/background/ [ <em/regexp/ ]<newline>
+Usage: <tt/color/ index <em/foreground/ <em/background/ [ <em/pattern/ ]<newline>
+Usage: <tt/uncolor/ index <em/pattern/ [ <em/pattern/ ... ]<newline>
+
+If your terminal supports color, you can spice up Mutt by creating your own
+color scheme. To define the color of an object (type of information), you
+must specify both a foreground color <bf/and/ a background color (it is not
+possible to only specify one or the other).
+
+<em/object/ can be one of:
+
+<itemize>
+<item>attachment
+<item>body (match <em/regexp/ in the body of messages)
+<item>bold (hiliting bold patterns in the body of messages)
+<item>error (error messages printed by Mutt)
+<item>header (match <em/regexp/ in the message header)
+<item>hdrdefault (default color of the message header in the pager)
+<item>index (match <em/pattern/ in the message index)
+<item>indicator (arrow or bar used to indicate the current item in a menu)
+<item>markers (the ``+'' markers at the beginning of wrapped lines in the pager)
+<item>message (informational messages)
+<item>normal
+<item>quoted (text matching <ref id="quote_regexp"
+name="$quote_regexp"> in the body of a message)
+<item>quoted1, quoted2, ..., quoted<bf/N/ (higher levels of quoting)
+<item>search (hiliting of words in the pager)
+<item>signature
+<item>status (mode lines used to display info about the mailbox or message)
+<item>tilde (the ``˜'' used to pad blank lines in the pager)
+<item>tree (thread tree drawn in the message index and attachment menu)
+<item>underline (hiliting underlined patterns in the body of messages)
+</itemize>
+
+<em/foreground/ and <em/background/ can be one of the following:
+
+<itemize>
+<item>white
+<item>black
+<item>green
+<item>magenta
+<item>blue
+<item>cyan
+<item>yellow
+<item>red
+<item>default
+<item>color<em/x/
+</itemize>
+
+<em/foreground/ can optionally be prefixed with the keyword <tt/bright/ to make
+the foreground color boldfaced (e.g., <tt/brightred/).
+
+If your terminal supports it, the special keyword <em/default/ can be
+used as a transparent color. The value <em/brightdefault/ is also valid.
+If Mutt is linked against the <em/S-Lang/ library, you also need to set
+the <em/COLORFGBG/ environment variable to the default colors of your
+terminal for this to work; for example (for Bourne-like shells):
+
+<tscreen><verb>
+set COLORFGBG="green;black"
+export COLORFGBG
+</verb></tscreen>
+
+<bf/Note:/ The <em/S-Lang/ library requires you to use the <em/lightgray/
+and <em/brown/ keywords instead of <em/white/ and <em/yellow/ when
+setting this variable.
+
+<bf/Note:/ The uncolor command can be applied to the index object only. It
+removes entries from the list. You <bf/must/ specify the same pattern
+specified in the color command for it to be removed. The pattern ``*'' is
+a special token which means to clear the color index list of all entries.
+
+Mutt also recognizes the keywords <em/color0/, <em/color1/, …,
+<em/color/<bf/N-1/ (<bf/N/ being the number of colors supported
+by your terminal). This is useful when you remap the colors for your
+display (for example by changing the color associated with <em/color2/
+for your xterm), since color names may then lose their normal meaning.
+
+If your terminal does not support color, it is still possible change the video
+attributes through the use of the ``mono'' command:
+
+Usage: <tt/mono/ <em/<object> <attribute>/ [ <em/regexp/ ]
+
+where <em/attribute/ is one of the following:
+
+<itemize>
+<item>none
+<item>bold
+<item>underline
+<item>reverse
+<item>standout
+</itemize>
+
+<sect1>Ignoring (weeding) unwanted message headers<label id="ignore">
+<p>
+Usage: <tt/[un]ignore/ <em/pattern/ [ <em/pattern/ ... ]
+
+Messages often have many header fields added by automatic processing systems,
+or which may not seem useful to display on the screen. This command allows
+you to specify header fields which you don't normally want to see.
+
+You do not need to specify the full header field name. For example,
+``ignore content-'' will ignore all header fields that begin with the pattern
+``content-''.
+
+To remove a previously added token from the list, use the ``unignore'' command.
+Note that if you do ``ignore x-'' it is not possible to ``unignore x-mailer,''
+for example. The ``unignore'' command does <bf/not/ make Mutt display headers
+with the given pattern.
+
+``unignore *'' will remove all tokens from the ignore list.
+
+For example:
+<tscreen><verb>
+# Sven's draconian header weeding
+ignore *
+unignore from date subject to cc
+unignore organization organisation x-mailer: x-newsreader: x-mailing-list:
+unignore posted-to:
+</verb></tscreen>
+
+<sect1>Mailing lists<label id="lists">
+<p>
+Usage: <tt/[un]lists/ <em/address/ [ <em/address/ ... ]
+
+Mutt has a few nice features for <ref id="using_lists" name="handling mailing
+lists">. In order to take advantage of them, you must specify which addresses
+belong to mailing lists.
+
+It is important to note that you should <bf/never/ specify the domain name (
+the part after the ``@'') with the lists command. You should only
+specify the ``mailbox'' portion of the address (the part before the ``@'').
+For example, if you've subscribed to the Mutt mailing list, you will receive
+mail addressed to <em/mutt-users@cs.hmc.edu/. So, to tell Mutt that this is a
+mailing list, you would add ``lists mutt-users'' to your initialization file.
+
+The ``unlists'' command is to remove a token from the list of mailing-lists.
+Use ``unlists *'' to remove all tokens.
+
+<sect1>Using Multiple spool mailboxes<label id="mbox-hook">
+<p>
+Usage: <tt/mbox-hook/ [!]<em/pattern/ <em/mailbox/
+
+This command is used to move read messages from a specified mailbox to a
+different mailbox automatically when you quit or change folders.
+<em/pattern/ is a regular expression specifying the mailbox to treat as a
+``spool'' mailbox and <em/mailbox/ specifies where mail should be saved when
+read.
+
+Unlike some of the other <em/hook/ commands, only the <em/first/ matching
+pattern is used (it is not possible to save read mail in more than a single
+mailbox).
+
+<sect1>Defining mailboxes which receive mail<label id="mailboxes">
+<p>
+Usage: <tt/mailboxes/ [!]<em/filename/ [ <em/filename/ ... ]
+
+This command specifies folders which can receive mail and
+which will be checked for new messages. By default, the
+main menu status bar displays how many of these folders have
+new messages.
+<p>
+When changing folders, pressing <em/space/ will cycle
+through folders with new mail.
+<p>
+Pressing TAB in the directory browser will bring up a menu showing the files
+specified by the <tt/mailboxes/ command, and indicate which contain new
+messages. Mutt will automatically enter this mode when invoked from the
+command line with the <tt/-y/ option.
+<p>
+<bf/Note:/ new mail is detected by comparing the last modification time to
+the last access time. Utilities like <tt/biff/ or <tt/frm/ or any other
+program which accesses the mailbox might cause Mutt to never detect new mail
+for that mailbox if they do not properly reset the access time.
+<p>
+
+<bf/Note:/ the filenames in the <tt/mailboxes/ command are resolved when
+the command is executed, so if these names contain <ref id="shortcuts"
+name="shortcut characters"> (such as ``='' and ``!''), any variable
+definition that affect these characters (like <ref id="folder"
+name="$folder"> and <ref id="spoolfile" name="$spool">)
+should be executed before the <tt/mailboxes/ command.
+
+<sect1>User defined headers<label id="my_hdr">
+<p>
+Usage:<newline>
+<tt/my_hdr/ <em/string/<newline>
+<tt/unmy_hdr/ <em/field/ [ <em/field/ ... ]
+
+The ``my_hdr'' command allows you to create your own header
+fields which will be added to every message you send.
+
+For example, if you would like to add an ``Organization:'' header field to
+all of your outgoing messages, you can put the command
+
+<quote>
+my_hdr Organization: A Really Big Company, Anytown, USA
+</quote>
+
+in your <tt/.muttrc/.
+
+<bf/Note:/ space characters are <em/not/ allowed between the keyword and
+the colon (``:''). The standard for electronic mail (RFC822) says that
+space is illegal there, so Mutt enforces the rule.
+
+If you would like to add a header field to a single message, you should
+either set the <ref id="edit_headers" name="edit_headers"> variable,
+or use the <em/edit-headers/ function (default: ``E'') in the send-menu so
+that you can edit the header of your message along with the body.
+
+To remove user defined header fields, use the ``unmy_hdr''
+command. You may specify an asterisk (``*'') to remove all header
+fields, or the fields to remove. For example, to remove all ``To'' and
+``Cc'' header fields, you could use:
+
+<quote>
+unmy_hdr to cc
+</quote>
+
+<sect1>Defining the order of headers when viewing messages<label id="hdr_order">
+<p>
+Usage: <tt/hdr_order/ <em/header1/ <em/header2/ <em/header3/
+
+With this command, you can specify an order in which mutt will attempt
+to present headers to you when viewing messages.
+
+<tscreen><verb>
+hdr_order From Date: From: To: Cc: Subject:
+</verb></tscreen>
+
+<sect1>Specify default save filename<label id="save-hook">
+<p>
+Usage: <tt/save-hook/ [!]<em/regexp/ <em/filename/
+
+This command is used to override the default filename used when saving
+messages. <em/filename/ will be used as the default filename if the message is
+<em/From:/ an address matching <em/regexp/ or if you are the author and the
+message is addressed <em/to:/ something matching <em/regexp/.
+
+See <ref id="pattern_hook" name="matching messages"> for information on the
+exact format of <em/regexp/.
+
+Examples:
+
+<tscreen><verb>
+save-hook me@(turing\\.)?cs\\.hmc\\.edu$ +elkins
+save-hook aol\\.com$ +spam
+</verb></tscreen>
+
+Also see the <ref id="fcc-save-hook" name="fcc-save-hook"> command.
+
+<sect1>Specify default Fcc: mailbox when composing<label id="fcc-hook">
+<p>
+Usage: <tt/fcc-hook/ [!]<em/regexp/ <em/mailbox/
+
+This command is used to save outgoing mail in a mailbox other than
+<ref id="record" name="$record">. Mutt searches the initial list of
+message recipients for the first matching <em/regexp/ and uses <em/mailbox/
+as the default Fcc: mailbox. If no match is found the message will be saved
+to <ref id="record" name="$record"> mailbox.
+
+See <ref id="pattern_hook" name="matching messages"> for information on the
+exact format of <em/regexp/.
+
+Example: <tt/fcc-hook aol.com$ +spammers/
+
+The above will save a copy of all messages going to the aol.com domain to
+the `+spammers' mailbox by default. Also see the <ref id="fcc-save-hook"
+name="fcc-save-hook"> command.
+
+<sect1>Specify default save filename and default Fcc: mailbox at once<label
+id="fcc-save-hook">
+<p>
+Usage: <tt/fcc-save-hook/ [!]<em/regexp/ <em/mailbox/
+
+This command is a shortcut, equivalent to doing both a <ref id="fcc-hook" name="fcc-hook">
+and a <ref id="save-hook" name="save-hook"> with its arguments.
+
+<sect1>Change settings based upon message recipients<label id="send-hook">
+<p>
+Usage: <tt/send-hook/ [!]<em/regexp/ <em/command/
+
+This command can be used to execute arbitrary configuration commands based
+upon recipients of the message. <em/regexp/ is a regular expression
+matching the desired address. <em/command/ is executed when <em/regexp/
+matches recipients of the message. When multiple matches occur, commands are
+executed in the order they are specified in the muttrc.
+
+See <ref id="pattern_hook" name="matching messages"> for information on the
+exact format of <em/regexp/.
+
+Example: <tt/send-hook mutt &dquot;set mime_forward signature=''&dquot;/
+
+Another typical use for this command is to change the values of the
+<ref id="attribution" name="$attribution">, <ref id="signature"
+name="$signature"> and <ref id="locale" name="$locale">
+variables in order to change the language of the attributions and
+signatures based upon the recipients.
+
+<bf/Note:/ the send-hook's are only executed ONCE after getting the initial
+list of recipients. Adding a recipient after replying or editing the
+message will NOT cause any send-hook to be executed.
+
+<sect1>Adding key sequences to the keyboard buffer<label id="push">
+<p>
+Usage: <tt/push/ <em/string/
+
+This command adds the named string to the keyboard buffer. You may
+use it to automatically run a sequence of commands at startup, or when
+entering certain folders.
+
+<sect1>Message Scoring<label id="score">
+<p>
+Usage: <tt/score/ <em/pattern/ <em/value/<newline>
+Usage: <tt/unscore/ <em/pattern/ [ <em/pattern/ ... ]
+
+The <tt/score/ commands adds <em/value/ to a message's score if <em/pattern/
+matches it. <em/pattern/ is a string in the format described in the <ref
+id="searching" name="searching"> section. <em/value/ is a positive or
+negative integer. A message's final score is the sum total of all matching
+<tt/score/ entries. However, you may optionally prefix <em/value/ with an
+equal sign (=) to cause evaluation to stop at a particular entry if there is
+a match. Negative final scores are rounded up to 0.
+
+The <tt/unscore/ command removes score entries from the list. You <bf/must/
+specify the same pattern specified in the <tt/score/ command for it to be
+removed. The pattern ``*'' is a special token which means to clear the list
+of all score entries.
+
+<sect1>Setting variables<label id="set">
+<p>
+Usage: <tt/set/ [no|inv]<em/variable/[=<em/value/] [ <em/variable/ ... ]<newline>
+Usage: <tt/toggle/ <em/variable/ [<em/variable/ ... ]<newline>
+Usage: <tt/unset/ <em/variable/ [<em/variable/ ... ]<newline>
+Usage: <tt/reset/ <em/variable/ [<em/variable/ ... ]
+
+This command is used to set (and unset) <ref id="variables"
+name="configuration variables">. There are four basic types of variables:
+boolean, number, string and quadoption. <em/boolean/ variables can be
+<em/set/ (true) or <em/unset/ (false). <em/number/ variables can be
+assigned a positive integer value.
+
+<em/string/ variables consist of any number of printable characters.
+<em/strings/ must be enclosed in quotes if they contain spaces or tabs. You
+may also use the ``C'' escape sequences <bf/\n/ and <bf/\t/ for
+newline and tab, respectively.
+
+<em/quadoption/ variables are used to control whether or not to be prompted
+for certain actions, or to specify a default action. A value of <em/yes/
+will cause the action to be carried out automatically as if you had answered
+yes to the question. Similarly, a value of <em/no/ will cause the the
+action to be carried out as if you had answered ``no.'' A value of
+<em/ask-yes/ will cause a prompt with a default answer of ``yes'' and
+<em/ask-no/ will provide a default answer of ``no.''
+
+Prefixing a variable with ``no'' will unset it. Example: <tt/set noaskbcc/.
+
+For <em/boolean/ variables, you may optionally prefix the variable name with
+<tt/inv/ to toggle the value (on or off). This is useful when writing
+macros. Example: <tt/set invsmart_wrap/.
+
+The <tt/toggle/ command automatically prepends the <tt/inv/ prefix to all
+specified variables.
+
+The <tt/unset/ command automatically prepends the <tt/no/ prefix to all
+specified variables.
+
+Using the enter-command function in the <em/index/ menu, you can query the
+value of a variable by prefixing the name of the variable with a question
+mark:
+
+<tscreen><verb>
+set ?allow_8bit
+</verb></tscreen>
+
+The question mark is actually only required for boolean variables.
+
+The <tt/reset/ command resets all given variables to the compile time
+defaults (hopefully mentioned in this manual). If you use the command
+<tt/set/ and prefix the variable with ``&'' this has the same
+behavior as the reset command.
+
+With the <tt/reset/ command there exists the special variable ``all'',
+which allows you to reset all variables to their system defaults.
+
+<sect1>Reading initialization commands from another file<label id="source">
+<p>
+Usage: <tt/source/ <em/filename/
+
+This command allows the inclusion of initialization commands
+from other files. For example, I place all of my aliases in
+<tt>˜/.mail_aliases</tt> so that I can make my
+<tt>˜/.muttrc</tt> readable and keep my aliases private.
+
+If the filename begins with a tilde (``˜''), it will be expanded to the
+path of your home directory.
+
+If the filename ends with a vertical bar (|), then <em/filename/ is
+considered to be an executable program from which to read input (eg.
+<tt/source ~/bin/myscript|/.
+
+<sect>Advanced Usage
+
+<sect1>Searching and Regular Expressions<label id="regex">
+<p>
+All text patterns for searching and matching in Mutt must be specified
+as regular expressions (regexp) in the ``POSIX extended'' syntax (which
+is more or less the syntax used by egrep and GNU awk). For your
+convenience, we have included below a brief description of this syntax.
+
+The search is case sensitive if the pattern contains at least one upper
+case letter, and case insensitive otherwise. Note that ``\''
+must be quoted if used for a regular expression in an initialization
+command: ``\\''. For more information, see the section on
+<ref id="searching" name="searching"> below.
+
+<sect2>Regular Expressions<label id="regexps">
+<p>
+A regular expression is a pattern that describes a set of strings.
+Regular expressions are constructed analogously to arithmetic
+expressions, by using various operators to combine smaller expressions.
+
+The fundamental building blocks are the regular expressions that match
+a single character. Most characters, including all letters and digits,
+are regular expressions that match themselves. Any metacharacter with
+special meaning may be quoted by preceding it with a backslash.
+
+The period ``.'' matches any single character. The caret ``ˆ'' and
+the dollar sign ``&dollar'' are metacharacters that respectively match
+the empty string at the beginning and end of a line.
+
+A list of characters enclosed by ``['' and ``&rsqb'' matches any
+single character in that list; if the first character of the list
+is a caret ``ˆ'' then it matches any character <bf/not/ in the
+list. For example, the regular expression <bf/[0123456789&rsqb/
+matches any single digit. A range of ASCII characters may be specified
+by giving the first and last characters, separated by a hyphen
+``‐''. Most metacharacters lose their special meaning inside
+lists. To include a literal ``&rsqb'' place it first in the list.
+Similarly, to include a literal ``ˆ'' place it anywhere but first.
+Finally, to include a literal hyphen ``‐'' place it last.
+
+Certain named classes of characters are predefined. Character classes
+consist of ``[:'', a keyword denoting the class, and ``:]''.
+The following classes are defined by the POSIX standard:
+
+<descrip>
+<tag/[:alnum:]/
+Alphanumeric characters.
+<tag/[:alpha:]/
+Alphabetic characters.
+<tag/[:blank:]/
+Space or tab characters.
+<tag/[:cntrl:]/
+Control characters.
+<tag/[:digit:]/
+Numeric characters.
+<tag/[:graph:]/
+Characters that are both printable and visible. (A space is printable,
+but not visible, while an ``a'' is both.)
+<tag/[:lower:]/
+Lower-case alphabetic characters.
+<tag/[:print:]/
+Printable characters (characters that are not control characters.)
+<tag/[:punct:]/
+Punctuation characters (characters that are not letter, digits, control
+characters, or space characters).
+<tag/[:space:]/
+Space characters (such as space, tab and formfeed, to name a few).
+<tag/[:upper:]/
+Upper-case alphabetic characters.
+<tag/[:xdigit:]/
+Characters that are hexadecimal digits.
+</descrip>
+
+A character class is only valid in a regular expression inside the
+brackets of a character list. Note that the brackets in these
+class names are part of the symbolic names, and must be included
+in addition to the brackets delimiting the bracket list. For
+example, <bf/[[:digit:]]/ is equivalent to
+<bf/[0-9]/.
+
+Two additional special sequences can appear in character lists. These
+apply to non-ASCII character sets, which can have single symbols (called
+collating elements) that are represented with more than one character,
+as well as several characters that are equivalent for collating or
+sorting purposes:
+
+<descrip>
+<tag/Collating Symbols/
+A collating symbols is a multi-character collating element enclosed in
+``[.'' and ``.]''. For example, if ``ch'' is a collating
+element, then <bf/[[.ch.]]/ is a regexp that matches
+this collating element, while <bf/[ch]/ is a regexp that
+matches either ``c'' or ``h''.
+<tag/Equivalence Classes/
+An equivalence class is a locale-specific name for a list of
+characters that are equivalent. The name is enclosed in ``[=''
+and ``=]''. For example, the name ``e'' might be used to
+represent all of ``è'' ``é'' and ``e''. In this case,
+<bf/[[=e=]]/ is a regexp that matches any of
+``è'', ``é'' and ``e''.
+</descrip>
+
+A regular expression matching a single character may be followed by one
+of several repetition operators:
+
+<descrip>
+<tag/?/
+The preceding item is optional and matched at most once.
+<tag/*/
+The preceding item will be matched zero or more times.
+<tag/+/
+The preceding item will be matched one or more times.
+<tag/{n}/
+The preceding item is matched exactly <em/n/ times.
+<tag/{n,}/
+The preceding item is matched <em/n/ or more times.
+<tag/{,m}/
+The preceding item is matched at most <em/m/ times.
+<tag/{n,m}/
+The preceding item is matched at least <em/n/ times, but no more than
+<em/m/ times.
+</descrip>
+
+Two regular expressions may be concatenated; the resulting regular
+expression matches any string formed by concatenating two substrings
+that respectively match the concatenated subexpressions.
+
+Two regular expressions may be joined by the infix operator ``|'';
+the resulting regular expression matches any string matching either
+subexpression.
+
+Repetition takes precedence over concatenation, which in turn takes
+precedence over alternation. A whole subexpression may be enclosed in
+parentheses to override these precedence rules.
+
+<bf/Note:/ If you compile Mutt with the GNU <em/rx/ package, the
+following operators may also be used in regular expressions:
+
+<descrip>
+<tag/\\y/
+Matches the empty string at either the beginning or the end of a word.
+<tag/\\B/
+Matches the empty string within a word.
+<tag/\\</
+Matches the empty string at the beginning of a word.
+<tag/\\>/
+Matches the empty string at the end of a word.
+<tag/\\w/
+Matches any word-constituent character (letter, digit, or underscore).
+<tag/\\W/
+Matches any character that is not word-constituent.
+<tag/\\`/
+Matches the empty string at the beginning of a buffer (string).
+<tag/\\'/
+Matches the empty string at the end of a buffer.
+</descrip>
+
+Please note however that these operators are not defined by POSIX, so
+they may or may not be available in stock libraries on various systems.
+
+<sect2>Searching<label id="searching">
+<p>
+Many of Mutt's commands allow you to specify a pattern to match
+(limit, tag-pattern, delete-pattern, etc.). There are several ways to select
+messages:
+
+<tscreen><verb>
+~A all messages
+~b PATTERN messages which contain PATTERN in the message body
+~c USER messages carbon-copied to USER
+~C PATTERN message is either to: or cc: PATTERN
+~D deleted messages
+~d [MIN]-[MAX] messages with ``date-sent'' in a Date range
+~E expired messages
+~e PATTERN message which contains PATTERN in the ``Sender'' field
+~F flagged messages
+~f USER messages originating from USER
+~h PATTERN messages which contain PATTERN in the message header
+~i ID message which match ID in the ``Message-ID'' field
+~L PATTERN message is either originated or received by PATTERN
+~l message is addressed to a known mailing list
+~m [MIN]-[MAX] message in the range MIN to MAX
+~n [MIN]-[MAX] messages with a score in the range MIN to MAX
+~N new messages
+~O old messages
+~p message is addressed to you (consults $alternates)
+~P message is from you (consults $alternates)
+~Q messages which have been replied to
+~R read messages
+~r [MIN]-[MAX] messages with ``date-received'' in a Date range
+~S superseded messages
+~s SUBJECT messages having SUBJECT in the ``Subject'' field.
+~T tagged messages
+~t USER messages addressed to USER
+~U unread messages
+~x PATTERN messages which contain PATTERN in the `References' field
+</verb></tscreen>
+
+Where PATTERN, USER, ID, and SUBJECT are
+<ref id="regex" name="regular expressions">.
+
+<sect2>Complex Searches
+<p>
+
+Logical AND is performed by specifying more than one criterion. For
+example:
+
+<tscreen><verb>
+~t mutt ~f elkins
+</verb></tscreen>
+
+would select messages which contain the word ``mutt'' in the list of
+recipients <bf/and/ that have the word ``elkins'' in the ``From'' header
+field.
+
+Mutt also recognizes the following operators to create more complex search
+patterns:
+
+<itemize>
+<item>! -- logical NOT operator
+<item>| -- logical OR operator
+<item>() -- logical grouping operator
+</itemize>
+
+Here is an example illustrating a complex search pattern. This pattern will
+select all messages which do not contain ``mutt'' in the ``To'' or ``Cc''
+field and which are from ``elkins''.
+
+<tscreen><verb>
+!(~t mutt|~c mutt) ~f elkins
+</verb></tscreen>
+
+<sect2>Searching by Date
+<p>
+Mutt supports two types of dates, <em/absolute/ and <em/relative/.
+
+<bf/Absolute/. Dates <bf/must/ be in DD/MM/YY format (month and year are
+optional, defaulting to the current month and year). An example of a valid
+range of dates is:
+
+<tscreen><verb>
+Limit to messages matching: ~d 20/1/95-31/10
+</verb></tscreen>
+
+If you omit the minimum (first) date, and just specify ``-DD/MM/YY'', all
+messages <em/before/ the given date will be selected. If you omit the maximum
+(second) date, and specify ``DD/MM/YY-'', all messages <em/after/ the given
+date will be selected. If you specify a single date with no dash (``-''),
+only messages sent on the given date will be selected.
+
+<bf/Relative/. This type of date is relative to the current date, and may
+be specified as:
+<itemize>
+<item>><em/offset/ (messages older than <em/offset/ units)
+<item><<em/offset/ (messages newer than <em/offset/ units)
+<item>=<em/offset/ (messages exactly <em/offset/ units old)
+</itemize>
+
+<em/offset/ is specified as a positive number with one of the following
+units:
+<verb>
+y years
+m months
+w weeks
+d days
+</verb>
+
+Example: to select messages less than 1 month old, you would use
+<tscreen><verb>
+Limit to messages matching: ~d <1m
+</verb></tscreen>
+
+<bf/Note:/ all dates used when searching are relative to the
+<bf/local/ time zone, so unless you change the setting of your <ref
+id="index_format" name="$index_format"> to include a
+<tt/%[...]/ format, these are <bf/not/ the dates shown
+in the main index.
+
+<sect1>Using Tags
+<p>
+
+Sometimes it is desirable to perform an operation on a group of messages all
+at once rather than one at a time. An example might be to save messages to
+a mailing list to a separate folder, or to delete all messages with a given
+subject. To tag all messages matching a pattern, use the tag-pattern
+function, which is bound to ``control-T'' by default. Or you can select
+individual messages by hand using the ``tag-message'' function, which is
+bound to ``t'' by default. See <ref id="searching" name="searching"> for
+Mutt's searching syntax.
+
+Once you have tagged the desired messages, you can use the
+``tag-prefix'' operator, which is the ``;'' (semicolon) key by default.
+When the ``tag-prefix'' operator is used, the <bf/next/ operation will
+be applied to all tagged messages if that operation can be used in that
+manner. If the <ref id="auto_tag" name="$auto_tag">
+variable is set, the next operation applies to the tagged messages
+automatically, without requiring the ``tag-prefix''.
+
+<sect1>Using Hooks<label id="hooks">
+<p>
+A <em/hook/ is a concept borrowed from the EMACS editor which allows you to
+execute arbitrary commands before performing some operation. For example,
+you may wish to tailor your configuration based upon which mailbox you are
+reading, or to whom you are sending mail. In the Mutt world, a <em/hook/
+consists of a <ref id="regex" name="regular expression"> along with a
+configuration option/command. See
+<itemize>
+<item><ref id="folder-hook" name="folder-hook">
+<item><ref id="send-hook" name="send-hook">
+<item><ref id="save-hook" name="save-hook">
+<item><ref id="mbox-hook" name="mbox-hook">
+<item><ref id="fcc-hook" name="fcc-hook">
+<item><ref id="fcc-save-hook" name="fcc-save-hook">
+</itemize>
+for specific details on each type of <em/hook/ available.
+
+<sect2>Message Matching in Hooks<label id="pattern_hook">
+<p>
+Hooks that act upon messages (<tt/send-hook, save-hook, fcc-hook/) are
+evaluated in a slightly different manner. For the other types of hooks, a
+<ref id="regex" name="regular expression">. But in dealing with messages a
+finer grain of control is needed for matching since for different purposes
+you want to match different criteria.
+
+Mutt allows the use of the <ref id="searching" name="search pattern">
+language for matching messages in hook commands. This works in exactly the
+same way as it would when <em/limiting/ or <em/searching/ the mailbox,
+except that you are restricted to those operators which match information
+from the envelope of the message (i.e. from, to, cc, date, subject, etc.).
+
+For example, if you wanted to set your return address based upon sending
+mail to a specific address, you could do something like:
+<tscreen><verb>
+send-hook '~t ^me@cs\.hmc\.edu$' 'my_hdr From: Mutt User <user@host>'
+</verb></tscreen>
+which would execute the given command when sending mail to
+<em/me@cs.hmc.edu/.
+
+However, it is not required that you write the pattern to match using the
+full searching language. You can still specify a simple <em/regular
+expression/ like the other hooks, in which case Mutt will translate your
+pattern into the full language, using the translation specified by the
+<ref id="default_hook" name="$dfault_hook"> variable. The pattern
+is translated at the time the hook is declared, so the value of
+<ref id="default_hook" name="$dfault_hook"> that is in effect
+at that time will be used.
+
+<sect1>External Address Queries<label id="query">
+<p>
+Mutt supports connecting to external directory databases such as LDAP,
+ph/qi, bbdb, or NIS through a wrapper script which connects to mutt
+using a simple interface. Using the <ref id="query_command"
+name="$query_command"> variable, you specify the wrapper
+command to use. For example:
+
+<tscreen><verb>
+set query_command = "mutt_ldap_query.pl '%s'"
+</verb></tscreen>
+
+The wrapper script should accept the query on the command-line. It
+should return a one line message, than each matching response on a
+single line, each line containing a tab separated address then name then
+some other optional information. On error, or if there are no matching
+addresses, return a non-zero exit code and a one line error message.
+
+An example multiple response output:
+<tscreen><verb>
+Searching database ... 20 entries ... 3 matching:
+me@cs.hmc.edu Michael Elkins mutt dude
+blong@fiction.net Brandon Long mutt and more
+roessler@guug.de Thomas Roessler mutt pgp
+</verb></tscreen>
+
+There are two mechanisms for accessing the query function of mutt. One
+is to do a query from the index menu using the query function (default: Q).
+This will prompt for a query, then bring up the query menu which will
+list the matching responses. From the query menu, you can select
+addresses to create aliases, or to mail. You can tag multiple messages
+to mail, start a new query, or have a new query appended to the current
+responses.
+
+The other mechanism for accessing the query function is for address
+completion, similar to the alias completion. In any prompt for address
+entry, you can use the complete-query function (default: ^T) to run a
+query based on the current address you have typed. Like aliases, mutt
+will look for what you have typed back to the last space or comma. If
+there is a single response for that query, mutt will expand the address
+in place. If there are multiple responses, mutt will activate the query
+menu. At the query menu, you can select one or more addresses to be
+added to the prompt.
+
+<sect1>Mailbox Formats
+<p>
+Mutt supports reading and writing of four different mailbox formats:
+mbox, MMDF, MH and Maildir. The mailbox type is autodetected, so there
+is no need to use a flag for different mailbox types. When creating new
+mailboxes, Mutt uses the default specified with the <ref id="mbox_type"
+name="$mbox_type"> variable.
+
+<bf/mbox/. This is the most widely used mailbox format for UNIX. All
+messages are stored in a single file. Each message has a line of the form:
+
+<tscreen><verb>
+From me@cs.hmc.edu Fri, 11 Apr 1997 11:44:56 PST
+</verb></tscreen>
+
+to denote the start of a new message (this is often referred to as the
+``From_'' line).
+
+<bf/MMDF/. This is a variant of the <em/mbox/ format. Each message is
+surrounded by lines containing ``^A^A^A^A'' (four control-A's).
+
+<bf/MH/. A radical departure from <em/mbox/ and <em/MMDF/, a mailbox
+consists of a directory and each message is stored in a separate file.
+The filename indicates the message number (however, this is may not
+correspond to the message number Mutt displays). Deleted messages are
+renamed with a comma (,) prepended to the filename. <bf/Note:/ Mutt
+detects this type of mailbox by looking for either <tt/.mh_sequences/
+or <tt/.xmhcache/ (needed to distinguish normal directories from MH
+mailboxes). Mutt does not update these files, yet.
+
+<bf/Maildir/. The newest of the mailbox formats, used by the Qmail MTA (a
+replacement for sendmail). Similar to <em/MH/, except that it adds three
+subdirectories of the mailbox: <em/tmp/, <em/new/ and <em/cur/. Filenames
+for the messages are chosen in such a way they are unique, even when two
+programs are writing the mailbox over NFS, which means that no file locking
+is needed.
+
+<sect1>Mailbox Shortcuts<label id="shortcuts">
+<p>
+There are a number of built in shortcuts which refer to specific mailboxes.
+These shortcuts can be used anywhere you are prompted for a file or mailbox
+path.
+
+<itemize>
+<item>! -- refers to your <ref id="spoolfile" name="$spool"> (incoming) mailbox
+<item>> -- refers to your <ref id="mbox" name="$mbox"> file
+<item>< -- refers to your <ref id="record" name="$record"> file
+<item>- -- refers to the file you've last visited
+<item>˜ -- refers to your home directory
+<item>= or + -- refers to your <ref id="folder" name="$folder"> directory
+</itemize>
+
+<sect1>Handling Mailing Lists<label id="using_lists">
+<p>
+
+Mutt has a few configuration options that make dealing with large amounts of
+mail easier. The first thing you must do is to let Mutt know what addresses
+you consider to be mailing lists (technically this does not have to be a
+mailing list, but that is what it is most often used for). This is
+accomplished through the use of the <ref id="lists" name="lists"> command in
+your muttrc.
+
+Now that Mutt knows what your mailing lists are, it can do several
+things, the first of which is the ability to show the list name in
+the <em/index/ menu display. This is useful to distinguish between
+personal and list mail in the same mailbox. In the <ref id="index_format"
+name="$index_format"> variable, the escape ``%L''
+will return the string ``To <list>'' when ``list'' appears in the
+``To'' field, and ``Cc <list>'' when it appears in the ``Cc''
+field (otherwise it returns the name of the author).
+
+Often times the ``To'' and ``Cc'' fields in mailing list messages tend to
+get quite large. Most people do not bother to remove the author of the
+message they are reply to from the list, resulting in two or more copies
+being sent to that person. The ``list-reply'' function, which by default is
+bound to ``L'' in the <em/index/ menu and <em/pager/, helps reduce the clutter
+by only replying to the mailing list addresses instead of all recipients.
+
+The other method some mailing list admins use is to generate a
+``Reply-To'' field which points back to the mailing list address rather
+than the author of the message. This can create problems when trying
+to reply directly to the author in private, since most mail clients
+will automatically reply to the address given in the ``Reply-To''
+field. Mutt uses the <ref id="reply_to" name="$reply_to">
+variable to help decide which address to use. If set, you will be
+prompted as to whether or not you would like to use the address given in
+the ``Reply-To'' field, or reply directly to the address given in the
+``From'' field. When unset, the ``Reply-To'' field will be used when
+present.
+
+Lastly, Mutt has the ability to <ref id="sort" name="sort"> the mailbox into
+<ref id="threads" name="threads">. A thread is a group of messages which all relate to the same
+subject. This is usually organized into a tree-like structure where a
+message and all of its replies are represented graphically. If you've ever
+used a threaded news client, this is the same concept. It makes dealing
+with large volume mailing lists easier because you can easily delete
+uninteresting threads and quickly find topics of value.
+
+<sect1>Delivery Status Notification (DSN) Support
+<p>
+RFC1894 defines a set of MIME content types for relaying information
+about the status of electronic mail messages. These can be thought of as
+``return receipts.'' Berkeley sendmail 8.8.x currently has some command
+line options in which the mail client can make requests as to what type
+of status messages should be returned.
+
+To support this, there are two variables. <ref id="dsn_notify"
+name="$dsn_notify"> is used to request receipts for
+different results (such as failed message, message delivered, etc.).
+<ref id="dsn_return" name="$dsn_return"> requests how much
+of your message should be returned with the receipt (headers or full
+message). Refer to the man page on sendmail for more details on DSN.
+
+<sect1>POP3 Support (OPTIONAL)
+<p>
+
+If Mutt was compiled with POP3 support (by running the <em/configure/
+script with the <em/--enable-pop/ flag), it has the ability to fetch
+your mail from a remote server for local browsing. When you invoke the
+<em/fetch-mail/ function (default: G), Mutt attempts to connect to <ref
+id="pop_host" name="pop_host"> and authenticate by logging in
+as <ref id="pop_user" name="pop_user">. After the connection
+is established, you will be prompted for your password on the remote
+system.
+
+Once you have been authenticated, Mutt will fetch all your new mail and
+place it in the local <ref id="spoolfile" name="spoolfile">. After this
+point, Mutt runs exactly as if the mail had always been local.
+
+<bf/Note:/ The POP3 support is there only for convenience,
+and it's rather limited. If you need more functionality you
+should consider using a specialized program, such as <htmlurl
+url="http://www.ccil.org/~esr/fetchmail" name="fetchmail">
+
+<sect>Mutt's MIME Support
+<p>
+Quite a bit of effort has been made to make Mutt the premier text-mode
+MIME MUA. Every effort has been made to provide the functionality that
+the discerning MIME user requires, and the conformance to the standards
+wherever possible. When configuring Mutt for MIME, there are two extra
+types of configuration files which Mutt uses. One is the
+<tt/mime.types/ file, which contains the mapping of file extensions to
+IANA MIME types. The other is the <tt/mailcap/ file, which specifies
+the external commands to use for handling specific MIME types.
+
+<sect1>Using MIME in Mutt
+<p>
+There are three areas/menus in Mutt which deal with MIME, they are the
+pager (while viewing a message), the attachment menu and the compose
+menu.
+
+<sect2>Viewing MIME messages in the pager
+<p>
+When you select a message from the index and view it in the pager, Mutt
+decodes the message to a text representation. Mutt internally supports
+a number of MIME types, including <tt>text/plain, text/enriched,
+message/rfc822, and message/news</tt>. In addition, the export
+controlled version of Mutt recognizes a variety of PGP MIME types,
+including PGP/MIME and application/pgp.
+
+Mutt will denote attachments with a couple lines describing them.
+These lines are of the form:
+<tscreen><verb>
+[-- Attachment #1: Description --]
+[-- Type: text/plain, Encoding: 7bit, Size: 10000 --]
+</verb></tscreen>
+Where the <tt/Description/ is the description or filename given for the
+attachment, and the <tt/Encoding/ is one of
+<tt>7bit/8bit/quoted-printable/base64/binary</tt>.
+
+If Mutt cannot deal with a MIME type, it will display a message like:
+<tscreen><verb>
+[-- image/gif is unsupported (use 'v' to view this part) --]
+</verb></tscreen>
+
+<sect2>The Attachment Menu<label id="attach_menu">
+<p>
+The default binding for <tt/view-attachments/ is `v', which displays the
+attachment menu for a message. The attachment menu displays a list of
+the attachments in a message. From the attachment menu, you can save,
+print, pipe, delete, and view attachments. You can apply these
+operations to a group of attachments at once, by tagging the attachments
+and by using the ``tag-prefix'' operator. You can also reply to the
+current message from this menu, and only the current attachment (or the
+attachments tagged) will be quoted in your reply. You can view
+attachments as text, or view them using the mailcap viewer definition.
+See the help on the attachment menu for more information.
+
+<sect2>The Compose Menu
+<p>
+The compose menu is the menu you see before you send a message. It
+allows you to edit the recipient list, the subject, and other aspects
+of your message. It also contains a list of the attachments of your
+message, including the main body. From this menu, you can print, copy,
+filter, pipe, edit, compose, review, and rename an attachment or a
+list of tagged attachments. You can also modifying the attachment
+information, notably the type, encoding and description.
+
+Attachments appear as follows:
+<verb>
+- 1 [text/plain, 7bit, 1K] /tmp/mutt-euler-8082-0 <no description>
+ 2 [applica/x-gunzip, base64, 422K] ~/src/mutt-0.85.tar.gz <no description>
+</verb>
+
+The '-' denotes that Mutt will delete the file after sending the
+message. It can be toggled with the <tt/toggle-unlink/ command
+(default: u). The next field is the MIME content-type, and can be
+changed with the <tt/edit-type/ command (default: ^T). The next field
+is the encoding for the attachment, which allows a binary message to
+be encoded for transmission on 7bit links. It can be changed with the
+<tt/edit-encoding/ command (default: ^E). The next field is the size
+of the attachment, rounded to kilobytes or megabytes. The next field
+is the filename, which can be changed with the <tt/rename-file/ command
+(default: R). The final field is the description of the attachment, and
+can be changed with the <tt/edit-description/ command (default: d).
+
+<sect1>MIME Type configuration with <tt/mime.types/
+<p>
+When you add an attachment to your mail message, Mutt searches your
+personal mime.types file at <tt>${HOME}/.mime.types</tt>, and then
+the system mime.types file at <tt>SHAREDIR/mime.types</tt>.
+<tt/SHAREDIR/ is defined at compilation time, and can be determined
+by typing <tt/mutt -v/ from the command line.
+
+The mime.types file consist of lines containing a MIME type and a space
+separated list of extensions. For example:
+<tscreen><verb>
+application/postscript ps eps
+application/pgp pgp
+audio/x-aiff aif aifc aiff
+</verb></tscreen>
+A sample <tt/mime.types/ file comes with the Mutt distribution, and
+should contain most of the MIME types you are likely to use.
+
+If Mutt can not determine the mime type by the extension of the file you
+attach, it will look at the file. If the file is free of binary
+information, Mutt will assume that the file is plain text, and mark it
+as <tt>text/plain</tt>. If the file contains binary information, then Mutt will
+mark it as <tt>application/octect-stream</tt>. You can change the MIME
+type that Mutt assigns to an attachment by using the <tt/edit-type/
+command from the compose menu (default: ^T). When typing in the MIME
+type, Mutt requires that major type be one of the 5 types: application,
+text, image, video, or audio. If you attempt to use a different major
+type, Mutt will abort the change.
+
+<sect1>MIME Viewer configuration with <tt/mailcap/
+<p>
+Mutt supports RFC 1524 MIME Configuration, in particular the Unix
+specific format specified in Appendix A of RFC 1524. This file format
+is commonly referred to as the mailcap format. Many MIME compliant
+programs utilize the mailcap format, allowing you to specify handling
+for all MIME types in one place for all programs. Programs known to
+use this format include Netscape, XMosaic, lynx and metamail.
+
+In order to handle various MIME types that Mutt can not handle
+internally, Mutt parses a series of external configuration files to
+find an external handler. The default search string for these files
+is a colon delimited list set to
+<tscreen><verb>
+${HOME}/.mailcap:SHAREDIR/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap
+</verb></tscreen>
+where <tt/$HOME/ is your home directory and <tt/SHAREDIR/ is the
+shared directory defined at compile time (visible from <tt/mutt -v/).
+
+In particular, the metamail distribution will install a mailcap file,
+usually as <tt>/usr/local/etc/mailcap</tt>, which contains some baseline
+entries.
+
+<sect2>The Basics of the mailcap file
+<p>
+A mailcap file consists of a series of lines which are comments, blank,
+or definitions.
+
+A comment line consists of a # character followed by anything you want.
+
+A blank line is blank.
+
+A definition line consists of a content type, a view command, and any
+number of optional fields. Each field of a definition line is divided
+by a semicolon ';' character.
+
+The content type is specified in the MIME standard type/subtype method.
+For example,
+<tt>text/plain, text/html, image/gif, </tt>
+etc. In addition, the mailcap format includes two formats for
+wildcards, one using the special '*' subtype, the other is the implicit
+wild, where you only include the major type. For example, <tt>image/*</tt>, or
+<tt>video,</tt> will match all image types and video types,
+respectively.
+
+The view command is a Unix command for viewing the type specified. There
+are two different types of commands supported. The default is to send
+the body of the MIME message to the command on stdin. You can change
+this behaviour by using %s as a parameter to your view command.
+This will cause Mutt to save the body of the MIME message to a temporary
+file, and then call the view command with the %s replaced by
+the name of the temporary file. In both cases, Mutt will turn over the
+terminal to the view program until the program quits, at which time Mutt
+will remove the temporary file if it exists.
+
+So, in the simplest form, you can send a text/plain message to the
+external pager more on stdin:
+<tscreen><verb>
+text/plain; more
+</verb></tscreen>
+Or, you could send the message as a file:
+<tscreen><verb>
+text/plain; more %s
+</verb></tscreen>
+Perhaps you would like to use lynx to interactively view a text/html
+message:
+<tscreen><verb>
+text/html; lynx "%s"
+</verb></tscreen>
+In this case, lynx does not support viewing a file from stdin, so you
+must use the %s syntax.
+<bf/Note:/ <em>Some older versions of lynx contain a bug where they
+will check the mailcap file for a viewer for text/html. They will find
+the line which calls lynx, and run it. This causes lynx to continuously
+spawn itself to view the object.</em>
+
+On the other hand, maybe you don't want to use lynx interactively, you
+just want to have it convert the text/html to text/plain, then you can
+use:
+<tscreen><verb>
+text/html; lynx -dump "%s" | more
+</verb></tscreen>
+
+Perhaps you wish to use lynx to view text/html files, and a pager on
+all other text formats, then you would use the following:
+<tscreen><verb>
+text/html; lynx "%s"
+text/*; more
+</verb></tscreen>
+This is the simplest form of a mailcap file.
+
+<sect2>Advanced mailcap Usage
+<p>
+
+<sect3>Optional Fields
+<p>
+In addition to the required content-type and view command fields, you
+can add semi-colon ';' separated fields to set flags and other options.
+Mutt recognizes the following optional fields:
+<descrip>
+<tag/copiousoutput/
+This flag tells Mutt that the command passes possibly large amounts of
+text on stdout. This causes Mutt to invoke a pager (either the internal
+pager or the external pager defined by the pager variable) on the output
+of the view command. Without this flag, Mutt assumes that the command
+is interactive. One could use this to replace the pipe to <tt>more</tt>
+in the <tt>lynx -dump</tt> example in the Basic section:
+<tscreen><verb>
+text/html; lynx -dump %s ; copiousoutput
+</verb></tscreen>
+This will cause lynx to format the text/html output as text/plain
+and Mutt will use your standard pager to display the results.
+<tag/needsterminal/
+Mutt uses this flag when viewing attachments with <ref id="auto_view"
+name="autoview">, in order to decide whether it should honor the setting
+of the <ref id="wait_key" name="$wait_key"> variable or
+not. When an attachment is viewed using an interactive program, and the
+corresponding mailcap entry has a <em/needsterminal/ flag, Mutt will use
+<ref id="wait_key" name="$wait_key"> and the exit status
+of the program to decide if it will ask you to press a key after the
+external program has exited. In all other situations it will not prompt
+you for a key.
+<tag>compose=<command></tag>
+This flag specifies the command to use to create a new attachment of a
+specific MIME type. Mutt supports this from the compose menu.
+<tag>composetyped=<command></tag>
+This flag specifies the command to use to create a new attachment of a
+specific MIME type. This command differs from the compose command in
+that mutt will expect standard MIME headers on the data. This can be
+used to specify parameters, filename, description, etc. for a new
+attachment. Mutt supports this from the compose menu.
+<tag>print=<command></tag>
+This flag specifies the command to use to print a specific MIME type.
+Mutt supports this from the attachment and compose menus.
+<tag>edit=<command></tag>
+This flag specifies the command to use to edit a specific MIME type.
+Mutt supports this from the compose menu, and also uses it to compose
+new attachments. Mutt will default to the defined editor for text
+attachments.
+<tag>nametemplate=<template></tag>
+This field specifies the format for the file denoted by %s in the
+command fields. Certain programs will require a certain file extension,
+for instance, to correctly view a file. For instance, lynx will only
+interpret a file as <tt>text/html</tt> if the file ends in <tt/.html/.
+So, you would specify lynx as a <tt>text/html</tt> viewer with a line in
+the mailcap file like:
+<tscreen><verb>
+text/html; lynx %s; nametemplate=%s.html
+</verb></tscreen>
+<tag>test=<command></tag>
+This field specifies a command to run to test whether this mailcap
+entry should be used. The command is defined with the command expansion
+rules defined in the next section. If the command returns 0, then the
+test passed, and Mutt uses this entry. If the command returns non-zero,
+then the test failed, and Mutt continues searching for the right entry.
+<bf/Note:/ <em>the content-type must match before Mutt performs the test.</em>
+For example:
+<tscreen><verb>
+text/html; netscape -remote 'openURL(%s)' ; test=RunningX
+text/html; lynx %s
+</verb></tscreen>
+In this example, Mutt will run the program RunningX which will return 0
+if the X Window manager is running, and non-zero if it isn't. If
+RunningX returns 0, then Mutt will call netscape to display the
+text/html object. If RunningX doesn't return 0, then Mutt will go on
+to the next entry and use lynx to display the text/html object.
+</descrip>
+
+<sect3>Search Order
+<p>
+When searching for an entry in the mailcap file, Mutt will search for
+the most useful entry for its purpose. For instance, if you are
+attempting to print an <tt>image/gif</tt>, and you have the following
+entries in your mailcap file, Mutt will search for an entry with the
+print command:
+<tscreen><verb>
+image/*; xv %s
+image/gif; ; print= anytopnm %s | pnmtops | lpr; \
+ nametemplate=%s.gif
+</verb></tscreen>
+Mutt will skip the <tt>image/*</tt> entry and use the <tt>image/gif</tt>
+entry with the print command.
+
+In addition, you can use this with <ref id="auto_view" name="Autoview">
+to denote two commands for viewing an attachment, one to be viewed
+automatically, the other to be viewed interactively from the attachment
+menu. In addition, you can then use the test feature to determine which
+viewer to use interactively depending on your environment.
+<tscreen><verb>
+text/html; netscape -remote 'openURL(%s)' ; test=RunningX
+text/html; lynx %s; nametemplate=%s.html
+text/html; lynx -dump %s; nametemplate=%s.html; copiousoutput
+</verb></tscreen>
+For <ref id="auto_view" name="Autoview">, Mutt will choose the third
+entry because of the copiousoutput tag. For interactive viewing, Mutt
+will run the program RunningX to determine if it should use the first
+entry. If the program returns non-zero, Mutt will use the second entry
+for interactive viewing.
+
+<sect3>Command Expansion
+<p>
+The various commands defined in the mailcap files are passed to the
+<tt>/bin/sh</tt> shell using the system() function. Before the
+command is passed to <tt>/bin/sh -c</tt>, it is parsed to expand
+various special parameters with information from Mutt. The keywords
+Mutt expands are:
+<descrip>
+<tag/%s/
+As seen in the basic mailcap section, this variable is expanded
+to a filename specified by the calling program. This file contains
+the body of the message to view/print/edit or where the composing
+program should place the results of composition. In addition, the
+use of this keyword causes Mutt to not pass the body of the message
+to the view/print/edit program on stdin.
+<tag/%t/
+Mutt will expand %t to the text representation of the content
+type of the message in the same form as the first parameter of the
+mailcap definition line, ie <tt>text/html</tt> or
+<tt>image/gif</tt>.
+<tag>%{<parameter>}</tag>
+Mutt will expand this to the value of the specified parameter
+from the Content-Type: line of the mail message. For instance, if
+Your mail message contains:
+<tscreen><verb>
+Content-Type: text/plain; charset=iso-8859-1
+</verb></tscreen>
+then Mutt will expand %{charset} to iso-8859-1. The default metamail
+mailcap file uses this feature to test the charset to spawn an xterm
+using the right charset to view the message.
+<tag>∖%</tag>
+This will be replaced by a %
+</descrip>
+Mutt does not currently support the %F and %n keywords
+specified in RFC 1524. The main purpose of these parameters is for
+multipart messages, which is handled internally by Mutt.
+
+<sect2>Example mailcap files
+<p>
+This mailcap file is fairly simple and standard:
+<code>
+# I'm always running X :)
+video/*; xanim %s > /dev/null
+image/*; xv %s > /dev/null
+
+# I'm always running netscape (if my computer had more memory, maybe)
+text/html; netscape -remote 'openURL(%s)'
+</code>
+
+This mailcap file shows quite a number of examples:
+
+<code>
+# Use xanim to view all videos Xanim produces a header on startup,
+# send that to /dev/null so I don't see it
+video/*; xanim %s > /dev/null
+
+# Send html to a running netscape by remote
+text/html; netscape -remote 'openURL(%s)'; test=RunningNetscape
+
+# If I'm not running netscape but I am running X, start netscape on the
+# object
+text/html; netscape %s; test=RunningX
+
+# Else use lynx to view it as text
+text/html; lynx %s
+
+# This version would convert the text/html to text/plain
+text/html; lynx -dump %s; copiousoutput
+
+# enriched.sh converts text/enriched to text/html and then uses
+# lynx -dump to convert it to text/plain
+text/enriched; enriched.sh ; copiousoutput
+
+# I use enscript to print text in two columns to a page
+text/*; more %s; print=enscript -2Gr %s
+
+# Netscape adds a flag to tell itself to view jpegs internally
+image/jpeg;xv %s; x-mozilla-flags=internal
+
+# Use xv to view images if I'm running X
+# In addition, this uses the \ to extend the line and set my editor
+# for images
+image/*;xv %s; test=RunningX; \
+ edit=xpaint %s
+
+# Convert images to text using the netpbm tools
+image/*; (anytopnm %s | pnmscale -xysize 80 46 | ppmtopgm | pgmtopbm |
+pbmtoascii -1x2 ) 2>&1 ; copiousoutput
+
+# Send excel spreadsheets to my NT box
+application/ms-excel; open.pl %s
+</code>
+
+<sect1>MIME Autoview<label id="auto_view">
+<p>
+In addition to explicitly telling Mutt to view an attachment with the
+MIME viewer defined in the mailcap file, Mutt has support for
+automatically viewing MIME attachments while in the pager.
+
+To work, you must define a viewer in the mailcap file which uses the
+<tt/copiousoutput/ option to denote that it is non-interactive.
+Usually, you also use the entry to convert the attachment to a text
+representation which you can view in the pager.
+
+You then use the <tt/auto_view/ muttrc command to list the
+content-types that you wish to view automatically.
+
+For instance, if you set auto_view to:
+<tscreen><verb>
+auto_view text/html text/enriched application/x-gunzip application/postscript image/gif application/x-tar-gz
+</verb></tscreen>
+
+Mutt could use the following mailcap entries to automatically view
+attachments of these types.
+<tscreen><verb>
+text/html; lynx -dump %s; copiousoutput; nametemplate=%s.html
+text/enriched; enriched.sh ; copiousoutput
+image/*; anytopnm %s | pnmscale -xsize 80 -ysize 50 | ppmtopgm | pgmtopbm | pbmtoascii ; copiousoutput
+application/x-gunzip; gzcat; copiousoutput
+application/x-tar-gz; gunzip -c %s | tar -tf - ; copiousoutput
+application/postscript; ps2ascii %s; copiousoutput
+</verb></tscreen>
+<sect1>MIME Multipart/Alternative<label id="alternative_order">
+<p>
+Mutt has some heuristics for determining which attachment of a
+multipart/alternative type to display. First, mutt will check the
+alternative_order list to determine if one of the available types
+is preferred. The alternative_order list consists of a number of
+mimetypes in order, including support for implicit and explicit
+wildcards, for example:
+<tscreen><verb>
+alternative_order text/enriched text/plain text application/postscript image/*
+</verb></tscreen>
+
+Next, mutt will check if any of the types have a defined
+<ref id="auto_view" name="auto_view">, and use that. Failing
+that, Mutt will look for any text type. As a last attempt, mutt will
+look for any type it knows how to handle.
+
+<sect>Reference
+<sect1>Command line options<label id="commandline">
+<p>
+Running <tt/mutt/ with no arguments will make Mutt attempt to read your spool
+mailbox. However, it is possible to read other mailboxes and
+to send messages from the command line as well.
+
+<tscreen><verb>
+-a attach a file to a message
+-c specify a carbon-copy (Cc) address
+-e specify a config command to be run after initilization files are read
+-F specify an alternate file to read initialization commands
+-f specify a mailbox to load
+-h print help on command line options
+-H specify a draft file from which to read a header and body
+-i specify a file to include in a message composition
+-n do not read the system Muttrc
+-m specify a default mailbox type
+-p recall a postponed message
+-R open mailbox in read-only mode
+-s specify a subject (enclose in quotes if it contains spaces)
+-v show version number and compile-time definitions
+-x simulate the mailx(1) compose mode
+-y show a menu containing the files specified by the mailboxes command
+-z exit immediately if there are no messages in the mailbox
+-Z open the first folder with new message,exit immediately if none
+</verb></tscreen>
+
+To read messages in a mailbox
+
+<tt/mutt/ [ -nz ] [ -F <em/muttrc/ ] [ -m <em/type/ ] [ -f <em/mailbox/ ]
+
+To compose a new message
+
+<tt/mutt/ [ -n ] [ -F <em/muttrc/ ] [ -a <em/file/ ] [ -c <em/address/ ] [ -i <em/filename/ ] [ -s <em/subject/ ] <em/address/ [ <em/address/ ... ]
+
+Mutt also supports a ``batch'' mode to send prepared messages. Simply redirect
+input from the file you wish to send. For example,
+
+<tt>mutt -s &dquot;data set for run #2&dquot; professor@bigschool.edu
+< ˜/run2.dat</tt>
+
+This command will send a message to ``professor@bigschool.edu'' with a subject
+of ``data set for run #2''. In the body of the message will be the contents
+of the file ``˜/run2.dat''.
+
+<sect1>Configuration Commands<label id="commands">
+<p>
+The following are the commands understood by mutt.
+
+<itemize>
+<item>
+<tt><ref id="alias" name="alias"></tt> <em/key/ <em/address/ [ , <em/address/, ... ]
+<item>
+<tt><ref id="alias" name="unalias"></tt> <em/key/ <em/address/ [ , <em/address/, ... ]
+<item>
+<tt><ref id="alternative_order" name="alternative_order"></tt> <em/mimetype/ [ <em/mimetype/ ... ]
+<item>
+<tt><ref id="auto_view" name="auto_view"></tt> <em/mimetype/ [ <em/mimetype/ ... ]
+<item>
+<tt><ref id="bind" name="bind"></tt> <em/map/ <em/key/ <em/function/
+<item>
+<tt><ref id="color" name="color"></tt> <em/object/ <em/foreground/ <em/background/ [ <em/regexp/ ]
+<item>
+<tt><ref id="folder-hook" name="folder-hook"></tt> <em/pattern/ <em/command/
+<item>
+<tt><ref id="ignore" name="ignore"></tt> <em/pattern/ [ <em/pattern/ ... ]
+<item>
+<tt><ref id="ignore" name="unignore"></tt> <em/pattern/ [ <em/pattern/ ... ]
+<item>
+<tt><ref id="hdr_order" name="hdr_order"></tt> <em/header/ [ <em/header/ ... ]
+<item>
+<tt><ref id="lists" name="lists"></tt> <em/address/ [ <em/address/ ... ]
+<item>
+<tt><ref id="lists" name="unlists"></tt> <em/address/ [ <em/address/ ... ]
+<item>
+<tt><ref id="macro" name="macro"></tt> <em/menu/ <em/key/ <em/sequence/
+<item>
+<tt><ref id="mailboxes" name="mailboxes"></tt> <em/filename/ [ <em/filename/ ... ]
+<item>
+<tt><ref id="color" name="mono"></tt> <em/object attribute/ [ <em/regexp/ ]
+<item>
+<tt><ref id="mbox-hook" name="mbox-hook"></tt> <em/pattern/ <em/mailbox/
+<item>
+<tt><ref id="my_hdr" name="my_hdr"></tt> <em/string/
+<item>
+<tt><ref id="my_hdr" name="unmy_hdr"></tt> <em/field/ [ <em/field/ ... ]
+<item>
+<tt><ref id="push" name="push"></tt> <em/string/
+<item>
+<tt><ref id="save-hook" name="save-hook"></tt> <em/regexp/ <em/filename/
+<item>
+<tt><ref id="send-hook" name="send-hook"></tt> <em/regexp/ <em/command/
+<item>
+<tt><ref id="set" name="set"></tt> [no|inv]<em/variable/[=<em/value/] [ <em/variable/ ... ]
+<item>
+<tt><ref id="set" name="toggle"></tt> <em/variable/ [<em/variable/ ... ]
+<item>
+<tt><ref id="set" name="unset"></tt> <em/variable/ [<em/variable/ ... ]
+<item>
+<tt><ref id="source" name="source"></tt> <em/filename/
+</itemize>
+
+<sect1>Configuration variables<label id="variables">
+<p>
+
+<sect2>abort_nosubject<label id="abort_nosubject">
+<p>
+Type: quadoption<newline>
+Default: ask-yes
+
+If set to <em/yes/, when composing messages and no subject is given
+at the subject prompt, composition will be aborted. If set to <em/no/,
+composing messages with no subject given at the subject prompt will
+never be aborted.
+
+<sect2>abort_unmodified<label id="abort_unmodified">
+<p>
+Type: quadoption<newline>
+Default: yes
+
+If set to <em/yes/, composition will automatically abort after editing the
+message body if no changes are made to the file (this check only happens
+after the <em/first/ edit of the file). When set to <em/no/, composition
+will never be aborted.
+
+<sect2>alias_file<label id="alias_file">
+<p>
+Type: string<newline>
+Default: ˜/.muttrc
+
+The default file in which to save aliases created by the
+<ref id="create-alias" name="create-alias"> function.
+
+<bf/Note:/ Mutt will not automatically source this file; you must
+explicitly use the <ref id="source" name="source"> command for it to be
+executed.
+
+<sect2>alias_format<label id="alias_format">
+<p>
+Type: string<newline>
+Default: &dquot;%2n %t %-10a %r&dquot;
+
+Specifies the format of the data displayed for the `alias' menu. The
+following printf(3)-style sequences are available.
+
+<verb>
+%a alias name
+%n index number
+%r address which alias expands to
+%t character which indicates if the alias is tagged for inclusion (*/ )
+</verb>
+
+<sect2>allow_8bit
+<p>
+Type: boolean<newline>
+Default: set
+
+Controls whether 8-bit data is converted to 7-bit using either Quoted-Printable
+or Base64 encoding when sending mail.
+
+<sect2>alternates<label id="alternates">
+<p>
+Type: string<newline>
+Default: none
+
+A regexp that allows you to specify <em/alternate/ addresses where you
+receive mail. This affects Mutt's idea about messages from you and
+addressed to you.
+
+<sect2>arrow_cursor<label id="arrow_cursor">
+<p>
+Type: boolean<newline>
+Default: unset
+
+When set, an arrow (``->'') will be used to indicate the current entry in
+menus instead of hiliting the whole line. On slow network or modem links
+this will make response faster because there is less that has to be redrawn
+on the screen when moving to the next or previous entries in the menu.
+
+<sect2>ascii_chars<label id="ascii_chars">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, Mutt will use plain ASCII characters when displaying thread and
+attachment trees, instead of the default <em/ACS/ characters.
+
+<sect2>askbcc<label id="askbcc">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, Mutt will prompt you for blind-carbon-copy (Bcc) recipients before
+editing an outgoing message.
+
+<sect2>askcc<label id="askcc">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, Mutt will prompt you for carbon-copy (Cc) recipients before editing
+the body of an outgoing message.
+
+<sect2>attribution<label id="attribution">
+<p>
+Type: format string<newline>
+Default: &dquot;On %d, %n wrote:&dquot;
+
+This is the string that will precede a message which has been included in
+a reply. For a full listing of defined escape sequences
+see the section on <ref id="index_format" name="$index_format">.
+
+<sect2>autoedit<label id="autoedit">
+<p>
+Type: boolean<newline>
+Default: unset
+
+When set, Mutt will skip the initial send-menu and allow you to immediately
+begin editing the body of your message when replying to another message.
+The send-menu may still be accessed once you have finished editing the body
+of your message.
+
+If the <ref id="edit_headers" name="$edit_headers"> variable is
+also set, the initial prompts in the send-menu are always skipped, even
+when composing a new message.
+
+<sect2>auto_tag<label id="auto_tag">
+<p>
+Type: boolean<newline>
+Default: unset
+
+When set, functions in the <em/index/ menu which affect a message will be
+applied to all tagged messages (if there are any). When unset, you must
+first use the tag-prefix function (default: ";") to make the next function
+apply to all tagged messages.
+
+<sect2>beep<label id="beep">
+<p>
+Type: boolean<newline>
+Default: set
+
+When this variable is set, mutt will beep when an error occurs.
+
+<sect2>beep_new<label id="beep_new">
+<p>
+Type boolean<newline>
+Default: unset
+
+When this variable is set, mutt will beep whenever it prints a
+message notifying you of new mail. This is independent of the
+setting of the <ref id="beep" name="beep"> variable.
+
+<sect2>charset<label id="charset">
+<p>
+Type: string<newline>
+Default: iso-8859-1
+
+Character set your terminal uses to display and enter textual data. This
+information is required to properly label outgoing messages which contain
+8-bit characters so that receiving parties can display your messages in the
+correct character set.
+
+<sect2>check_new<label id="check_new">
+<p>
+Type: boolean<newline>
+Default: set
+
+<bf/Note:/ this option only affects <em/maildir/ and <em/MH/ style
+mailboxes.
+
+When <em/set/, Mutt will check for new mail delivered while the mailbox
+is open. Especially with MH mailboxes, this operation can take quite
+some time since it involves scanning the directory and checking each
+file to see if it has already been looked at. If <em/check_new/
+is <em/unset/, no check for new mail is performed while the mailbox is
+open.
+
+<sect2>confirmappend<label id="confirmappend">
+<p>
+Type: boolean<newline>
+Default: set
+
+When set, Mutt will prompt for confirmation when appending messages to an
+existing mailbox.
+
+<sect2>confirmcreate<label id="confirmcreate">
+<p>
+Type: boolean<newline>
+Default: set
+
+When set, Mutt will prompt for confirmation when saving messages to a
+mailbox which does not yet exist before creating it.
+
+<sect2>copy<label id="copy">
+<p>
+Type: quadoption<newline>
+Default: yes
+
+This variable controls whether or not copies of your outgoing messages will be
+saved for later references. Also see <ref id="record" name="record">,
+<ref id="save_name" name="save_name">,
+<ref id="force_name" name="force_name"> and
+<ref id="fcc-hook" name="fcc-hook">.
+
+<sect2>date_format<label id="date_format">
+<p>
+Type: string<newline>
+Default: &dquot;!%a, %b %d, %Y at %I:%M:%S%p %Z&dquot;
+
+This variable controls the format of the date printed
+by the ``%d'' sequence in <ref id="index_format"
+name="$index_format">. This is passed to the <em/strftime/
+call to process the date. See the man page for <em/strftime(3)/ for the
+proper syntax.
+
+Unless the first character in the string is a bang (``!''), the month
+and week day names are expanded according to the locale specified in
+the variable <ref id="locale" name="locale">. If the first character in
+the string is a bang, the bang is discarded, and the month and week day
+names in the rest of the string are expanded in the <em/C/ locale (that
+is in US English).
+
+<sect2>default_hook<label id="default_hook">
+<p>
+Type: string<newline>
+Default: "˜f %s | (˜P (˜c %s | ˜t %s))"
+
+This variable controls how send-hooks, save-hooks, and fcc-hooks will be
+interpreted if they are specified with only a simple regexp, instead of a
+matching pattern. The hooks are expanded when they are declared, so a hook
+will be interpreted according to the value of this variable at the time the
+hook is declared. The default value matches if the message is either from a
+user matching the regular expression given, or if it is from you (if the from
+address matches <ref id="alternates" name="alternates">) and is to or cc'ed to
+a user matching the given regular expression.
+
+<sect2>delete<label id="delete">
+<p>
+Type: quadoption<newline>
+Default: ask-yes
+
+Controls whether or not messages are really deleted when closing or
+synchronizing a mailbox. If set to <em/yes/, messages marked for deleting
+will automatically be purged without prompting. If set to <em/no/, messages
+marked for deletion will be kept in the mailbox.
+
+<sect2>delete_format<label id="delete_format">
+<p>
+Type: string<newline>
+Default: "[-- Attachment from %u deleted on %<%D> --]"
+
+This variable controls the format of the message used to replace an
+attachment when the attachment is deleted. It uses the same format
+sequences as the <ref id="index_format" name="$index_format">
+variable.
+
+<sect2>dsn_notify<label id="dsn_notify">
+<p>
+Type: string<newline>
+Default: none
+
+<bf/Note:/ you should not enable this unless you are using Sendmail 8.8.x or
+greater.
+
+This variable sets the request for when notification is returned. The
+string consists of a comma separated list (no spaces!) of one or more of the
+following: <em/never/, to never request notification, <em/failure/, to
+request notification on transmission failure, <em/delay/, to be notified of
+message delays, <em/success/, to be notified of successful transmission.
+
+Example: <tt/set dsn_notify=&dquot;failure,delay&dquot;/
+
+<sect2>dsn_return<label id="dsn_return">
+<p>
+Type: string
+Default: none
+
+<bf/Note:/ you should not enable this unless you are using Sendmail 8.8.x or
+greater.
+
+This variable controls how much of your message is returned in DSN messages.
+It may be set to either <em/hdrs/ to return just the message header, or
+<em/full/ to return the full message.
+
+Example: <tt/set dsn_return=hdrs/
+
+<sect2>edit_headers<label id="edit_headers">
+<p>
+Type: boolean<newline>
+Default: unset
+
+This option allows you to edit the header of your outgoing messages along
+with the body of your message.
+
+<sect2>editor<label id="editor">
+<p>
+Type: String<newline>
+Default: value of environment variable $VISUAL, $EDITOR, or &dquot;vi&dquot;
+
+This variable specifies which editor to use when composing messages.
+
+<sect2>empty_to<label id="empty_to">
+<p>
+Type: string<newline>
+Default: undisclosed-recipients
+
+Specifies the text Mutt inserts in the <tt/To:/ header of a message you're
+sending, if the <tt/To:/ and <tt/Cc:/ fields are empty.
+
+<sect2>escape<label id="escape">
+<p>
+Type: string<newline>
+Default: ˜
+
+Escape character to use for functions in the builtin editor.
+
+<sect2>fast_reply<label id="fast_reply">
+<p>
+Type: boolean<newline>
+Default: unset
+
+When set, the initial prompt for recipients and subject are skipped when
+replying to messages, and the initial prompt for subject is skipped when
+forwarding messages.
+<p>
+<bf/Note:/ this variable has no effect when the <ref id="autoedit"
+name="$autoedit"> variable is set.
+
+<sect2>fcc_attach<label id="fcc_attach">
+<p>
+Type: boolean<newline>
+Default: set
+
+This variable controls whether or not attachments on outgoing messages are
+saved along with the main body of your message.
+
+<sect2>folder<label id="folder">
+<p>
+Type: String<newline>
+Default: ˜/Mail
+
+Specifies the default location of your mailboxes. A `+' or `=' at the
+beginning of a pathname will be expanded to the value of this variable.
+Note that if you change this variable from the default value you need to
+make sure that the assignment occurs <em/before/ you use `+' or `=' for any
+other variables since expansion takes place during the `set' command.
+
+<sect2>followup_to<label id="followup_to">
+<p>
+Type: boolean<newline>
+Default: set
+
+Controls whether or not the <em/Mail-Followup-To/ header field is generated
+when sending mail. When <em/set/, Mutt will generate this field when you
+are replying to a known mailing <ref id="lists" name="lists">.
+
+The purpose of this field is to prevent you from receiving duplicate copies of
+replies to messages which you send by specifying that you will receive a copy
+of the message if it is addressed to the mailing list (and thus there is no
+need to also include your address in a group reply).
+
+<sect2>force_name<label id="force_name">
+<p>
+Type: boolean<newline>
+Default: unset
+
+This variable is similar to <ref id="save_name"
+name="$save_name">, except that Mutt will store a copy of
+your outgoing message by the username of the address you are sending
+to even if that mailbox does not exist.
+
+Also see the <ref id="record" name="$record"> variable.
+
+<sect2>forward_decode<label id="forward_decode">
+<p>
+Type: boolean<newline>
+Default: set
+
+Controls the decoding of complex MIME messages into text/plain when
+forwarding a message and the message header is also RFC2047 decoded.
+This variable is only used, if <ref id="mime_forward"
+name="mime_forward"> is <em/unset/, otherwise <ref
+id="mime_forward_decode" name="mime_forward_decode"> is
+used instead.
+
+<sect2>forward_format<label id="forward_format">
+<p>
+Type: format string<newline>
+Default: "[%a: %s]"
+
+This variable controls the default subject when forwarding a message. It
+uses the same format sequences as the <ref id="index_format"
+name="$index_format"> variable.
+
+<sect2>forward_quote<label id="forward_quote">
+<p>
+Type: boolean<newline>
+Default: unset
+
+When <em/set/ forwarded messages included in the main body of the message
+(when <ref id="mime_forward" name="mime_forward"> is <em/unset/) will be
+quoted using <ref id="indent_string" name="indent_string">.
+
+<sect2>index_format<label id="index_format">
+<p>
+Type: format string<newline>
+Default: &dquot;%4C %Z %{%b %d} %-15.15L (%4l) %s&dquot;
+
+This variable allows you to customize the message index display to your
+personal taste.
+
+``Format strings'' are similar to the strings used in the ``C'' function
+<tt/printf/ to format output (see the man page for more detail). The
+following sequences are defined in Mutt:
+
+<tscreen><verb>
+%a address of the author
+%b filename of the original message folder (think mailBox)
+%B the list to which the letter was sent, or else the folder name (%b).
+%c number of characters (bytes) in the message
+%C current message number
+%d date and time of the message in the format specified by
+ ``date_format''
+%f entire From: line (address + real name)
+%F author name, or recipient name if the message is from you
+%i message-id of the current message
+%l number of lines in the message
+%L list-from function
+%m total number of message in the mailbox
+%N message score
+%n author's real name (or address if missing)
+%O (_O_riginal save folder) Where mutt would formerly have stashed the
+ message: list name or recipient name if no list
+%s subject of the message
+%S status of the message (N/D/d/!/*/r)
+%t `to:' field (recipients)
+%T the appropriate character from the $to_chars string
+%u user (login) name of the author
+%Z message status flags
+
+%{fmt} the date and time of the message is converted to sender's
+ time zone, and ``fmt'' is expanded by the system call
+ ``strftime''; a leading bang disables locales
+%[fmt] the date and time of the message is converted to the local
+ time zone, and ``fmt'' is expanded by the system call
+ ``strftime''; a leading bang disables locales
+%(fmt) the local date and time when the message was received.
+ ``fmt'' is expanded by the system call ``strftime'';
+ a leading bang disables locales
+%<fmt> the current local time. ``fmt'' is expanded by the system
+ call ``strftime''; a leading bang disables locales.
+
+%>X right justify the rest of the string and pad with character "X"
+%|X pad to the end of the line with character "X"
+</verb></tscreen>
+
+See also: <ref id="to_chars" name="$to_chars">.
+
+<sect2>hdrs<label id="hdrs">
+<p>
+Type: boolean<newline>
+Default: set
+
+When unset, the header fields normally added by the <ref id="my_hdr"
+name="my_hdr"> command are not created. This variable <em/must/ be
+unset before composing a new message or replying in order to take effect. If
+set, the user defined header fields are added to every new message.
+
+<sect2>header
+<p>
+Type: boolean<newline>
+Default: unset
+
+When set, this variable causes Mutt to include the <em/full/ header of
+the message you are replying to into the edit buffer.
+
+<sect2>help<label id="help">
+<p>
+Type: boolean<newline>
+Default: set
+
+When set, help lines describing the bindings for the major functions
+provided by each menu are displayed on the first line of the screen.
+
+<bf/Note:/ The binding will not be displayed correctly if the function is
+bound to a sequence rather than a single keystroke. Also, the help line
+may not be updated if a binding is changed while Mutt is running. Since
+this variable is primarily aimed at new users, neither of these should
+present a major problem.
+
+<sect2>history<label id="history">
+<p>
+Type: number<newline>
+Default: 10
+
+This variable controls the size (in number of strings remembered) of the
+string history buffer. The buffer is cleared each time the variable is
+set.
+
+<sect2>hostname<label id="hostname">
+<p>
+Type: string<newline>
+Default: varies
+
+Specifies the hostname to use after the ``@'' in local e-mail addresses. This
+overrides the compile time definition obtained from /etc/resolv.conf.
+
+<sect2>ignore_list_reply_to<label
+id="ignore_list_reply_to">
+<p>
+Type: boolean<newline>
+Default: unset
+
+Affects the behaviour of the <em/reply/ function when replying to
+messages from mailing lists. When set, if the ``Reply-To:'' field
+is set to the same value as the ``To:'' field, Mutt assumes that the
+``Reply-To:'' field was set by the mailing list to automate responses
+to the list, and will ignore this field. To direct a response to the
+mailing list when this option is set, use the <em/list-reply/ function;
+<em/group-reply/ will reply to both the sender and the list.
+
+<sect2>in_reply_to
+<p>
+Type: format string<newline>
+Default: &dquot;%i; from \&dquot;%n\&dquot; on %{!%a, %b %d, %Y at %I:%M:%S%p}&dquot;
+
+This specifies the format of the <tt/In-Reply-To:/ header
+field added when replying to a message. For a full listing of
+defined escape sequences see the section on <ref id="index_format"
+name="$index_format">.
+
+<sect2>include<label id="include">
+<p>
+Type: quadoption<newline>
+Default: ask-yes
+
+Controls whether or not a copy of the message(s) you are replying to is
+included in your reply.
+
+<sect2>indent_string<label id="indent_string">
+<p>
+Type: format string<newline>
+Default: "> "
+
+Specifies the string to prepend to each line of text quoted in a message to
+which you are replying. You are strongly encouraged not to change this
+value, as it tends to agitate the more fanatical netizens.
+
+<sect2>ispell<label id="ispell">
+<p>
+Type: string<newline>
+Default: &dquot;ispell&dquot;
+
+How to invoke ispell (GNU's spell-checking software).
+
+<sect2>locale<label id="locale">
+<p>
+Type: string<newline>
+Default: "C"
+
+The locale used by <em/strftime(3)/ to format dates. Legal values are
+the strings your system accepts for the locale variable <em/LC_TIME/.
+
+<sect2>mailcap_path<label id="mailcap_path">
+<p>
+Type: string<newline>
+Default: $MAILCAPS or ˜/.mailcap:/usr/local/share/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap
+
+This variable specifies which files to consult when attempting to display
+MIME bodies not directly supported by Mutt.
+
+<sect2>mark_old
+<p>
+Type: Boolean<newline>
+Default: set
+
+Controls whether or not Mutt makes the distinction between <em/new/ messages
+and <em/old/ <bf/unread/ messages. By default, Mutt will mark new messages
+as old if you exit a mailbox without reading them. The next time you start
+Mutt, the messages will show up with an "O" next to them in the index menu,
+indicating that they are old. In order to make Mutt treat all unread messages
+as new only, you can unset this variable.
+
+<sect2>markers<label id="markers">
+<p>
+Type: boolean<newline>
+Default: set
+
+Controls the display of wrapped lines in the internal pager. If set, a
+``+'' marker is displayed at the beginning of wrapped lines. Also see
+the <ref id="smart_wrap" name="$smart_wrap"> variable.
+
+<sect2>mask<label id="mask">
+<p>
+Type: string<newline>
+Default: "^(\.\.$|[^.])"
+
+A regular expression used in the file browser. Files whose names don't
+match this mask will not be shown.
+
+<sect2>mbox<label id="mbox">
+<p>
+Type: String<newline>
+Default: +inbox
+
+This specifies the folder into which read mail in your <ref id="spoolfile"
+name="spoolfile"> folder will be appended.
+
+<sect2>mbox_type<label id="mbox_type">
+<p>
+Type: String<newline>
+Default: mbox
+
+The default mailbox type used when creating new folders. May be any of
+mbox, MMDF, MH and Maildir.
+
+<sect2>metoo<label id="metoo">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If unset, Mutt will remove your address from the list of recipients when
+replying to a message. If you are replying to a message sent by you,
+Mutt will also assume that you want to reply to the recipients of that
+message rather than to yourself.
+
+<sect2>menu_scroll<label id="menu_scroll">
+<p>
+Type: boolean<newline>
+Default: unset
+
+When <em/set/, menus will be scrolled up or down one line when you attempt
+to move across a screen boundary. If <em/unset/, the screen is cleared and
+the next or previous page of the menu is displayed (useful for slow links to
+avoid many redraws).
+
+<sect2>meta_key<label id="meta_key">
+<p>
+Type: Boolean<newline>
+Default: unset
+
+If set, forces Mutt to interpret keystrokes with the high bit (bit 8) set as
+if the user had pressed the ESC key and whatever key remains after having
+the high bit removed. For example, if the key pressed has an ASCII value of
+0xf4, then this is treated as if the user had pressed ESC then ``x''. This
+is because the result of removing the high bit from ``0xf4'' is ``0x74'',
+which is the ASCII character ``x''.
+
+<sect2>mime_forward<label id="mime_forward">
+<p>
+Type: boolean<newline>
+Default: unset
+
+When set, the message you are forwarding will be attached as a separate
+MIME part instead of included in the main body of the message. This is
+useful for forwarding MIME messages so the receiver can properly view the
+message as it was delivered to you.
+
+Also see <ref id="forward_decode" name="forward_decode"> and
+<ref id="mime_forward_decode"
+name="mime_forward_decode">.
+
+<sect2>mime_forward_decode<label id="mime_forward_decode">
+<p>
+Type: boolean<newline>
+Default: unset
+
+Controls the decoding of complex MIME messages into text/plain when
+forwarding a message while <ref id="mime_forward"
+name="mime_forward"> is <em/set/. Otherwise <ref
+id="forward_decode" name="forward_decode"> is used instead.
+
+<sect2>move<label id="move">
+<p>
+Type: quadoption<newline>
+Default: ask-no
+
+Controls whether you will be asked to confirm moving read messages from your
+spool mailbox to your <ref id="mbox" name="$mbox"> mailbox, or as a
+result of a <ref id="mbox-hook" name="mbox-hook"> command.
+
+<sect2>message_format<label id="message_format">
+<p>
+Type: string<newline>
+Default: &dquot%s&dquot;
+
+This is the string displayed in the <ref id="attach_menu"
+name="attachment"> menu for attachments of type <em>message/rfc822</em>.
+For a full listing of defined escape sequences see the section on <ref
+id="index_format" name="index_format">.
+
+<sect2>pager<label id="pager">
+<p>
+Type: string<newline>
+Default: builtin
+
+This variable specifies which pager you would like to use to view messages.
+<tt/builtin/ means to use the builtin pager, otherwise this variable should
+specify the pathname of the external pager you would like to use.
+
+<sect2>pager_context<label id="pager_context">
+<p>
+Type: number<newline>
+Default: 0
+
+This variable controls the number of lines of context that are given when
+displaying the next or previous page in the internal pager. By default,
+Mutt will display the line after the last one on the screen at the top of
+the next page (0 lines of context).
+
+<sect2>pager_format<label id="pager_format">
+<p>
+Type: format string<newline>
+Default: &dquot;-%S- %C/%m: %-20.20n %s&dquot;
+
+This variable controls the format of the one-line message ``status''
+displayed before each message in either the internal or an external
+pager. The valid sequences are listed in the <ref id="index_format"
+name="index_format"> section.
+
+<sect2>pager_index_lines<label id="pager_index_lines">
+<p>
+Type: number<newline>
+Default: 0
+
+Determines the number of lines of a mini-index which is shown
+when in the pager. The current message, unless near the top or
+bottom of the folder, will be roughly one third of the way down
+this mini-index, giving the reader the context of a few messages
+before and after the message. This is useful, for example, to
+determine how many messages remain to be read in the current thread.
+One of the lines is reserved for the status bar from the index,
+so a <em/pager_index_lines/ of 6 will only show 5
+lines of the actual index. A value of 0 results in no index being
+shown. If the number of messages in the current folder is less than
+<em/pager_index_lines/, then the index will only use as
+many lines as it needs.
+
+<sect2>pager_stop<label id="pager_stop">
+<p>
+Type: boolean<newline>
+Default: unset
+
+When set, the internal-pager will <bf/not/ move to the next message when
+you are at the end of a message and invoke the <em/next-page/ function.
+
+<sect2>pgp_autoencrypt<label id="pgp_autoencrypt">
+<p>
+Type: boolean<newline>
+Default: unset
+
+Setting this variable will cause Mutt to always attempt to PGP/MIME
+encrypt outgoing messages. This is probably only useful in connection
+to the <em/send-hook/ command. It can be overridden by use of the
+<em/pgp-menu/, when encryption is not required or signing is requested
+as well.
+
+<sect2>pgp_autosign<label id="pgp_autosign">
+<p>
+Type: boolean<newline>
+Default: unset
+
+Setting this variable will cause Mutt to always attempt to PGP/MIME sign
+outgoing messages. This can be overridden by use of the <em/pgp-menu/,
+when signing is not required or encryption is requested as well.
+
+<sect2>pgp_default_version<label id="pgp_default_version">
+<p>
+Type: string<newline>
+Default: pgp2 (or pgp5, if PGP 2.* is not installed)
+
+Set this to pgp2 (PGP 2.*), or pgp5 (PGP 5.*) depending on the
+version, you are using primary. This variable is directly used, but it
+is the default for the variables <ref id="pgp_receive_version"
+name="$pgp_receive_version">, <ref
+id="pgp_send_version" name="$pgp_send_version">,
+and <ref id="pgp_key_version"
+name="$pgp_key_version">.
+
+<sect2>pgp_encryptself<label id="pgp_encryptself">
+<p>
+Type: boolean<newline>
+Default: set
+
+If set, the PGP <em/+encrypttoself/ flag is used when encrypting messages.
+
+<sect2>pgp_key_version<label id="pgp_key_version">
+<p>
+Type: string<newline>
+Default: ``default''
+
+This variable determines, which PGP version used for key ring
+operations like extracting keys from messages and extracting keys from
+your keyring. If you set this to default, the default defined in <ref
+id="pgp_default_version"
+name="$pgp_default_version"> is used. Set this to
+pgp2 (PGP 2.*), or pgp5 (PGP 5.*) if you want a different PGP version
+for key operations.
+
+<sect2>pgp_long_ids<label id="pgp_long_ids">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, use 64 bit PGP key IDs. Unset uses the normal 32 bit Key IDs.
+
+<sect2>pgp_receive_version<label id="pgp_receive_version">
+<p>
+Type: string<newline>
+Default: ``default''
+
+This variable determines, which PGP version used for decrypting
+messages and verifying signatures. If you set this to default, the
+default defined in <ref id="pgp_default_version"
+name="$pgp_default_version"> will be used. Set
+this to pgp2 (PGP 2.*), or pgp5 (PGP 5.*) if you want a different PGP
+version for receiving operations.
+
+<sect2>pgp_replyencrypt<label id="pgp_replyencrypt">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, automatically PGP encrypt replies to messages which are encrypted.
+
+<sect2>pgp_replysign<label id="pgp_replysign">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, automatically PGP sign replies to messages which are signed.
+
+<bf/Note:/ this does not work on messages, that are encrypted <bf/and/
+signed!
+
+<sect2>pgp_send_version<label id="pgp_send_version">
+<p>
+Type: string<newline>
+Default: ``default''
+
+This variable determines, which PGP version used for composing new
+messages like encrypting and signing. If you set this to default, the
+default defined in <ref id="pgp_default_version"
+name="$pgp_default_version"> will be used. Set
+this to pgp2 (PGP 2.*), or pgp5 (PGP 5.*) if you want a different PGP
+version for sending operations.
+
+<sect2>pgp_sign_as<label id="pgp_sign_as">
+<p>
+Type: string<newline>
+Default: unset
+
+If you have more than one key pair, this option allows you to specify which
+of your private keys to use. It is recommended that you use the keyid form
+to specify your key (e.g., ``0xABCDEFGH'').
+
+<sect2>pgp_sign_micalg<label id="pgp_sign_micalg">
+<p>
+Type: string<newline>
+Default: pgp-md5
+
+This variable contains the default message integrity check algorithm.
+Valid values are ``pgp-md5'', ``pgp-sha1'', and ``pgp-rmd160''. If you
+select a signing key using the sign as option on the compose menu,
+mutt will automagically figure out the correct value to insert here,
+but it does not know about the user's default key.
+
+So if you are using an RSA key for signing, set this variable to
+``pgp-md5'', if you use a PGP 5 DSS key for signing, say ``pgp-sha1''
+here. The value of this variable will show up in the micalg parameter
+of MIME headers when creating RFC 2015 signatures.
+
+<sect2>pgp_strict_enc<label id="pgp_strict_enc">
+<p>
+Type: boolean<newline>
+Default: set
+
+If set, Mutt will automatically encode PGP/MIME signed
+messages as <em/quoted-printable/. Please note that
+unsetting this variable may lead to problems with
+non-verifyable PGP signatures, so only change this if you
+know what you are doing.
+
+<sect2>pgp_timeout<label id="pgp_timeout">
+<p>
+Type: number<newline>
+Default: 300
+
+The number of seconds after which a cached passphrase will expire if not
+used.
+
+<sect2>pgp_v2<label id="pgp_v2">
+<p>
+Type: string<newline>
+Default: system dependent
+
+This variable allows you to override the compile time definition of
+where the PGP 2.* binary resides on your system.
+
+<sect2>pgp_v2_language<label id="pgp_v2_language">
+<p>
+Type: string<newline>
+Default: en
+
+Sets the language, which PGP 2.* should use. If you use language.txt
+from the mutt doc directory, you can try the languages
+&dquot;mutt&dquot; (English) or &dquot;muttde&dquot; (German) to
+reduce the noise produced by pgp.
+
+<sect2>pgp_v2_pubring<label id="pgp_v2_pubring">
+<p>
+Type: string<newline>
+Default: $PGPPATH/pubring.pgp or ˜/.pgp/pubring.pgp if
+$PGPPATH isn't set.
+
+Points to the PGP 2.* public keyring.
+
+<sect2>pgp_v2_secring<label id="pgp_v2_secring">
+<p>
+Type: string<newline>
+Default: $PGPPATH/secring.pgp or ˜/.pgp/secring.pgp if
+$PGPPATH isn't set.
+
+Points to the PGP 2.* secret keyring.
+
+<sect2>pgp_v5<label id="pgp_v5">
+<p>
+Type: string<newline>
+Default: system dependent
+
+This variable allows you to override the compile time definition of
+where the PGP 5.* binary resides on your system.
+
+<sect2>pgp_v5_language<label id="pgp_v5_language">
+<p>
+Type: string<newline>
+Default: en
+
+Sets the language, which PGP 5.* should use. If you use language50.txt
+from the mutt doc directory, you can try the languages
+&dquot;mutt&dquot; (English) to reduce the noise produced by pgp.
+
+<sect2>pgp_v5_pubring<label id="pgp_v5_pubring">
+<p>
+Type: string<newline>
+Default: $PGPPATH/pubring.pkr or ˜/.pgp/pubring.pkr if
+$PGPPATH isn't set.
+
+Points to the PGP 5.* public keyring.
+
+<sect2>pgp_v5_secring<label id="pgp_v5_secring">
+<p>
+Type: string<newline>
+Default: $PGPPATH/secring.skr or ˜/.pgp/secring.skr if
+$PGPPATH isn't set.
+
+Points to the PGP 5.* secret keyring.
+
+<sect2>pipe_decode<label id="pipe_decode">
+<p>
+Type: boolean<newline>
+Default: unset
+
+Used in connection with the <em/pipe-message/ command.
+When unset, Mutt will pipe the messages without any preprocessing. When
+set, Mutt will weed headers and will attempt to PGP/MIME decode the
+messages first.
+
+<sect2>pipe_sep<label id="pipe_sep">
+<p>
+Type: string<newline>
+Default: newline
+
+The separator to add between messages when piping a list of tagged messages
+to an external Unix command.
+
+<sect2>pipe_split<label id="pipe_split">
+<p>
+Type: boolean<newline>
+Default: unset
+
+Used in connection with the <em/pipe-message/ command and the
+``tag-prefix'' operator. If this variable is unset, when piping a list
+of tagged messages Mutt will concatenate the messages and will pipe them
+as a single folder. When set, Mutt will pipe the messages one by one.
+In both cases the the messages are piped in the current sorted order,
+and the <ref id="pipe_sep" name="$pipe_sep"> separator is
+added after each message.
+
+<sect2>pop_delete<label id="pop_delete">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, Mutt will delete successfully downloaded messages from the POP
+server when using the fetch-mail function. When unset, Mutt will download
+messages but also leave them on the POP server.
+
+<sect2>pop_host<label id="pop_host">
+<p>
+Type: string<newline>
+Default: none
+
+The name or address of your POP3 server.
+
+<sect2>pop_pass<label id="pop_pass">
+<p>
+Type: string<newline>
+Default: unset
+
+Specifies the password for you POP account. If unset, Mutt will prompt you
+for your password when you invoke the fetch-mail function. <bf/Warning/:
+you should only use this option when you are on a fairly secure machine,
+because the superuser can read your muttrc even if you are the only one who
+can read the file.
+
+<sect2>pop_port<label id="pop_port">
+<p>
+Type: number<newline>
+Default: 110
+
+This variable specifies which port your POP server is listening on.
+
+<sect2>pop_user<label id="pop_user">
+<p>
+Type: string<newline>
+Default: login name on local system
+
+Your login name on the POP3 server.
+
+<sect2>post_indent_string<label id="post_indent_string">
+<p>
+Type: format string<newline>
+Default: none
+
+Similar to the <ref id="attribution" name="$attribution">
+variable, Mutt will append this string after the inclusion of a message
+which is being replied to.
+
+<sect2>postpone<label id="postpone">
+<p>
+Type: quadoption<newline>
+Default: ask-yes
+
+Controls whether or not messages are saved in the <ref id="postponed"
+name="$postponed"> mailbox when you elect not to send
+immediately.
+
+<sect2>postponed<label id="postponed">
+<p>
+Type: string<newline>
+Default: ˜/postponed
+
+Mutt allows you to indefinitely <ref id="postponing_mail" name="postpone
+sending a message"> which you are editing. When you choose to postpone a
+message, Mutt saves it in the folder specified by this variable. Also see
+the <ref id="postpone" name="$postpone"> variable.
+
+<sect2>print
+<p>
+Type: quadoption<newline>
+Default: ask-no
+
+Controls whether or not Mutt asks for confirmation before printing. This
+is useful for people (like me) who accidentally hit ``p'' often.
+
+<sect2>print_command<label id="print_command">
+<p>
+Type: string<newline>
+Default: lpr
+
+This specifies the command pipe that should be used to print messages.
+
+<sect2>prompt_after<label id="prompt_after">
+<p>
+Type: boolean<newline>
+Default: set
+
+If you use an <em/external/ <ref id="pager" name="pager">, setting this
+variable will cause Mutt to prompt you for a command when the pager
+exits rather than returning to the index menu. If unset, Mutt will return
+to the index menu when the external pager exits.
+
+<sect2>query_command<label id="query_command">
+<p>
+Type: string<newline>
+Default: null
+
+This specifies the command that mutt will use to make external address
+queries. The string should contain a %s, which will be substituted with
+the query string the user types. See <ref id="query" name="query"> for
+more information.
+
+<sect2>quit<label id="quit">
+<p>
+Type: quadoption<newline>
+Default: yes
+
+This variable controls whether ``quit'' and ``exit'' actually quit from mutt.
+If it set to yes, they do quit, if it is set to no, they have no effect, and
+if it is set to ask-yes or ask-no, you are prompted for confirmation when you
+try to quit.
+
+<sect2>quote_regexp<label id="quote_regexp">
+<p>
+Type: string<newline>
+Default: "^([ \t]*[>|#:}])+"
+
+A regular expression used in the internal-pager to determine quoted
+sections of text in the body of a message.
+
+<bf/Note:/ In order to use the <em/quoted/<bf/x/ patterns in the
+internal pager, you need to set this to a regular expression that
+matches <em/exactly/ the quote characters at the beginning of quoted
+lines.
+
+<sect2>read_inc<label id="read_inc">
+<p>
+Type: number<newline>
+Default: 10
+
+If set to a value greater than 0, Mutt will display which message it is
+currently on when reading a mailbox. The message is printed after
+<em/read_inc/ messages have been read (e.g., if set to 25, Mutt will
+print a message when it reads message 25, and then again when it gets to
+message 50). This variable is meant to indicate progress when reading
+large mailboxes which may take some time.
+
+When set to 0, only a single message will appear before the reading
+the mailbox.
+
+Also see the <ref id="write_inc" name="$write_inc"> variable.
+
+<sect2>read_only<label id="read_only">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, all folders are opened in read-only mode.
+
+<sect2>realname<label id="realname">
+<p>
+Type: string<newline>
+Default: GCOS field from /etc/passwd
+
+This variable specifies what "real" or "personal" name should be used when
+sending messages.
+
+<sect2>recall<label id="recall">
+<p>
+Type: quadoption<newline>
+Default: ask-yes
+
+Controls whether or not you are prompted to recall postponed messages when
+composing a new message. Also see <ref id="postponed" name="postponed">
+
+<sect2>record<label id="record">
+<p>
+Type: string<newline>
+Default: none
+
+This specifies the file into which your outgoing messages should be
+appended. (This is meant as the primary method for saving a copy of
+your messages, but another way to do this is using the <ref id="my_hdr"
+name="my_hdr"> command to create a <em/Bcc:/ field with your
+email address in it.)
+
+The value of <em/$record/ is overridden by the
+<ref id="force_name" name="$force_name"> and
+<ref id="save_name" name="$save_name"> variables, and the
+<ref id="fcc-hook" name="fcc-hook"> command.
+
+<sect2>reply_regexp<label id="reply_regexp">
+<p>
+Type: string<newline>
+Default: "^(re|aw):[ \t]*"
+
+A regular expression used to recognize reply messages when threading and
+replying. The default value corresponds to the English "Re:" and the
+German "Aw:".
+
+<sect2>reply_to<label id="reply_to">
+<p>
+Type: quadoption<newline>
+Default: ask-yes
+
+If set, Mutt will ask you if you want to use the address listed in the
+Reply-To: header field when replying to a message. If you answer no, it will
+use the address in the From: header field instead. This option is useful for
+reading a mailing list that sets the Reply-To: header field to the list address
+and you want to send a private message to the author of a message.
+
+<sect2>resolve<label id="resolve">
+<p>
+Type: boolean<newline>
+Default: set
+
+When set, the cursor will be automatically advanced to the next (possibly
+undeleted) message whenever a command that modifies the current message is
+executed.
+
+<sect2>reverse_alias<label id="reverse_alias">
+<p>
+Type: boolean<newline>
+Default: unset
+
+This variable controls whether or not Mutt will display the "personal" name
+from your aliases in the index menu if it finds an alias that matches the
+message's sender. For example, if you have the following alias:
+
+<tscreen><verb>
+alias juser abd30425@somewhere.net (Joe User)
+</verb></tscreen>
+
+and then you receive mail which contains the following header:
+
+<tscreen><verb>
+From: abd30425@somewhere.net
+</verb></tscreen>
+
+It would be displayed in the index menu as ``Joe User'' instead of
+``abd30425@somewhere.net.'' This is useful when the person's e-mail address
+is not human friendly (like Compu$erve addresses).
+
+<sect2>reverse_name<label id="reverse_name">
+<p>
+Type: boolean<newline>
+Default: unset
+
+It may sometimes arrive that you receive mail to a certain machine,
+move the messages to another machine, and reply to some the messages
+from there. If this variable is set, the default <em/From:/ line of
+the reply messages is built using the address where you received the
+messages you are replying to. If the variable is unset, the <em/From:/
+line will use your address on the current machine.
+
+<sect2>save_address<label id="save_address">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, mutt will take the sender's full address when choosing a
+default folder for saving a mail. If <ref id="save_name"
+name="save_name"> or <ref id="force_name"
+name="force_name"> is set too, the selection of the fcc folder
+will be changed as well.
+
+<sect2>save_empty<label id="save_empty">
+<p>
+Type: boolean<newline>
+Default: set
+
+When unset, mailboxes which contain no saved messages will be removed when
+closed (the exception is <ref id="spoolfile" name="spoolfile"> which is
+never removed). If set, mailboxes are never removed.
+
+<bf/Note:/ This only applies to mbox and MMDF folders, Mutt does not
+delete MH and Maildir directories.
+
+<sect2>save_name<label id="save_name">
+<p>
+Type: boolean<newline>
+Default: unset
+
+This variable controls how copies of outgoing messages are saved. When
+set, a check is made to see if a mailbox specified by the recipient
+address exists (this is done by searching for a mailbox in the <ref
+id="folder" name="folder"> directory with the <em/username/ part of
+the recipient address). If the mailbox exists, the outgoing message
+will be saved to that mailbox, otherwise the message is saved to the
+<ref id="record" name="record"> mailbox.
+
+Also see the <ref id="force_name" name="$force_name"> variable.
+
+<sect2>sendmail<label id="sendmail">
+<p>
+Type: string<newline>
+Default: /usr/lib/sendmail -oi -oem
+
+Specifies the program and arguments used to deliver mail sent by Mutt.
+Mutt expects that the specified program will read the message header for
+recipients.
+
+<sect2>sendmail_wait<label id="sendmail_wait">
+<p>
+Type: number<newline>
+Default: 0
+
+Specifies the number of seconds to wait for the <ref id="sendmail"
+name="sendmail"> process to finish before giving up and putting delivery in
+the background.
+
+Mutt interprets the value of this variable as follows:
+<verb>
+>0 number of seconds to wait for sendmail to finish before continuing
+0 wait forever for sendmail to finish
+<0 always put sendmail in the background without waiting
+</verb>
+
+Note that if you specify a value other than 0, the output of the child
+process will be put in a temporary file. If there is some error, you will
+be informed as to where to find the output.
+
+<sect2>shell<label id="shell">
+<p>
+Type: string<newline>
+Default: retrieved from passwd file
+
+Command to use when spawning a subshell.
+
+<sect2>sig_dashes<label id="sig_dashes">
+<p>
+Type: boolean<newline>
+Default: set
+
+If set, a line containing ``-- '' will be inserted before your <ref
+id="signature" name="signature">. It is <bf/strongly/ recommended that you
+not unset this variable unless your ``signature'' contains just your name.
+The reason for this is because many software packages use ``-- \n'' to
+detect your signature. For example, Mutt has the ability to highlight the
+signature in a different color in the builtin pager.
+
+<sect2>signature<label id="signature">
+<p>
+Type: string<newline>
+Default: ˜/.signature
+
+Specifies the filename of your signature, which is appended to all outgoing
+messages. If the filename ends with a pipe (``|''), it is assumed that
+filename is a shell command and input should be read from its stdout.
+
+<sect2>simple_search<label id="simple_search">
+<p>
+Type: string<newline>
+Default: &dquot;˜f %s | ˜s %s&dquot;
+
+Specifies how Mutt should expand a simple search into a real search pattern.
+A simple search is one that does not contain any of the <tt/˜/
+operators. See <ref id="searching" name="searching"> for more information
+on search patterns.
+
+For example, if you simply type <tt/joe/ at a search or limit prompt, Mutt
+will automatically expand it to the value specified by this variable. For
+the default value it would be:
+
+<tt/˜f joe | ˜s joe/
+
+<sect2>smart_wrap<label id="smart_wrap">
+<p>
+Type: boolean<newline>
+Default: set
+
+Controls the display of lines longer then the screen width in the
+internal pager. If set, long lines are wrapped at a word boundary.
+If unset, lines are simply wrapped at the screen edge. Also see the
+<ref id="markers" name="$markers"> variable.
+
+<sect2>sort<label id="sort">
+<p>
+Type: string<newline>
+Default: date-sent
+
+Specifies how to sort messages in the <em/index/ menu. Valid values are
+
+<itemize>
+<item>date-sent
+<item>date-received
+<item>from
+<item>mailbox-order (unsorted)
+<item>score
+<item>subject
+<item>threads
+<item>to
+</itemize>
+
+You may optionally use the <tt/reverse-/ prefix to specify reverse sorting
+order (example: <tt/set sort=reverse-date-sent/).
+
+<sect2>sort_alias<label id="sort_alias">
+<p>
+Type: string<newline>
+Default: alias
+
+Specifies how the entries in the `alias' menu are sorted. The following are
+legal values:
+<verb>
+alias sort alphabetically by alias name
+address sort alphabetically by email address
+unsorted leave in order specified in .muttrc
+</verb>
+
+<sect2>sort_aux<label id="sort_aux">
+<p>
+Type: string<newline>
+Default: date-sent
+
+When sorting by threads, this variable controls how threads are sorted in
+relation to other threads, and how the branches of the thread trees are
+sorted. This can be set to any value that <ref id="sort" name="sort"> can,
+except <tt/threads/ (in that case, mutt will just use <tt/date-sent/). You
+can also specify the <tt/last-/ prefix in addition to the <tt/reverse-/
+prefix, but <tt/last-/ must come after <tt/reverse-/. The <tt/last-/ prefix
+causes messages to be sorted against its siblings by which has the last
+descendant, using the rest of sort_aux as an ordering. For instance,
+<tt/set sort_aux=last-date-received/ would mean that if a new message is
+received in a thread, that thread becomes the last one displayed (or the
+first, if you have <tt/set sort=reverse-threads/.)
+
+<sect2>sort_browser<label id="sort_browser">
+<p>
+Type: string<newline>
+
+Specifies how to sort entries in the file browser. By default, the entries
+are sorted alphabetically. Valid values:
+
+<itemize>
+<item>date
+<item>alpha (alphabetically)
+</itemize>
+
+You may optionally use the <tt/reverse-/ prefix to specify reverse sorting
+order (example: <tt/set sort_browser=reverse-date/).
+
+<sect2>spoolfile<label id="spoolfile">
+<p>
+Type: string<newline>
+Default: most likely /var/mail/$USER or /usr/spool/mail/$USER
+
+If your spool mailbox is in a non-default place where Mutt cannot find it,
+you can specify its location with this variable. Mutt will automatically
+set this variable to the value of the environment variable <tt/$MAIL/
+if it is not set.
+
+<sect2>sort_re<label id="sort_re">
+<p>
+Type: boolean
+Default: set
+
+This variable is only useful when sorting by threads with
+<ref id="strict_threads" name="strict_threads"> unset. In that case,
+it changes the heuristic mutt uses to thread messages by subject. With
+sort_re set, mutt will only attach a message as the child of another message
+by subject if the subject of the child message starts with a substring matching
+the setting of <ref id="reply_regexp" name="reply_regexp">. With
+sort_re unset, mutt will attach the message whether or not this is the case,
+as long as the non-<ref id="reply_regexp" name="reply_regexp"> parts of
+both messages are identical.
+
+<sect2>status_chars<label id="status_chars">
+<p>
+Type: string<newline>
+Default: &dquot;-*%&dquot;
+
+Controls the characters used by the &dquot;%r&dquot; indicator
+in <ref id="status_format" name="status_format">. The first
+character is used when the mailbox is unchanged. The second is used when
+the mailbox has been changed, and it needs to be resynchronized. The
+third is used if the mailbox is in read-only mode, or if the mailbox
+will not be written when exiting that mailbox (You can toggle whether
+to write changes to a mailbox with the toggle-write operation, bound by
+default to &dquot;%&dquot;).
+
+<sect2>status_format<label id="status_format">
+<p>
+Type: string<newline>
+Default: &dquot;-%r-Mutt: %f [Msgs:%?M?%M/?%m%?n? New:%n?%?o? Old:%o?%?d? Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b? %?l? %l?]---(%s/%S)-%>-(%P)---&dquot;
+
+Controls the format of the status line displayed in the
+<em/index/ menu. This string is similar to <ref id="index_format"
+name="$index_format">, but has its own set of printf()-like
+sequences:
+
+<tscreen><verb>
+%b number of mailboxes with new mail *
+%d number of deleted messages *
+%h local hostname
+%f the full pathname of the current mailbox
+%F number of flagged messages *
+%l size (in bytes) of the current mailbox *
+%L size (in bytes) of the messages shown (i.e., which match the current limit) *
+%m the number of messages in the mailbox *
+%M the number of messages shown (i.e., which match the current limit) *
+%n number of new messages in the mailbox *
+%o number of old unread messages
+%p number of postponed messages *
+%P percentage of the way through the index
+%r modified/read-only/won't-write indicator, according to $status_chars
+%s current sorting mode ($sort)
+%S current aux sorting method ($sort_aux)
+%t number of tagged messages *
+%u number of unread messages *
+%v Mutt version string
+%V currently active limit pattern, if any *
+
+%>X right justify the rest of the string and pad with "X"
+%|X pad to the end of the line with "X"
+
+* = can be optionally printed if nonzero
+</verb></tscreen>
+
+Some of the above sequences can be used to optionally print a string if
+their value is nonzero. For example, you may only want to see the number of
+flagged messages if such messages exist, since zero is not particularly
+meaningful. To optionally print a string based upon one of the above
+sequences, the following construct is used
+
+<tscreen><verb>
+ %?<sequence_char>?<optional_string>?
+</verb></tscreen>
+
+where <em/sequence_char/ is a character from the table above, and
+<em/optional_string/ is the string you would like printed if
+<em/status_char/ is nonzero. <em/optional_string/ <bf/may/
+contain other sequence as well as normal text, but you may <bf/not/ nest
+optional strings.
+
+Here is an example illustrating how to optionally print the number of new
+messages in a mailbox:
+
+<tscreen><verb>
+ %?n?%n new messages.?
+</verb></tscreen>
+
+<sect2>status_on_top<label id="status_on_top">
+<p>
+Type: boolean<newline>
+Default: unset
+
+Setting this variable causes the <ref id="status_format" name="status
+bar"> to be displayed on the first line of the screen rather than near
+the bottom.
+
+<sect2>strict_threads<label id="strict_threads">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, threading will only make use of the ``In-Reply-To'' and
+``References'' fields when <ref id="sort" name="sorting"> by message
+threads. By default, messages with the same subject are grouped together in
+``pseudo threads.'' This may not always be desirable, such as in a personal
+mailbox where you might have several unrelated messages with the subject
+``hi'' which will get grouped together.
+
+<sect2>suspend<label id="suspend">
+<p>
+Type: boolean<newline>
+Default: set
+
+When <em/unset/, mutt won't stop when the user presses the
+terminal's <em/susp/ key, usually ``control-Z''. This is useful
+if you run mutt inside an xterm using a command like <tt/xterm
+-e mutt/.
+
+<sect2>thorough_search<label id="thorough_search">
+<p>
+Type: boolean<newline>
+Default: unset
+
+Affects the <em/˜b/ and <em/˜h/ search operations described
+in section <ref id="searching" name="Searching"> above. If set, the
+headers and attachments of messages to be searched are decoded before
+searching. If unset, messages are searched as they appear in the
+folder.
+
+<sect2>tilde<label id="tilde">
+<p>
+Type: boolean<newline>
+Default: unset
+
+When set, the internal-pager will pad blank lines to the bottom of the
+screen with a tilde (˜).
+
+<sect2>timeout<label id="timeout">
+<p>
+Type: number<newline>
+Default: 600
+
+This variable controls the <em/number of seconds/ Mutt will wait for a key
+to be pressed in the main menu before timing out and checking for new mail.
+A value of zero or less will cause Mutt not to ever time out.
+
+<sect2>tmpdir<label id="tmpdir">
+<p>
+Type: string<newline>
+Default: /tmp
+
+This variable allows you to specify where Mutt will place its temporary
+files needed for displaying and composing messages.
+
+<sect2>to_chars<label id="to_chars">
+<p>
+Type: string<newline>
+Default: &dquot; +TCF&dquot;
+
+Controls the character used to indicate mail addressed to you. The first
+character is the one used when the mail is NOT addressed to your address
+(default: space). The second is used when you are the only recipient of the
+message (default: +). The third is when your address appears in the TO
+header field, but you are not the only recipient of the message (default:
+T). The fourth character is used when your address is specified in the CC
+header field, but you are not the only recipient. The fifth character is used
+to indicate mail that was sent by <em/you/.
+
+<sect2>use_8bitmime<label id="use_8bitmime">
+<p>
+Type: boolean<newline>
+Default: unset
+
+<bf/Warning:/ do not set this variable unless you are using a version of
+sendmail which supports the <tt/-B8BITMIME/ flag (such as sendmail 8.8.x) or
+you may not be able to send mail.
+
+When <em/set/, Mutt will invoke <ref id="sendmail" name="$sendmail">
+with the <tt/-B8BITMIME/ flag when sending 8-bit messages to enable ESMTP
+negotiation.
+
+<sect2>use_domain<label id="use_domain">
+<p>
+Type: boolean<newline>
+Default: set
+
+When set, Mutt will qualify all local addresses (ones without the @host
+portion) with the value of <ref id="hostname" name="$hostname">. If
+<em/unset/, no addresses will be qualified.
+
+<sect2>use_from<label id="use_from">
+<p>
+Type: boolean<newline>
+Default: set
+
+When <em/set/, Mutt will generate the `From:' header field when sending
+messages. If <em/unset/, no `From:' header field will be generated unless
+the user explicitly sets one using the <ref id="my_hdr"
+name="my_hdr"> command.
+
+<sect2>use_mailcap<label id="use_mailcap">
+<p>
+Type: quad-option<newline>
+Default: ask
+
+If set to ``yes'', always try to use a mailcap entry to display a MIME
+part that Mutt can't understand what to do with. If ``ask'', prompt as
+to whether to display as text or to use a mailcap entry. If ``no'',
+always view unsupported MIME types as text.
+
+<bf/Note:/ For compatibility with <bf/metamail/, Mutt will also look at the
+environment variable <em/MM_NOASK/. Setting this to <bf/1/ is
+equivalent to setting <em/use_mailcap/ to ``yes''. Otherwise, the
+value of <em/MM_NOASK/ is interpreted as a comma-separated list of type
+names (without white space) for which the corresponding mailcap entries will
+be used to display MIME parts without prompting the user for confirmation.
+
+<sect2>pgp_verify_sig<label id="pgp_verify_sig">
+<p>
+Type: quad-option<newline>
+Default: yes
+
+If ``yes'', always attempt to verify PGP/MIME signatures. If ``ask'',
+ask whether or not to verify the signature. If ``no'', never attempt to
+verify PGP/MIME signatures.
+
+<sect2>visual<label id="visual">
+<p>
+Type: string<newline>
+Default: $VISUAL
+
+Specifies the visual editor to invoke when the <em/˜v/ command is given in
+the builtin editor.
+
+<sect2>wait_key<label id="wait_key">
+<p>
+Type: boolean<newline>
+Default: set
+
+Controls whether Mutt will ask you to press a key after
+<em/shell-escape/, <em/pipe-message/, <em/pipe-entry/,
+<em/print-message/, and <em/print-entry/ commands.
+
+It is also used when viewing attachments with <ref id="auto_view"
+name="autoview">, provided that the corresponding mailcap entry has a
+<em/needsterminal/ flag, and the external program is interactive.
+
+When set, Mutt will always ask for a key. When unset, Mutt will wait for
+a key only if the external command returned a non-zero status.
+
+<sect2>wrap_search<label id="wrap_search">
+<p>
+Type: boolean<newline>
+Default: set
+
+Controls whether searches wrap around the end of the mailbox.
+
+When set, searches will wrap around the first (or last) message. When
+unset, searches will not wrap.
+
+<sect2>write_inc<label id="write_inc">
+<p>
+Type: number<newline>
+Default: 10
+
+When writing a mailbox, a message will be printed every
+<em/write_inc/ messages to indicate progress. If set to 0, only
+a single message will be displayed before writing a mailbox.
+
+Also see the <ref id="read_inc" name="$read_inc"> variable.
+
+<sect1>Functions<label id="functions">
+<p>
+The following is the list of available functions listed by the mapping
+in which they are available. The default key setting is given, and an
+explanation of what the function does. The key bindings of these
+functions can be changed with the <ref name="bind" id="bind">
+command.
+
+<sect2>generic
+<p>
+
+The <em/generic/ menu is not a real menu, but specifies common functions
+(such as movement) available in all menus except for <em/pager/ and
+<em/editor/. Changing settings for this menu will affect the default
+bindings for all menus (except as noted).
+
+<verb>
+bottom-page L move to the bottom of the page
+current-bottom not bound move current entry to bottom of page
+current-middle not bound move current entry to middle of page
+current-top not bound move current entry to top of page
+enter-command : enter a muttrc command
+exit q exit this menu
+first-entry = move to the first entry
+half-down ] scroll down 1/2 page
+half-up [ scroll up 1/2 page
+help ? this screen
+jump number jump to an index number
+last-entry * move to the last entry
+middle-page M move to the middle of the page
+next-entry j move to the next entry
+next-line > scroll down one line
+next-page z move to the next page
+previous-entry k move to the previous entry
+previous-line < scroll up one line
+previous-page Z move to the previous page
+refresh ^L clear and redraw the screen
+search / search for a regular expression
+search-next n search for next match
+search-opposite not bound search for next match in opposite direction
+search-reverse ESC / search backwards for a regular expression
+select-entry RET select the current entry
+shell-escape ! run a program in a subshell
+tag-entry t toggle the tag on the current entry
+tag-prefix ; apply next command to tagged entries
+top-page H move to the top of the page
+</verb>
+<sect2>index
+<p>
+<verb>
+bounce-message b remail a message to another user
+change-folder c open a different folder
+change-folder-readonly ESC c open a different folder in read only mode
+clear-flag W clear a status flag from a message
+copy-message C copy a message to a file/mailbox
+create-alias a create an alias from a message sender
+decode-copy ESC C decode a message and copy it to a file/mailbox
+decode-save ESC s decode a message and save it to a file/mailbox
+delete-message d delete the current entry
+delete-pattern D delete messages matching a pattern
+delete-subthread ESC d delete all messages in subthread
+delete-thread ^D delete all messages in thread
+display-address @ display full address of sender
+display-headers h display message with full headers
+display-message RET display a message
+exit x exit without saving changes
+extract-keys ^K extract PGP public keys
+fetch-mail G retrieve mail from POP server
+flag-message F toggle a message's 'important' flag
+forget-passphrase ^F wipe PGP passphrase from memory
+forward-message f forward a message with comments
+group-reply g reply to all recipients
+limit l show only messages matching a pattern
+list-reply L reply to specified mailing list
+mail m compose a new mail message
+mail-key ESC k mail a PGP public key
+next-new TAB jump to the next new message
+next-subthread ESC n jump to the next subthread
+next-thread ^N jump to the next thread
+next-undeleted j move to the next undeleted message
+next-unread not bound jump to the next unread message
+pipe-message | pipe message/attachment to a shell command
+previous-new ESC TAB jump to the previous new message
+previous-page Z move to the previous page
+previous-subthread ESC p jump to previous subthread
+previous-thread ^P jump to previous thread
+previous-undeleted k move to the last undelete message
+previous-unread not bound jump to the previous unread message
+print-message p print the current entry
+query Q query external program for addresses
+quit q save changes to mailbox and quit
+read-subthread ESC r mark the current subthread as read
+read-thread ^R mark the current thread as read
+recall-message R recall a postponed message
+reply r reply to a message
+save-message s save message/attachment to a file
+set-flag w set a status flag on a message
+show-version V show the Mutt version number and date
+show-limit ESC l show currently active limit pattern, if any
+sort-mailbox o sort messages
+sort-reverse O sort messages in reverse order
+sync-mailbox $ save changes to mailbox
+tag-pattern T tag messages matching a pattern
+tag-thread ESC t tag/untag all messages in the current thread
+toggle-new N toggle a message's 'new' flag
+toggle-write % toggle whether the mailbox will be rewritten
+undelete-message u undelete the current entry
+undelete-pattern U undelete messages matching a pattern
+undelete-subthread ESC u undelete all messages in subthread
+undelete-thread ^U undelete all messages in thread
+untag-pattern ^T untag messages matching a pattern
+view-attachments v show MIME attachments
+</verb>
+<sect2>pager
+<p>
+<verb>
+bottom $ jump to the bottom of the message
+bounce-message b remail a message to another user
+change-folder c open a different folder
+change-folder-readonly ESC c open a different folder in read only mode
+copy-message C copy a message to a file/mailbox
+create-alias a create an alias from a message sender
+decode-copy ESC C decode a message and copy it to a file/mailbox
+decode-save ESC s decode a message and save it to a file/mailbox
+delete-message d delete the current entry
+delete-subthread ESC d delete all messages in subthread
+delete-thread ^D delete all messages in thread
+display-address @ display full address of sender
+display-headers h display message with full headers
+enter-command : enter a muttrc command
+exit i return to the main-menu
+extract-keys ^K extract PGP public keys
+flag-message F toggle a message's 'important' flag
+forget-passphrase ^F wipe PGP passphrase from memory
+forward-message f forward a message with comments
+group-reply g reply to all recipients
+half-up not bound move up one-half page
+half-down not bound move down one-half page
+help ? this screen
+list-reply L reply to specified mailing list
+mail m compose a new mail message
+mail-key ESC k mail a PGP public key
+mark-as-new N toggle a message's 'new' flag
+next-line RET scroll down one line
+next-message J move to the next entry
+next-new TAB jump to the next new message
+next-page move to the next page
+next-subthread ESC n jump to the next subthread
+next-thread ^N jump to the next thread
+next-undeleted j move to the next undeleted message
+next-unread not bound jump to the next unread message
+pipe-message | pipe message/attachment to a shell command
+previous-line BackSpace scroll up one line
+previous-message K move to the previous entry
+previous-new not bound jump to the previous new message
+previous-page - move to the previous page
+previous-subthread ESC p jump to previous subthread
+previous-thread ^P jump to previous thread
+previous-undeleted k move to the last undelete message
+previous-unread not bound jump to the previous unread message
+print-message p print the current entry
+quit Q save changes to mailbox and quit
+read-subthread ESC r mark the current subthread as read
+read-thread ^R mark the current thread as read
+recall-message R recall a postponed message
+redraw-screen ^L clear and redraw the screen
+reply r reply to a message
+save-message s save message/attachment to a file
+search / search for a regular expression
+search-next n search for next match
+search-opposite not bound search for next match in opposite direction
+search-reverse ESC / search backwards for a regular expression
+search-toggle \ toggle search pattern coloring
+shell-escape ! invoke a command in a subshell
+show-version V show the Mutt version number and date
+skip-quoted S skip beyond quoted text
+tag-message t tag a message
+toggle-quoted T toggle display of quoted text
+top ^ jump to the top of the message
+undelete-message u undelete the current entry
+undelete-subthread ESC u undelete all messages in subthread
+undelete-thread ^U undelete all messages in thread
+view-attachments v show MIME attachments
+</verb>
+<sect2>alias
+<p>
+<verb>
+search / search for a regular expression
+search-next n search for next match
+search-reverse ESC / search backwards for a regular expression
+</verb>
+<sect2>query
+<p>
+<verb>
+create-alias a create an alias from a message sender
+mail m compose a new mail message
+query Q query external program for addresses
+query-append A append new query results to current results
+search / search for a regular expression
+search-next n search for next match
+search-opposite not bound search for next match in opposite direction
+search-reverse ESC / search backwards for a regular expression
+</verb>
+<sect2>attach
+<p>
+<verb>
+bounce-message b remail a message to another user
+decode-copy ESC C decode a message and copy it to a file/mailbox
+decode-save ESC s decode a message and save it to a file/mailbox
+delete-entry d delete the current entry
+display-headers h display message with full headers
+extract-keys ^K extract PGP public keys
+forward-message f forward a message with comments
+group-reply g reply to all recipients
+list-reply L reply to specified mailing list
+pipe-entry | pipe message/attachment to a shell command
+print-entry p print the current entry
+reply r reply to a message
+save-entry s save message/attachment to a file
+undelete-entry u undelete the current entry
+view-attach RET view attachment using mailcap entry if necessary
+view-mailcap m force viewing of attachment using mailcap
+view-text T view attachment as text
+</verb>
+<sect2>compose
+<p>
+<verb>
+attach-file a attach a file(s) to this message
+attach-key ESC k attach a PGP public key
+copy-file C save message/attachment to a file
+detach-file D delete the current entry
+display-headers h display message with full headers
+edit-bcc b edit the BCC list
+edit-cc c edit the CC list
+edit-description d edit attachment description
+edit-encoding ^E edit attachment trasfer-encoding
+edit-fcc f enter a file to save a copy of this message in
+edit-from ESC f edit the from: field
+edit-file ^X e edit the file to be attached
+edit-headers E edit the message with headers
+edit-message e edit the message
+edit-mime m edit attachment using mailcap entry
+edit-reply-to r edit the Reply-To field
+edit-subject s edit the subject of this message
+edit-to t edit the TO list
+edit-type ^T edit attachment type
+filter-entry F filter attachment through a shell command
+forget-passphrase ^F wipe PGP passphrase from memory
+ispell i run ispell on the message
+new-mime n compose new attachment using mailcap entry
+pgp-menu p show PGP options
+pipe-entry | pipe message/attachment to a shell command
+postpone-message P save this message to send later
+print-entry l print the current entry
+rename-file R rename/move an attached file
+send-message y send the message
+toggle-unlink u toggle whether to delete file after sending it
+view-attach RET view attachment using mailcap entry if necessary
+</verb>
+<sect2>postponed
+<p>
+<verb>
+delete-entry d delete the current entry
+undelete-entry u undelete the current entry
+</verb>
+<sect2>browser
+<p>
+<verb>
+change-dir c change directories
+check-new TAB check mailboxes for new mail
+enter-mask m enter a file mask
+search / search for a regular expression
+search-next n search for next match
+search-reverse ESC / search backwards for a regular expression
+select-new N select a new file in this directory
+sort o sort messages
+sort-reverse O sort messages in reverse order
+</verb>
+<sect2>pgp
+<p>
+<verb>
+view-name % view the key's user id
+verify-key c verify a PGP public key
+</verb>
+<sect2>editor
+<p>
+<verb>
+backspace BackSpace delete the char in front of the cursor
+backward-char ^B move the cursor one character to the left
+bol ^A jump to the beginning of the line
+buffy-cycle Space cycle among incoming mailboxes
+complete TAB complete filename or alias
+complete-query ^T complete address with query
+delete-char ^D delete the char under the cursor
+eol ^E jump to the end of the line
+forward-char ^F move the cursor one character to the right
+history-down not bound scroll up through the history list
+history-up not bound scroll up through the history list
+kill-eol ^K delete chars from cursor to end of line
+kill-line ^U delete all chars on the line
+kill-word ^W delete the word in front of the cursor
+quote-char ^V quote the next typed key
+</verb>
+
+<sect>Miscellany
+<p>
+
+<sect1>Acknowledgements
+<p>
+Kari Hurrta
+<htmlurl url="mailto:kari.hurtta@fmi.fi" name="<kari.hurtta@fmi.fi>">
+co-developed the original MIME parsing code back in the ELM-ME days.
+
+The following people have been very helpful to the development of Mutt:
+
+Francois Berjon <htmlurl url="mailto:Francois.Berjon@aar.alcatel-alsthom.fr"
+name="<Francois.Berjon@aar.alcatel-alsthom.fr>">,<newline>
+Aric Blumer <htmlurl url="mailto:aric@fore.com" name="<aric@fore.com>">,<newline>
+John Capo <htmlurl url="mailto:jc@irbs.com" name="<jc@irbs.com>">,<newline>
+Liviu Daia <htmlurl url="mailto:daia@stoilow.imar.ro" name="<daia@stoilow.imar.ro>">,<newline>
+David DeSimone <htmlurl url="mailto:fox@convex.hp.com" name="<fox@convex.hp.com>">,<newline>
+Nickolay N. Dudorov <htmlurl url="mailto:nnd@wint.itfs.nsk.su" name="<nnd@wint.itfs.nsk.su>">,<newline>
+Michael Finken <htmlurl url="mailto:finken@conware.de" name="<finken@conware.de>">,<newline>
+Sven Guckes <htmlurl url="mailto:guckes@math.fu-berlin.de" name="<guckes@math.fu-berlin.de>">,<newline>
+Mark Holloman <htmlurl url="mailto:holloman@nando.net" name="<holloman@nando.net>">,<newline>
+Andreas Holzmann <htmlurl url="mailto:holzmann@fmi.uni-passau.de" name="<holzmann@fmi.uni-passau.de>">,<newline>
+David Jeske <htmlurl url="mailto:jeske@igcom.net" name="<jeske@igcom.net>">,<newline>
+Christophe Kalt <htmlurl url="mailto:kalt@hugo.int-evry.fr" name="<kalt@hugo.int-evry.fr>">,<newline>
+Felix von Leitner (a.k.a ``Fefe'') <htmlurl url="mailto:leitner@math.fu-berlin.de" name="<leitner@math.fu-berlin.de>">,<newline>
+Brandon Long <htmlurl url="mailto:blong@fiction.net" name="<blong@fiction.net>">,<newline>
+Lars Marowsky-Bree <htmlurl url="mailto:lmb@pointer.in-minden.de" name="<lmb@pointer.in-minden.de>">,<newline>
+Thomas ``Mike'' Michlmayr <htmlurl url="mailto:mike@cosy.sbg.ac.at" name="<mike@cosy.sbg.ac.at>">,<newline>
+David O'Brien <htmlurl url="mailto:obrien@Nuxi.cs.ucdavis.edu" name="<obrien@Nuxi.cs.ucdavis.edu>">,<newline>
+Clint Olsen <htmlurl url="mailto:olsenc@ichips.intel.com" name="<olsenc@ichips.intel.com>">,<newline>
+Park Myeong Seok <htmlurl url="mailto:pms@romance.kaist.ac.kr" name="<pms@romance.kaist.ac.kr>">,<newline>
+Thomas Parmelan <htmlurl url="mailto:tom@ankh.fr.eu.org" name="<tom@ankh.fr.eu.org>">,<newline>
+Ollivier Robert <htmlurl url="mailto:roberto@keltia.freenix.fr" name="<roberto@keltia.freenix.fr>">,<newline>
+Thomas Roessler <htmlurl url="mailto:roessler@guug.de" name="<roessler@guug.de>">,<newline>
+Allain Thivillon <htmlurl url="mailto:Allain.Thivillon@alma.fr" name="<Allain.Thivillon@alma.fr>">,<newline>
+Ken Weinert <htmlurl url="mailto:kenw@ihs.com" name="<kenw@ihs.com>">
+
+<sect1>About this document
+<p>
+This document was written in SGML, and then rendered using the
+<htmlurl url="http://pobox.com/~cg/sgmltools/" name="sgml-tools"> package.
+
+</article>
--- /dev/null
+ The Mutt E-Mail Client
+ by Michael Elkins <me@cs.hmc.edu>
+ v0.92.2, 23 April 1998
+
+ ``All mail clients suck. This one just sucks less.'' -me, circa 1995
+
+ 1\b1.\b. I\bIn\bnt\btr\bro\bod\bdu\buc\bct\bti\bio\bon\bn
+
+ M\bMu\but\btt\bt is a small but very powerful text-based MIME mail client. Mutt
+ is highly configurable, and is well suited to the mail power user with
+ advanced features like key bindings, keyboard macros, mail threading,
+ regular expression searches and a powerful pattern matching language
+ for selecting groups of messages.
+
+ 1\b1.\b.1\b1.\b. M\bMu\but\btt\bt H\bHo\bom\bme\be P\bPa\bag\bge\be
+
+ http://www.cs.hmc.edu/~me/mutt/index.html
+
+ 1\b1.\b.2\b2.\b. M\bMa\bai\bil\bli\bin\bng\bg L\bLi\bis\bst\bts\bs
+
+ To subscribe to one of the following mailing lists, send a message
+ with the word _\bs_\bu_\bb_\bs_\bc_\br_\bi_\bb_\be in the subject to list-name_\b-
+ _\br_\be_\bq_\bu_\be_\bs_\bt@cs.hmc.edu.
+
+ +\bo mutt-announce@cs.hmc.edu -- low traffic list for announcements
+
+ +\bo mutt-users@cs.hmc.edu -- help, bug reports and feature requests
+
+ +\bo mutt-dev@cs.hmc.edu -- development mailing list
+
+ N\bNo\bot\bte\be:\b: all messages posted to _\bm_\bu_\bt_\bt_\b-_\ba_\bn_\bn_\bo_\bu_\bn_\bc_\be are automatically forwarded
+ to _\bm_\bu_\bt_\bt_\b-_\bu_\bs_\be_\br_\bs, so you do not need to be subscribed to both lists.
+
+ 1\b1.\b.3\b3.\b. S\bSo\bof\bft\btw\bwa\bar\bre\be D\bDi\bis\bst\btr\bri\bib\bbu\but\bti\bio\bon\bn S\bSi\bit\bte\bes\bs
+
+ +\bo ftp://ftp.cs.hmc.edu/pub/me/mutt/
+
+ 1\b1.\b.4\b4.\b. I\bIR\bRC\bC
+
+ Visit channel _\b#_\bm_\bu_\bt_\bt on DALnet (www.dal.net) to chat with other people
+ interested in Mutt.
+
+ 1\b1.\b.5\b5.\b. U\bUS\bSE\bEN\bNE\bET\bT
+
+ See the newsgroup comp.mail.mutt.
+
+ 1\b1.\b.6\b6.\b. C\bCo\bop\bpy\byr\bri\big\bgh\bht\bt
+
+ Mutt is Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ 2\b2.\b. G\bGe\bet\btt\bti\bin\bng\bg S\bSt\bta\bar\brt\bte\bed\bd
+
+ This section is intended as a brief overview of how to use Mutt.
+ There are many other features which are described elsewhere in the
+ manual. There is even more information available in the Mutt FAQ and
+ various web pages. See the Mutt Page for more details.
+
+ The keybindings described in this section are the defaults as
+ distributed. Your local system administrator may have altered the
+ defaults for your site. You can always type ``?'' in any menu to
+ display the current bindings.
+
+ The first thing you need to do is invoke mutt, simply by typing mutt
+ at the command line. There are various command-line options, see
+ either the mutt man page or the ``reference''.
+
+ 2\b2.\b.1\b1.\b. M\bMo\bov\bvi\bin\bng\bg A\bAr\bro\bou\bun\bnd\bd i\bin\bn M\bMe\ben\bnu\bus\bs
+
+ Information is presented in menus, very similar to ELM. Here is a
+ table showing the common keys used to navigate menus in Mutt.
+
+ j or Down next-entry move to the next entry
+ k or Up previous-entry move to the previous entry
+ z or PageDn page-down go to the next page
+ Z or PageUp page-up go to the previous page
+ = or Home first-entry jump to the first entry
+ * or End last-entry jump to the last entry
+ q quit exit the current menu
+ ? help list all keybindings for the current menu
+
+ 2\b2.\b.2\b2.\b. E\bEd\bdi\bit\bti\bin\bng\bg I\bIn\bnp\bpu\but\bt F\bFi\bie\bel\bld\bds\bs
+
+ Mutt has a builtin line editor which is used as the primary way to
+ input textual data such as email addresses or filenames. The keys
+ used to move around while editing are very similar to those of Emacs.
+
+ ^A or <Home> bol move to the start of the line
+ ^B or <Left> backward-char move back one char
+ ^D or <Delete> delete-char delete the char under the cursor
+ ^E or <End> eol move to the end of the line
+ ^F or <Right> forward-char move forward one char
+ ^K kill-eol delete to the end of the line
+ ^U kill-line delete entire line
+ ^W kill-word kill the word in front of the cursor
+ <Up> history-up recall previous string from history
+ <Down> history-down recall next string from history
+ <BackSpace> backspace kill the char in front of the cursor
+ ^G n/a abort
+ <Tab> n/a complete filename (only when prompting for a file)
+ <Return> n/a finish editing
+
+ You can remap the _\be_\bd_\bi_\bt_\bo_\br functions using the ``bind'' command. For
+ example, to make the _\bD_\be_\bl_\be_\bt_\be key delete the character in front of the
+ cursor rather than under, you could use
+
+ bind editor delete backspace
+
+ 2\b2.\b.3\b3.\b. R\bRe\bea\bad\bdi\bin\bng\bg M\bMa\bai\bil\bl -\b- T\bTh\bhe\be I\bIn\bnd\bde\bex\bx a\ban\bnd\bd P\bPa\bag\bge\ber\br
+
+ Similar to many other mail clients, there are two modes in which mail
+ is read in Mutt. The first is the index of messages in the mailbox,
+ which is called the ``index'' in Mutt. The second mode is the display
+ of the message contents. This is called the ``pager.''
+
+ The next few sections describe the functions provided in each of these
+ modes.
+
+ 2\b2.\b.3\b3.\b.1\b1.\b. T\bTh\bhe\be M\bMe\bes\bss\bsa\bag\bge\be I\bIn\bnd\bde\bex\bx
+
+ c change to a different mailbox
+ ESC c change to a folder in read-only mode
+ C copy the current message to another mailbox
+ ESC C decode a message and copy it to a folder
+ ESC s decode a message and save it to a folder
+ D delete messages matching a pattern
+ d delete the current message
+ F mark as important
+ l show messages matching a pattern
+ N mark message as new
+ o change the current sort method
+ O reverse sort the mailbox
+ q save changes and exit
+ s save-message
+ t toggle the tag on a message
+ ESC t toggle tag on entire message thread
+ u undelete-message
+ v view-attachments
+ x abort changes and exit
+ <Return> display-message
+ <Tab> jump to the next new message
+ @ show the author's full e-mail address
+ $ save changes to mailbox
+ / search
+ ESC / search-reverse
+ ^L clear and redraw the screen
+ ^T tag messages matching a pattern
+ ^U undelete messages matching a pattern
+
+ 2\b2.\b.3\b3.\b.1\b1.\b.1\b1.\b. S\bSt\bta\bat\btu\bus\bs F\bFl\bla\bag\bgs\bs
+
+ In addition to who sent the message and the subject, a short summary
+ of the disposition of each message is printed beside the message
+ number. Zero or more of the following ``flags'' may appear, which
+ mean:
+
+ D message is deleted
+ K contains a PGP public key
+ M requires mailcap to view
+ N message is new
+ O message is old
+ P message is PGP encrypted
+ r message has been replied to
+ S message is PGP signed
+ ! message is flagged
+ * message is tagged
+
+ Some of the status flags can be turned on or off using
+
+ +\bo s\bse\bet\bt-\b-f\bfl\bla\bag\bg (default: w)
+
+ +\bo c\bcl\ble\bea\bar\br-\b-f\bfl\bla\bag\bg (default: W)
+
+ Furthermore, the following flags reflect who the message is addressed
+ to. They can be customized with the ``$to_chars'' variable.
+
+ + message is to you and you only
+ T message is to you, but also to or cc'ed to others
+ C message is cc'ed to you
+ F message is from you
+
+ 2\b2.\b.3\b3.\b.2\b2.\b. T\bTh\bhe\be P\bPa\bag\bge\ber\br
+
+ By default, Mutt uses its builtin pager to display the body of
+ messages. The pager is very similar to the Unix program _\bl_\be_\bs_\bs though
+ not nearly as featureful.
+
+ <Return> go down one line
+ <Space> display the next page (or next message if at the end of a message)
+ - go back to the previous page
+ n display the next message
+ ? show keybindings
+ / search for a regular expression (pattern)
+ \ toggle search pattern coloring
+
+ In addition, many of the functions from the _\bi_\bn_\bd_\be_\bx are available in the
+ pager, such as _\bd_\be_\bl_\be_\bt_\be_\b-_\bm_\be_\bs_\bs_\ba_\bg_\be or _\bc_\bo_\bp_\by_\b-_\bm_\be_\bs_\bs_\ba_\bg_\be (this is one advantage
+ over using an external pager to view messages).
+
+ Also, the internal pager supports a couple other advanced features.
+ For one, it will accept and translate the ``standard'' nroff sequences
+ for bold and underline. These sequences are a series of either the
+ letter, backspace (^H), the letter again for bold or the letter,
+ backspace, ``_'' for denoting underline. Mutt will attempt to display
+ these in bold and underline respectively if your terminal supports
+ them. If not, you can use the bold and underline ``color'' objects to
+ specify a color or mono attribute for them.
+
+ Additionally, the internal pager supports the ANSI escape sequences
+ for character attributes. Mutt translates them into the correct color
+ and character settings. The sequences Mutt supports are:
+
+ ESC [ Ps;Ps;Ps;...;Ps m
+ where Ps =
+ 0 All Attributes Off
+ 1 Bold on
+ 4 Underline on
+ 5 Blink on
+ 7 Reverse video on
+ 3x Foreground color is x
+ 4x Background color is x
+
+ Colors are
+ 0 black
+ 1 red
+ 2 green
+ 3 yellow
+ 4 blue
+ 5 magenta
+ 6 cyan
+ 7 white
+
+ Mutt uses these attributes for handling text/enriched messages, and
+ they can also be used by an external ``autoview'' script for
+ highlighting purposes. N\bNo\bot\bte\be:\b: If you change the colors for your
+ display, for example by changing the color associated with color2 for
+ your xterm, then that color will be used instead of green.
+
+ 2\b2.\b.3\b3.\b.3\b3.\b. T\bTh\bhr\bre\bea\bad\bde\bed\bd M\bMo\bod\bde\be
+
+ When the mailbox is ``sorted'' by _\bt_\bh_\br_\be_\ba_\bd_\bs, there are a few additional
+ functions available in the _\bi_\bn_\bd_\be_\bx and _\bp_\ba_\bg_\be_\br modes.
+
+ ^D delete-thread delete all messages in the current thread
+ ^U undelete-thread undelete all messages in the current thread
+ ^N next-thread jump to the start of the next thread
+ ^P previous-thread jump to the start of the previous thread
+ ^R read-thread mark the current thread as read
+ ESC d delete-subthread delete all messages in the current subthread
+ ESC u undelete-subthread undelete all messages in the current subthread
+ ESC n next-subthread jump to the start of the next subthread
+ ESC p previous-subthread jump to the start of the previous subthread
+ ESC r read-subthread mark the current subthread as read
+ ESC t tag-thread toggle the tag on the current thread
+
+ See also: ``$strict_threads''.
+
+ 2\b2.\b.3\b3.\b.4\b4.\b. M\bMi\bis\bsc\bce\bel\bll\bla\ban\bne\beo\bou\bus\bs F\bFu\bun\bnc\bct\bti\bio\bon\bns\bs
+
+ c\bcr\bre\bea\bat\bte\be-\b-a\bal\bli\bia\bas\bs (default: a)
+
+ Creates a new alias based upon the current message (or prompts for a
+ new one). Once editing is complete, an ``alias'' command is added to
+ the file specified by the ``$alias_file'' variable for future use.
+ N\bNo\bot\bte\be:\b: Specifying an ``$alias_file'' does not add the aliases specified
+ there-in, you must also ``source'' the file.
+
+ d\bdi\bis\bsp\bpl\bla\bay\by-\b-h\bhe\bea\bad\bde\ber\brs\bs (default: h)
+
+ Toggles the weeding of message header fields specified by ``ignore''
+ commands.
+
+ e\ben\bnt\bte\ber\br-\b-c\bco\bom\bmm\bma\ban\bnd\bd (default: ``:'')
+
+ This command is used to execute any command you would normally put in
+ a configuration file. A common use is to check the settings of
+ variables, or in conjunction with ``macros'' to change settings on the
+ fly.
+
+ e\bex\bxt\btr\bra\bac\bct\bt-\b-k\bke\bey\bys\bs (default: ESC k)
+
+ This command extracts PGP public keys from the current or tagged
+ message(s) and adds them to your ``$pgp_v2_pubring'' or
+ ``$pgp_v5_pubring'' depending on ``$pgp_key_version''.
+
+ f\bfo\bor\brg\bge\bet\bt-\b-p\bpa\bas\bss\bsp\bph\bhr\bra\bas\bse\be (default: ^F)
+
+ This command wipes the PGP passphrase from memory. It is useful, if
+ you misspelled the passphrase.
+
+ l\bli\bis\bst\bt-\b-r\bre\bep\bpl\bly\by (default: L)
+
+ Reply to the current or tagged message(s) by extracting any addresses
+ which match the addresses given by the ``lists'' command. Using this
+ when replying to messages posted to mailing lists help avoid duplicate
+ copies being sent to the author of the message you are replying to.
+
+ p\bpi\bip\bpe\be-\b-m\bme\bes\bss\bsa\bag\bge\be (default: |)
+
+ Asks for an external Unix command and pipes the current or tagged
+ message(s) to it. The variables ``$pipe_decode'', ``$pipe_split'',
+ ``$pipe_sep'' and ``$wait_key'' control the exact behaviour of this
+ function.
+
+ s\bsh\bhe\bel\bll\bl-\b-e\bes\bsc\bca\bap\bpe\be (default: !)
+
+ Asks for an external Unix command and executes it. The ``$wait_key''
+ can be used to control whether Mutt will wait for a key to be pressed
+ when the command returns (presumably to let the user read the output
+ of the command), based on the return status of the named command.
+
+ t\bto\bog\bgg\bgl\ble\be-\b-q\bqu\buo\bot\bte\bed\bd (default: T)
+
+ The _\bp_\ba_\bg_\be_\br uses the ``$quote_regexp'' variable to detect quoted text
+ when displaying the body of the message. This function toggles the
+ display of the quoted material in the message. It is particularly
+ useful when are interested in just the response and there is a large
+ amount of quoted text in the way.
+
+ s\bsk\bki\bip\bp-\b-q\bqu\buo\bot\bte\bed\bd (default: S)
+
+ This function will go to the next line of non-quoted text which come
+ after a line of quoted text in the internal pager.
+
+ 2\b2.\b.4\b4.\b. S\bSe\ben\bnd\bdi\bin\bng\bg M\bMa\bai\bil\bl
+
+ The following bindings are available in the _\bi_\bn_\bd_\be_\bx for sending
+ messages.
+
+ m compose compose a new message
+ r reply reply to sender
+ g group-reply reply to all recipients
+ L list-reply reply to mailing list address
+ f forward forward message
+ b bounce bounce (remail) message
+ ESC k mail-key mail a PGP public key to someone
+
+ Bouncing a message sends the message as is to the recipient you
+ specify. Forwarding a message allows you to add comments or modify
+ the message you are forwarding. Bouncing a message uses the
+ ``sendmail'' command to send a copy of a message to recipients as if
+ they were original recipients of the message. See also
+ ``$mime_forward''.
+
+ Mutt will then enter the _\bc_\bo_\bm_\bp_\bo_\bs_\be menu and prompt you for the
+ recipients to place on the ``To:'' header field. Next, it will ask
+ you for the ``Subject:'' field for the message, providing a default if
+ you are replying to or forwarding a message. See also ``$askcc'',
+ ``$askbcc'', ``$autoedit'', and ``$fast_reply'' for changing how Mutt
+ asks these questions.
+
+ Mutt will then automatically start your ``$editor'' on the message
+ body. If the ``$edit_headers'' variable is set, the headers will be
+ at the top of the message in your editor. Any messages you are
+ replying to will be added in sort order to the message, with
+ appropriate ``$attribution'', ``$indent_string'' and
+ ``$post_indent_string''. When forwarding a message, if the
+ ``$mime_forward'' variable is unset, a copy of the forwarded message
+ will be included. If you have specified a ``$signature'', it will be
+ appended to the message.
+
+ Once you have finished editing the body of your mail message, you are
+ returned to the _\bc_\bo_\bm_\bp_\bo_\bs_\be menu. The following options are available:
+
+ a attach-file attach a file
+ ESC k attach-key attach a PGP public key
+ d edit-description edit description on attachment
+ D detach-file detach a file
+ T edit-to edit the To field
+ c edit-cc edit the Cc field
+ b edit-bcc edit the Bcc field
+ y send-message send the message
+ s edit-subject edit the Subject
+ f edit-fcc specify an ``Fcc'' mailbox
+ p pgp-menu select PGP options (``i'' version only)
+ P postpone-message postpone this message until later
+ q quit quit (abort) sending the message
+ i ispell check spelling (if available on your system)
+ ^F forget-passphrase whipe PGP passphrase from memory
+
+ 2\b2.\b.4\b4.\b.1\b1.\b. E\bEd\bdi\bit\bti\bin\bng\bg t\bth\bhe\be m\bme\bes\bss\bsa\bag\bge\be h\bhe\bea\bad\bde\ber\br
+
+ When editing the header of your outgoing message, there are a couple
+ of special features available.
+
+ If you specify
+ Fcc: _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be
+ Mutt will pick up _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be just as if you had used the _\be_\bd_\bi_\bt_\b-_\bf_\bc_\bc
+ function in the _\bc_\bo_\bm_\bp_\bo_\bs_\be menu.
+
+ You can also attach files to your message by specifying
+ Attach: _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be [ _\bd_\be_\bs_\bc_\br_\bi_\bp_\bt_\bi_\bo_\bn ]
+ where _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be is the file to attach and _\bd_\be_\bs_\bc_\br_\bi_\bp_\bt_\bi_\bo_\bn is an optional
+ string to use as the description of the attached file.
+
+ When replying to messages, if you remove the _\bI_\bn_\b-_\bR_\be_\bp_\bl_\by_\b-_\bT_\bo_\b: field from
+ the header field, Mutt will not generate a _\bR_\be_\bf_\be_\br_\be_\bn_\bc_\be_\bs_\b: field, which
+ allows you to create a new message thread.
+
+ If you want to use PGP, you can specify
+
+ Pgp: [ E | S | S<id> ]
+
+ ``E'' encrypts, ``S'' signs and ``S<id>'' signs with the given key,
+ setting ``$pgp_sign_as'' permanently.
+
+ Also see ``edit_headers''.
+
+ 2\b2.\b.5\b5.\b. P\bPo\bos\bst\btp\bpo\bon\bni\bin\bng\bg M\bMa\bai\bil\bl
+
+ At times it is desirable to delay sending a message that you have
+ already begun to compose. When the _\bp_\bo_\bs_\bt_\bp_\bo_\bn_\be_\b-_\bm_\be_\bs_\bs_\ba_\bg_\be function is used
+ in the _\bc_\bo_\bm_\bp_\bo_\bs_\be menu, the body of your message and attachments are
+ stored in the mailbox specified by the ``$postponed'' variable. This
+ means that you can recall the message even if you exit Mutt and then
+ restart it at a later time.
+
+ Once a message is postponed, there are several ways to resume it.
+ From the command line you can use the ``-p'' option, or if you _\bc_\bo_\bm_\bp_\bo_\bs_\be
+ a new message from the _\bi_\bn_\bd_\be_\bx or _\bp_\ba_\bg_\be_\br you will be prompted if
+ postponed messages exist. If multiple messages are currently
+ postponed, the _\bp_\bo_\bs_\bt_\bp_\bo_\bn_\be_\bd menu will pop up and you can select which
+ message you would like to resume.
+
+ N\bNo\bot\bte\be:\b: If you postpone a reply to a message, the reply setting of the
+ message is only updated when you actually finish the message and send
+ it. Also, you must be in the same folder with the message you replied
+ to for the status of the message to be updated.
+
+ See also the ``$postpone'' quad-option.
+
+ 3\b3.\b. C\bCo\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn
+
+ While the default configuration (or ``preferences'') make Mutt usable
+ right out of the box, it is often desirable to tailor Mutt to suit
+ your own tastes. When Mutt is first invoked, it will attempt to read
+ the ``system'' configuration file (defaults set by your local system
+ administrator), unless the ``-n'' ``command line'' option is
+ specified. This file is typically /usr/local/share/Muttrc or
+ /usr/local/lib/Muttrc. Next, it looks for a file in your home
+ directory named .muttrc. In this file is where you place ``commands''
+ to configure Mutt.
+ In addition, mutt supports version specific configuration files that
+ are parsed instead of the default files as explained above. For
+ instance, if your system has a Muttrc-0.88 file in the system
+ configuration directory, and you are running version 0.88 of mutt,
+ this file will be sourced instead of the Muttrc file. The same is
+ true of the user configuration file, if you have a file .muttrc-0.88.6
+ in your home directory, when you run mutt version 0.88.6, it will
+ source this file instead of the default .muttrc file. The version
+ number is the same which is visible using the ``-v'' ``command line''
+ switch or using the show-version key (default: V) from the index menu.
+
+ 3\b3.\b.1\b1.\b. S\bSy\byn\bnt\bta\bax\bx o\bof\bf I\bIn\bni\bit\bti\bia\bal\bli\biz\bza\bat\bti\bio\bon\bn F\bFi\bil\ble\bes\bs
+
+ An initialization file consists of a series of ``commands'', each on
+ its own line. The hash mark, or pound sign (``#''), is used as a
+ ``comment'' character. You can use it to annotate your initialization
+ file. All text after the comment character to the end of the line is
+ ignored. For example,
+
+ my_hdr X-Disclaimer: Why are you listening to me? # This is a comment
+
+ Single quotes (') and double quotes (") can be used to quote strings
+ which contain spaces or other special characters. The difference
+ between the two types of quotes is similar to that of many popular
+ shell programs, namely that a single quote is used to specify a
+ literal string (one that is not interpreted for shell variables or
+ quoting with a backslash [see next paragraph]), while double quotes
+ indicate a string for which should be evaluated. For example,
+ backtics are evaluated inside of double quotes, but n\bno\bot\bt for single
+ quotes.
+
+ \ quotes the next character, just as in shells such as bash and zsh.
+ For example, if want to put quotes ``"'' inside of a string, you can
+ use ``\'' to force the next character to be a literal instead of
+ interpreted character.
+
+ set realname="Michael \"MuttDude\" Elkins"
+
+ ``\\'' means to insert a literal ``\'' into the line. ``\n'' and
+ ``\r'' have their usual C meanings of linefeed and carriage-return,
+ respectively.
+
+ A \ at the end of a line can be used to split commands over multiple
+ lines, provided that the split points don't appear in the middle of
+ command names.
+
+ It is also possible to substitute the output of a Unix command in an
+ initialization file. This is accomplished by enclosing the command in
+ backquotes (``). For example,
+
+ my_hdr X-Operating-System: `uname -a`
+
+ The output of the Unix command ``uname -a'' will be substituted before
+ the line is parsed. Note that since initialization files are line
+ oriented, only the first line of output from the Unix command will be
+ substituted.
+
+ For a complete list of the commands understood by mutt, see the
+ ``command reference''.
+
+ 3\b3.\b.2\b2.\b. D\bDe\bef\bfi\bin\bni\bin\bng\bg/\b/U\bUs\bsi\bin\bng\bg a\bal\bli\bia\bas\bse\bes\bs
+
+ Usage: alias _\bk_\be_\by _\ba_\bd_\bd_\br_\be_\bs_\bs [ , _\ba_\bd_\bd_\br_\be_\bs_\bs, ... ]
+
+ It's usually very cumbersome to remember or type out the address of
+ someone you are communicating with. Mutt allows you to create
+ ``aliases'' which map a short string to a full address.
+
+ N\bNo\bot\bte\be:\b: if you want to create an alias for a group (by specifying more
+ than one address), you m\bmu\bus\bst\bt separate the addresses with a comma
+ (``,'').
+
+ To remove an alias or aliases:
+
+ unalias _\ba_\bd_\bd_\br [ _\ba_\bd_\bd_\br _\b._\b._\b. ]
+
+ alias muttdude me@cs.hmc.edu (Michael Elkins)
+ alias theguys manny, moe, jack
+
+ Unlike other mailers, Mutt doesn't require aliases to be defined in a
+ special file. The alias command can appear anywhere in a
+ configuration file, as long as this file is ``sourced''.
+ Consequently, you can have multiple alias files, or you can have all
+ aliases defined in your muttrc.
+
+ On the other hand, the ``create-alias'' function can use only one
+ file, the one pointed to by the ``$alias_file'' variable (which is
+ ~/.muttrc by default). This file is not special either, in the sense
+ that Mutt will happily append aliases to any file, but in order for
+ the new aliases to take effect you need to explicitly ``source'' this
+ file too.
+
+ For example:
+
+ source /usr/local/share/Mutt.aliases
+ source ~/.mail_aliases
+ set alias_file=~/.mail_aliases
+
+ To use aliases, you merely use the alias at any place in mutt where
+ mutt prompts for addresses, such as the _\bT_\bo_\b: or _\bC_\bc_\b: prompt. You can
+ also enter aliases in your editor at the appropriate headers if you
+ have the ``$edit_headers'' variable set.
+
+ In addition, at the various address prompts, you can use the tab
+ character to expand a partial alias to the full alias. If there are
+ multiple matches, mutt will bring up a menu with the matching aliases.
+ In order to be presented with the full list of aliases, you must hit
+ tab with out a partial alias, such as at the beginning of the prompt
+ or after a comma denoting multiple addresses.
+
+ In the alias menu, you can select as many aliases as you want with the
+ _\bs_\be_\bl_\be_\bc_\bt_\b-_\be_\bn_\bt_\br_\by key (default: RET), and use the _\be_\bx_\bi_\bt key (default: q) to
+ return to the address prompt.
+
+ 3\b3.\b.3\b3.\b. C\bCh\bha\ban\bng\bgi\bin\bng\bg t\bth\bhe\be d\bde\bef\bfa\bau\bul\blt\bt k\bke\bey\by b\bbi\bin\bnd\bdi\bin\bng\bgs\bs
+
+ Usage: bind _\bm_\ba_\bp _\bk_\be_\by _\bf_\bu_\bn_\bc_\bt_\bi_\bo_\bn
+
+ This command allows you to change the default key bindings (operation
+ invoked when pressing a key).
+
+ _\bm_\ba_\bp specifies in which menu the binding belongs. The currently
+ defined maps are:
+
+ +\bo generic
+
+ +\bo alias
+
+ +\bo attach
+
+ +\bo browser
+
+ +\bo editor
+
+ +\bo index
+
+ +\bo compose
+
+ +\bo pager
+
+ +\bo pgp
+
+ +\bo url
+
+ _\bk_\be_\by is the key (or key sequence) you wish to bind. To specify a
+ control character, use the sequence _\b\_\bC_\bx, where _\bx is the letter of the
+ control character (for example, to specify control-A use ``\Ca'').
+ Note that the case of _\bx as well as _\b\_\bC is ignored, so that _\b\_\bC_\bA_\b, _\b\_\bC_\ba_\b,
+ _\b\_\bc_\bA and _\b\_\bc_\ba are all equivalent. An alternative form is to specify the
+ key as a three digit octal number prefixed with a ``\'' (for example
+ _\b\_\b1_\b7_\b7 is equivalent to _\b\_\bc_\b?).
+
+ In addition, _\bk_\be_\by may consist of:
+
+ \t tab
+ \r carriage return
+ \n newline
+ \e escape
+ up up arrow
+ down down arrow
+ left left arrow
+ right right arrow
+ pageup Page Up
+ pagedown Page Down
+ backspace Backspace
+ delete Delete
+ insert Insert
+ enter Enter
+ home Home
+ end End
+ f1 function key 1
+ f10 function key 10
+
+ _\bk_\be_\by does not need to be enclosed in quotes unless it contains a space
+ (`` '').
+
+ _\bf_\bu_\bn_\bc_\bt_\bi_\bo_\bn specifies which action to take when _\bk_\be_\by is pressed. For a
+ complete list of functions, see the ``reference''. The special
+ function noop unbinds the specify key sequence.
+
+ 3\b3.\b.4\b4.\b. S\bSe\bet\btt\bti\bin\bng\bg v\bva\bar\bri\bia\bab\bbl\ble\bes\bs b\bba\bas\bse\bed\bd u\bup\bpo\bon\bn m\bma\bai\bil\blb\bbo\box\bx
+
+ Usage: folder-hook [!]_\bp_\ba_\bt_\bt_\be_\br_\bn _\bc_\bo_\bm_\bm_\ba_\bn_\bd
+
+ It is often desirable to change settings based on which mailbox you
+ are reading. The folder-hook command provides a method by which you
+ can execute any configuration command. _\bp_\ba_\bt_\bt_\be_\br_\bn is a regular
+ expression specifying in which mailboxes to execute _\bc_\bo_\bm_\bm_\ba_\bn_\bd before
+ loading. If a mailbox matches multiple folder-hook's, they are
+ executed in the order given in the muttrc.
+
+ N\bNo\bot\bte\be:\b: if you use the ``!'' shortcut for ``$spoolfile'' at the
+ beginning of the pattern, you must place it inside of double or single
+ quotes in order to distinguish it from the logical _\bn_\bo_\bt operator for
+ the expression.
+
+ Note that the settings are _\bn_\bo_\bt restored when you leave the mailbox.
+ For example, a command action to perform is to change the sorting
+ method based upon the mailbox being read:
+
+ folder-hook mutt set sort=threads
+
+ However, the sorting method is not restored to its previous value when
+ reading a different mailbox. To specify a _\bd_\be_\bf_\ba_\bu_\bl_\bt command, use the
+ pattern ``.'':
+
+ folder-hook . set sort=date-sent
+
+ 3\b3.\b.5\b5.\b. K\bKe\bey\byb\bbo\boa\bar\brd\bd m\bma\bac\bcr\bro\bos\bs
+
+ Usage: macro _\bm_\be_\bn_\bu _\bk_\be_\by _\bs_\be_\bq_\bu_\be_\bn_\bc_\be
+
+ Macros are useful when you would like a single key to perform a series
+ of actions. When you press _\bk_\be_\by in menu _\bm_\be_\bn_\bu, Mutt will behave as if
+ you had typed _\bs_\be_\bq_\bu_\be_\bn_\bc_\be. So if you have a common sequence of commands
+ you type, you can create a macro to execute those commands with a
+ single key.
+
+ _\bk_\be_\by and _\bs_\be_\bq_\bu_\be_\bn_\bc_\be are expanded by the same rules as the ``key
+ bindings'', with the addition that control characters in _\bs_\be_\bq_\bu_\be_\bn_\bc_\be can
+ also be specified as _\b^_\bx. In order to get a caret (``^'') you need to
+ use _\b^_\b^.
+
+ N\bNo\bot\bte\be:\b: Macro definitions (if any) listed in the help screen(s), are
+ silently truncated at the screen width, and are not wrapped.
+
+ 3\b3.\b.6\b6.\b. U\bUs\bsi\bin\bng\bg c\bco\bol\blo\bor\br a\ban\bnd\bd m\bmo\bon\bno\bo v\bvi\bid\bde\beo\bo a\bat\btt\btr\bri\bib\bbu\but\bte\bes\bs
+
+ Usage: color _\bo_\bb_\bj_\be_\bc_\bt _\bf_\bo_\br_\be_\bg_\br_\bo_\bu_\bn_\bd _\bb_\ba_\bc_\bk_\bg_\br_\bo_\bu_\bn_\bd [ _\br_\be_\bg_\be_\bx_\bp ]
+
+ If your terminal supports color, you can spice up Mutt by creating
+ your own color scheme. To define the color of an object (type of
+ information), you must specify both a foreground color a\ban\bnd\bd a
+ background color (it is not possible to only specify one or the
+ other).
+
+ _\bo_\bb_\bj_\be_\bc_\bt can be one of:
+
+ +\bo attachment
+
+ +\bo body (match _\br_\be_\bg_\be_\bx_\bp in the body of messages)
+
+ +\bo bold (hiliting bold patterns in the body of messages)
+
+ +\bo error (error messages printed by Mutt)
+
+ +\bo header (match _\br_\be_\bg_\be_\bx_\bp in the message header)
+
+ +\bo hdrdefault (default color of the message header in the pager)
+
+ +\bo indicator (arrow or bar used to indicate the current item in a
+ menu)
+
+ +\bo markers (the ``+'' markers at the beginning of wrapped lines in the
+ pager)
+
+ +\bo message (informational messages)
+
+ +\bo normal
+
+ +\bo quoted (text matching ``$quote_regexp'' in the body of a message)
+
+ +\bo quoted1, quoted2, ..., quotedN\bN (higher levels of quoting)
+
+ +\bo search (hiliting of words in the pager)
+
+ +\bo signature
+
+ +\bo status (mode lines used to display info about the mailbox or
+ message)
+
+ +\bo tilde (the ``~'' used to pad blank lines in the pager)
+
+ +\bo tree (thread tree drawn in the message index and attachment menu)
+
+ +\bo underline (hiliting underlined patterns in the body of messages)
+
+ _\bf_\bo_\br_\be_\bg_\br_\bo_\bu_\bn_\bd and _\bb_\ba_\bc_\bk_\bg_\br_\bo_\bu_\bn_\bd can be one of the following:
+
+ +\bo white
+
+ +\bo black
+
+ +\bo green
+
+ +\bo magenta
+
+ +\bo blue
+
+ +\bo cyan
+
+ +\bo yellow
+
+ +\bo red
+
+ +\bo default
+
+ +\bo color_\bx
+
+ _\bf_\bo_\br_\be_\bg_\br_\bo_\bu_\bn_\bd can optionally be prefixed with the keyword bright to make
+ the foreground color boldfaced (e.g., brightred).
+
+ If your terminal supports it, the special keyword _\bd_\be_\bf_\ba_\bu_\bl_\bt can be used
+ as a transparent color. The value _\bb_\br_\bi_\bg_\bh_\bt_\bd_\be_\bf_\ba_\bu_\bl_\bt is also valid. If
+ Mutt is linked against the _\bS_\b-_\bL_\ba_\bn_\bg library, you also need to set the
+ _\bC_\bO_\bL_\bO_\bR_\bF_\bG_\bB_\bG environment variable to the default colors of your terminal
+ for this to work; for example (for Bourne-like shells):
+
+ set COLORFGBG="green;black"
+ export COLORFGBG
+
+ N\bNo\bot\bte\be:\b: The _\bS_\b-_\bL_\ba_\bn_\bg library requires you to use the _\bl_\bi_\bg_\bh_\bt_\bg_\br_\ba_\by and _\bb_\br_\bo_\bw_\bn
+ keywords instead of _\bw_\bh_\bi_\bt_\be and _\by_\be_\bl_\bl_\bo_\bw when setting this variable.
+
+ Mutt also recognizes the keywords _\bc_\bo_\bl_\bo_\br_\b0, _\bc_\bo_\bl_\bo_\br_\b1, ..., _\bc_\bo_\bl_\bo_\brN\bN-\b-1\b1 (N\bN
+ being the number of colors supported by your terminal). This is
+ useful when you remap the colors for your display (for example by
+ changing the color associated with _\bc_\bo_\bl_\bo_\br_\b2 for your xterm), since color
+ names may then lose their normal meaning.
+
+ If your terminal does not support color, it is still possible change
+ the video attributes through the use of the ``mono'' command:
+
+ Usage: mono _\b<_\bo_\bb_\bj_\be_\bc_\bt_\b> _\b<_\ba_\bt_\bt_\br_\bi_\bb_\bu_\bt_\be_\b> [ _\br_\be_\bg_\be_\bx_\bp ]
+
+ where _\ba_\bt_\bt_\br_\bi_\bb_\bu_\bt_\be is one of the following:
+
+ +\bo none
+
+ +\bo bold
+
+ +\bo underline
+
+ +\bo reverse
+
+ +\bo standout
+
+ 3\b3.\b.7\b7.\b. I\bIg\bgn\bno\bor\bri\bin\bng\bg (\b(w\bwe\bee\bed\bdi\bin\bng\bg)\b) u\bun\bnw\bwa\ban\bnt\bte\bed\bd m\bme\bes\bss\bsa\bag\bge\be h\bhe\bea\bad\bde\ber\brs\bs
+
+ Usage: [un]ignore _\bp_\ba_\bt_\bt_\be_\br_\bn [ _\bp_\ba_\bt_\bt_\be_\br_\bn ... ]
+
+ Messages often have many header fields added by automatic processing
+ systems, or which may not seem useful to display on the screen. This
+ command allows you to specify header fields which you don't normally
+ want to see.
+
+ You do not need to specify the full header field name. For example,
+ ``ignore content-'' will ignore all header fields that begin with the
+ pattern ``content-''.
+
+ To remove a previously added token from the list, use the ``unignore''
+ command. Note that if you do ``ignore x-'' it is not possible to
+ ``unignore x-mailer,'' for example. The ``unignore'' command does n\bno\bot\bt
+ make Mutt display headers with the given pattern.
+
+ ``unignore *'' will remove all tokens from the ignore list.
+
+ For example:
+
+ # Sven's draconian header weeding
+ ignore *
+ unignore from date subject to cc
+ unignore organization organisation x-mailer: x-newsreader: x-mailing-list:
+ unignore posted-to:
+
+ 3\b3.\b.8\b8.\b. M\bMa\bai\bil\bli\bin\bng\bg l\bli\bis\bst\bts\bs
+
+ Usage: [un]lists _\ba_\bd_\bd_\br_\be_\bs_\bs [ _\ba_\bd_\bd_\br_\be_\bs_\bs ... ]
+
+ Mutt has a few nice features for ``handling mailing lists''. In order
+ to take advantage of them, you must specify which addresses belong to
+ mailing lists.
+
+ It is important to note that you should n\bne\bev\bve\ber\br specify the domain name
+ ( the part after the ``@'') with the lists command. You should only
+ specify the ``mailbox'' portion of the address (the part before the
+ ``@''). For example, if you've subscribed to the Mutt mailing list,
+ you will receive mail addressed to _\bm_\bu_\bt_\bt_\b-_\bu_\bs_\be_\br_\bs_\b@_\bc_\bs_\b._\bh_\bm_\bc_\b._\be_\bd_\bu. So, to tell
+ Mutt that this is a mailing list, you would add ``lists mutt-users''
+ to your initialization file.
+
+ The ``unlists'' command is to remove a token from the list of mailing-
+ lists. Use ``unlists *'' to remove all tokens.
+
+ 3\b3.\b.9\b9.\b. U\bUs\bsi\bin\bng\bg M\bMu\bul\blt\bti\bip\bpl\ble\be s\bsp\bpo\boo\bol\bl m\bma\bai\bil\blb\bbo\box\bxe\bes\bs
+
+ Usage: mbox-hook [!]_\bp_\ba_\bt_\bt_\be_\br_\bn _\bm_\ba_\bi_\bl_\bb_\bo_\bx
+
+ This command is used to move read messages from a specified mailbox to
+ a different mailbox automatically when you quit or change folders.
+ _\bp_\ba_\bt_\bt_\be_\br_\bn is a regular expression specifying the mailbox to treat as a
+ ``spool'' mailbox and _\bm_\ba_\bi_\bl_\bb_\bo_\bx specifies where mail should be saved
+ when read.
+
+ Unlike some of the other _\bh_\bo_\bo_\bk commands, only the _\bf_\bi_\br_\bs_\bt matching
+ pattern is used (it is not possible to save read mail in more than a
+ single mailbox).
+
+ 3\b3.\b.1\b10\b0.\b. D\bDe\bef\bfi\bin\bni\bin\bng\bg m\bma\bai\bil\blb\bbo\box\bxe\bes\bs w\bwh\bhi\bic\bch\bh r\bre\bec\bce\bei\biv\bve\be m\bma\bai\bil\bl
+
+ Usage: mailboxes [!]_\bf_\bi_\bl_\be_\bn_\ba_\bm_\be [ _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be ... ]
+
+ This command specifies folders which can receive mail and which will
+ be checked for new messages. By default, the main menu status bar
+ displays how many of these folders have new messages.
+
+ When changing folders, pressing _\bs_\bp_\ba_\bc_\be will cycle through folders with
+ new mail.
+
+ Pressing TAB in the directory browser will bring up a menu showing the
+ files specified by the mailboxes command, and indicate which contain
+ new messages. Mutt will automatically enter this mode when invoked
+ from the command line with the -y option.
+
+ N\bNo\bot\bte\be:\b: new mail is detected by comparing the last modification time to
+ the last access time. Utilities like biff or frm or any other program
+ which accesses the mailbox might cause Mutt to never detect new mail
+ for that mailbox if they do not properly reset the access time.
+
+ N\bNo\bot\bte\be:\b: the filenames in the mailboxes command are resolved when the
+ command is executed, so if these names contain ``shortcut characters''
+ (such as ``='' and ``!''), any variable definition that affect these
+ characters (like ``$folder'' and ``$spool'') should be executed before
+ the mailboxes command.
+
+ 3\b3.\b.1\b11\b1.\b. U\bUs\bse\ber\br d\bde\bef\bfi\bin\bne\bed\bd h\bhe\bea\bad\bde\ber\brs\bs
+
+ Usage:
+ my_hdr _\bs_\bt_\br_\bi_\bn_\bg
+ unmy_hdr _\bf_\bi_\be_\bl_\bd [ _\bf_\bi_\be_\bl_\bd ... ]
+
+ The ``my_hdr'' command allows you to create your own header fields
+ which will be added to every message you send.
+
+ For example, if you would like to add an ``Organization:'' header
+ field to all of your outgoing messages, you can put the command
+
+ my_hdr Organization: A Really Big Company, Anytown, USA
+
+ in your .muttrc.
+
+ N\bNo\bot\bte\be:\b: space characters are _\bn_\bo_\bt allowed between the keyword and the
+ colon (``:''). The standard for electronic mail (RFC822) says that
+ space is illegal there, so Mutt enforces the rule.
+
+ If you would like to add a header field to a single message, you
+ should either set the ``edit_headers'' variable, or use the _\be_\bd_\bi_\bt_\b-
+ _\bh_\be_\ba_\bd_\be_\br_\bs function (default: ``E'') in the send-menu so that you can
+ edit the header of your message along with the body.
+ To remove user defined header fields, use the ``unmy_hdr'' command.
+ You may specify an asterisk (``*'') to remove all header fields, or
+ the fields to remove. For example, to remove all ``To'' and ``Cc''
+ header fields, you could use:
+
+ unmy_hdr to cc
+
+ 3\b3.\b.1\b12\b2.\b. D\bDe\bef\bfi\bin\bni\bin\bng\bg t\bth\bhe\be o\bor\brd\bde\ber\br o\bof\bf h\bhe\bea\bad\bde\ber\brs\bs w\bwh\bhe\ben\bn v\bvi\bie\bew\bwi\bin\bng\bg m\bme\bes\bss\bsa\bag\bge\bes\bs
+
+ Usage: hdr_order _\bh_\be_\ba_\bd_\be_\br_\b1 _\bh_\be_\ba_\bd_\be_\br_\b2 _\bh_\be_\ba_\bd_\be_\br_\b3
+
+ With this command, you can specify an order in which mutt will attempt
+ to present headers to you when viewing messages.
+
+ hdr_order From Date: From: To: Cc: Subject:
+
+ 3\b3.\b.1\b13\b3.\b. S\bSp\bpe\bec\bci\bif\bfy\by d\bde\bef\bfa\bau\bul\blt\bt s\bsa\bav\bve\be f\bfi\bil\ble\ben\bna\bam\bme\be
+
+ Usage: save-hook [!]_\br_\be_\bg_\be_\bx_\bp _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be
+
+ This command is used to override the default filename used when saving
+ messages. _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be will be used as the default filename if the
+ message is _\bF_\br_\bo_\bm_\b: an address matching _\br_\be_\bg_\be_\bx_\bp or if you are the author
+ and the message is addressed _\bt_\bo_\b: something matching _\br_\be_\bg_\be_\bx_\bp.
+
+ See ``matching messages'' for information on the exact format of
+ _\br_\be_\bg_\be_\bx_\bp.
+
+ Examples:
+
+ save-hook me@(turing\\.)?cs\\.hmc\\.edu$ +elkins
+ save-hook aol\\.com$ +spam
+
+ Also see the ``fcc-save-hook'' command.
+
+ 3\b3.\b.1\b14\b4.\b. S\bSp\bpe\bec\bci\bif\bfy\by d\bde\bef\bfa\bau\bul\blt\bt F\bFc\bcc\bc:\b: m\bma\bai\bil\blb\bbo\box\bx w\bwh\bhe\ben\bn c\bco\bom\bmp\bpo\bos\bsi\bin\bng\bg
+
+ Usage: fcc-hook [!]_\br_\be_\bg_\be_\bx_\bp _\bm_\ba_\bi_\bl_\bb_\bo_\bx
+
+ This command is used to save outgoing mail in a mailbox other than
+ ``$record''. Mutt searches the initial list of message recipients for
+ the first matching _\br_\be_\bg_\be_\bx_\bp and uses _\bm_\ba_\bi_\bl_\bb_\bo_\bx as the default Fcc:
+ mailbox. If no match is found the message will be saved to
+ ``$record'' mailbox.
+
+ See ``matching messages'' for information on the exact format of
+ _\br_\be_\bg_\be_\bx_\bp.
+
+ Example: fcc-hook aol.com$ +spammers
+
+ The above will save a copy of all messages going to the aol.com domain
+ to the `+spammers' mailbox by default. Also see the ``fcc-save-hook''
+ command.
+
+ 3\b3.\b.1\b15\b5.\b. S\bSp\bpe\bec\bci\bif\bfy\by d\bde\bef\bfa\bau\bul\blt\bt s\bsa\bav\bve\be f\bfi\bil\ble\ben\bna\bam\bme\be a\ban\bnd\bd d\bde\bef\bfa\bau\bul\blt\bt F\bFc\bcc\bc:\b: m\bma\bai\bil\blb\bbo\box\bx a\bat\bt o\bon\bnc\bce\be
+
+ Usage: fcc-save-hook [!]_\br_\be_\bg_\be_\bx_\bp _\bm_\ba_\bi_\bl_\bb_\bo_\bx
+
+ This command is a shortcut, equivalent to doing both a ``fcc-hook''
+ and a ``save-hook'' with its arguments.
+
+ 3\b3.\b.1\b16\b6.\b. C\bCh\bha\ban\bng\bge\be s\bse\bet\btt\bti\bin\bng\bgs\bs b\bba\bas\bse\bed\bd u\bup\bpo\bon\bn m\bme\bes\bss\bsa\bag\bge\be r\bre\bec\bci\bip\bpi\bie\ben\bnt\bts\bs
+
+ Usage: send-hook [!]_\br_\be_\bg_\be_\bx_\bp _\bc_\bo_\bm_\bm_\ba_\bn_\bd
+
+ This command can be used to execute arbitrary configuration commands
+ based upon recipients of the message. _\br_\be_\bg_\be_\bx_\bp is a regular expression
+ matching the desired address. _\bc_\bo_\bm_\bm_\ba_\bn_\bd is executed when _\br_\be_\bg_\be_\bx_\bp matches
+ recipients of the message. When multiple matches occur, commands are
+ executed in the order they are specified in the muttrc.
+
+ See ``matching messages'' for information on the exact format of
+ _\br_\be_\bg_\be_\bx_\bp.
+
+ Example: send-hook mutt "set mime_fwd signature=''"
+
+ Another typical use for this command is to change the values of the
+ ``$attribution'', ``$signature'' and ``$locale'' variables in order to
+ change the language of the attributions and signatures based upon the
+ recipients.
+
+ N\bNo\bot\bte\be:\b: the send-hook's are only executed ONCE after getting the initial
+ list of recipients. Adding a recipient after replying or editing the
+ message will NOT cause any send-hook to be executed.
+
+ 3\b3.\b.1\b17\b7.\b. A\bAd\bdd\bdi\bin\bng\bg k\bke\bey\by s\bse\beq\bqu\bue\ben\bnc\bce\bes\bs t\bto\bo t\bth\bhe\be k\bke\bey\byb\bbo\boa\bar\brd\bd b\bbu\buf\bff\bfe\ber\br
+
+ Usage: push _\bs_\bt_\br_\bi_\bn_\bg
+
+ This command adds the named string to the keyboard buffer. You may
+ use it to automatically run a sequence of commands at startup, or when
+ entering certain folders.
+
+ 3\b3.\b.1\b18\b8.\b. M\bMe\bes\bss\bsa\bag\bge\be S\bSc\bco\bor\bri\bin\bng\bg
+
+ Usage: score _\bp_\ba_\bt_\bt_\be_\br_\bn _\bv_\ba_\bl_\bu_\be
+ Usage: unscore _\bp_\ba_\bt_\bt_\be_\br_\bn [ _\bp_\ba_\bt_\bt_\be_\br_\bn ... ]
+
+ The score commands adds _\bv_\ba_\bl_\bu_\be to a message's score if _\bp_\ba_\bt_\bt_\be_\br_\bn matches
+ it. _\bp_\ba_\bt_\bt_\be_\br_\bn is a string in the format described in the ``searching''
+ section. _\bv_\ba_\bl_\bu_\be is a positive or negative integer. A message's final
+ score is the sum total of all matching score entries. However, you
+ may optionally prefix _\bv_\ba_\bl_\bu_\be with an equal sign (=) to cause evaluation
+ to stop at a particular entry if there is a match. Negative final
+ scores are rounded up to 0.
+
+ The unscore command removes score entries from the list. You m\bmu\bus\bst\bt
+ specify the same pattern specified in the score command for it to be
+ removed. The pattern ``*'' is a special token which means to clear
+ the list of all score entries.
+
+ 3\b3.\b.1\b19\b9.\b. S\bSe\bet\btt\bti\bin\bng\bg v\bva\bar\bri\bia\bab\bbl\ble\bes\bs
+
+ Usage: set [no|inv]_\bv_\ba_\br_\bi_\ba_\bb_\bl_\be[=_\bv_\ba_\bl_\bu_\be] [ _\bv_\ba_\br_\bi_\ba_\bb_\bl_\be ... ]
+ Usage: toggle _\bv_\ba_\br_\bi_\ba_\bb_\bl_\be [_\bv_\ba_\br_\bi_\ba_\bb_\bl_\be ... ]
+ Usage: unset _\bv_\ba_\br_\bi_\ba_\bb_\bl_\be [_\bv_\ba_\br_\bi_\ba_\bb_\bl_\be ... ]
+ Usage: reset _\bv_\ba_\br_\bi_\ba_\bb_\bl_\be [_\bv_\ba_\br_\bi_\ba_\bb_\bl_\be ... ]
+
+ This command is used to set (and unset) ``configuration variables''.
+ There are four basic types of variables: boolean, number, string and
+ quadoption. _\bb_\bo_\bo_\bl_\be_\ba_\bn variables can be _\bs_\be_\bt (true) or _\bu_\bn_\bs_\be_\bt (false).
+ _\bn_\bu_\bm_\bb_\be_\br variables can be assigned a positive integer value.
+
+ _\bs_\bt_\br_\bi_\bn_\bg variables consist of any number of printable characters.
+ _\bs_\bt_\br_\bi_\bn_\bg_\bs must be enclosed in quotes if they contain spaces or tabs.
+ You may also use the ``C'' escape sequences \\b\n\bn and \\b\t\bt for newline and
+ tab, respectively.
+
+ _\bq_\bu_\ba_\bd_\bo_\bp_\bt_\bi_\bo_\bn variables are used to control whether or not to be prompted
+ for certain actions, or to specify a default action. A value of _\by_\be_\bs
+ will cause the action to be carried out automatically as if you had
+ answered yes to the question. Similarly, a value of _\bn_\bo will cause the
+ the action to be carried out as if you had answered ``no.'' A value
+ of _\ba_\bs_\bk_\b-_\by_\be_\bs will cause a prompt with a default answer of ``yes'' and
+ _\ba_\bs_\bk_\b-_\bn_\bo will provide a default answer of ``no.''
+
+ Prefixing a variable with ``no'' will unset it. Example: set
+ noaskbcc.
+
+ For _\bb_\bo_\bo_\bl_\be_\ba_\bn variables, you may optionally prefix the variable name
+ with inv to toggle the value (on or off). This is useful when writing
+ macros. Example: set invsmart_wrap.
+
+ The toggle command automatically prepends the inv prefix to all
+ specified variables.
+
+ The unset command automatically prepends the no prefix to all
+ specified variables.
+
+ Using the enter-command function in the _\bi_\bn_\bd_\be_\bx menu, you can query the
+ value of a variable by prefixing the name of the variable with a
+ question mark:
+
+ set ?allow_8bit
+
+ The question mark is actually only required for boolean variables.
+
+ The reset command resets all given variables to the compile time
+ defaults (hopefully mentioned in this manual). If you use the command
+ set and prefix the variable with ``&'' this has the same behavior as
+ the reset command.
+
+ With the reset command there exists the special variable ``all'',
+ which allows you to reset all variables to their system defaults.
+
+ 3\b3.\b.2\b20\b0.\b. R\bRe\bea\bad\bdi\bin\bng\bg i\bin\bni\bit\bti\bia\bal\bli\biz\bza\bat\bti\bio\bon\bn c\bco\bom\bmm\bma\ban\bnd\bds\bs f\bfr\bro\bom\bm a\ban\bno\bot\bth\bhe\ber\br f\bfi\bil\ble\be
+
+ Usage: source _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be
+
+ This command allows the inclusion of initialization commands from
+ other files. For example, I place all of my aliases in
+ ~/.mail_aliases so that I can make my ~/.muttrc readable and keep my
+ aliases private.
+
+ If the filename begins with a tilde (``~''), it will be expanded to
+ the path of your home directory.
+
+ 4\b4.\b. A\bAd\bdv\bva\ban\bnc\bce\bed\bd U\bUs\bsa\bag\bge\be
+
+ 4\b4.\b.1\b1.\b. S\bSe\bea\bar\brc\bch\bhi\bin\bng\bg a\ban\bnd\bd R\bRe\beg\bgu\bul\bla\bar\br E\bEx\bxp\bpr\bre\bes\bss\bsi\bio\bon\bns\bs
+
+ All text patterns for searching and matching in Mutt must be specified
+ as regular expressions (regexp) in the ``POSIX extended'' syntax
+ (which is more or less the syntax used by egrep and GNU awk). For
+ your convenience, we have included below a brief description of this
+ syntax.
+
+ The search is case sensitive if the pattern contains at least one
+ upper case letter, and case insensitive otherwise. Note that ``\''
+ must be quoted if used for a regular expression in an initialization
+ command: ``\\''. For more information, see the section on
+ ``searching'' below.
+
+ 4\b4.\b.1\b1.\b.1\b1.\b. R\bRe\beg\bgu\bul\bla\bar\br E\bEx\bxp\bpr\bre\bes\bss\bsi\bio\bon\bns\bs
+
+ A regular expression is a pattern that describes a set of strings.
+ Regular expressions are constructed analogously to arithmetic
+ expressions, by using various operators to combine smaller
+ expressions.
+
+ The fundamental building blocks are the regular expressions that match
+ a single character. Most characters, including all letters and
+ digits, are regular expressions that match themselves. Any
+ metacharacter with special meaning may be quoted by preceding it with
+ a backslash.
+
+ The period ``.'' matches any single character. The caret ``^'' and
+ the dollar sign ``$'' are metacharacters that respectively match the
+ empty string at the beginning and end of a line.
+
+ A list of characters enclosed by ``['' and ``]'' matches any single
+ character in that list; if the first character of the list is a caret
+ ``^'' then it matches any character n\bno\bot\bt in the list. For example, the
+ regular expression [\b[0\b01\b12\b23\b34\b45\b56\b67\b78\b89\b9]\b] matches any single digit. A range of
+ ASCII characters may be specified by giving the first and last
+ characters, separated by a hyphen ``-''. Most metacharacters lose
+ their special meaning inside lists. To include a literal ``]'' place
+ it first in the list. Similarly, to include a literal ``^'' place it
+ anywhere but first. Finally, to include a literal hyphen ``-'' place
+ it last.
+
+ Certain named classes of characters are predefined. Character classes
+ consist of ``[:'', a keyword denoting the class, and ``:]''. The
+ following classes are defined by the POSIX standard:
+
+ [\b[:\b:a\bal\bln\bnu\bum\bm:\b:]\b]
+ Alphanumeric characters.
+
+ [\b[:\b:a\bal\blp\bph\bha\ba:\b:]\b]
+ Alphabetic characters.
+
+ [\b[:\b:b\bbl\bla\ban\bnk\bk:\b:]\b]
+ Space or tab characters.
+
+ [\b[:\b:c\bcn\bnt\btr\brl\bl:\b:]\b]
+ Control characters.
+
+ [\b[:\b:d\bdi\big\bgi\bit\bt:\b:]\b]
+ Numeric characters.
+
+ [\b[:\b:g\bgr\bra\bap\bph\bh:\b:]\b]
+ Characters that are both printable and visible. (A space is
+ printable, but not visible, while an ``a'' is both.)
+
+ [\b[:\b:l\blo\bow\bwe\ber\br:\b:]\b]
+ Lower-case alphabetic characters.
+
+ [\b[:\b:p\bpr\bri\bin\bnt\bt:\b:]\b]
+ Printable characters (characters that are not control
+ characters.)
+
+ [\b[:\b:p\bpu\bun\bnc\bct\bt:\b:]\b]
+ Punctuation characters (characters that are not letter, digits,
+ control characters, or space characters).
+
+ [\b[:\b:s\bsp\bpa\bac\bce\be:\b:]\b]
+ Space characters (such as space, tab and formfeed, to name a
+ few).
+
+ [\b[:\b:u\bup\bpp\bpe\ber\br:\b:]\b]
+ Upper-case alphabetic characters.
+
+ [\b[:\b:x\bxd\bdi\big\bgi\bit\bt:\b:]\b]
+ Characters that are hexadecimal digits.
+
+ A character class is only valid in a regular expression inside the
+ brackets of a character list. Note that the brackets in these class
+ names are part of the symbolic names, and must be included in addition
+ to the brackets delimiting the bracket list. For example, [\b[[\b[:\b:d\bdi\big\bgi\bit\bt:\b:]\b]]\b]
+ is equivalent to [\b[0\b0-\b-9\b9]\b].
+
+ Two additional special sequences can appear in character lists. These
+ apply to non-ASCII character sets, which can have single symbols
+ (called collating elements) that are represented with more than one
+ character, as well as several characters that are equivalent for
+ collating or sorting purposes:
+
+ C\bCo\bol\bll\bla\bat\bti\bin\bng\bg S\bSy\bym\bmb\bbo\bol\bls\bs
+ A collating symbols is a multi-character collating element
+ enclosed in ``[.'' and ``.]''. For example, if ``ch'' is a
+ collating element, then [\b[[\b[.\b.c\bch\bh.\b.]\b]]\b] is a regexp that matches this
+ collating element, while [\b[c\bch\bh]\b] is a regexp that matches either
+ ``c'' or ``h''.
+
+ E\bEq\bqu\bui\biv\bva\bal\ble\ben\bnc\bce\be C\bCl\bla\bas\bss\bse\bes\bs
+ An equivalence class is a locale-specific name for a list of
+ characters that are equivalent. The name is enclosed in ``[=''
+ and ``=]''. For example, the name ``e'' might be used to
+ represent all of ``e'' ``e'' and ``e''. In this case, [\b[[\b[=\b=e\be=\b=]\b]]\b]
+ is a regexp that matches any of ``e'', ``e'' and ``e''.
+
+ A regular expression matching a single character may be followed by
+ one of several repetition operators:
+
+ ?\b? The preceding item is optional and matched at most once.
+
+ *\b* The preceding item will be matched zero or more times.
+
+ +\b+ The preceding item will be matched one or more times.
+
+ {\b{n\bn}\b}
+ The preceding item is matched exactly _\bn times.
+
+ {\b{n\bn,\b,}\b}
+ The preceding item is matched _\bn or more times.
+
+ {\b{,\b,m\bm}\b}
+ The preceding item is matched at most _\bm times.
+
+ {\b{n\bn,\b,m\bm}\b}
+ The preceding item is matched at least _\bn times, but no more than
+ _\bm times.
+
+ Two regular expressions may be concatenated; the resulting regular
+ expression matches any string formed by concatenating two substrings
+ that respectively match the concatenated subexpressions.
+
+ Two regular expressions may be joined by the infix operator ``|''; the
+ resulting regular expression matches any string matching either
+ subexpression.
+
+ Repetition takes precedence over concatenation, which in turn takes
+ precedence over alternation. A whole subexpression may be enclosed in
+ parentheses to override these precedence rules.
+
+ N\bNo\bot\bte\be:\b: If you compile Mutt with the GNU _\br_\bx package, the following
+ operators may also be used in regular expressions:
+
+ \\b\y\by Matches the empty string at either the beginning or the end of a
+ word.
+
+ \\b\B\bB Matches the empty string within a word.
+
+ \\b\<\b< Matches the empty string at the beginning of a word.
+
+ \\b\>\b> Matches the empty string at the end of a word.
+
+ \\b\w\bw Matches any word-constituent character (letter, digit, or
+ underscore).
+
+ \\b\W\bW Matches any character that is not word-constituent.
+
+ \\b\`\b` Matches the empty string at the beginning of a buffer (string).
+
+ \\b\'\b' Matches the empty string at the end of a buffer.
+
+ Please note however that these operators are not defined by POSIX, so
+ they may or may not be available in stock libraries on various
+ systems.
+
+ 4\b4.\b.1\b1.\b.2\b2.\b. S\bSe\bea\bar\brc\bch\bhi\bin\bng\bg
+
+ Many of Mutt's commands allow you to specify a pattern to match
+ (limit, tag-pattern, delete-pattern, etc.). There are several ways to
+ select messages:
+
+ ~A all messages
+ ~b PATTERN messages which contain PATTERN in the message body
+ ~c USER messages carbon-copied to USER
+ ~C PATTERN message is either to: or cc: PATTERN
+ ~D deleted messages
+ ~d [MIN]-[MAX] messages with ``date-sent'' in a Date range
+ ~E expired messages
+ ~e PATTERN message which contains PATTERN in the ``Sender'' field
+ ~F flagged messages
+ ~f USER messages originating from USER
+ ~h PATTERN messages which contain PATTERN in the message header
+ ~i ID message which match ID in the ``Message-ID'' field
+ ~L PATTERN message is either originated or recieved by PATTERN
+ ~l message is addressed to a known mailing list
+ ~m [MIN]-[MAX] message in the range MIN to MAX
+ ~n [MIN]-[MAX] messages with a score in the range MIN to MAX
+ ~N new messages
+ ~O old messages
+ ~p message is addressed to you (consults $alternates)
+ ~P message is from you (consults $alternates)
+ ~Q messages which have been replied to
+ ~R read messages
+ ~r [MIN]-[MAX] messages with ``date-received'' in a Date range
+ ~S superseded messages
+ ~s SUBJECT messages having SUBJECT in the ``Subject'' field.
+ ~T tagged messages
+ ~t USER messages addressed to USER
+ ~U unread messages
+ ~x PATTERN messages which contain PATTERN in the `References' field
+
+ Where PATTERN, USER, ID, and SUBJECT are ``regular expressions''.
+
+ 4\b4.\b.1\b1.\b.3\b3.\b. C\bCo\bom\bmp\bpl\ble\bex\bx S\bSe\bea\bar\brc\bch\bhe\bes\bs
+
+ Logical AND is performed by specifying more than one criterion. For
+ example:
+
+ ~t mutt ~f elkins
+
+ would select messages which contain the word ``mutt'' in the list of
+ recipients a\ban\bnd\bd that have the word ``elkins'' in the ``From'' header
+ field.
+
+ Mutt also recognizes the following operators to create more complex
+ search patterns:
+
+ +\bo ! -- logical NOT operator
+
+ +\bo | -- logical OR operator
+
+ +\bo () -- logical grouping operator
+
+ Here is an example illustrating a complex search pattern. This
+ pattern will select all messages which do not contain ``mutt'' in the
+ ``To'' or ``Cc'' field and which are from ``elkins''.
+ !(~t mutt|~c mutt) ~f elkins
+
+ 4\b4.\b.1\b1.\b.4\b4.\b. S\bSe\bea\bar\brc\bch\bhi\bin\bng\bg b\bby\by D\bDa\bat\bte\be
+
+ Mutt supports two types of dates, _\ba_\bb_\bs_\bo_\bl_\bu_\bt_\be and _\br_\be_\bl_\ba_\bt_\bi_\bv_\be.
+
+ A\bAb\bbs\bso\bol\blu\but\bte\be. Dates m\bmu\bus\bst\bt be in DD/MM/YY format (month and year are
+ optional, defaulting to the current month and year). An example of a
+ valid range of dates is:
+
+ Limit to messages matching: ~d 20/1/95-31/10
+
+ If you omit the minimum (first) date, and just specify ``-DD/MM/YY'',
+ all messages _\bb_\be_\bf_\bo_\br_\be the given date will be selected. If you omit the
+ maximum (second) date, and specify ``DD/MM/YY-'', all messages _\ba_\bf_\bt_\be_\br
+ the given date will be selected. If you specify a single date with no
+ dash (``-''), only messages sent on the given date will be selected.
+
+ R\bRe\bel\bla\bat\bti\biv\bve\be. This type of date is relative to the current date, and may
+ be specified as:
+
+ +\bo >_\bo_\bf_\bf_\bs_\be_\bt (messages older than _\bo_\bf_\bf_\bs_\be_\bt units)
+
+ +\bo <_\bo_\bf_\bf_\bs_\be_\bt (messages newer than _\bo_\bf_\bf_\bs_\be_\bt units)
+
+ +\bo =_\bo_\bf_\bf_\bs_\be_\bt (messages exactly _\bo_\bf_\bf_\bs_\be_\bt units old)
+
+ _\bo_\bf_\bf_\bs_\be_\bt is specified as a positive number with one of the following
+ units:
+
+ y years
+ m months
+ w weeks
+ d days
+
+ Example: to select messages less than 1 month old, you would use
+
+ Limit to messages matching: ~d <1m
+
+ N\bNo\bot\bte\be:\b: all dates used when searching are relative to the l\blo\boc\bca\bal\bl time
+ zone, so unless you change the setting of your ``$header_format'' to
+ include a %[...] format, these are n\bno\bot\bt the dates shown in the main
+ index.
+
+ 4\b4.\b.2\b2.\b. U\bUs\bsi\bin\bng\bg T\bTa\bag\bgs\bs
+
+ Sometimes it is desirable to perform an operation on a group of
+ messages all at once rather than one at a time. An example might be
+ to save messages to a mailing list to a separate folder, or to delete
+ all messages with a given subject. To tag all messages matching a
+ pattern, use the tag-pattern function, which is bound to ``control-T''
+ by default. Or you can select individual messages by hand using the
+ ``tag-message'' function, which is bound to ``t'' by default. See
+ ``searching'' for Mutt's searching syntax.
+
+ Once you have tagged the desired messages, you can use the ``tag-
+ prefix'' operator, which is the ``;'' (semicolon) key by default.
+ When the ``tag-prefix'' operator is used, the n\bne\bex\bxt\bt operation will be
+ applied to all tagged messages if that operation can be used in that
+ manner. If the ``$auto_tag'' variable is set, the next operation
+ applies to the tagged messages automatically, without requiring the
+ ``tag-prefix''.
+
+ 4\b4.\b.3\b3.\b. U\bUs\bsi\bin\bng\bg H\bHo\boo\bok\bks\bs
+
+ A _\bh_\bo_\bo_\bk is a concept borrowed from the EMACS editor which allows you to
+ execute arbitrary commands before performing some operation. For
+ example, you may wish to tailor your configuration based upon which
+ mailbox you are reading, or to whom you are sending mail. In the Mutt
+ world, a _\bh_\bo_\bo_\bk consists of a ``regular expression'' along with a
+ configuration option/command. See
+
+ +\bo ``folder-hook''
+
+ +\bo ``send-hook''
+
+ +\bo ``save-hook''
+
+ +\bo ``mbox-hook''
+
+ +\bo ``fcc-hook''
+
+ +\bo ``fcc-save-hook''
+
+ for specific details on each type of _\bh_\bo_\bo_\bk available.
+
+ 4\b4.\b.3\b3.\b.1\b1.\b. M\bMe\bes\bss\bsa\bag\bge\be M\bMa\bat\btc\bch\bhi\bin\bng\bg i\bin\bn H\bHo\boo\bok\bks\bs
+
+ Hooks that act upon messages (send-hook, save-hook, fcc-hook) are
+ evaluated in a slightly different manner. For the other types of
+ hooks, a ``regular expression''. But in dealing with messages a finer
+ grain of control is needed for matching since for different purposes
+ you want to match different criteria.
+
+ Mutt allows the use of the ``search pattern'' language for matching
+ messages in hook commands. This works in exactly the same way as it
+ would when _\bl_\bi_\bm_\bi_\bt_\bi_\bn_\bg or _\bs_\be_\ba_\br_\bc_\bh_\bi_\bn_\bg the mailbox, except that you are
+ restricted to those operators which match information from the
+ envelope of the message (i.e. from, to, cc, date, subject, etc.).
+
+ For example, if you wanted to set your return address based upon
+ sending mail to a specific address, you could do something like:
+
+ send-hook '~t ^me@cs\.hmc\.edu$' 'my_hdr From: Mutt User <user@host>'
+
+ which would execute the given command when sending mail to
+ _\bm_\be_\b@_\bc_\bs_\b._\bh_\bm_\bc_\b._\be_\bd_\bu.
+
+ However, it is not required that you write the pattern to match using
+ the full searching language. You can still specify a simple _\br_\be_\bg_\bu_\bl_\ba_\br
+ _\be_\bx_\bp_\br_\be_\bs_\bs_\bi_\bo_\bn like the other hooks, in which case Mutt will translate
+ your pattern into the full language, using the translation specified
+ by the ``$dfault_hook'' variable. The pattern is translated at the
+ time the hook is declared, so the value of ``$dfault_hook'' that is in
+ effect at that time will be used.
+
+ 4\b4.\b.4\b4.\b. E\bEx\bxt\bte\ber\brn\bna\bal\bl A\bAd\bdd\bdr\bre\bes\bss\bs Q\bQu\bue\ber\bri\bie\bes\bs
+
+ Mutt supports connecting to external directory databases such as LDAP,
+ ph/qi, bbdb, or NIS through a wrapper script which connects to mutt
+ using a simple interface. Using the ``$query_command'' variable, you
+ specify the wrapper command to use. For example:
+
+ set query_command = "mutt_ldap_query.pl '%s'"
+
+ The wrapper script should accept the query on the command-line. It
+ should return a one line message, than each matching response on a
+ single line, each line containing a tab separated address then name
+ then some other optional information. On error, or if there are no
+ matching addresses, return a non-zero exit code and a one line error
+ message.
+
+ An example multiple response output:
+
+ Searching database ... 20 entries ... 3 matching:
+ me@cs.hmc.edu Michael Elkins mutt dude
+ blong@fiction.net Brandon Long mutt and more
+ roessler@guug.de Thomas Roessler mutt pgp
+
+ There are two mechanisms for accessing the query function of mutt.
+ One is to do a query from the index menu using the query function
+ (default: Q). This will prompt for a query, then bring up the query
+ menu which will list the matching responses. From the query menu, you
+ can select addresses to create aliases, or to mail. You can tag
+ multiple messages to mail, start a new query, or have a new query
+ appended to the current responses.
+
+ The other mechanism for accessing the query function is for address
+ completion, similar to the alias completion. In any prompt for
+ address entry, you can use the complete-query function (default: ^T)
+ to run a query based on the current address you have typed. Like
+ aliases, mutt will look for what you have typed back to the last space
+ or comma. If there is a single response for that query, mutt will
+ expand the address in place. If there are multiple responses, mutt
+ will activate the query menu. At the query menu, you can select one
+ or more addresses to be added to the prompt.
+
+ 4\b4.\b.5\b5.\b. M\bMa\bai\bil\blb\bbo\box\bx F\bFo\bor\brm\bma\bat\bts\bs
+
+ Mutt supports reading and writing of four different mailbox formats:
+ mbox, MMDF, MH and Maildir. The mailbox type is autodetected, so
+ there is no need to use a flag for different mailbox types. When
+ creating new mailboxes, Mutt uses the default specified with the
+ ``$mbox_type'' variable.
+
+ m\bmb\bbo\box\bx. This is the most widely used mailbox format for UNIX. All
+ messages are stored in a single file. Each message has a line of the
+ form:
+
+ From me@cs.hmc.edu Fri, 11 Apr 1997 11:44:56 PST
+
+ to denote the start of a new message (this is often referred to as the
+ ``From_'' line).
+
+ M\bMM\bMD\bDF\bF. This is a variant of the _\bm_\bb_\bo_\bx format. Each message is
+ surrounded by lines containing ``^A^A^A^A'' (four control-A's).
+
+ M\bMH\bH. A radical departure from _\bm_\bb_\bo_\bx and _\bM_\bM_\bD_\bF, a mailbox consists of a
+ directory and each message is stored in a separate file. The filename
+ indicates the message number (however, this is may not correspond to
+ the message number Mutt displays). Deleted messages are renamed with a
+ comma (,) prepended to the filename. N\bNo\bot\bte\be:\b: Mutt detects this type of
+ mailbox by looking for either .mh_sequences or .xmhcache (needed to
+ distinguish normal directories from MH mailboxes). Mutt does not
+ update these files, yet.
+
+ M\bMa\bai\bil\bld\bdi\bir\br. The newest of the mailbox formats, used by the Qmail MTA (a
+ replacement for sendmail). Similar to _\bM_\bH, except that it adds three
+ subdirectories of the mailbox: _\bt_\bm_\bp, _\bn_\be_\bw and _\bc_\bu_\br. Filenames for the
+ messages are chosen in such a way they are unique, even when two
+ programs are writing the mailbox over NFS, which means that no file
+ locking is needed.
+
+ 4\b4.\b.6\b6.\b. M\bMa\bai\bil\blb\bbo\box\bx S\bSh\bho\bor\brt\btc\bcu\but\bts\bs
+
+ There are a number of built in shortcuts which refer to specific
+ mailboxes. These shortcuts can be used anywhere you are prompted for
+ a file or mailbox path.
+
+ +\bo ! -- refers to your ``$spool'' (incoming) mailbox
+
+ +\bo > -- refers to your ``$mbox'' file
+
+ +\bo < -- refers to your ``$record'' file
+
+ +\bo - -- refers to the file you've last visited
+
+ +\bo ~ -- refers to your home directory
+
+ +\bo = or + -- refers to your ``$folder'' directory
+
+ 4\b4.\b.7\b7.\b. H\bHa\ban\bnd\bdl\bli\bin\bng\bg M\bMa\bai\bil\bli\bin\bng\bg L\bLi\bis\bst\bts\bs
+
+ Mutt has a few configuration options that make dealing with large
+ amounts of mail easier. The first thing you must do is to let Mutt
+ know what addresses you consider to be mailing lists (technically this
+ does not have to be a mailing list, but that is what it is most often
+ used for). This is accomplished through the use of the ``lists''
+ command in your muttrc.
+
+ Now that Mutt knows what your mailing lists are, it can do several
+ things, the first of which is the ability to show the list name in the
+ _\bi_\bn_\bd_\be_\bx menu display. This is useful to distinguish between personal
+ and list mail in the same mailbox. In the ``$header_format''
+ variable, the escape ``%L'' will return the string ``To <list>'' when
+ ``list'' appears in the ``To'' field, and ``Cc <list>'' when it
+ appears in the ``Cc'' field (otherwise it returns the name of the
+ author).
+
+ Often times the ``To'' and ``Cc'' fields in mailing list messages tend
+ to get quite large. Most people do not bother to remove the author of
+ the message they are reply to from the list, resulting in two or more
+ copies being sent to that person. The ``list-reply'' function, which
+ by default is bound to ``L'' in the _\bi_\bn_\bd_\be_\bx menu and _\bp_\ba_\bg_\be_\br, helps reduce
+ the clutter by only replying to the mailing list addresses instead of
+ all recipients.
+
+ The other method some mailing list admins use is to generate a
+ ``Reply-To'' field which points back to the mailing list address
+ rather than the author of the message. This can create problems when
+ trying to reply directly to the author in private, since most mail
+ clients will automatically reply to the address given in the ``Reply-
+ To'' field. Mutt uses the ``$reply_to'' variable to help decide which
+ address to use. If set, you will be prompted as to whether or not you
+ would like to use the address given in the ``Reply-To'' field, or
+ reply directly to the address given in the ``From'' field. When
+ unset, the ``Reply-To'' field will be used when present.
+
+ Lastly, Mutt has the ability to ``sort'' the mailbox into ``threads''.
+ A thread is a group of messages which all relate to the same subject.
+ This is usually organized into a tree-like structure where a message
+ and all of its replies are represented graphically. If you've ever
+ used a threaded news client, this is the same concept. It makes
+ dealing with large volume mailing lists easier because you can easily
+ delete uninteresting threads and quickly find topics of value.
+
+ 4\b4.\b.8\b8.\b. D\bDe\bel\bli\biv\bve\ber\bry\by S\bSt\bta\bat\btu\bus\bs N\bNo\bot\bti\bif\bfi\bic\bca\bat\bti\bio\bon\bn (\b(D\bDS\bSN\bN)\b) S\bSu\bup\bpp\bpo\bor\brt\bt
+
+ RFC1894 defines a set of MIME content types for relaying information
+ about the status of electronic mail messages. These can be thought of
+ as ``return receipts.'' Berkeley sendmail 8.8.x currently has some
+ command line options in which the mail client can make requests as to
+ what type of status messages should be returned.
+
+ To support this, there are two variables. ``$dsn_notify'' is used to
+ request receipts for different results (such as failed message,
+ message delivered, etc.). ``$dsn_return'' requests how much of your
+ message should be returned with the receipt (headers or full message).
+ Refer to the man page on sendmail for more details on DSN.
+
+ 4\b4.\b.9\b9.\b. P\bPO\bOP\bP3\b3 S\bSu\bup\bpp\bpo\bor\brt\bt (\b(O\bOP\bPT\bTI\bIO\bON\bNA\bAL\bL)\b)
+
+ If Mutt was compiled with POP3 support (by running the _\bc_\bo_\bn_\bf_\bi_\bg_\bu_\br_\be
+ script with the _\b-_\b-_\be_\bn_\ba_\bb_\bl_\be_\b-_\bp_\bo_\bp flag), it has the ability to fetch your
+ mail from a remote server for local browsing. When you invoke the
+ _\bf_\be_\bt_\bc_\bh_\b-_\bm_\ba_\bi_\bl function (default: G), Mutt attempts to connect to
+ ``pop_host'' and authenticate by logging in as ``pop_user''. After
+ the connection is established, you will be prompted for your password
+ on the remote system.
+
+ Once you have been authenticated, Mutt will fetch all your new mail
+ and place it in the local ``spoolfile''. After this point, Mutt runs
+ exactly as if the mail had always been local.
+ N\bNo\bot\bte\be:\b: The POP3 support is there only for convenience, and it's rather
+ limited. If you need more functionality you should consider using a
+ specialized program, such as fetchmail
+
+ 5\b5.\b. M\bMu\but\btt\bt'\b's\bs M\bMI\bIM\bME\bE S\bSu\bup\bpp\bpo\bor\brt\bt
+
+ Quite a bit of effort has been made to make Mutt the premier text-mode
+ MIME MUA. Every effort has been made to provide the functionality
+ that the discerning MIME user requires, and the conformance to the
+ standards wherever possible. When configuring Mutt for MIME, there
+ are two extra types of configuration files which Mutt uses. One is
+ the mime.types file, which contains the mapping of file extensions to
+ IANA MIME types. The other is the mailcap file, which specifies the
+ external commands to use for handling specific MIME types.
+
+ 5\b5.\b.1\b1.\b. U\bUs\bsi\bin\bng\bg M\bMI\bIM\bME\bE i\bin\bn M\bMu\but\btt\bt
+
+ There are three areas/menus in Mutt which deal with MIME, they are the
+ pager (while viewing a message), the attachment menu and the compose
+ menu.
+
+ 5\b5.\b.1\b1.\b.1\b1.\b. V\bVi\bie\bew\bwi\bin\bng\bg M\bMI\bIM\bME\bE m\bme\bes\bss\bsa\bag\bge\bes\bs i\bin\bn t\bth\bhe\be p\bpa\bag\bge\ber\br
+
+ When you select a message from the index and view it in the pager,
+ Mutt decodes the message to a text representation. Mutt internally
+ supports a number of MIME types, including text/plain, text/enriched,
+ message/rfc822, and message/news. In addition, the export controlled
+ version of Mutt recognizes a variety of PGP MIME types, including
+ PGP/MIME and application/pgp.
+
+ Mutt will denote attachments with a couple lines describing them.
+ These lines are of the form:
+
+ [-- Attachment #1: Description --]
+ [-- Type: text/plain, Encoding: 7bit, Size: 10000 --]
+
+ Where the Description is the description or filename given for the
+ attachment, and the Encoding is one of 7bit/8bit/quoted-print-
+ able/base64/binary.
+
+ If Mutt cannot deal with a MIME type, it will display a message like:
+
+ [-- image/gif is unsupported (use 'v' to view this part) --]
+
+ 5\b5.\b.1\b1.\b.2\b2.\b. T\bTh\bhe\be A\bAt\btt\bta\bac\bch\bhm\bme\ben\bnt\bt M\bMe\ben\bnu\bu
+
+ The default binding for view-attachments is `v', which displays the
+ attachment menu for a message. The attachment menu displays a list of
+ the attachments in a message. From the attachment menu, you can save,
+ print, pipe, delete, and view attachments. You can apply these
+ operations to a group of attachments at once, by tagging the
+ attachments and by using the ``tag-prefix'' operator. You can also
+ reply to the current message from this menu, and only the current
+ attachment (or the attachments tagged) will be quoted in your reply.
+ You can view attachments as text, or view them using the mailcap
+ viewer definition. See the help on the attachment menu for more
+ information.
+
+ 5\b5.\b.1\b1.\b.3\b3.\b. T\bTh\bhe\be C\bCo\bom\bmp\bpo\bos\bse\be M\bMe\ben\bnu\bu
+
+ The compose menu is the menu you see before you send a message. It
+ allows you to edit the recipient list, the subject, and other aspects
+ of your message. It also contains a list of the attachments of your
+ message, including the main body. From this menu, you can print,
+ copy, filter, pipe, edit, compose, review, and rename an attachment or
+ a list of tagged attachments. You can also modifying the attachment
+ information, notably the type, encoding and description.
+
+ Attachments appear as follows:
+
+ - 1 [text/plain, 7bit, 1K] /tmp/mutt-euler-8082-0 <no description>
+ 2 [applica/x-gunzip, base64, 422K] ~/src/mutt-0.85.tar.gz <no description>
+
+ The '-' denotes that Mutt will delete the file after sending the
+ message. It can be toggled with the toggle-unlink command (default:
+ u). The next field is the MIME content-type, and can be changed with
+ the edit-type command (default: ^T). The next field is the encoding
+ for the attachment, which allows a binary message to be encoded for
+ transmission on 7bit links. It can be changed with the edit-encoding
+ command (default: ^E). The next field is the size of the attachment,
+ rounded to kilobytes or megabytes. The next field is the filename,
+ which can be changed with the rename-file command (default: R). The
+ final field is the description of the attachment, and can be changed
+ with the edit-description command (default: d).
+
+ 5\b5.\b.2\b2.\b. M\bMI\bIM\bME\bE T\bTy\byp\bpe\be c\bco\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn w\bwi\bit\bth\bh mime.types
+
+ When you add an attachment to your mail message, Mutt searches your
+ personal mime.types file at ${HOME}/.mime.types, and then the system
+ mime.types file at SHAREDIR/mime.types. SHAREDIR is defined at
+ compilation time, and can be determined by typing mutt -v from the
+ command line.
+
+ The mime.types file consist of lines containing a MIME type and a
+ space separated list of extensions. For example:
+
+ application/postscript ps eps
+ application/pgp pgp
+ audio/x-aiff aif aifc aiff
+
+ A sample mime.types file comes with the Mutt distribution, and should
+ contain most of the MIME types you are likely to use.
+
+ If Mutt can not determine the mime type by the extension of the file
+ you attach, it will look at the file. If the file is free of binary
+ information, Mutt will assume that the file is plain text, and mark it
+ as text/plain. If the file contains binary information, then Mutt
+ will mark it as application/octect-stream. You can change the MIME
+ type that Mutt assigns to an attachment by using the edit-type command
+ from the compose menu (default: ^T). When typing in the MIME type,
+ Mutt requires that major type be one of the 5 types: application,
+ text, image, video, or audio. If you attempt to use a different major
+ type, Mutt will abort the change.
+
+ 5\b5.\b.3\b3.\b. M\bMI\bIM\bME\bE V\bVi\bie\bew\bwe\ber\br c\bco\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn w\bwi\bit\bth\bh mailcap
+
+ Mutt supports RFC 1524 MIME Configuration, in particular the Unix
+ specific format specified in Appendix A of RFC 1524. This file format
+ is commonly referred to as the mailcap format. Many MIME compliant
+ programs utilize the mailcap format, allowing you to specify handling
+ for all MIME types in one place for all programs. Programs known to
+ use this format include Netscape, XMosaic, lynx and metamail.
+
+ In order to handle various MIME types that Mutt can not handle
+ internally, Mutt parses a series of external configuration files to
+ find an external handler. The default search string for these files
+ is a colon delimited list set to
+
+ ${HOME}/.mailcap:SHAREDIR/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap
+
+ where $HOME is your home directory and SHAREDIR is the shared direc-
+ tory defined at compile time (visible from mutt -v).
+
+ In particular, the metamail distribution will install a mailcap file,
+ usually as /usr/local/etc/mailcap, which contains some baseline
+ entries.
+
+ 5\b5.\b.3\b3.\b.1\b1.\b. T\bTh\bhe\be B\bBa\bas\bsi\bic\bcs\bs o\bof\bf t\bth\bhe\be m\bma\bai\bil\blc\bca\bap\bp f\bfi\bil\ble\be
+
+ A mailcap file consists of a series of lines which are comments,
+ blank, or definitions.
+
+ A comment line consists of a # character followed by anything you
+ want.
+
+ A blank line is blank.
+
+ A definition line consists of a content type, a view command, and any
+ number of optional fields. Each field of a definition line is divided
+ by a semicolon ';' character.
+
+ The content type is specified in the MIME standard type/subtype
+ method. For example, text/plain, text/html, image/gif, etc. In
+ addition, the mailcap format includes two formats for wildcards, one
+ using the special '*' subtype, the other is the implicit wild, where
+ you only include the major type. For example, image/*, or video, will
+ match all image types and video types, respectively.
+
+ The view command is a Unix command for viewing the type specified.
+ There are two different types of commands supported. The default is to
+ send the body of the MIME message to the command on stdin. You can
+ change this behaviour by using %s as a parameter to your view command.
+ This will cause Mutt to save the body of the MIME message to a
+ temporary file, and then call the view command with the %s replaced by
+ the name of the temporary file. In both cases, Mutt will turn over the
+ terminal to the view program until the program quits, at which time
+ Mutt will remove the temporary file if it exists.
+
+ So, in the simplest form, you can send a text/plain message to the
+ external pager more on stdin:
+
+ text/plain; more
+
+ Or, you could send the message as a file:
+
+ text/plain; more %s
+
+ Perhaps you would like to use lynx to interactively view a text/html
+ message:
+
+ text/html; lynx "%s"
+
+ In this case, lynx does not support viewing a file from stdin, so you
+ must use the %s syntax. N\bNo\bot\bte\be:\b: _\bS_\bo_\bm_\be _\bo_\bl_\bd_\be_\br _\bv_\be_\br_\bs_\bi_\bo_\bn_\bs _\bo_\bf _\bl_\by_\bn_\bx _\bc_\bo_\bn_\bt_\ba_\bi_\bn _\ba
+ _\bb_\bu_\bg _\bw_\bh_\be_\br_\be _\bt_\bh_\be_\by _\bw_\bi_\bl_\bl _\bc_\bh_\be_\bc_\bk _\bt_\bh_\be _\bm_\ba_\bi_\bl_\bc_\ba_\bp _\bf_\bi_\bl_\be _\bf_\bo_\br _\ba _\bv_\bi_\be_\bw_\be_\br _\bf_\bo_\br _\bt_\be_\bx_\bt_\b/_\bh_\bt_\bm_\bl_\b.
+ _\bT_\bh_\be_\by _\bw_\bi_\bl_\bl _\bf_\bi_\bn_\bd _\bt_\bh_\be _\bl_\bi_\bn_\be _\bw_\bh_\bi_\bc_\bh _\bc_\ba_\bl_\bl_\bs _\bl_\by_\bn_\bx_\b, _\ba_\bn_\bd _\br_\bu_\bn _\bi_\bt_\b. _\bT_\bh_\bi_\bs _\bc_\ba_\bu_\bs_\be_\bs
+ _\bl_\by_\bn_\bx _\bt_\bo _\bc_\bo_\bn_\bt_\bi_\bn_\bu_\bo_\bu_\bs_\bl_\by _\bs_\bp_\ba_\bw_\bn _\bi_\bt_\bs_\be_\bl_\bf _\bt_\bo _\bv_\bi_\be_\bw _\bt_\bh_\be _\bo_\bb_\bj_\be_\bc_\bt_\b.
+
+ On the other hand, maybe you don't want to use lynx interactively, you
+ just want to have it convert the text/html to text/plain, then you can
+ use:
+
+ text/html; lynx -dump "%s" | more
+
+ Perhaps you wish to use lynx to view text/html files, and a pager on
+ all other text formats, then you would use the following:
+
+ text/html; lynx "%s"
+ text/*; more
+
+ This is the simplest form of a mailcap file.
+
+ 5\b5.\b.3\b3.\b.2\b2.\b. A\bAd\bdv\bva\ban\bnc\bce\bed\bd m\bma\bai\bil\blc\bca\bap\bp U\bUs\bsa\bag\bge\be
+
+ 5\b5.\b.3\b3.\b.2\b2.\b.1\b1.\b. O\bOp\bpt\bti\bio\bon\bna\bal\bl F\bFi\bie\bel\bld\bds\bs
+
+ In addition to the required content-type and view command fields, you
+ can add semi-colon ';' separated fields to set flags and other
+ options. Mutt recognizes the following optional fields:
+
+ c\bco\bop\bpi\bio\bou\bus\bso\bou\but\btp\bpu\but\bt
+ This flag tells Mutt that the command passes possibly large
+ amounts of text on stdout. This causes Mutt to invoke a pager
+ (either the internal pager or the external pager defined by the
+ pager variable) on the output of the view command. Without this
+ flag, Mutt assumes that the command is interactive. One could
+ use this to replace the pipe to more in the lynx -dump example
+ in the Basic section:
+
+ text/html; lynx -dump %s ; copiousoutput
+
+ This will cause lynx to format the text/html output as text/plain
+ and Mutt will use your standard pager to display the results.
+
+ n\bne\bee\bed\bds\bst\bte\ber\brm\bmi\bin\bna\bal\bl
+ Mutt uses this flag when viewing attachments with ``autoview'',
+ in order to decide whether it should honor the setting of the
+ ``$wait_key'' variable or not. When an attachment is viewed
+ using an interactive program, and the corresponding mailcap
+ entry has a _\bn_\be_\be_\bd_\bs_\bt_\be_\br_\bm_\bi_\bn_\ba_\bl flag, Mutt will use ``$wait_key'' and
+ the exit status of the program to decide if it will ask you to
+ press a key after the external program has exited. In all other
+ situations it will not prompt you for a key.
+
+ c\bco\bom\bmp\bpo\bos\bse\be=\b=<\b<c\bco\bom\bmm\bma\ban\bnd\bd>\b>
+ This flag specifies the command to use to create a new
+ attachment of a specific MIME type. Mutt supports this from the
+ compose menu.
+
+ c\bco\bom\bmp\bpo\bos\bse\bet\bty\byp\bpe\bed\bd=\b=<\b<c\bco\bom\bmm\bma\ban\bnd\bd>\b>
+ This flag specifies the command to use to create a new
+ attachment of a specific MIME type. This command differs from
+ the compose command in that mutt will expect standard MIME
+ headers on the data. This can be used to specify parameters,
+ filename, description, etc. for a new attachment. Mutt
+ supports this from the compose menu.
+
+ p\bpr\bri\bin\bnt\bt=\b=<\b<c\bco\bom\bmm\bma\ban\bnd\bd>\b>
+ This flag specifies the command to use to print a specific MIME
+ type. Mutt supports this from the attachment and compose menus.
+
+ e\bed\bdi\bit\bt=\b=<\b<c\bco\bom\bmm\bma\ban\bnd\bd>\b>
+ This flag specifies the command to use to edit a specific MIME
+ type. Mutt supports this from the compose menu, and also uses
+ it to compose new attachments. Mutt will default to the defined
+ editor for text attachments.
+
+ n\bna\bam\bme\bet\bte\bem\bmp\bpl\bla\bat\bte\be=\b=<\b<t\bte\bem\bmp\bpl\bla\bat\bte\be>\b>
+ This field specifies the format for the file denoted by %s in
+ the command fields. Certain programs will require a certain
+ file extension, for instance, to correctly view a file. For
+ instance, lynx will only interpret a file as text/html if the
+ file ends in .html. So, you would specify lynx as a text/html
+ viewer with a line in the mailcap file like:
+
+ text/html; lynx %s; nametemplate=%s.html
+
+ t\bte\bes\bst\bt=\b=<\b<c\bco\bom\bmm\bma\ban\bnd\bd>\b>
+ This field specifies a command to run to test whether this
+ mailcap entry should be used. The command is defined with the
+ command expansion rules defined in the next section. If the
+ command returns 0, then the test passed, and Mutt uses this
+ entry. If the command returns non-zero, then the test failed,
+ and Mutt continues searching for the right entry. N\bNo\bot\bte\be:\b: _\bt_\bh_\be
+ _\bc_\bo_\bn_\bt_\be_\bn_\bt_\b-_\bt_\by_\bp_\be _\bm_\bu_\bs_\bt _\bm_\ba_\bt_\bc_\bh _\bb_\be_\bf_\bo_\br_\be _\bM_\bu_\bt_\bt _\bp_\be_\br_\bf_\bo_\br_\bm_\bs _\bt_\bh_\be _\bt_\be_\bs_\bt_\b. For
+ example:
+
+ text/html; netscape -remote 'openURL(%s)' ; test=RunningX
+ text/html; lynx %s
+
+ In this example, Mutt will run the program RunningX which will
+ return 0 if the X Window manager is running, and non-zero if it
+ isn't. If RunningX returns 0, then Mutt will call netscape to dis-
+ play the text/html object. If RunningX doesn't return 0, then Mutt
+ will go on to the next entry and use lynx to display the text/html
+ object.
+
+ 5\b5.\b.3\b3.\b.2\b2.\b.2\b2.\b. S\bSe\bea\bar\brc\bch\bh O\bOr\brd\bde\ber\br
+
+ When searching for an entry in the mailcap file, Mutt will search for
+ the most useful entry for its purpose. For instance, if you are
+ attempting to print an image/gif, and you have the following entries
+ in your mailcap file, Mutt will search for an entry with the print
+ command:
+
+ image/*; xv %s
+ image/gif; ; print= anytopnm %s | pnmtops | lpr; \
+ nametemplate=%s.gif
+
+ Mutt will skip the image/* entry and use the image/gif entry with the
+ print command.
+
+ In addition, you can use this with ``Autoview'' to denote two commands
+ for viewing an attachment, one to be viewed automatically, the other
+ to be viewed interactively from the attachment menu. In addition, you
+ can then use the test feature to determine which viewer to use
+ interactively depending on your environment.
+
+ text/html; netscape -remote 'openURL(%s)' ; test=RunningX
+ text/html; lynx %s; nametemplate=%s.html
+ text/html; lynx -dump %s; nametemplate=%s.html; copiousoutput
+
+ For ``Autoview'', Mutt will choose the third entry because of the
+ copiousoutput tag. For interactive viewing, Mutt will run the program
+ RunningX to determine if it should use the first entry. If the pro-
+ gram returns non-zero, Mutt will use the second entry for interactive
+ viewing.
+
+ 5\b5.\b.3\b3.\b.2\b2.\b.3\b3.\b. C\bCo\bom\bmm\bma\ban\bnd\bd E\bEx\bxp\bpa\ban\bns\bsi\bio\bon\bn
+
+ The various commands defined in the mailcap files are passed to the
+ /bin/sh shell using the system() function. Before the command is
+ passed to /bin/sh -c, it is parsed to expand various special
+ parameters with information from Mutt. The keywords Mutt expands are:
+
+ %\b%s\bs As seen in the basic mailcap section, this variable is expanded
+ to a filename specified by the calling program. This file
+ contains the body of the message to view/print/edit or where the
+ composing program should place the results of composition. In
+ addition, the use of this keyword causes Mutt to not pass the
+ body of the message to the view/print/edit program on stdin.
+
+ %\b%t\bt Mutt will expand %t to the text representation of the content
+ type of the message in the same form as the first parameter of
+ the mailcap definition line, ie text/html or image/gif.
+
+ %\b%{\b{<\b<p\bpa\bar\bra\bam\bme\bet\bte\ber\br>\b>}\b}
+ Mutt will expand this to the value of the specified parameter
+ from the Content-Type: line of the mail message. For instance,
+ if Your mail message contains:
+
+ Content-Type: text/plain; charset=iso-8859-1
+
+ then Mutt will expand %{charset} to iso-8859-1. The default meta-
+ mail mailcap file uses this feature to test the charset to spawn an
+ xterm using the right charset to view the message.
+
+ \\b\%\b% This will be replaced by a %
+
+ Mutt does not currently support the %F and %n keywords specified in
+ RFC 1524. The main purpose of these parameters is for multipart mes-
+ sages, which is handled internally by Mutt.
+
+ 5\b5.\b.3\b3.\b.3\b3.\b. E\bEx\bxa\bam\bmp\bpl\ble\be m\bma\bai\bil\blc\bca\bap\bp f\bfi\bil\ble\bes\bs
+
+ This mailcap file is fairly simple and standard:
+
+ ______________________________________________________________________
+ # I'm always running X :)
+ video/*; xanim %s > /dev/null
+ image/*; xv %s > /dev/null
+
+ # I'm always running netscape (if my computer had more memory, maybe)
+ text/html; netscape -remote 'openURL(%s)'
+ ______________________________________________________________________
+
+ This mailcap file shows quite a number of examples:
+
+ ______________________________________________________________________
+ # Use xanim to view all videos Xanim produces a header on startup,
+ # send that to /dev/null so I don't see it
+ video/*; xanim %s > /dev/null
+
+ # Send html to a running netscape by remote
+ text/html; netscape -remote 'openURL(%s)'; test=RunningNetscape
+
+ # If I'm not running netscape but I am running X, start netscape on the
+ # object
+ text/html; netscape %s; test=RunningX
+
+ # Else use lynx to view it as text
+ text/html; lynx %s
+
+ # This version would convert the text/html to text/plain
+ text/html; lynx -dump %s; copiousoutput
+
+ # enriched.sh converts text/enriched to text/html and then uses
+ # lynx -dump to convert it to text/plain
+ text/enriched; enriched.sh ; copiousoutput
+
+ # I use enscript to print text in two columns to a page
+ text/*; more %s; print=enscript -2Gr %s
+
+ # Netscape adds a flag to tell itself to view jpegs internally
+ image/jpeg;xv %s; x-mozilla-flags=internal
+
+ # Use xv to view images if I'm running X
+ # In addition, this uses the \ to extend the line and set my editor
+ # for images
+ image/*;xv %s; test=RunningX; \
+ edit=xpaint %s
+
+ # Convert images to text using the netpbm tools
+ image/*; (anytopnm %s | pnmscale -xysize 80 46 | ppmtopgm | pgmtopbm |
+ pbmtoascii -1x2 ) 2>&1 ; copiousoutput
+
+ # Send excel spreadsheets to my NT box
+ application/ms-excel; open.pl %s
+ ______________________________________________________________________
+
+ 5\b5.\b.4\b4.\b. M\bMI\bIM\bME\bE A\bAu\but\bto\bov\bvi\bie\bew\bw
+
+ In addition to explicitly telling Mutt to view an attachment with the
+ MIME viewer defined in the mailcap file, Mutt has support for
+ automatically viewing MIME attachments while in the pager.
+
+ To work, you must define a viewer in the mailcap file which uses the
+ copiousoutput option to denote that it is non-interactive. Usually,
+ you also use the entry to convert the attachment to a text
+ representation which you can view in the pager.
+
+ You then use the auto_view muttrc command to list the content-types
+ that you wish to view automatically.
+
+ For instance, if you set auto_view to:
+
+ auto_view text/html text/enriched application/x-gunzip application/postscript image/gif application/x-tar-gz
+
+ Mutt could use the following mailcap entries to automatically view
+ attachments of these types.
+
+ text/html; lynx -dump %s; copiousoutput; nametemplate=%s.html
+ text/enriched; enriched.sh ; copiousoutput
+ image/*; anytopnm %s | pnmscale -xsize 80 -ysize 50 | ppmtopgm | pgmtopbm | pbmtoascii ; copiousoutput
+ application/x-gunzip; gzcat; copiousoutput
+ application/x-tar-gz; gunzip -c %s | tar -tf - ; copiousoutput
+ application/postscript; ps2ascii %s; copiousoutput
+
+ 5\b5.\b.5\b5.\b. M\bMI\bIM\bME\bE M\bMu\bul\blt\bti\bip\bpa\bar\brt\bt/\b/A\bAl\blt\bte\ber\brn\bna\bat\bti\biv\bve\be
+
+ Mutt has some heuristics for determining which attachment of a
+ multipart/alternative type to display. First, mutt will check the
+ alternative_order list to determine if one of the available types is
+ preferred. The alternative_order list consists of a number of
+ mimetypes in order, including support for implicit and explicit
+ wildcards, for example:
+
+ alternative_order text/enriched text/plain text application/postscript image/*
+
+ Next, mutt will check if any of the types have a defined
+ ``auto_view'', and use that. Failing that, Mutt will look for any
+ text type. As a last attempt, mutt will look for any type it knows
+ how to handle.
+
+ 6\b6.\b. R\bRe\bef\bfe\ber\bre\ben\bnc\bce\be
+
+ 6\b6.\b.1\b1.\b. C\bCo\bom\bmm\bma\ban\bnd\bd l\bli\bin\bne\be o\bop\bpt\bti\bio\bon\bns\bs
+
+ Running mutt with no arguments will make Mutt attempt to read your
+ spool mailbox. However, it is possible to read other mailboxes and to
+ send messages from the command line as well.
+
+ -a attach a file to a message
+ -c specify a carbon-copy (Cc) address
+ -e specify a config command to be run after initilization files are read
+ -F specify an alternate file to read initialization commands
+ -f specify a mailbox to load
+ -h print help on command line options
+ -H specify a draft file from which to read a header and body
+ -i specify a file to include in a message composition
+ -n do not read the system Muttrc
+ -m specify a default mailbox type
+ -p recall a postponed message
+ -R open mailbox in read-only mode
+ -s specify a subject (enclose in quotes if it contains spaces)
+ -v show version number and compile-time definitions
+ -x simulate the mailx(1) compose mode
+ -y show a menu containing the files specified by the mailboxes command
+ -z exit immediately if there are no messages in the mailbox
+ -Z open the first folder with new message,exit immediately if none
+
+ To read messages in a mailbox
+
+ mutt [ -nz ] [ -F _\bm_\bu_\bt_\bt_\br_\bc ] [ -m _\bt_\by_\bp_\be ] [ -f _\bm_\ba_\bi_\bl_\bb_\bo_\bx ]
+
+ To compose a new message
+
+ mutt [ -n ] [ -F _\bm_\bu_\bt_\bt_\br_\bc ] [ -a _\bf_\bi_\bl_\be ] [ -c _\ba_\bd_\bd_\br_\be_\bs_\bs ] [ -i _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be ] [
+ -s _\bs_\bu_\bb_\bj_\be_\bc_\bt ] _\ba_\bd_\bd_\br_\be_\bs_\bs [ _\ba_\bd_\bd_\br_\be_\bs_\bs ... ]
+
+ Mutt also supports a ``batch'' mode to send prepared messages. Simply
+ redirect input from the file you wish to send. For example,
+
+ mutt -s "data set for run #2" professor@bigschool.edu < ~/run2.dat
+
+ This command will send a message to ``professor@bigschool.edu'' with a
+ subject of ``data set for run #2''. In the body of the message will
+ be the contents of the file ``~/run2.dat''.
+
+ 6\b6.\b.2\b2.\b. C\bCo\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn C\bCo\bom\bmm\bma\ban\bnd\bds\bs
+
+ The following are the commands understood by mutt.
+
+ +\bo ``alias'' _\bk_\be_\by _\ba_\bd_\bd_\br_\be_\bs_\bs [ , _\ba_\bd_\bd_\br_\be_\bs_\bs, ... ]
+
+ +\bo ``unalias'' _\bk_\be_\by _\ba_\bd_\bd_\br_\be_\bs_\bs [ , _\ba_\bd_\bd_\br_\be_\bs_\bs, ... ]
+
+ +\bo ``alternative_order'' _\bm_\bi_\bm_\be_\bt_\by_\bp_\be [ _\bm_\bi_\bm_\be_\bt_\by_\bp_\be ... ]
+
+ +\bo ``auto_view'' _\bm_\bi_\bm_\be_\bt_\by_\bp_\be [ _\bm_\bi_\bm_\be_\bt_\by_\bp_\be ... ]
+
+ +\bo ``bind'' _\bm_\ba_\bp _\bk_\be_\by _\bf_\bu_\bn_\bc_\bt_\bi_\bo_\bn
+
+ +\bo ``color'' _\bo_\bb_\bj_\be_\bc_\bt _\bf_\bo_\br_\be_\bg_\br_\bo_\bu_\bn_\bd _\bb_\ba_\bc_\bk_\bg_\br_\bo_\bu_\bn_\bd [ _\br_\be_\bg_\be_\bx_\bp ]
+
+ +\bo ``folder-hook'' _\bp_\ba_\bt_\bt_\be_\br_\bn _\bc_\bo_\bm_\bm_\ba_\bn_\bd
+
+ +\bo ``ignore'' _\bp_\ba_\bt_\bt_\be_\br_\bn [ _\bp_\ba_\bt_\bt_\be_\br_\bn ... ]
+
+ +\bo ``unignore'' _\bp_\ba_\bt_\bt_\be_\br_\bn [ _\bp_\ba_\bt_\bt_\be_\br_\bn ... ]
+
+ +\bo ``hdr_order'' _\bh_\be_\ba_\bd_\be_\br [ _\bh_\be_\ba_\bd_\be_\br ... ]
+
+ +\bo ``lists'' _\ba_\bd_\bd_\br_\be_\bs_\bs [ _\ba_\bd_\bd_\br_\be_\bs_\bs ... ]
+
+ +\bo ``unlists'' _\ba_\bd_\bd_\br_\be_\bs_\bs [ _\ba_\bd_\bd_\br_\be_\bs_\bs ... ]
+
+ +\bo ``macro'' _\bm_\be_\bn_\bu _\bk_\be_\by _\bs_\be_\bq_\bu_\be_\bn_\bc_\be
+
+ +\bo ``mailboxes'' _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be [ _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be ... ]
+
+ +\bo ``mono'' _\bo_\bb_\bj_\be_\bc_\bt _\ba_\bt_\bt_\br_\bi_\bb_\bu_\bt_\be [ _\br_\be_\bg_\be_\bx_\bp ]
+
+ +\bo ``mbox-hook'' _\bp_\ba_\bt_\bt_\be_\br_\bn _\bm_\ba_\bi_\bl_\bb_\bo_\bx
+
+ +\bo ``my_hdr'' _\bs_\bt_\br_\bi_\bn_\bg
+
+ +\bo ``unmy_hdr'' _\bf_\bi_\be_\bl_\bd [ _\bf_\bi_\be_\bl_\bd ... ]
+
+ +\bo ``push'' _\bs_\bt_\br_\bi_\bn_\bg
+
+ +\bo ``save-hook'' _\br_\be_\bg_\be_\bx_\bp _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be
+
+ +\bo ``send-hook'' _\br_\be_\bg_\be_\bx_\bp _\bc_\bo_\bm_\bm_\ba_\bn_\bd
+
+ +\bo ``set'' [no|inv]_\bv_\ba_\br_\bi_\ba_\bb_\bl_\be[=_\bv_\ba_\bl_\bu_\be] [ _\bv_\ba_\br_\bi_\ba_\bb_\bl_\be ... ]
+
+ +\bo ``toggle'' _\bv_\ba_\br_\bi_\ba_\bb_\bl_\be [_\bv_\ba_\br_\bi_\ba_\bb_\bl_\be ... ]
+
+ +\bo ``unset'' _\bv_\ba_\br_\bi_\ba_\bb_\bl_\be [_\bv_\ba_\br_\bi_\ba_\bb_\bl_\be ... ]
+
+ +\bo ``source'' _\bf_\bi_\bl_\be_\bn_\ba_\bm_\be
+
+ 6\b6.\b.3\b3.\b. C\bCo\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn v\bva\bar\bri\bia\bab\bbl\ble\bes\bs
+
+ 6\b6.\b.3\b3.\b.1\b1.\b. a\bab\bbo\bor\brt\bt_\b_n\bno\bos\bsu\bub\bbj\bje\bec\bct\bt
+
+ Type: quadoption
+ Default: ask-yes
+
+ If set to _\by_\be_\bs, when composing messages and no subject is given at the
+ subject prompt, composition will be aborted. If set to _\bn_\bo, composing
+ messages with no subject given at the subject prompt will never be
+ aborted.
+
+ 6\b6.\b.3\b3.\b.2\b2.\b. a\bab\bbo\bor\brt\bt_\b_u\bun\bnm\bmo\bod\bdi\bif\bfi\bie\bed\bd
+
+ Type: quadoption
+ Default: yes
+
+ If set to _\by_\be_\bs, composition will automatically abort after editing the
+ message body if no changes are made to the file (this check only
+ happens after the _\bf_\bi_\br_\bs_\bt edit of the file). When set to _\bn_\bo,
+ composition will never be aborted.
+
+ 6\b6.\b.3\b3.\b.3\b3.\b. a\bal\bli\bia\bas\bs_\b_f\bfi\bil\ble\be
+
+ Type: string
+ Default: ~/.muttrc
+
+ The default file in which to save aliases created by the ``create-
+ alias'' function.
+
+ N\bNo\bot\bte\be:\b: Mutt will not automatically source this file; you must
+ explicitly use the ``source'' command for it to be executed.
+
+ 6\b6.\b.3\b3.\b.4\b4.\b. a\bal\bli\bia\bas\bs_\b_f\bfo\bor\brm\bma\bat\bt
+
+ Type: string
+ Default: "%2n %t %-10a %r"
+
+ Specifies the format of the data displayed for the `alias' menu. The
+ following printf(3)-style sequences are available.
+
+ %a alias name
+ %n index number
+ %r address which alias expands to
+ %t character which indicates if the alias is tagged for inclusion (*/ )
+
+ 6\b6.\b.3\b3.\b.5\b5.\b. a\bal\bll\blo\bow\bw_\b_8\b8b\bbi\bit\bt
+
+ Type: boolean
+ Default: set
+
+ Controls whether 8-bit data is converted to 7-bit using either Quoted-
+ Printable or Base64 encoding when sending mail.
+
+ 6\b6.\b.3\b3.\b.6\b6.\b. a\bal\blt\bte\ber\brn\bna\bat\bte\bes\bs
+
+ Type: string
+ Default: none
+
+ A regexp that allows you to specify _\ba_\bl_\bt_\be_\br_\bn_\ba_\bt_\be addresses where you
+ receive mail. This affects Mutt's idea about messages from you and
+ addressed to you.
+
+ 6\b6.\b.3\b3.\b.7\b7.\b. a\bar\brr\bro\bow\bw_\b_c\bcu\bur\brs\bso\bor\br
+
+ Type: boolean
+ Default: unset
+
+ When set, an arrow (``->'') will be used to indicate the current entry
+ in menus instead of hiliting the whole line. On slow network or modem
+ links this will make response faster because there is less that has to
+ be redrawn on the screen when moving to the next or previous entries
+ in the menu.
+
+ 6\b6.\b.3\b3.\b.8\b8.\b. a\bas\bsc\bci\bii\bi_\b_c\bch\bha\bar\brs\bs
+
+ Type: boolean
+ Default: unset
+
+ If set, Mutt will use plain ASCII characters when displaying thread
+ and attachment trees, instead of the default _\bA_\bC_\bS characters.
+
+ 6\b6.\b.3\b3.\b.9\b9.\b. a\bas\bsk\bkb\bbc\bcc\bc
+
+ Type: boolean
+ Default: unset
+
+ If set, Mutt will prompt you for blind-carbon-copy (Bcc) recipients
+ before editing an outgoing message.
+
+ 6\b6.\b.3\b3.\b.1\b10\b0.\b. a\bas\bsk\bkc\bcc\bc
+
+ Type: boolean
+ Default: unset
+
+ If set, Mutt will prompt you for carbon-copy (Cc) recipients before
+ editing the body of an outgoing message.
+
+ 6\b6.\b.3\b3.\b.1\b11\b1.\b. a\bat\btt\bta\bac\bch\bh_\b_s\bse\bep\bp
+
+ Type: string
+ Default: newline
+
+ The separator to add between attachments when piping or saving a list
+ of tagged attachments to an external Unix command.
+
+ 6\b6.\b.3\b3.\b.1\b12\b2.\b. a\bat\btt\bta\bac\bch\bh_\b_s\bsp\bpl\bli\bit\bt
+
+ Type: boolean
+ Default: set
+
+ Used in connection with the _\bp_\bi_\bp_\be_\b-_\be_\bn_\bt_\br_\by and _\bs_\ba_\bv_\be_\b-_\be_\bn_\bt_\br_\by commands and the
+ ``tag-prefix'' operator in the ``attachment'' menu. If this variable
+ is unset, when piping or saving a list of tagged attachments Mutt will
+ concatenate the attachments and will pipe or save them as a single
+ file. The ``attach_sep'' separator will be added after each message.
+ When set, Mutt will pipe or save the messages one by one. In both
+ cases the the messages are processed in the displayed order.
+
+ 6\b6.\b.3\b3.\b.1\b13\b3.\b. a\bat\btt\btr\bri\bib\bbu\but\bti\bio\bon\bn
+
+ Type: format string
+ Default: "On %d, %n wrote:"
+
+ This is the string that will precede a message which has been included
+ in a reply. For a full listing of defined escape sequences see the
+ section on ``$header_format''.
+
+ 6\b6.\b.3\b3.\b.1\b14\b4.\b. a\bau\but\bto\boe\bed\bdi\bit\bt
+
+ Type: boolean
+ Default: unset
+
+ When set, Mutt will skip the initial send-menu and allow you to
+ immediately begin editing the body of your message when replying to
+ another message. The send-menu may still be accessed once you have
+ finished editing the body of your message.
+
+ If the ``$edit_headers'' variable is also set, the initial prompts in
+ the send-menu are always skipped, even when composing a new message.
+
+ 6\b6.\b.3\b3.\b.1\b15\b5.\b. a\bau\but\bto\bo_\b_t\bta\bag\bg
+
+ Type: boolean
+ Default: unset
+
+ When set, functions in the _\bi_\bn_\bd_\be_\bx menu which affect a message will be
+ applied to all tagged messages (if there are any). When unset, you
+ must first use the tag-prefix function (default: ";") to make the next
+ function apply to all tagged messages.
+
+ 6\b6.\b.3\b3.\b.1\b16\b6.\b. b\bbe\bee\bep\bp
+
+ Type: boolean
+ Default: set
+
+ When this variable is set, mutt will beep when an error occurs.
+
+ 6\b6.\b.3\b3.\b.1\b17\b7.\b. b\bbe\bee\bep\bp_\b_n\bne\bew\bw
+
+ Type boolean
+ Default: unset
+
+ When this variable is set, mutt will beep whenever it prints a message
+ notifying you of new mail. This is independent of the setting of the
+ ``beep'' variable.
+
+ 6\b6.\b.3\b3.\b.1\b18\b8.\b. c\bch\bha\bar\brs\bse\bet\bt
+
+ Type: string
+ Default: iso-8859-1
+
+ Character set your terminal uses to display and enter textual data.
+ This information is required to properly label outgoing messages which
+ contain 8-bit characters so that receiving parties can display your
+ messages in the correct character set.
+
+ 6\b6.\b.3\b3.\b.1\b19\b9.\b. c\bch\bhe\bec\bck\bk_\b_n\bne\bew\bw
+
+ Type: boolean
+ Default: set
+
+ N\bNo\bot\bte\be:\b: this option only affects _\bm_\ba_\bi_\bl_\bd_\bi_\br and _\bM_\bH style mailboxes.
+
+ When _\bs_\be_\bt, Mutt will check for new mail delivered while the mailbox is
+ open. Especially with MH mailboxes, this operation can take quite
+ some time since it involves scanning the directory and checking each
+ file to see if it has already been looked at. If _\bc_\bh_\be_\bc_\bk_\b__\bn_\be_\bw is _\bu_\bn_\bs_\be_\bt,
+ no check for new mail is performed while the mailbox is open.
+
+ 6\b6.\b.3\b3.\b.2\b20\b0.\b. c\bco\bon\bnf\bfi\bir\brm\bma\bap\bpp\bpe\ben\bnd\bd
+
+ Type: boolean
+ Default: set
+
+ When set, Mutt will prompt for confirmation when appending messages to
+ an existing mailbox.
+
+ 6\b6.\b.3\b3.\b.2\b21\b1.\b. c\bco\bon\bnf\bfi\bir\brm\bmc\bcr\bre\bea\bat\bte\be
+
+ Type: boolean
+ Default: set
+
+ When set, Mutt will prompt for confirmation when saving messages to a
+ mailbox which does not yet exist before creating it.
+
+ 6\b6.\b.3\b3.\b.2\b22\b2.\b. c\bco\bop\bpy\by
+
+ Type: quadoption
+ Default: yes
+
+ This variable controls whether or not copies of your outgoing messages
+ will be saved for later references. Also see ``record'',
+ ``save_name'', ``force_name'' and ``fcc-hook''.
+
+ 6\b6.\b.3\b3.\b.2\b23\b3.\b. d\bda\bat\bte\be_\b_f\bfo\bor\brm\bma\bat\bt
+
+ Type: string
+ Default: "!%a, %b %d, %Y at %I:%M:%S%p %Z"
+
+ This variable controls the format of the date printed by the ``%d''
+ sequence in ``$header_format''. This is passed to the _\bs_\bt_\br_\bf_\bt_\bi_\bm_\be call
+ to process the date. See the man page for _\bs_\bt_\br_\bf_\bt_\bi_\bm_\be_\b(_\b3_\b) for the proper
+ syntax.
+
+ Unless the first character in the string is a bang (``!''), the month
+ and week day names are expanded according to the locale specified in
+ the variable ``locale''. If the first character in the string is a
+ bang, the bang is discarded, and the month and week day names in the
+ rest of the string are expanded in the _\bC locale (that is in US
+ English).
+
+ 6\b6.\b.3\b3.\b.2\b24\b4.\b. d\bde\bef\bfa\bau\bul\blt\bt_\b_h\bho\boo\bok\bk
+
+ Type: string
+ Default: "~f %s | (~P (~c %s | ~t %s))"
+
+ This variable controls how send-hooks, save-hooks, and fcc-hooks will
+ be interpreted if they are specified with only a simple regexp,
+ instead of a matching pattern. The hooks are expanded when they are
+ declared, so a hook will be interpreted according to the value of this
+ variable at the time the hook is declared. The default value matches
+ if the message is either from a user matching the regular expression
+ given, or if it is from you (if the from address matches
+ ``alternates'') and is to or cc'ed to a user matching the given
+ regular expression.
+
+ 6\b6.\b.3\b3.\b.2\b25\b5.\b. d\bde\bel\ble\bet\bte\be
+
+ Type: quadoption
+ Default: ask-yes
+
+ Controls whether or not messages are really deleted when closing or
+ synchronizing a mailbox. If set to _\by_\be_\bs, messages marked for deleting
+ will automatically be purged without prompting. If set to _\bn_\bo,
+ messages marked for deletion will be kept in the mailbox.
+
+ 6\b6.\b.3\b3.\b.2\b26\b6.\b. d\bde\bel\ble\bet\bte\be_\b_f\bfo\bor\brm\bma\bat\bt
+
+ Type: string
+ Default: "[-- Attachment from %u deleted on %<%D> --]"
+
+ This variable controls the format of the message used to replace an
+ attachment when the attachment is deleted. It uses the same format
+ sequences as the ``$header_format'' variable.
+
+ 6\b6.\b.3\b3.\b.2\b27\b7.\b. d\bds\bsn\bn_\b_n\bno\bot\bti\bif\bfy\by
+
+ Type: string
+ Default: none
+
+ N\bNo\bot\bte\be:\b: you should not enable this unless you are using Sendmail 8.8.x
+ or greater.
+
+ This variable sets the request for when notification is returned. The
+ string consists of a comma separated list (no spaces!) of one or more
+ of the following: _\bn_\be_\bv_\be_\br, to never request notification, _\bf_\ba_\bi_\bl_\bu_\br_\be, to
+ request notification on transmission failure, _\bd_\be_\bl_\ba_\by, to be notified of
+ message delays, _\bs_\bu_\bc_\bc_\be_\bs_\bs, to be notified of successful transmission.
+
+ Example: set dsn_notify="failure,delay"
+
+ 6\b6.\b.3\b3.\b.2\b28\b8.\b. d\bds\bsn\bn_\b_r\bre\bet\btu\bur\brn\bn
+
+ Type: string Default: none
+
+ N\bNo\bot\bte\be:\b: you should not enable this unless you are using Sendmail 8.8.x
+ or greater.
+
+ This variable controls how much of your message is returned in DSN
+ messages. It may be set to either _\bh_\bd_\br_\bs to return just the message
+ header, or _\bf_\bu_\bl_\bl to return the full message.
+
+ Example: set dsn_return=hdrs
+
+ 6\b6.\b.3\b3.\b.2\b29\b9.\b. e\bed\bdi\bit\bt_\b_h\bhe\bea\bad\bde\ber\brs\bs
+
+ Type: boolean
+ Default: unset
+
+ This option allows you to edit the header of your outgoing messages
+ along with the body of your message.
+
+ 6\b6.\b.3\b3.\b.3\b30\b0.\b. e\bed\bdi\bit\bto\bor\br
+
+ Type: String
+ Default: value of environment variable $VISUAL, $EDITOR, or "vi"
+
+ This variable specifies which editor to use when composing messages.
+
+ 6\b6.\b.3\b3.\b.3\b31\b1.\b. e\bes\bsc\bca\bap\bpe\be
+
+ Type: string
+ Default: ~
+
+ Escape character to use for functions in the builtin editor.
+
+ 6\b6.\b.3\b3.\b.3\b32\b2.\b. f\bfa\bas\bst\bt_\b_r\bre\bep\bpl\bly\by
+
+ Type: boolean
+ Default: unset
+
+ When set, the initial prompt for recipients and subject are skipped
+ when replying to messages, and the initial prompt for subject is
+ skipped when forwarding messages.
+
+ N\bNo\bot\bte\be:\b: this variable has no effect when the ``$autoedit'' variable is
+ set.
+
+ 6\b6.\b.3\b3.\b.3\b33\b3.\b. f\bfc\bcc\bc_\b_a\bat\btt\bta\bac\bch\bh
+
+ Type: boolean
+ Default: set
+
+ This variable controls whether or not attachments on outgoing messages
+ are saved along with the main body of your message.
+
+ 6\b6.\b.3\b3.\b.3\b34\b4.\b. f\bfo\bol\bld\bde\ber\br
+
+ Type: String
+ Default: ~/Mail
+
+ Specifies the default location of your mailboxes. A `+' or `=' at the
+ beginning of a pathname will be expanded to the value of this
+ variable. Note that if you change this variable from the default
+ value you need to make sure that the assignment occurs _\bb_\be_\bf_\bo_\br_\be you use
+ `+' or `=' for any other variables since expansion takes place during
+ the `set' command.
+
+ 6\b6.\b.3\b3.\b.3\b35\b5.\b. f\bfo\bol\bll\blo\bow\bwu\bup\bp_\b_t\bto\bo
+
+ Type: boolean
+ Default: set
+
+ Controls whether or not the _\bM_\ba_\bi_\bl_\b-_\bF_\bo_\bl_\bl_\bo_\bw_\bu_\bp_\b-_\bT_\bo header field is generated
+ when sending mail. When _\bs_\be_\bt, Mutt will generate this field when you
+ are replying to a known mailing ``lists''.
+
+ The purpose of this field is to prevent you from receiving duplicate
+ copies of replies to messages which you send by specifying that you
+ will receive a copy of the message if it is addressed to the mailing
+ list (and thus there is no need to also include your address in a
+ group reply).
+
+ 6\b6.\b.3\b3.\b.3\b36\b6.\b. f\bfo\bor\brc\bce\be_\b_n\bna\bam\bme\be
+
+ Type: boolean
+ Default: unset
+
+ This variable is similar to ``$save_name'', except that Mutt will
+ store a copy of your outgoing message by the username of the address
+ you are sending to even if that mailbox does not exist.
+
+ Also see the ``$record'' variable.
+
+ 6\b6.\b.3\b3.\b.3\b37\b7.\b. f\bfo\bor\brw\bwa\bar\brd\bd_\b_d\bde\bec\bco\bod\bde\be
+
+ Type: boolean
+ Default: unset
+
+ Controls the decoding of complex MIME messages into text/plain when
+ forwarding a message. If ``mime_forward'' is _\bu_\bn_\bs_\be_\bt, then the message
+ header is also RFC2047 decoded (this cannot be done when forwarding a
+ message as a message/rfc822 attachment because it would violate the
+ MIME spec, which states that you must only use US-ASCII in the
+ header).
+
+ 6\b6.\b.3\b3.\b.3\b38\b8.\b. f\bfo\bor\brw\bwa\bar\brd\bd_\b_f\bfo\bor\brm\bma\bat\bt
+
+ Type: format string
+ Default: "[%a: %s]"
+
+ This variable controls the default subject when forwarding a message.
+ It uses the same format sequences as the ``$header_format'' variable.
+
+ 6\b6.\b.3\b3.\b.3\b39\b9.\b. f\bfo\bor\brw\bwa\bar\brd\bd_\b_q\bqu\buo\bot\bte\be
+
+ Type: boolean
+ Default: unset
+
+ When _\bs_\be_\bt forwarded messages included in the main body of the message
+ (when ``mime_forward'' is _\bu_\bn_\bs_\be_\bt) will be quoted using
+ ``indent_string''.
+
+ 6\b6.\b.3\b3.\b.4\b40\b0.\b. h\bhe\bea\bad\bde\ber\br_\b_f\bfo\bor\brm\bma\bat\bt
+
+ Type: format string
+ Default: "%4C %Z %{%b %d} %-15.15L (%4l) %s"
+
+ This variable allows you to customize the message index display to
+ your personal taste.
+
+ ``Format strings'' are similar to the strings used in the ``C''
+ function printf to format output (see the man page for more detail).
+ The following sequences are defined in Mutt:
+
+ %a address of the author
+ %b filename of the original message folder (think mailBox)
+ %B the list to which the letter was sent, or else the folder
+ name (%b).
+ %c number of characters (bytes) in the message
+ %C current message number
+ %d date and time of the message in the format specified by
+ ``date_format''
+ %f entire From: line (address + real name)
+ %F author name, or recipient name if the message is from you
+ %i message-id of the current message
+ %l number of lines in the message
+ %L list-from function
+ %m total number of message in the mailbox
+ %N message score
+ %n author's real name (or address if missing)
+ %O (_O_riginal save folder) Where mutt would formerly have
+ stashed the message: list name or recipient name if no list
+ %s subject of the message
+ %S status of the message (N/D/d/!/*/r)
+ %t `to:' field (recipients)
+ %T the appropriate character from the $to_chars string
+ %u user (login) name of the author
+ %Z message status flags
+
+ %{fmt} the date and time of the message is converted to sender's
+ time zone, and ``fmt'' is expanded by the system call
+ ``strftime''; a leading bang disables locales
+ %[fmt] the date and time of the message is converted to the local
+ time zone, and ``fmt'' is expanded by the system call
+ ``strftime''; a leading bang disables locales
+ %(fmt) the local date and time when the message was received.
+ ``fmt'' is expanded by the system call ``strftime'';
+ a leading bang disables locales
+ %<fmt> the current local time. ``fmt'' is expanded by the system
+ call ``strftime''; a leading bang disables locales.
+
+ %>X right justify the rest of the string and pad with character "X"
+ %|X pad to the end of the line with character "X"
+
+ See also: ``$to_chars''.
+
+ 6\b6.\b.3\b3.\b.4\b41\b1.\b. h\bhd\bdr\brs\bs
+
+ Type: boolean
+ Default: set
+
+ When unset, the header fields normally added by the ``my_hdr'' command
+ are not created. This variable _\bm_\bu_\bs_\bt be unset before composing a new
+ message or replying in order to take effect. If set, the user defined
+ header fields are added to every new message.
+
+ 6\b6.\b.3\b3.\b.4\b42\b2.\b. h\bhe\bea\bad\bde\ber\br
+
+ Type: boolean
+ Default: unset
+
+ When set, this variable causes Mutt to include the _\bf_\bu_\bl_\bl header of the
+ message you are replying to into the edit buffer.
+
+ 6\b6.\b.3\b3.\b.4\b43\b3.\b. h\bhe\bel\blp\bp
+
+ Type: boolean
+ Default: set
+
+ When set, help lines describing the bindings for the major functions
+ provided by each menu are displayed on the first line of the screen.
+
+ N\bNo\bot\bte\be:\b: The binding will not be displayed correctly if the function is
+ bound to a sequence rather than a single keystroke. Also, the help
+ line may not be updated if a binding is changed while Mutt is running.
+ Since this variable is primarily aimed at new users, neither of these
+ should present a major problem.
+
+ 6\b6.\b.3\b3.\b.4\b44\b4.\b. h\bhi\bis\bst\bto\bor\bry\by
+
+ Type: number
+ Default: 10
+
+ This variable controls the size (in number of strings remembered) of
+ the string history buffer. The buffer is cleared each time the
+ variable is set.
+
+ 6\b6.\b.3\b3.\b.4\b45\b5.\b. h\bho\bos\bst\btn\bna\bam\bme\be
+
+ Type: string
+ Default: varies
+
+ Specifies the hostname to use after the ``@'' in local e-mail
+ addresses. This overrides the compile time definition obtained from
+ /etc/resolv.conf.
+
+ 6\b6.\b.3\b3.\b.4\b46\b6.\b. i\big\bgn\bno\bor\bre\be_\b_l\bli\bis\bst\bt_\b_r\bre\bep\bpl\bly\by_\b_t\bto\bo
+
+ Type: boolean
+ Default: unset
+
+ Affects the behaviour of the _\br_\be_\bp_\bl_\by function when replying to messages
+ from mailing lists. When set, if the ``Reply-To:'' field is set to
+ the same value as the ``To:'' field, Mutt assumes that the ``Reply-
+ To:'' field was set by the mailing list to automate responses to the
+ list, and will ignore this field. To direct a response to the mailing
+ list when this option is set, use the _\bl_\bi_\bs_\bt_\b-_\br_\be_\bp_\bl_\by function; _\bg_\br_\bo_\bu_\bp_\b-_\br_\be_\bp_\bl_\by
+ will reply to both the sender and the list.
+
+ 6\b6.\b.3\b3.\b.4\b47\b7.\b. i\bin\bn_\b_r\bre\bep\bpl\bly\by_\b_t\bto\bo
+
+ Type: format string
+ Default: "%i; from \"%n\" on %{!%a, %b %d, %Y at %I:%M:%S%p}"
+
+ This specifies the format of the In-Reply-To: header field added when
+ replying to a message. For a full listing of defined escape sequences
+ see the section on ``$header_format''.
+
+ 6\b6.\b.3\b3.\b.4\b48\b8.\b. i\bin\bnc\bcl\blu\bud\bde\be
+
+ Type: quadoption
+ Default: ask-yes
+
+ Controls whether or not a copy of the message(s) you are replying to
+ is included in your reply.
+
+ 6\b6.\b.3\b3.\b.4\b49\b9.\b. i\bin\bnd\bde\ben\bnt\bt_\b_s\bst\btr\bri\bin\bng\bg
+
+ Type: format string
+ Default: "> "
+
+ Specifies the string to prepend to each line of text quoted in a
+ message to which you are replying. You are strongly encouraged not to
+ change this value, as it tends to agitate the more fanatical netizens.
+
+ 6\b6.\b.3\b3.\b.5\b50\b0.\b. i\bis\bsp\bpe\bel\bll\bl
+
+ Type: string
+ Default: "ispell"
+
+ How to invoke ispell (GNU's spell-checking software).
+
+ 6\b6.\b.3\b3.\b.5\b51\b1.\b. l\blo\boc\bca\bal\ble\be
+
+ Type: string
+ Default: "C"
+
+ The locale used by _\bs_\bt_\br_\bf_\bt_\bi_\bm_\be_\b(_\b3_\b) to format dates. Legal values are the
+ strings your system accepts for the locale variable _\bL_\bC_\b__\bT_\bI_\bM_\bE.
+
+ 6\b6.\b.3\b3.\b.5\b52\b2.\b. m\bma\bai\bil\blc\bca\bap\bp_\b_p\bpa\bat\bth\bh
+
+ Type: string
+ Default: $MAILCAPS or
+ ~/.mailcap:/usr/local/share/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap
+
+ This variable specifies which files to consult when attempting to
+ display MIME bodies not directly supported by Mutt.
+
+ 6\b6.\b.3\b3.\b.5\b53\b3.\b. m\bma\bar\brk\bk_\b_o\bol\bld\bd
+
+ Type: Boolean
+ Default: set
+
+ Controls whether or not Mutt makes the distinction between _\bn_\be_\bw
+ messages and _\bo_\bl_\bd u\bun\bnr\bre\bea\bad\bd messages. By default, Mutt will mark new
+ messages as old if you exit a mailbox without reading them. The next
+ time you start Mutt, the messages will show up with an "O" next to
+ them in the index menu, indicating that they are old. In order to
+ make Mutt treat all unread messages as new only, you can unset this
+ variable.
+
+ 6\b6.\b.3\b3.\b.5\b54\b4.\b. m\bma\bar\brk\bke\ber\brs\bs
+
+ Type: boolean
+ Default: set
+
+ Controls the display of wrapped lines in the internal pager. If set, a
+ ``+'' marker is displayed at the beginning of wrapped lines. Also see
+ the ``$smart_wrap'' variable.
+
+ 6\b6.\b.3\b3.\b.5\b55\b5.\b. m\bma\bas\bsk\bk
+
+ Type: string
+ Default: "^(\.\.$|[^.])"
+
+ A regular expression used in the file browser. Files whose names don't
+ match this mask will not be shown.
+
+ 6\b6.\b.3\b3.\b.5\b56\b6.\b. m\bmb\bbo\box\bx
+
+ Type: String
+ Default: +inbox
+
+ This specifies the folder into which read mail in your ``spoolfile''
+ folder will be appended.
+
+ 6\b6.\b.3\b3.\b.5\b57\b7.\b. m\bmb\bbo\box\bx_\b_t\bty\byp\bpe\be
+
+ Type: String
+ Default: mbox
+
+ The default mailbox type used when creating new folders. May be any of
+ mbox, MMDF, MH and Maildir.
+
+ 6\b6.\b.3\b3.\b.5\b58\b8.\b. m\bme\bet\bto\boo\bo
+
+ Type: boolean
+ Default: unset
+
+ If unset, Mutt will remove your address from the list of recipients
+ when replying to a message. If you are replying to a message sent by
+ you, Mutt will also assume that you want to reply to the recipients of
+ that message rather than to yourself.
+
+ 6\b6.\b.3\b3.\b.5\b59\b9.\b. m\bme\ben\bnu\bu_\b_s\bsc\bcr\bro\bol\bll\bl
+
+ Type: boolean
+ Default: unset
+
+ When _\bs_\be_\bt, menus will be scrolled up or down one line when you attempt
+ to move across a screen boundary. If _\bu_\bn_\bs_\be_\bt, the screen is cleared and
+ the next or previous page of the menu is displayed (useful for slow
+ links to avoid many redraws).
+
+ 6\b6.\b.3\b3.\b.6\b60\b0.\b. m\bme\bet\bta\ba_\b_k\bke\bey\by
+
+ Type: Boolean
+ Default: unset
+
+ If set, forces Mutt to interpret keystrokes with the high bit (bit 8)
+ set as if the user had pressed the ESC key and whatever key remains
+ after having the high bit removed. For example, if the key pressed
+ has an ASCII value of 0xf4, then this is treated as if the user had
+ pressed ESC then ``x''. This is because the result of removing the
+ high bit from ``0xf4'' is ``0x74'', which is the ASCII character
+ ``x''.
+ 6\b6.\b.3\b3.\b.6\b61\b1.\b. m\bmi\bim\bme\be_\b_f\bfo\bor\brw\bwa\bar\brd\bd
+
+ Type: boolean
+ Default: unset
+
+ When set, the message you are forwarding will be attached as a
+ separate MIME part instead of included in the main body of the
+ message. This is useful for forwarding MIME messages so the receiver
+ can properly view the message as it was delivered to you.
+
+ Also see ``forward_decode''.
+
+ 6\b6.\b.3\b3.\b.6\b62\b2.\b. m\bmo\bov\bve\be
+
+ Type: quadoption
+ Default: ask-no
+
+ Controls whether you will be asked to confirm moving read messages
+ from your spool mailbox to your ``$mbox'' mailbox, or as a result of a
+ ``mbox-hook'' command.
+
+ 6\b6.\b.3\b3.\b.6\b63\b3.\b. m\bme\bes\bss\bsa\bag\bge\be_\b_f\bfo\bor\brm\bma\bat\bt
+
+ Type: string
+ Default: "%s"
+
+ This is the string displayed in the ``attachment'' menu for
+ attachments of type _\bm_\be_\bs_\bs_\ba_\bg_\be_\b/_\br_\bf_\bc_\b8_\b2_\b2. For a full listing of defined
+ escape sequences see the section on ``header_format''.
+
+ 6\b6.\b.3\b3.\b.6\b64\b4.\b. p\bpa\bag\bge\ber\br
+
+ Type: string
+ Default: builtin
+
+ This variable specifies which pager you would like to use to view
+ messages. builtin means to use the builtin pager, otherwise this
+ variable should specify the pathname of the external pager you would
+ like to use.
+
+ 6\b6.\b.3\b3.\b.6\b65\b5.\b. p\bpa\bag\bge\ber\br_\b_c\bco\bon\bnt\bte\bex\bxt\bt
+
+ Type: number
+ Default: 0
+
+ This variable controls the number of lines of context that are given
+ when displaying the next or previous page in the internal pager. By
+ default, Mutt will display the line after the last one on the screen
+ at the top of the next page (0 lines of context).
+
+ 6\b6.\b.3\b3.\b.6\b66\b6.\b. p\bpa\bag\bge\ber\br_\b_f\bfo\bor\brm\bma\bat\bt
+
+ Type: format string
+ Default: "-%S- %C/%m: %-20.20n %s"
+
+ This variable controls the format of the one-line message ``status''
+ displayed before each message in either the internal or an external
+ pager. The valid sequences are listed in the ``header_format''
+ section.
+
+ 6\b6.\b.3\b3.\b.6\b67\b7.\b. p\bpa\bag\bge\ber\br_\b_i\bin\bnd\bde\bex\bx_\b_l\bli\bin\bne\bes\bs
+
+ Type: number
+ Default: 0
+
+ Determines the number of lines of a mini-index which is shown when in
+ the pager. The current message, unless near the top or bottom of the
+ folder, will be roughly one third of the way down this mini-index,
+ giving the reader the context of a few messages before and after the
+ message. This is useful, for example, to determine how many messages
+ remain to be read in the current thread. One of the lines is reserved
+ for the status bar from the index, so a _\bp_\ba_\bg_\be_\br_\b__\bi_\bn_\bd_\be_\bx_\b__\bl_\bi_\bn_\be_\bs of 6 will
+ only show 5 lines of the actual index. A value of 0 results in no
+ index being shown. If the number of messages in the current folder is
+ less than _\bp_\ba_\bg_\be_\br_\b__\bi_\bn_\bd_\be_\bx_\b__\bl_\bi_\bn_\be_\bs, then the index will only use as many
+ lines as it needs.
+
+ 6\b6.\b.3\b3.\b.6\b68\b8.\b. p\bpa\bag\bge\ber\br_\b_s\bst\bto\bop\bp
+
+ Type: boolean
+ Default: unset
+
+ When set, the internal-pager will n\bno\bot\bt move to the next message when
+ you are at the end of a message and invoke the _\bn_\be_\bx_\bt_\b-_\bp_\ba_\bg_\be function.
+
+ 6\b6.\b.3\b3.\b.6\b69\b9.\b. p\bpg\bgp\bp_\b_a\bau\but\bto\boe\ben\bnc\bcr\bry\byp\bpt\bt
+
+ Type: boolean
+ Default: unset
+
+ Setting this variable will cause Mutt to always attempt to PGP/MIME
+ encrypt outgoing messages. This is probably only useful in connection
+ to the _\bs_\be_\bn_\bd_\b-_\bh_\bo_\bo_\bk command. It can be overridden by use of the _\bp_\bg_\bp_\b-
+ _\bm_\be_\bn_\bu, when encryption is not required or signing is requested as well.
+
+ 6\b6.\b.3\b3.\b.7\b70\b0.\b. p\bpg\bgp\bp_\b_a\bau\but\bto\bos\bsi\big\bgn\bn
+
+ Type: boolean
+ Default: unset
+
+ Setting this variable will cause Mutt to always attempt to PGP/MIME
+ sign outgoing messages. This can be overridden by use of the _\bp_\bg_\bp_\b-
+ _\bm_\be_\bn_\bu, when signing is not required or encryption is requested as well.
+
+ 6\b6.\b.3\b3.\b.7\b71\b1.\b. p\bpg\bgp\bp_\b_d\bde\bef\bfa\bau\bul\blt\bt_\b_v\bve\ber\brs\bsi\bio\bon\bn
+
+ Type: string
+ Default: pgp2 (or pgp5, if PGP 2.* is not installed)
+
+ Set this to pgp2 (PGP 2.*), or pgp5 (PGP 5.*) depending on the
+ version, you are using primary. This variable is directly used, but it
+ is the default for the variables ``$pgp_receive_version'',
+ ``$pgp_send_version'', and ``$pgp_key_version''.
+
+ 6\b6.\b.3\b3.\b.7\b72\b2.\b. p\bpg\bgp\bp_\b_e\ben\bnc\bcr\bry\byp\bpt\bts\bse\bel\blf\bf
+
+ Type: boolean
+ Default: set
+
+ If set, the PGP _\b+_\be_\bn_\bc_\br_\by_\bp_\bt_\bt_\bo_\bs_\be_\bl_\bf flag is used when encrypting messages.
+
+ 6\b6.\b.3\b3.\b.7\b73\b3.\b. p\bpg\bgp\bp_\b_k\bke\bey\by_\b_v\bve\ber\brs\bsi\bio\bon\bn
+
+ Type: string
+ Default: ``default''
+
+ This variable determines, which PGP version used for key ring
+ operations like extracting keys from messages and extracting keys from
+ your keyring. If you set this to default, the default defined in
+ ``$pgp_default_version'' is used. Set this to pgp2 (PGP 2.*), or pgp5
+ (PGP 5.*) if you want a different PGP version for key operations.
+
+ 6\b6.\b.3\b3.\b.7\b74\b4.\b. p\bpg\bgp\bp_\b_l\blo\bon\bng\bg_\b_i\bid\bds\bs
+
+ Type: boolean
+ Default: unset
+
+ If set, use 64 bit PGP key IDs. Unset uses the normal 32 bit Key IDs.
+
+ 6\b6.\b.3\b3.\b.7\b75\b5.\b. p\bpg\bgp\bp_\b_r\bre\bec\bce\bei\biv\bve\be_\b_v\bve\ber\brs\bsi\bio\bon\bn
+
+ Type: string
+ Default: ``default''
+
+ This variable determines, which PGP version used for decrypting
+ messages and verifying signatures. If you set this to default, the
+ default defined in ``$pgp_default_version'' will be used. Set this to
+ pgp2 (PGP 2.*), or pgp5 (PGP 5.*) if you want a different PGP version
+ for receiving operations.
+
+ 6\b6.\b.3\b3.\b.7\b76\b6.\b. p\bpg\bgp\bp_\b_r\bre\bep\bpl\bly\bye\ben\bnc\bcr\bry\byp\bpt\bt
+
+ Type: boolean
+ Default: unset
+
+ If set, automatically PGP encrypt replies to messages which are
+ encrypted.
+
+ 6\b6.\b.3\b3.\b.7\b77\b7.\b. p\bpg\bgp\bp_\b_r\bre\bep\bpl\bly\bys\bsi\big\bgn\bn
+
+ Type: boolean
+ Default: unset
+
+ If set, automatically PGP sign replies to messages which are signed.
+
+ N\bNo\bot\bte\be:\b: this does not work on messages, that are encrypted a\ban\bnd\bd signed!
+
+ 6\b6.\b.3\b3.\b.7\b78\b8.\b. p\bpg\bgp\bp_\b_s\bse\ben\bnd\bd_\b_v\bve\ber\brs\bsi\bio\bon\bn
+
+ Type: string
+ Default: ``default''
+
+ This variable determines, which PGP version used for composing new
+ messages like encrypting and signing. If you set this to default, the
+ default defined in ``$pgp_default_version'' will be used. Set this to
+ pgp2 (PGP 2.*), or pgp5 (PGP 5.*) if you want a different PGP version
+ for sending operations.
+
+ 6\b6.\b.3\b3.\b.7\b79\b9.\b. p\bpg\bgp\bp_\b_s\bsi\big\bgn\bn_\b_a\bas\bs
+
+ Type: string
+ Default: unset
+
+ If you have more than one key pair, this option allows you to specify
+ which of your private keys to use. It is recommended that you use the
+ keyid form to specify your key (e.g., ``0xABCDEFGH'').
+
+ 6\b6.\b.3\b3.\b.8\b80\b0.\b. p\bpg\bgp\bp_\b_s\bsi\big\bgn\bn_\b_m\bmi\bic\bca\bal\blg\bg
+
+ Type: string
+ Default: pgp-md5
+
+ This variable contains the default message integrity check algorithm.
+ Valid values are ``pgp-md5'', ``pgp-sha1'', and ``pgp-rmd160''. If you
+ select a signing key using the sign as option on the compose menu,
+ mutt will automagically figure out the correct value to insert here,
+ but it does not know about the user's default key.
+
+ So if you are using an RSA key for signing, set this variable to
+ ``pgp-md5'', if you use a PGP 5 DSS key for signing, say ``pgp-sha1''
+ here. The value of this variable will show up in the micalg parameter
+ of MIME headers when creating RFC 2015 signatures.
+
+ 6\b6.\b.3\b3.\b.8\b81\b1.\b. p\bpg\bgp\bp_\b_s\bst\btr\bri\bic\bct\bt_\b_e\ben\bnc\bc
+
+ Type: boolean
+ Default: set
+
+ If set, Mutt will automatically encode PGP/MIME signed messages as
+ _\bq_\bu_\bo_\bt_\be_\bd_\b-_\bp_\br_\bi_\bn_\bt_\ba_\bb_\bl_\be. Please note that unsetting this variable may lead
+ to problems with non-verifyable PGP signatures, so only change this if
+ you know what you are doing.
+
+ 6\b6.\b.3\b3.\b.8\b82\b2.\b. p\bpg\bgp\bp_\b_t\bti\bim\bme\beo\bou\but\bt
+
+ Type: number
+ Default: 300
+
+ The number of seconds after which a cached passphrase will expire if
+ not used.
+
+ 6\b6.\b.3\b3.\b.8\b83\b3.\b. p\bpg\bgp\bp_\b_v\bv2\b2
+
+ Type: string
+ Default: system dependent
+
+ This variable allows you to override the compile time definition of
+ where the PGP 2.* binary resides on your system.
+
+ 6\b6.\b.3\b3.\b.8\b84\b4.\b. p\bpg\bgp\bp_\b_v\bv2\b2_\b_l\bla\ban\bng\bgu\bua\bag\bge\be
+
+ Type: string
+ Default: en
+
+ Sets the language, which PGP 2.* should use. If you use language.txt
+ from the mutt doc directory, you can try the languages "mutt"
+ (English) or "muttde" (German) to reduce the noise produced by pgp.
+
+ 6\b6.\b.3\b3.\b.8\b85\b5.\b. p\bpg\bgp\bp_\b_v\bv2\b2_\b_p\bpu\bub\bbr\bri\bin\bng\bg
+
+ Type: string
+ Default: $PGPPATH/pubring.pgp or ~/.pgp/pubring.pgp if $PGPPATH isn't
+ set.
+
+ Points to the PGP 2.* public keyring.
+
+ 6\b6.\b.3\b3.\b.8\b86\b6.\b. p\bpg\bgp\bp_\b_v\bv2\b2_\b_s\bse\bec\bcr\bri\bin\bng\bg
+
+ Type: string
+ Default: $PGPPATH/secring.pgp or ~/.pgp/secring.pgp if $PGPPATH isn't
+ set.
+
+ Points to the PGP 2.* secret keyring.
+
+ 6\b6.\b.3\b3.\b.8\b87\b7.\b. p\bpg\bgp\bp_\b_v\bv5\b5
+
+ Type: string
+ Default: system dependent
+
+ This variable allows you to override the compile time definition of
+ where the PGP 5.* binary resides on your system.
+
+ 6\b6.\b.3\b3.\b.8\b88\b8.\b. p\bpg\bgp\bp_\b_v\bv5\b5_\b_l\bla\ban\bng\bgu\bua\bag\bge\be
+
+ Type: string
+ Default: en
+
+ Sets the language, which PGP 5.* should use. If you use language50.txt
+ from the mutt doc directory, you can try the languages "mutt"
+ (English) to reduce the noise produced by pgp.
+
+ 6\b6.\b.3\b3.\b.8\b89\b9.\b. p\bpg\bgp\bp_\b_v\bv5\b5_\b_p\bpu\bub\bbr\bri\bin\bng\bg
+
+ Type: string
+ Default: $PGPPATH/pubring.pkr or ~/.pgp/pubring.pkr if $PGPPATH isn't
+ set.
+
+ Points to the PGP 5.* public keyring.
+
+ 6\b6.\b.3\b3.\b.9\b90\b0.\b. p\bpg\bgp\bp_\b_v\bv5\b5_\b_s\bse\bec\bcr\bri\bin\bng\bg
+
+ Type: string
+ Default: $PGPPATH/secring.skr or ~/.pgp/secring.skr if $PGPPATH isn't
+ set.
+
+ Points to the PGP 5.* secret keyring.
+
+ 6\b6.\b.3\b3.\b.9\b91\b1.\b. p\bpi\bip\bpe\be_\b_d\bde\bec\bco\bod\bde\be
+
+ Type: boolean
+ Default: unset
+
+ Used in connection with the _\bp_\bi_\bp_\be_\b-_\bm_\be_\bs_\bs_\ba_\bg_\be command. When unset, Mutt
+ will pipe the messages without any preprocessing. When set, Mutt will
+ weed headers and will attempt to PGP/MIME decode the messages first.
+
+ 6\b6.\b.3\b3.\b.9\b92\b2.\b. p\bpi\bip\bpe\be_\b_s\bse\bep\bp
+
+ Type: string
+ Default: newline
+
+ The separator to add between messages when piping a list of tagged
+ messages to an external Unix command.
+
+ 6\b6.\b.3\b3.\b.9\b93\b3.\b. p\bpi\bip\bpe\be_\b_s\bsp\bpl\bli\bit\bt
+
+ Type: boolean
+ Default: unset
+
+ Used in connection with the _\bp_\bi_\bp_\be_\b-_\bm_\be_\bs_\bs_\ba_\bg_\be command and the ``tag-
+ prefix'' operator. If this variable is unset, when piping a list of
+ tagged messages Mutt will concatenate the messages and will pipe them
+ as a single folder. When set, Mutt will pipe the messages one by one.
+ In both cases the the messages are piped in the current sorted order,
+ and the ``$pipe_sep'' separator is added after each message.
+
+ 6\b6.\b.3\b3.\b.9\b94\b4.\b. p\bpo\bop\bp_\b_d\bde\bel\ble\bet\bte\be
+
+ Type: boolean
+ Default: unset
+
+ If set, Mutt will delete successfully downloaded messages from the POP
+ server when using the fetch-mail function. When unset, Mutt will
+ download messages but also leave them on the POP server.
+
+ 6\b6.\b.3\b3.\b.9\b95\b5.\b. p\bpo\bop\bp_\b_h\bho\bos\bst\bt
+
+ Type: string
+ Default: none
+
+ The name or address of your POP3 server.
+
+ 6\b6.\b.3\b3.\b.9\b96\b6.\b. p\bpo\bop\bp_\b_p\bpa\bas\bss\bs
+
+ Type: string
+ Default: unset
+
+ Specifies the password for you POP account. If unset, Mutt will
+ prompt you for your password when you invoke the fetch-mail function.
+ W\bWa\bar\brn\bni\bin\bng\bg: you should only use this option when you are on a fairly
+ secure machine, because the superuser can read your muttrc even if you
+ are the only one who can read the file.
+
+ 6\b6.\b.3\b3.\b.9\b97\b7.\b. p\bpo\bop\bp_\b_p\bpo\bor\brt\bt
+
+ Type: number
+ Default: 110
+
+ This variable specifies which port your POP server is listening on.
+
+ 6\b6.\b.3\b3.\b.9\b98\b8.\b. p\bpo\bop\bp_\b_u\bus\bse\ber\br
+
+ Type: string
+ Default: login name on local system
+
+ Your login name on the POP3 server.
+
+ 6\b6.\b.3\b3.\b.9\b99\b9.\b. p\bpo\bos\bst\bt_\b_i\bin\bnd\bde\ben\bnt\bt_\b_s\bst\btr\bri\bin\bng\bg
+
+ Type: format string
+ Default: none
+
+ Similar to the ``$attribution'' variable, Mutt will append this string
+ after the inclusion of a message which is being replied to.
+
+ 6\b6.\b.3\b3.\b.1\b10\b00\b0.\b. p\bpo\bos\bst\btp\bpo\bon\bne\be
+
+ Type: quadoption
+ Default: ask-yes
+
+ Controls whether or not messages are saved in the ``$postponed''
+ mailbox when you elect not to send immediately.
+
+ 6\b6.\b.3\b3.\b.1\b10\b01\b1.\b. p\bpo\bos\bst\btp\bpo\bon\bne\bed\bd
+
+ Type: string
+ Default: ~/postponed
+
+ Mutt allows you to indefinitely ``postpone sending a message'' which
+ you are editing. When you choose to postpone a message, Mutt saves it
+ in the folder specified by this variable. Also see the ``$postpone''
+ variable.
+
+ 6\b6.\b.3\b3.\b.1\b10\b02\b2.\b. p\bpr\bri\bin\bnt\bt
+
+ Type: quadoption
+ Default: ask-no
+
+ Controls whether or not Mutt asks for confirmation before printing.
+ This is useful for people (like me) who accidentally hit ``p'' often.
+
+ 6\b6.\b.3\b3.\b.1\b10\b03\b3.\b. p\bpr\bri\bin\bnt\bt_\b_c\bco\bom\bmm\bma\ban\bnd\bd
+
+ Type: string
+ Default: lpr
+
+ This specifies the command pipe that should be used to print messages.
+
+ 6\b6.\b.3\b3.\b.1\b10\b04\b4.\b. p\bpr\bro\bom\bmp\bpt\bt_\b_a\baf\bft\bte\ber\br
+
+ Type: boolean
+ Default: set
+
+ If you use an _\be_\bx_\bt_\be_\br_\bn_\ba_\bl ``pager'', setting this variable will cause
+ Mutt to prompt you for a command when the pager exits rather than
+ returning to the index menu. If unset, Mutt will return to the index
+ menu when the external pager exits.
+
+ 6\b6.\b.3\b3.\b.1\b10\b05\b5.\b. q\bqu\bue\ber\bry\by_\b_c\bco\bom\bmm\bma\ban\bnd\bd
+
+ Type: string
+ Default: null
+
+ This specifies the command that mutt will use to make external address
+ queries. The string should contain a %s, which will be substituted
+ with the query string the user types. See ``query'' for more
+ information.
+
+ 6\b6.\b.3\b3.\b.1\b10\b06\b6.\b. q\bqu\buo\bot\bte\be_\b_r\bre\beg\bge\bex\bxp\bp
+
+ Type: string
+ Default: "^([ \t]*[>|#:}])+"
+
+ A regular expression used in the internal-pager to determine quoted
+ sections of text in the body of a message.
+
+ N\bNo\bot\bte\be:\b: In order to use the _\bq_\bu_\bo_\bt_\be_\bdx\bx patterns in the internal pager, you
+ need to set this to a regular expression that matches _\be_\bx_\ba_\bc_\bt_\bl_\by the
+ quote characters at the beginning of quoted lines.
+
+ 6\b6.\b.3\b3.\b.1\b10\b07\b7.\b. r\bre\bea\bad\bd_\b_i\bin\bnc\bc
+
+ Type: number
+ Default: 10
+
+ If set to a value greater than 0, Mutt will display which message it
+ is currently on when reading a mailbox. The message is printed after
+ _\br_\be_\ba_\bd_\b__\bi_\bn_\bc messages have been read (e.g., if set to 25, Mutt will print
+ a message when it reads message 25, and then again when it gets to
+ message 50). This variable is meant to indicate progress when reading
+ large mailboxes which may take some time.
+
+ When set to 0, only a single message will appear before the reading
+ the mailbox.
+
+ Also see the ``$write_inc'' variable.
+
+ 6\b6.\b.3\b3.\b.1\b10\b08\b8.\b. r\bre\bea\bad\bd_\b_o\bon\bnl\bly\by
+
+ Type: boolean
+ Default: unset
+
+ If set, all folders are opened in read-only mode.
+
+ 6\b6.\b.3\b3.\b.1\b10\b09\b9.\b. r\bre\bea\bal\bln\bna\bam\bme\be
+
+ Type: string
+ Default: GCOS field from /etc/passwd
+
+ This variable specifies what "real" or "personal" name should be used
+ when sending messages.
+
+ 6\b6.\b.3\b3.\b.1\b11\b10\b0.\b. r\bre\bec\bca\bal\bll\bl
+
+ Type: quadoption
+ Default: ask-yes
+
+ Controls whether or not you are prompted to recall postponed messages
+ when composing a new message. Also see ``postponed''
+
+ 6\b6.\b.3\b3.\b.1\b11\b11\b1.\b. r\bre\bec\bco\bor\brd\bd
+
+ Type: string
+ Default: none
+
+ This specifies the file into which your outgoing messages should be
+ appended. (This is meant as the primary method for saving a copy of
+ your messages, but another way to do this is using the ``my_hdr''
+ command to create a _\bB_\bc_\bc_\b: field with your email address in it.)
+
+ The value of _\b$_\br_\be_\bc_\bo_\br_\bd is overridden by the ``$force_name'' and
+ ``$save_name'' variables, and the ``fcc-hook'' command.
+
+ 6\b6.\b.3\b3.\b.1\b11\b12\b2.\b. r\bre\bep\bpl\bly\by_\b_r\bre\beg\bge\bex\bxp\bp
+
+ Type: string
+ Default: "^(re|aw):[ \t]*"
+
+ A regular expression used to recognize reply messages when threading
+ and replying. The default value corresponds to the English "Re:" and
+ the German "Aw:".
+
+ 6\b6.\b.3\b3.\b.1\b11\b13\b3.\b. r\bre\bep\bpl\bly\by_\b_t\bto\bo
+
+ Type: quadoption
+ Default: ask-yes
+
+ If set, Mutt will ask you if you want to use the address listed in the
+ Reply-To: header field when replying to a message. If you answer no,
+ it will use the address in the From: header field instead. This
+ option is useful for reading a mailing list that sets the Reply-To:
+ header field to the list address and you want to send a private
+ message to the author of a message.
+
+ 6\b6.\b.3\b3.\b.1\b11\b14\b4.\b. r\bre\bes\bso\bol\blv\bve\be
+
+ Type: boolean
+ Default: set
+
+ When set, the cursor will be automatically advanced to the next
+ (possibly undeleted) message whenever a command that modifies the
+ current message is executed.
+
+ 6\b6.\b.3\b3.\b.1\b11\b15\b5.\b. r\bre\bev\bve\ber\brs\bse\be_\b_a\bal\bli\bia\bas\bs
+
+ Type: boolean
+ Default: unset
+
+ This variable controls whether or not Mutt will display the "personal"
+ name from your aliases in the index menu if it finds an alias that
+ matches the message's sender. For example, if you have the following
+ alias:
+
+ alias juser abd30425@somewhere.net (Joe User)
+
+ and then you receive mail which contains the following header:
+
+ From: abd30425@somewhere.net
+
+ It would be displayed in the index menu as ``Joe User'' instead of
+ ``abd30425@somewhere.net.'' This is useful when the person's e-mail
+ address is not human friendly (like Compu$erve addresses).
+
+ 6\b6.\b.3\b3.\b.1\b11\b16\b6.\b. r\bre\bev\bve\ber\brs\bse\be_\b_n\bna\bam\bme\be
+
+ Type: boolean
+ Default: unset
+
+ It may sometimes arrive that you receive mail to a certain machine,
+ move the messages to another machine, and reply to some the messages
+ from there. If this variable is set, the default _\bF_\br_\bo_\bm_\b: line of the
+ reply messages is built using the address where you received the
+ messages you are replying to. If the variable is unset, the _\bF_\br_\bo_\bm_\b:
+ line will use your address on the current machine.
+
+ 6\b6.\b.3\b3.\b.1\b11\b17\b7.\b. s\bsa\bav\bve\be_\b_a\bad\bdd\bdr\bre\bes\bss\bs
+
+ Type: boolean
+ Default: unset
+
+ If set, mutt will take the sender's full address when choosing a
+ default folder for saving a mail. If ``save_name'' or ``force_name''
+ is set too, the selection of the fcc folder will be changed as well.
+
+ 6\b6.\b.3\b3.\b.1\b11\b18\b8.\b. s\bsa\bav\bve\be_\b_e\bem\bmp\bpt\bty\by
+
+ Type: boolean
+ Default: set
+
+ When unset, mailboxes which contain no saved messages will be removed
+ when closed (the exception is ``spoolfile'' which is never removed).
+ If set, mailboxes are never removed.
+
+ N\bNo\bot\bte\be:\b: This only applies to mbox and MMDF folders, Mutt does not delete
+ MH and Maildir directories.
+
+ 6\b6.\b.3\b3.\b.1\b11\b19\b9.\b. s\bsa\bav\bve\be_\b_n\bna\bam\bme\be
+
+ Type: boolean
+ Default: unset
+
+ This variable controls how copies of outgoing messages are saved.
+ When set, a check is made to see if a mailbox specified by the
+ recipient address exists (this is done by searching for a mailbox in
+ the ``folder'' directory with the _\bu_\bs_\be_\br_\bn_\ba_\bm_\be part of the recipient
+ address). If the mailbox exists, the outgoing message will be saved
+ to that mailbox, otherwise the message is saved to the ``record''
+ mailbox.
+
+ Also see the ``$force_name'' variable.
+
+ 6\b6.\b.3\b3.\b.1\b12\b20\b0.\b. s\bse\ben\bnd\bdm\bma\bai\bil\bl
+
+ Type: string
+ Default: /usr/lib/sendmail -oi -oem
+ Specifies the program and arguments used to deliver mail sent by Mutt.
+ Mutt expects that the specified program will read the message header
+ for recipients.
+
+ 6\b6.\b.3\b3.\b.1\b12\b21\b1.\b. s\bse\ben\bnd\bdm\bma\bai\bil\bl_\b_w\bwa\bai\bit\bt
+
+ Type: number
+ Default: 0
+
+ Specifies the number of seconds to wait for the ``sendmail'' process
+ to finish before giving up and putting delivery in the background.
+
+ Mutt interprets the value of this variable as follows:
+
+ >0 number of seconds to wait for sendmail to finish before continuing
+ 0 wait forever for sendmail to finish
+ <0 always put sendmail in the background without waiting
+
+ Note that if you specify a value other than 0, the output of the child
+ process will be put in a temporary file. If there is some error, you
+ will be informed as to where to find the output.
+
+ 6\b6.\b.3\b3.\b.1\b12\b22\b2.\b. s\bsh\bhe\bel\bll\bl
+
+ Type: string
+ Default: retrieved from passwd file
+
+ Command to use when spawning a subshell.
+
+ 6\b6.\b.3\b3.\b.1\b12\b23\b3.\b. s\bsi\big\bg_\b_d\bda\bas\bsh\bhe\bes\bs
+
+ Type: boolean
+ Default: set
+
+ If set, a line containing ``-- '' will be inserted before your
+ ``signature''. It is s\bst\btr\bro\bon\bng\bgl\bly\by recommended that you not unset this
+ variable unless your ``signature'' contains just your name. The
+ reason for this is because many software packages use ``-- \n'' to
+ detect your signature. For example, Mutt has the ability to highlight
+ the signature in a different color in the builtin pager.
+
+ 6\b6.\b.3\b3.\b.1\b12\b24\b4.\b. s\bsi\big\bgn\bna\bat\btu\bur\bre\be
+
+ Type: string
+ Default: ~/.signature
+
+ Specifies the filename of your signature, which is appended to all
+ outgoing messages. If the filename ends with a pipe (``|''), it is
+ assumed that filename is a shell command and input should be read from
+ its stdout.
+
+ 6\b6.\b.3\b3.\b.1\b12\b25\b5.\b. s\bsi\bim\bmp\bpl\ble\be_\b_s\bse\bea\bar\brc\bch\bh
+
+ Type: string
+ Default: "~f %s | ~s %s"
+
+ Specifies how Mutt should expand a simple search into a real search
+ pattern. A simple search is one that does not contain any of the ~
+ operators. See ``searching'' for more information on search patterns.
+ For example, if you simply type joe at a search or limit prompt, Mutt
+ will automatically expand it to the value specified by this variable.
+ For the default value it would be:
+
+ ~f joe | ~s joe
+
+ 6\b6.\b.3\b3.\b.1\b12\b26\b6.\b. s\bsm\bma\bar\brt\bt_\b_w\bwr\bra\bap\bp
+
+ Type: boolean
+ Default: set
+
+ Controls the display of lines longer then the screen width in the
+ internal pager. If set, long lines are wrapped at a word boundary. If
+ unset, lines are simply wrapped at the screen edge. Also see the
+ ``$markers'' variable.
+
+ 6\b6.\b.3\b3.\b.1\b12\b27\b7.\b. s\bso\bor\brt\bt
+
+ Type: string
+ Default: date-sent
+
+ Specifies how to sort messages in the _\bi_\bn_\bd_\be_\bx menu. Valid values are
+
+ +\bo date-sent
+
+ +\bo date-received
+
+ +\bo from
+
+ +\bo mailbox-order (unsorted)
+
+ +\bo score
+
+ +\bo subject
+
+ +\bo threads
+
+ +\bo to
+
+ You may optionally use the reverse- prefix to specify reverse sorting
+ order (example: set sort=reverse-date-sent).
+
+ 6\b6.\b.3\b3.\b.1\b12\b28\b8.\b. s\bso\bor\brt\bt_\b_a\bal\bli\bia\bas\bs
+
+ Type: string
+ Default: alias
+
+ Specifies how the entries in the `alias' menu are sorted. The
+ following are legal values:
+
+ alias sort alphabetically by alias name
+ address sort alphabetically by email address
+ unsorted leave in order specified in .muttrc
+
+ 6\b6.\b.3\b3.\b.1\b12\b29\b9.\b. s\bso\bor\brt\bt_\b_a\bau\bux\bx
+
+ Type: string
+ Default: date-sent
+
+ When sorting by threads, this variable controls how threads are sorted
+ in relation to other threads, and how the branches of the thread trees
+ are sorted. This can be set to any value that ``sort'' can, except
+ threads (in that case, mutt will just use date-sent). You can also
+ specify the last- prefix in addition to the reverse- prefix, but last-
+ must come after reverse-. The last- prefix causes messages to be
+ sorted against its siblings by which has the last descendant, using
+ the rest of sort_aux as an ordering. For instance, set sort_aux=last-
+ date-received would mean that if a new message is received in a
+ thread, that thread becomes the last one displayed (or the first, if
+ you have set sort=reverse-threads.)
+
+ 6\b6.\b.3\b3.\b.1\b13\b30\b0.\b. s\bso\bor\brt\bt_\b_b\bbr\bro\bow\bws\bse\ber\br
+
+ Type: string
+
+ Specifies how to sort entries in the file browser. By default, the
+ entries are sorted alphabetically. Valid values:
+
+ +\bo date
+
+ +\bo alpha (alphabetically)
+
+ You may optionally use the reverse- prefix to specify reverse sorting
+ order (example: set sort_browser=reverse-date).
+
+ 6\b6.\b.3\b3.\b.1\b13\b31\b1.\b. s\bsp\bpo\boo\bol\blf\bfi\bil\ble\be
+
+ Type: string
+ Default: most likely /var/mail/$USER or /usr/spool/mail/$USER
+
+ If your spool mailbox is in a non-default place where Mutt cannot find
+ it, you can specify its location with this variable. Mutt will
+ automatically set this variable to the value of the environment
+ variable $MAIL if it is not set.
+
+ 6\b6.\b.3\b3.\b.1\b13\b32\b2.\b. s\bso\bor\brt\bt_\b_r\bre\be
+
+ Type: boolean Default: set
+
+ This variable is only useful when sorting by threads with
+ ``strict_threads'' unset. In that case, it changes the heuristic mutt
+ uses to thread messages by subject. With sort_re set, mutt will only
+ attach a message as the child of another message by subject if the
+ subject of the child message starts with a substring matching the
+ setting of ``reply_regexp''. With sort_re unset, mutt will attach the
+ message whether or not this is the case, as long as the
+ non-``reply_regexp'' parts of both messages are identical.
+
+ 6\b6.\b.3\b3.\b.1\b13\b33\b3.\b. s\bst\bta\bat\btu\bus\bs_\b_c\bch\bha\bar\brs\bs
+
+ Type: string
+ Default: "-*%"
+
+ Controls the characters used by the "%r" indicator in
+ ``status_format''. The first character is used when the mailbox is
+ unchanged. The second is used when the mailbox has been changed, and
+ it needs to be resynchronized. The third is used if the mailbox is in
+ read-only mode, or if the mailbox will not be written when exiting
+ that mailbox (You can toggle whether to write changes to a mailbox
+ with the toggle-write operation, bound by default to "%").
+ 6\b6.\b.3\b3.\b.1\b13\b34\b4.\b. s\bst\bta\bat\btu\bus\bs_\b_f\bfo\bor\brm\bma\bat\bt
+
+ Type: string
+ Default: "-%r-Mutt: %f [Msgs:%?M?%M/?%m%?n? New:%n?%?o? Old:%o?%?d?
+ Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b? %?l?
+ %l?]---(%s/%S)-%>-(%P)---"
+
+ Controls the format of the status line displayed in the _\bi_\bn_\bd_\be_\bx menu.
+ This string is similar to ``$header_format'', but has its own set of
+ printf()-like sequences:
+
+ %b number of mailboxes with new mail *
+ %d number of deleted messages *
+ %h local hostname
+ %f the full pathname of the current mailbox
+ %F number of flagged messages *
+ %l size (in bytes) of the current mailbox *
+ %L size (in bytes) of the messages shown (i.e., which match the current limit) *
+ %m the number of messages in the mailbox *
+ %M the number of messages shown (i.e., which match the current limit) *
+ %n number of new messages in the mailbox *
+ %o number of old unread messages
+ %p number of postponed messages *
+ %P percentage of the way through the index
+ %r modified/read-only/won't-write indicator, according to $status_chars
+ %s current sorting mode ($sort)
+ %S current aux sorting method ($sort_aux)
+ %t number of tagged messages *
+ %u number of unread messages *
+ %v Mutt version string
+
+ %>X right justify the rest of the string and pad with "X"
+ %|X pad to the end of the line with "X"
+
+ * = can be optionally printed if nonzero
+
+ Some of the above sequences can be used to optionally print a string
+ if their value is nonzero. For example, you may only want to see the
+ number of flagged messages if such messages exist, since zero is not
+ particularly meaningful. To optionally print a string based upon one
+ of the above sequences, the following construct is used
+
+ %?<sequence_char>?<optional_string>?
+
+ where _\bs_\be_\bq_\bu_\be_\bn_\bc_\be_\b__\bc_\bh_\ba_\br is a character from the table above, and
+ _\bo_\bp_\bt_\bi_\bo_\bn_\ba_\bl_\b__\bs_\bt_\br_\bi_\bn_\bg is the string you would like printed if _\bs_\bt_\ba_\bt_\bu_\bs_\b__\bc_\bh_\ba_\br is
+ nonzero. _\bo_\bp_\bt_\bi_\bo_\bn_\ba_\bl_\b__\bs_\bt_\br_\bi_\bn_\bg m\bma\bay\by contain other sequence as well as normal
+ text, but you may n\bno\bot\bt nest optional strings.
+
+ Here is an example illustrating how to optionally print the number of
+ new messages in a mailbox:
+
+ %?n?%n new messages.?
+
+ 6\b6.\b.3\b3.\b.1\b13\b35\b5.\b. s\bst\bta\bat\btu\bus\bs_\b_o\bon\bn_\b_t\bto\bop\bp
+
+ Type: boolean
+ Default: unset
+
+ Setting this variable causes the ``status bar'' to be displayed on the
+ first line of the screen rather than near the bottom.
+
+ 6\b6.\b.3\b3.\b.1\b13\b36\b6.\b. s\bst\btr\bri\bic\bct\bt_\b_t\bth\bhr\bre\bea\bad\bds\bs
+
+ Type: boolean
+ Default: unset
+
+ If set, threading will only make use of the ``In-Reply-To'' and
+ ``References'' fields when ``sorting'' by message threads. By
+ default, messages with the same subject are grouped together in
+ ``pseudo threads.'' This may not always be desirable, such as in a
+ personal mailbox where you might have several unrelated messages with
+ the subject ``hi'' which will get grouped together.
+
+ 6\b6.\b.3\b3.\b.1\b13\b37\b7.\b. s\bsu\bus\bsp\bpe\ben\bnd\bd
+
+ Type: boolean
+ Default: set
+
+ When _\bu_\bn_\bs_\be_\bt, mutt won't stop when the user presses the terminal's _\bs_\bu_\bs_\bp
+ key, usually ``control-Z''. This is useful if you run mutt inside an
+ xterm using a command like xterm -e mutt.
+
+ 6\b6.\b.3\b3.\b.1\b13\b38\b8.\b. t\bth\bho\bor\bro\bou\bug\bgh\bh_\b_s\bse\bea\bar\brc\bch\bh
+
+ Type: boolean
+ Default: unset
+
+ Affects the _\b~_\bb and _\b~_\bh search operations described in section
+ ``Searching'' above. If set, the headers and attachments of messages
+ to be searched are decoded before searching. If unset, messages are
+ searched as they appear in the folder.
+
+ 6\b6.\b.3\b3.\b.1\b13\b39\b9.\b. t\bti\bil\bld\bde\be
+
+ Type: boolean
+ Default: unset
+
+ When set, the internal-pager will pad blank lines to the bottom of the
+ screen with a tilde (~).
+
+ 6\b6.\b.3\b3.\b.1\b14\b40\b0.\b. t\bti\bim\bme\beo\bou\but\bt
+
+ Type: number
+ Default: 600
+
+ This variable controls the _\bn_\bu_\bm_\bb_\be_\br _\bo_\bf _\bs_\be_\bc_\bo_\bn_\bd_\bs Mutt will wait for a key
+ to be pressed in the main menu before timing out and checking for new
+ mail. A value of zero or less will cause Mutt not to ever time out.
+
+ 6\b6.\b.3\b3.\b.1\b14\b41\b1.\b. t\btm\bmp\bpd\bdi\bir\br
+
+ Type: string
+ Default: /tmp
+ This variable allows you to specify where Mutt will place its
+ temporary files needed for displaying and composing messages.
+
+ 6\b6.\b.3\b3.\b.1\b14\b42\b2.\b. t\bto\bo_\b_c\bch\bha\bar\brs\bs
+
+ Type: string
+ Default: " +TCF"
+
+ Controls the character used to indicate mail addressed to you. The
+ first character is the one used when the mail is NOT addressed to your
+ address (default: space). The second is used when you are the only
+ recipient of the message (default: +). The third is when your address
+ appears in the TO header field, but you are not the only recipient of
+ the message (default: T). The fourth character is used when your
+ address is specified in the CC header field, but you are not the only
+ recipient. The fifth character is used to indicate mail that was sent
+ by _\by_\bo_\bu.
+
+ 6\b6.\b.3\b3.\b.1\b14\b43\b3.\b. u\bus\bse\be_\b_8\b8b\bbi\bit\btm\bmi\bim\bme\be
+
+ Type: boolean
+ Default: unset
+
+ W\bWa\bar\brn\bni\bin\bng\bg:\b: do not set this variable unless you are using a version of
+ sendmail which supports the -B8BITMIME flag (such as sendmail 8.8.x)
+ or you may not be able to send mail.
+
+ When _\bs_\be_\bt, Mutt will invoke ``$sendmail'' with the -B8BITMIME flag when
+ sending 8-bit messages to enable ESMTP negotiation.
+
+ 6\b6.\b.3\b3.\b.1\b14\b44\b4.\b. u\bus\bse\be_\b_d\bdo\bom\bma\bai\bin\bn
+
+ Type: boolean
+ Default: set
+
+ When set, Mutt will qualify all local addresses (ones without the
+ @host portion) with the value of ``$hostname''. If _\bu_\bn_\bs_\be_\bt, no
+ addresses will be qualified.
+
+ 6\b6.\b.3\b3.\b.1\b14\b45\b5.\b. u\bus\bse\be_\b_f\bfr\bro\bom\bm
+
+ Type: boolean
+ Default: set
+
+ When _\bs_\be_\bt, Mutt will generate the `From:' header field when sending
+ messages. If _\bu_\bn_\bs_\be_\bt, no `From:' header field will be generated unless
+ the user explicitly sets one using the ``my_hdr'' command.
+
+ 6\b6.\b.3\b3.\b.1\b14\b46\b6.\b. u\bus\bse\be_\b_m\bma\bai\bil\blc\bca\bap\bp
+
+ Type: quad-option
+ Default: ask
+
+ If set to ``yes'', always try to use a mailcap entry to display a MIME
+ part that Mutt can't understand what to do with. If ``ask'', prompt
+ as to whether to display as text or to use a mailcap entry. If
+ ``no'', always view unsupported MIME types as text.
+
+ N\bNo\bot\bte\be:\b: For compatibility with m\bme\bet\bta\bam\bma\bai\bil\bl, Mutt will also look at the
+ environment variable _\bM_\bM_\b__\bN_\bO_\bA_\bS_\bK. Setting this to 1\b1 is equivalent to
+ setting _\bu_\bs_\be_\b__\bm_\ba_\bi_\bl_\bc_\ba_\bp to ``yes''. Otherwise, the value of _\bM_\bM_\b__\bN_\bO_\bA_\bS_\bK is
+ interpreted as a comma-separated list of type names (without white
+ space) for which the corresponding mailcap entries will be used to
+ display MIME parts without prompting the user for confirmation.
+
+ 6\b6.\b.3\b3.\b.1\b14\b47\b7.\b. p\bpg\bgp\bp_\b_v\bve\ber\bri\bif\bfy\by_\b_s\bsi\big\bg
+
+ Type: quad-option
+ Default: yes
+
+ If ``yes'', always attempt to verify PGP/MIME signatures. If ``ask'',
+ ask whether or not to verify the signature. If ``no'', never attempt
+ to verify PGP/MIME signatures.
+
+ 6\b6.\b.3\b3.\b.1\b14\b48\b8.\b. v\bvi\bis\bsu\bua\bal\bl
+
+ Type: string
+ Default: $VISUAL
+
+ Specifies the visual editor to invoke when the _\b~_\bv command is given in
+ the builtin editor.
+
+ 6\b6.\b.3\b3.\b.1\b14\b49\b9.\b. w\bwa\bai\bit\bt_\b_k\bke\bey\by
+
+ Type: boolean
+ Default: set
+
+ Controls whether Mutt will ask you to press a key after _\bs_\bh_\be_\bl_\bl_\b-_\be_\bs_\bc_\ba_\bp_\be,
+ _\bp_\bi_\bp_\be_\b-_\bm_\be_\bs_\bs_\ba_\bg_\be, _\bp_\bi_\bp_\be_\b-_\be_\bn_\bt_\br_\by, _\bp_\br_\bi_\bn_\bt_\b-_\bm_\be_\bs_\bs_\ba_\bg_\be, and _\bp_\br_\bi_\bn_\bt_\b-_\be_\bn_\bt_\br_\by commands.
+
+ It is also used when viewing attachments with ``autoview'', provided
+ that the corresponding mailcap entry has a _\bn_\be_\be_\bd_\bs_\bt_\be_\br_\bm_\bi_\bn_\ba_\bl flag, and the
+ external program is interactive.
+
+ When set, Mutt will always ask for a key. When unset, Mutt will wait
+ for a key only if the external command returned a non-zero status.
+
+ 6\b6.\b.3\b3.\b.1\b15\b50\b0.\b. w\bwr\bri\bit\bte\be_\b_i\bin\bnc\bc
+
+ Type: number
+ Default: 10
+
+ When writing a mailbox, a message will be printed every _\bw_\br_\bi_\bt_\be_\b__\bi_\bn_\bc
+ messages to indicate progress. If set to 0, only a single message
+ will be displayed before writing a mailbox.
+
+ Also see the ``$read_inc'' variable.
+
+ 6\b6.\b.4\b4.\b. F\bFu\bun\bnc\bct\bti\bio\bon\bns\bs
+
+ The following is the list of available functions listed by the mapping
+ in which they are available. The default key setting is given, and an
+ explanation of what the function does. The key bindings of these
+ functions can be changed with the ``bind'' command.
+
+ 6\b6.\b.4\b4.\b.1\b1.\b. g\bge\ben\bne\ber\bri\bic\bc
+
+ The _\bg_\be_\bn_\be_\br_\bi_\bc menu is not a real menu, but specifies common functions
+ (such as movement) available in all menus except for _\bp_\ba_\bg_\be_\br and _\be_\bd_\bi_\bt_\bo_\br.
+ Changing settings for this menu will affect the default bindings for
+ all menus (except as noted).
+
+ bottom-page L move to the bottom of the page
+ current-bottom not bound move current entry to bottom of page
+ current-middle not bound move current entry to middle of page
+ current-top not bound move current entry to top of page
+ enter-command : enter a muttrc command
+ exit q exit this menu
+ first-entry = move to the first entry
+ half-down ] scroll down 1/2 page
+ half-up [ scroll up 1/2 page
+ help ? this screen
+ jump number jump to an index number
+ last-entry * move to the last entry
+ middle-page M move to the middle of the page
+ next-entry j move to the next entry
+ next-line > scroll down one line
+ next-page z move to the next page
+ previous-entry k move to the previous entry
+ previous-line < scroll up one line
+ previous-page Z move to the previous page
+ refresh ^L clear and redraw the screen
+ search / search for a regular expression
+ search-next n search for next match
+ search-opposite not bound search for next match in opposite direction
+ search-reverse ESC / search backwards for a regular expression
+ select-entry RET select the current entry
+ shell-escape ! run a program in a subshell
+ tag-entry t toggle the tag on the current entry
+ tag-prefix ; apply next command to tagged entries
+ top-page H move to the top of the page
+
+ 6\b6.\b.4\b4.\b.2\b2.\b. i\bin\bnd\bde\bex\bx
+
+ bounce-message b remail a message to another user
+ change-folder c open a different folder
+ change-folder-readonly ESC c open a different folder in read only mode
+ clear-flag W clear a status flag from a message
+ copy-message C copy a message to a file/mailbox
+ create-alias a create an alias from a message sender
+ decode-copy ESC C decode a message and copy it to a file/mailbox
+ decode-save ESC s decode a message and save it to a file/mailbox
+ delete-message d delete the current entry
+ delete-pattern D delete messages matching a pattern
+ delete-subthread ESC d delete all messages in subthread
+ delete-thread ^D delete all messages in thread
+ display-address @ display full address of sender
+ display-headers h display message with full headers
+ display-message RET display a message
+ exit x exit without saving changes
+ extract-keys ^K extract PGP public keys
+ fetch-mail G retrieve mail from POP server
+ flag-message F toggle a message's 'important' flag
+ forget-passphrase ^F wipe PGP passphrase from memory
+ forward-message f forward a message with comments
+ group-reply g reply to all recipients
+ limit l show only messages matching a pattern
+ list-reply L reply to specified mailing list
+ mail m compose a new mail message
+ mail-key ESC k mail a PGP public key
+ next-new TAB jump to the next new message
+ next-subthread ESC n jump to the next subthread
+ next-thread ^N jump to the next thread
+ next-undeleted j move to the next undeleted message
+ next-unread not bound jump to the next unread message
+ pipe-message | pipe message/attachment to a shell command
+ previous-new ESC TAB jump to the previous new message
+ previous-page Z move to the previous page
+ previous-subthread ESC p jump to previous subthread
+ previous-thread ^P jump to previous thread
+ previous-undeleted k move to the last undelete message
+ previous-unread not bound jump to the previous unread message
+ print-message p print the current entry
+ query Q query external program for addresses
+ quit q save changes to mailbox and quit
+ read-subthread ESC r mark the current subthread as read
+ read-thread ^R mark the current thread as read
+ recall-message R recall a postponed message
+ reply r reply to a message
+ save-message s save message/attachment to a file
+ set-flag w set a status flag on a message
+ show-version V show the Mutt version number and date
+ sort-mailbox o sort messages
+ sort-reverse O sort messages in reverse order
+ sync-mailbox $ save changes to mailbox
+ tag-pattern T tag messages matching a pattern
+ tag-thread ESC t tag/untag all messages in the current thread
+ toggle-new N toggle a message's 'new' flag
+ toggle-write % toggle whether the mailbox will be rewritten
+ undelete-message u undelete the current entry
+ undelete-pattern U undelete messages matching a pattern
+ undelete-subthread ESC u undelete all messages in subthread
+ undelete-thread ^U undelete all messages in thread
+ untag-pattern ^T untag messages matching a pattern
+ view-attachments v show MIME attachments
+
+ 6\b6.\b.4\b4.\b.3\b3.\b. p\bpa\bag\bge\ber\br
+
+ bottom $ jump to the bottom of the message
+ bounce-message b remail a message to another user
+ change-folder c open a different folder
+ change-folder-readonly ESC c open a different folder in read only mode
+ copy-message C copy a message to a file/mailbox
+ create-alias a create an alias from a message sender
+ decode-copy ESC C decode a message and copy it to a file/mailbox
+ decode-save ESC s decode a message and save it to a file/mailbox
+ delete-message d delete the current entry
+ delete-subthread ESC d delete all messages in subthread
+ delete-thread ^D delete all messages in thread
+ display-address @ display full address of sender
+ display-headers h display message with full headers
+ enter-command : enter a muttrc command
+ exit i return to the main-menu
+ extract-keys ^K extract PGP public keys
+ flag-message F toggle a message's 'important' flag
+ forget-passphrase ^F wipe PGP passphrase from memory
+ forward-message f forward a message with comments
+ group-reply g reply to all recipients
+ half-up not bound move up one-half page
+ half-down not bound move down one-half page
+ help ? this screen
+ list-reply L reply to specified mailing list
+ mail m compose a new mail message
+ mail-key ESC k mail a PGP public key
+ mark-as-new N toggle a message's 'new' flag
+ next-line RET scroll down one line
+ next-message J move to the next entry
+ next-new TAB jump to the next new message
+ next-page move to the next page
+ next-subthread ESC n jump to the next subthread
+ next-thread ^N jump to the next thread
+ next-undeleted j move to the next undeleted message
+ next-unread not bound jump to the next unread message
+ pipe-message | pipe message/attachment to a shell command
+ previous-line BackSpace scroll up one line
+ previous-message K move to the previous entry
+ previous-new not bound jump to the previous new message
+ previous-page - move to the previous page
+ previous-subthread ESC p jump to previous subthread
+ previous-thread ^P jump to previous thread
+ previous-undeleted k move to the last undelete message
+ previous-unread not bound jump to the previous unread message
+ print-message p print the current entry
+ quit Q save changes to mailbox and quit
+ read-subthread ESC r mark the current subthread as read
+ read-thread ^R mark the current thread as read
+ recall-message R recall a postponed message
+ redraw-screen ^L clear and redraw the screen
+ reply r reply to a message
+ save-message s save message/attachment to a file
+ search / search for a regular expression
+ search-next n search for next match
+ search-opposite not bound search for next match in opposite direction
+ search-reverse ESC / search backwards for a regular expression
+ search-toggle \ toggle search pattern coloring
+ shell-escape ! invoke a command in a subshell
+ show-version V show the Mutt version number and date
+ skip-quoted S skip beyond quoted text
+ tag-message t tag a message
+ toggle-quoted T toggle display of quoted text
+ top ^ jump to the top of the message
+ undelete-message u undelete the current entry
+ undelete-subthread ESC u undelete all messages in subthread
+ undelete-thread ^U undelete all messages in thread
+ view-attachments v show MIME attachments
+
+ 6\b6.\b.4\b4.\b.4\b4.\b. a\bal\bli\bia\bas\bs
+
+ search / search for a regular expression
+ search-next n search for next match
+ search-reverse ESC / search backwards for a regular expression
+
+ 6\b6.\b.4\b4.\b.5\b5.\b. q\bqu\bue\ber\bry\by
+
+ create-alias a create an alias from a message sender
+ mail m compose a new mail message
+ query Q query external program for addresses
+ query-append A append new query results to current results
+ search / search for a regular expression
+ search-next n search for next match
+ search-opposite not bound search for next match in opposite direction
+ search-reverse ESC / search backwards for a regular expression
+
+ 6\b6.\b.4\b4.\b.6\b6.\b. a\bat\btt\bta\bac\bch\bh
+
+ bounce-message b remail a message to another user
+ decode-copy ESC C decode a message and copy it to a file/mailbox
+ decode-save ESC s decode a message and save it to a file/mailbox
+ delete-entry d delete the current entry
+ display-headers h display message with full headers
+ extract-keys ^K extract PGP public keys
+ forward-message f forward a message with comments
+ group-reply g reply to all recipients
+ list-reply L reply to specified mailing list
+ pipe-entry | pipe message/attachment to a shell command
+ print-entry p print the current entry
+ reply r reply to a message
+ save-entry s save message/attachment to a file
+ undelete-entry u undelete the current entry
+ view-attach RET view attachment using mailcap entry if necessary
+ view-mailcap m force viewing of attachment using mailcap
+ view-text T view attachment as text
+
+ 6\b6.\b.4\b4.\b.7\b7.\b. c\bco\bom\bmp\bpo\bos\bse\be
+
+ attach-file a attach a file(s) to this message
+ attach-key ESC k attach a PGP public key
+ copy-file C save message/attachment to a file
+ detach-file D delete the current entry
+ display-headers h display message with full headers
+ edit-bcc b edit the BCC list
+ edit-cc c edit the CC list
+ edit-description d edit attachment description
+ edit-encoding ^E edit attachment trasfer-encoding
+ edit-fcc f enter a file to save a copy of this message in
+ edit-from ESC f edit the from: field
+ edit-file ^X e edit the file to be attached
+ edit-headers E edit the message with headers
+ edit-message e edit the message
+ edit-mime m edit attachment using mailcap entry
+ edit-reply-to r edit the Reply-To field
+ edit-subject s edit the subject of this message
+ edit-to t edit the TO list
+ edit-type ^T edit attachment type
+ filter-entry F filter attachment through a shell command
+ forget-passphrase ^F wipe PGP passphrase from memory
+ ispell i run ispell on the message
+ new-mime n compose new attachment using mailcap entry
+ pgp-menu p show PGP options
+ pipe-entry | pipe message/attachment to a shell command
+ postpone-message P save this message to send later
+ print-entry l print the current entry
+ rename-file R rename/move an attached file
+ send-message y send the message
+ toggle-unlink u toggle whether to delete file after sending it
+ view-attach RET view attachment using mailcap entry if necessary
+
+ 6\b6.\b.4\b4.\b.8\b8.\b. p\bpo\bos\bst\btp\bpo\bon\bne\bed\bd
+
+ delete-entry d delete the current entry
+ undelete-entry u undelete the current entry
+
+ 6\b6.\b.4\b4.\b.9\b9.\b. b\bbr\bro\bow\bws\bse\ber\br
+
+ change-dir c change directories
+ check-new TAB check mailboxes for new mail
+ enter-mask m enter a file mask
+ search / search for a regular expression
+ search-next n search for next match
+ search-reverse ESC / search backwards for a regular expression
+ select-new N select a new file in this directory
+ sort o sort messages
+ sort-reverse O sort messages in reverse order
+
+ 6\b6.\b.4\b4.\b.1\b10\b0.\b. p\bpg\bgp\bp
+
+ view-name % view the key's user id
+ verify-key c verify a PGP public key
+
+ 6\b6.\b.4\b4.\b.1\b11\b1.\b. e\bed\bdi\bit\bto\bor\br
+
+ backspace BackSpace delete the char in front of the cursor
+ backward-char ^B move the cursor one character to the left
+ bol ^A jump to the beginning of the line
+ buffy-cycle Space cycle among incoming mailboxes
+ complete TAB complete filename or alias
+ complete-query ^T complete address with query
+ delete-char ^D delete the char under the cursor
+ eol ^E jump to the end of the line
+ forward-char ^F move the cursor one character to the right
+ history-down not bound scroll up through the history list
+ history-up not bound scroll up through the history list
+ kill-eol ^K delete chars from cursor to end of line
+ kill-line ^U delete all chars on the line
+ kill-word ^W delete the word in front of the cursor
+ quote-char ^V quote the next typed key
+
+ 7\b7.\b. M\bMi\bis\bsc\bce\bel\bll\bla\ban\bny\by
+
+ 7\b7.\b.1\b1.\b. A\bAc\bck\bkn\bno\bow\bwl\ble\bed\bdg\bge\bem\bme\ben\bnt\bts\bs
+
+ Kari Hurrta <kari.hurtta@fmi.fi> co-developed the original MIME
+ parsing code back in the ELM-ME days.
+
+ The following people have been very helpful to the development of
+ Mutt:
+
+ Francois Berjon <Francois.Berjon@aar.alcatel-alsthom.fr>,
+ Aric Blumer <aric@fore.com>,
+ John Capo <jc@irbs.com>,
+ Liviu Daia <daia@stoilow.imar.ro>,
+ David DeSimone <fox@convex.hp.com>,
+ Nickolay N. Dudorov <nnd@wint.itfs.nsk.su>,
+ Michael Finken <finken@conware.de>,
+ Sven Guckes <guckes@math.fu-berlin.de>,
+ Mark Holloman <holloman@nando.net>,
+ Andreas Holzmann <holzmann@fmi.uni-passau.de>,
+ David Jeske <jeske@igcom.net>,
+ Christophe Kalt <kalt@hugo.int-evry.fr>,
+ Felix von Leitner (a.k.a ``Fefe'') <leitner@math.fu-berlin.de>,
+ Brandon Long <blong@fiction.net>,
+ Lars Marowsky-Bree <lmb@pointer.in-minden.de>,
+ Thomas ``Mike'' Michlmayr <mike@cosy.sbg.ac.at>,
+ David O'Brien <obrien@Nuxi.cs.ucdavis.edu>,
+ Clint Olsen <olsenc@ichips.intel.com>,
+ Park Myeong Seok <pms@romance.kaist.ac.kr>,
+ Thomas Parmelan <tom@ankh.fr.eu.org>,
+ Ollivier Robert <roberto@keltia.freenix.fr>,
+ Thomas Roessler <roessler@guug.de>,
+ Allain Thivillon <Allain.Thivillon@alma.fr>,
+ Ken Weinert <kenw@ihs.com>
+
+ 7\b7.\b.2\b2.\b. A\bAb\bbo\bou\but\bt t\bth\bhi\bis\bs d\bdo\boc\bcu\bum\bme\ben\bnt\bt
+
+ This document was written in SGML, and then rendered using the sgml-
+ tools package.
+
--- /dev/null
+.if n .ds Q \&"
+.if t .ds Q ``
+.if n .ds U \&"
+.if t .ds U ''
+.TH "Mutt" 1
+.tr \a\&
+.nr bi 0
+.nr ll 0
+.nr el 0
+.de DS
+..
+.de DE
+..
+.de Pp
+.ie \\n(ll>0 \{\
+.ie \\n(bi=1 \{\
+.nr bi 0
+.if \\n(t\\n(ll=0 \{.IP \\(bu\}
+.if \\n(t\\n(ll=1 \{.IP \\n+(e\\n(el.\}
+.\}
+.el .sp
+.\}
+.el \{\
+.ie \\nh=1 \{\
+.LP
+.nr h 0
+.\}
+.el .PP
+.\}
+..
+.SH NAME
+
+.Pp
+mutt - The Mutt Mail User Agent
+.Pp
+.SH SYNOPSIS
+
+.Pp
+mutt [ -hnpRvxyzZ ]
+[ -a \fIfile\fP ]
+[ -b \fIaddress\fP ]
+[ -c \fIaddress\fP ]
+[ -e \fIcommand\fP ]
+[ -f \fImailbox\fP ]
+[ -F \fImuttrc\fP ]
+[ -H \fIdraftfile\fP ]
+[ -i \fIinclude\fP ]
+[ -m \fItype\fP ]
+[ -s \fIsubject\fP ]
+[ \fIaddress\fP ... ]
+.Pp
+.SH DESCRIPTION
+
+.Pp
+Mutt is a small but very powerful text based program for reading electronic
+mail under unix operating systems, including support color terminals, MIME,
+and a threaded sorting mode.
+.Pp
+.SH OPTIONS
+
+.Pp
+.nr ll +1
+.nr t\n(ll 2
+.if \n(ll>1 .RS
+.IP "-a \fIfile\fP"
+.nr bi 1
+.Pp
+Attach a file to your message using MIME.
+.IP "-b \fIaddress\fP"
+.nr bi 1
+.Pp
+Specify a blind-carbon-copy (BCC) recipient
+.IP "-c \fIaddress\fP"
+.nr bi 1
+.Pp
+Specify a carbon-copy (CC) recipient
+.IP "-e \fIcommand\fP"
+.nr bi 1
+.Pp
+Specify a configuration command to be run after processing of initialization
+files.
+.IP "-f \fImailbox\fP"
+.nr bi 1
+.Pp
+Specify which mailbox to load.
+.IP "-F \fImuttrc\fP"
+.nr bi 1
+.Pp
+Specify an initialization file to read instead of ~/.muttrc
+.IP "-h"
+.nr bi 1
+.Pp
+Display help.
+.IP "-H \fIdraft\fP"
+.nr bi 1
+.Pp
+Specify a draft file which contains header and body to use to send a
+message.
+.IP "-i \fIinclude\fP"
+.nr bi 1
+.Pp
+Specify a file to include into the body of a message.
+.IP "-m \fItype\fP "
+.nr bi 1
+.Pp
+specify a default mailbox type
+.IP "-n"
+.nr bi 1
+.Pp
+Causes Mutt to bypass the system configuration file.
+.IP "-p"
+.nr bi 1
+.Pp
+Resume a postponed message.
+.IP "-R"
+.nr bi 1
+.Pp
+Open a mailbox in \fIread-only\fP mode.
+.IP "-s \fIsubject\fP"
+.nr bi 1
+.Pp
+Specify the subject of the message.
+.IP "-v"
+.nr bi 1
+.Pp
+Display the Mutt version number and compile-time definitions.
+.IP "-x"
+.nr bi 1
+.Pp
+Emulate the mailx compose mode.
+.IP "-y"
+.nr bi 1
+.Pp
+Start Mutt with a listing of all mailboxes specified by the \fImailboxes\fP
+command.
+.IP "-z"
+.nr bi 1
+.Pp
+When used with -f, causes Mutt not to start if there are no messages in the
+mailbox.
+.IP "-Z"
+.nr bi 1
+.Pp
+Causes Mutt to open the first mailbox specified by the \fImailboxes\fP
+command which contains new mail.
+.if \n(ll>1 .RE
+.nr ll -1
+.Pp
+.SH ENVIRONMENT
+
+.Pp
+.nr ll +1
+.nr t\n(ll 2
+.if \n(ll>1 .RS
+.IP "EDITOR"
+.nr bi 1
+.Pp
+Editor to invoke when composing a message.
+.IP "HOME"
+.nr bi 1
+.Pp
+Full path of the user's home directory.
+.IP "MAIL"
+.nr bi 1
+.Pp
+Full path of the user's spool mailbox.
+.IP "MAILCAPS"
+.nr bi 1
+.Pp
+Path to search for mailcap files.
+.IP "MM_NOASK"
+.nr bi 1
+.Pp
+If this variable is set, mailcap are always used without prompting first.
+.IP "PGPPATH"
+.nr bi 1
+.Pp
+Directory in which the user's PGP public keyring can be found.
+.IP "TMPDIR"
+.nr bi 1
+.Pp
+Directory in which temporary files are created.
+.IP "REPLYTO"
+.nr bi 1
+.Pp
+Default Reply-To address.
+.IP "VISUAL"
+.nr bi 1
+.Pp
+Editor to invoke when the ~v command is given in the builtin editor.
+.if \n(ll>1 .RE
+.nr ll -1
+.Pp
+.SH FILES
+
+.Pp
+.nr ll +1
+.nr t\n(ll 2
+.if \n(ll>1 .RS
+.IP "~/.muttrc"
+.nr bi 1
+.Pp
+User configuration file.
+.IP "/usr/local/share/Muttrc"
+.nr bi 1
+.Pp
+System-wide configuration file.
+.IP "/tmp/muttXXXXXX"
+.nr bi 1
+.Pp
+Temporary files created by Mutt.
+.IP "~/.mailcap"
+.nr bi 1
+.Pp
+User definition for handling non-text MIME types.
+.IP "/usr/local/share/mailcap"
+.nr bi 1
+.Pp
+System definition for handing non-text MIME types.
+.IP "~/.mime.types"
+.nr bi 1
+.Pp
+User's personal mapping between MIME types and file extensions.
+.IP "/usr/local/share/mime.types"
+.nr bi 1
+.Pp
+System mapping between MIME types and file extensions.
+.Pp
+.if \n(ll>1 .RE
+.nr ll -1
+.Pp
+.SH BUGS
+
+.Pp
+suspend/resume while editing a file with an external editor does not work
+under SunOS 4.x if you use the curses lib in /usr/5lib. It \fIdoes\fP work
+with the S-Lang library, however.
+.Pp
+Resizing the screen while using an external pager causes Mutt to go haywire
+on some systems.
+.Pp
+suspend/resume does not work under Ultrix.
+.Pp
+The help line for the index menu is not updated if you change the bindings
+for one of the functions listed while Mutt is running.
+.Pp
+.SH SEE ALSO
+
+.Pp
+curses(3), ncurses(3), sendmail(1), smail(1), mailcap(5)
+.Pp
+Mutt Home Page: http://www.cs.hmc.edu/~me/mutt/
+.Pp
+.SH AUTHOR
+
+.Pp
+Michael Elkins <me@cs.hmc.edu>
+.Pp
+http://www.cs.hmc.edu/~me
\ No newline at end of file
--- /dev/null
+<!doctype linuxdoc system>
+
+<manpage title="Mutt" sectnum="1">
+
+<sect1>NAME
+<p>
+mutt - The Mutt Mail User Agent
+
+<sect1>SYNOPSIS
+<p>
+mutt [ -hnpRvxyzZ ]
+[ -a <em/file/ ]
+[ -b <em/address/ ]
+[ -c <em/address/ ]
+[ -e <em/command/ ]
+[ -f <em/mailbox/ ]
+[ -F <em/muttrc/ ]
+[ -H <em/draftfile/ ]
+[ -i <em/include/ ]
+[ -m <em/type/ ]
+[ -s <em/subject/ ]
+&lsqb <em/address/ ... ]
+
+<sect1>DESCRIPTION
+<p>
+Mutt is a small but very powerful text based program for reading electronic
+mail under unix operating systems, including support color terminals, MIME,
+and a threaded sorting mode.
+
+<sect1>OPTIONS
+<p>
+<descrip>
+<tag>-a <em/file/
+<p>
+Attach a file to your message using MIME.
+<tag>-b <em/address/
+<p>
+Specify a blind-carbon-copy (BCC) recipient
+<tag>-c <em/address/
+<p>
+Specify a carbon-copy (CC) recipient
+<tag>-e <em/command/
+<p>
+Specify a configuration command to be run after processing of initialization
+files.
+<tag>-f <em/mailbox/
+<p>
+Specify which mailbox to load.
+<tag>-F <em/muttrc/
+<p>
+Specify an initialization file to read instead of ˜/.muttrc
+<tag>-h
+<p>
+Display help.
+<tag>-H <em/draft/
+<p>
+Specify a draft file which contains header and body to use to send a
+message.
+<tag>-i <em/include/
+<p>
+Specify a file to include into the body of a message.
+<tag>-m <em/type/
+<p>
+specify a default mailbox type
+<tag>-n
+<p>
+Causes Mutt to bypass the system configuration file.
+<tag>-p
+<p>
+Resume a postponed message.
+<tag>-R
+<p>
+Open a mailbox in <em/read-only/ mode.
+<tag>-s <em/subject/
+<p>
+Specify the subject of the message.
+<tag>-v
+<p>
+Display the Mutt version number and compile-time definitions.
+<tag>-x
+<p>
+Emulate the mailx compose mode.
+<tag>-y
+<p>
+Start Mutt with a listing of all mailboxes specified by the <em/mailboxes/
+command.
+<tag>-z
+<p>
+When used with -f, causes Mutt not to start if there are no messages in the
+mailbox.
+<tag>-Z
+<p>
+Causes Mutt to open the first mailbox specified by the <em/mailboxes/
+command which contains new mail.
+</descrip>
+
+<sect1>ENVIRONMENT
+<p>
+<descrip>
+<tag>EDITOR
+<p>
+Editor to invoke when composing a message.
+<tag>HOME
+<p>
+Full path of the user's home directory.
+<tag>MAIL
+<p>
+Full path of the user's spool mailbox.
+<tag>MAILCAPS
+<p>
+Path to search for mailcap files.
+<tag>MM_NOASK
+<p>
+If this variable is set, mailcap are always used without prompting first.
+<tag>PGPPATH
+<p>
+Directory in which the user's PGP public keyring can be found.
+<tag>TMPDIR
+<p>
+Directory in which temporary files are created.
+<tag>REPLYTO
+<p>
+Default Reply-To address.
+<tag>VISUAL
+<p>
+Editor to invoke when the ˜v command is given in the builtin editor.
+</descrip>
+
+<sect1>FILES
+<p>
+<descrip>
+<tag>˜/.muttrc
+<p>
+User configuration file.
+<tag>/usr/local/share/Muttrc
+<p>
+System-wide configuration file.
+<tag>/tmp/muttXXXXXX
+<p>
+Temporary files created by Mutt.
+<tag>˜/.mailcap
+<p>
+User definition for handling non-text MIME types.
+<tag>/usr/local/share/mailcap
+<p>
+System definition for handing non-text MIME types.
+<tag>˜/.mime.types
+<p>
+User's personal mapping between MIME types and file extensions.
+<tag>/usr/local/share/mime.types
+<p>
+System mapping between MIME types and file extensions.
+<p>
+</descrip>
+
+<sect1>BUGS
+<p>
+suspend/resume while editing a file with an external editor does not work
+under SunOS 4.x if you use the curses lib in /usr/5lib. It <em/does/ work
+with the S-Lang library, however.
+<p>
+Resizing the screen while using an external pager causes Mutt to go haywire
+on some systems.
+<p>
+suspend/resume does not work under Ultrix.
+<p>
+The help line for the index menu is not updated if you change the bindings
+for one of the functions listed while Mutt is running.
+
+<sect1>SEE ALSO
+<p>
+curses(3), ncurses(3), sendmail(1), smail(1), mailcap(5)
+<p>
+Mutt Home Page: http://www.cs.hmc.edu/˜me/mutt/
+
+<sect1>AUTHOR
+<p>
+Michael Elkins <me@cs.hmc.edu>
+<p>
+http://www.cs.hmc.edu/˜me
+</manpage>
--- /dev/null
+ Some notes on Mutt's PGP integration
+
+ 1997-12-04, tlr <roessler@guug.de>
+
+ Last updated: 1998-03-11, tlr
+
+
+While encryption, verification and signing of messages are
+done by an externally invoked PGP binary, the key
+selection process is handled by mutt itself. The public
+key ring (2.6 or 5.0 format) is parsed; PGP's cached trust
+parameters are evaluated and used to select the proper
+numerical key IDs for a message's recipients. These key
+IDs are then passed to the external PGP binary on the
+command line.
+
+
+Recent Changes
+--------------
+
+$pgp_pubring, $pgp_language, $pgp_secring, and $pgp are gone.
+They have been replaced by the following variables:
+
+ pgp_v2_language pgp_v5_language
+ pgp_v2_pubring pgp_v5_pubring
+ pgp_v2_secring pgp_v5_secring
+ pgp_v2 pgp_v5
+
+For all of these variables, we use "reasonable" defaults.
+This includes a fix for the outstanding "pkr/skr" problem
+for people using pgp 5.
+
+$pgp_version has been split up into a bunch of variables:
+
+ pgp_default_version
+ pgp_send_version
+ pgp_receive_version
+ pgp_key_version
+
+The latter three may be set to the value "default" (which
+is the default ;-); in this case, the value of
+$pgp_default_version will be used instead.
+
+$pgp_send_version is the version of pgp used for composing
+new messages. $pgp_receive version is used for decrypting
+messages and verifying signatures. $pgp_key_version is the
+one which is used for key ring operations (extracting keys
+from messages, extracting keys from your public key ring).
+
+Valid values for _all_ variables include "pgp5", "pgp3",
+"pgp2"; "pgp3" and "pgp5" are equivalent. "g10" has been
+removed for now: The program has changed it's name to
+GNUPG; Support for that program will be included soon.
+Support will be added as soon as the current state of the
+code turns out to be stable.
+
+
+A new variable named $pgp_sign_micalg has been introduced.
+It contains the default message integrity check algorithm.
+Valid values are "pgp-md5", "pgp-sha1", and "pgp-rmd160".
+If you select a signing key using the "sign as" option on
+the compose menu, mutt will automagically figure out the
+correct value to insert here, but it does not know about
+the user's default key.
+
+So if you are using an RSA key for signing, set this
+variable to "pgp-md5", if you use a PGP 5 DSS key for
+signing, say "pgp-sha1" here. The value of this variable
+will show up in the "micalg" parameter of MIME headers
+when creating RFC 2015 signatures.
+
+
+
+Frequently Asked Questions and Tips
+-----------------------------------
+
+Q: "How do it get PGP 5 support working?"
+
+It should work out of the box - just put the following
+into your ~/.muttrc:
+
+ set pgp_default_version=pgp5
+
+
+Q: "People are sending PGP messages which mutt doesn't
+ recognize. What can I do?"
+
+Add the following lines to your ~/.procmailrc (you are
+using procmail, aren't you?):
+
+------------------------------
+
+ ##
+ ## PGP
+ ##
+
+ :0 H
+ * ^Content-Type: text
+ {
+ :0 fBw
+ * ^-----BEGIN PGP MESSAGE-----
+ | formail -I "Content-Type: application/pgp; format=text; x-action=encryptsign"
+
+ :0 fBw
+ * ^-----BEGIN PGP SIGNED MESSAGE-----
+ | formail -I "Content-Type: application/pgp; format=text; x-action=sign"
+ }
+
+ ##
+ ## Add a "Content-Type: application/pgp" header so Mutt will know the
+ ## mail is encrypted.
+ ##
+
+ :0 fBw
+ * ^-----BEGIN PGP MESSAGE-----
+ | formail -a "Content-Type: application/pgp; format=text; x-action=encryptsign"
+
+ ##
+ ## Add a "Content-Type: application/pgp" header so Mutt will know the
+ ## mail is signed.
+ ##
+
+ :0 fBw
+ * ^-----BEGIN PGP SIGNED MESSAGE-----
+ | formail -a "Content-Type: application/pgp; format=text; x-action=sign"
+
+------------------------------
+
+
+Q: "I don't like that PGP/MIME stuff, but want to use the
+ old way of PGP-signing my mails. Can't you include
+ that with mutt?"
+
+No. Application/pgp is not really suited to a world with
+MIME, non-textual body parts and similar things. Anyway,
+if you really want to generate these old-style
+attachments, include the following macro in your ~/.muttrc
+(line breaks for readibility, this is actually one line):
+
+ macro compose S "Fpgp +verbose=0 -fast
+ +clearsig=on\ny^T^Uapplication/pgp; format=text;
+ x-action=sign\n"
+
+
+
+Q: "I don't like all the ^Gs and various other verbosity
+ PGP is presenting me with."
+
+Roland Rosenfeld <roland@spinnaker.rhein.de> has found a
+quite elegant solution to this problem: PGP has some
+pretty good foreign language support. So we just
+introduce a language called "mutt" which contains empty
+strings for the messages we don't want to see. To use
+this, copy either language.txt or language50.txt
+(depending on what PGP version you are using) to your
+$PGPPATH and add the following line to your muttrc:
+
+ set pgp_language="mutt"
+
+For PGP 2.6, a German version called "muttde" is available
+as well.
+
--- /dev/null
+Mutt Programming Style Guide
+============================
+
+This information is meant for those of you who are hacking on Mutt and
+submitting patches to me. If you follow these guidelines, it will make it
+much easier for me to integrate patches.
+
+- global functions should have the prefix "mutt_". All other functions
+ should be declared "static".
+
+- avoid global vars where possible. If one is required, try to contain it
+ to a single source file and declare it "static". Global vars should have
+ the first letter of each word capitilized, and no underscores should be
+ used (e.g., MailGid, LastFolder, MailDir).
+
+- re-use code as much as possible. There are a lot of "library" functions.
+ One of the biggest causes of bloat in ELM and PINE is the tremendous
+ duplication of code... Help keep Mutt small!
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Close approximation of the mailx(1) builtin editor for sending mail. */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/*
+ * SLcurses_waddnstr() can't take a "const char *", so this is only
+ * declared "static" (sigh)
+ */
+static char EditorHelp[] = "\
+~~ insert a line begining with a single ~\n\
+~b users add users to the Bcc: field\n\
+~c users add users to the Cc: field\n\
+~f messages include messages\n\
+~F messages same as ~f, except also include headers\n\
+~h edit the message header\n\
+~m messages include and quote messages\n\
+~M messages same as ~m, except include headers\n\
+~p print the message\n\
+~q write file and quit editor\n\
+~r file read a file into the editor\n\
+~t users add users to the To: field\n\
+~u recall the previous line\n\
+~v edit message with the $visual editor\n\
+~w file write message to file\n\
+~x abort changes and quit editor\n\
+~? this message\n\
+. on a line by itself ends input\n";
+
+static char **
+be_snarf_data (FILE *f, char **buf, int *bufmax, int *buflen, int offset,
+ int bytes, int prefix)
+{
+ char tmp[HUGE_STRING];
+ char *p = tmp;
+ int tmplen = sizeof (tmp);
+
+ tmp[sizeof (tmp) - 1] = 0;
+ if (prefix)
+ {
+ strfcpy (tmp, Prefix, sizeof (tmp));
+ tmplen = strlen (tmp);
+ p = tmp + tmplen;
+ tmplen = sizeof (tmp) - tmplen;
+ }
+
+ fseek (f, offset, 0);
+ while (bytes > 0)
+ {
+ if (fgets (p, tmplen - 1, f) == NULL) break;
+ bytes -= strlen (p);
+ if (*bufmax == *buflen)
+ safe_realloc ((void **)&buf, sizeof (char *) * (*bufmax += 25));
+ buf[(*buflen)++] = safe_strdup (tmp);
+ }
+ if (buf) buf[*buflen] = NULL;
+ return (buf);
+}
+
+static char **
+be_snarf_file (const char *path, char **buf, int *max, int *len, int verbose)
+{
+ FILE *f;
+ char tmp[LONG_STRING];
+ struct stat sb;
+
+ if ((f = fopen (path, "r")))
+ {
+ fstat (fileno (f), &sb);
+ buf = be_snarf_data (f, buf, max, len, 0, sb.st_size, 0);
+ if (verbose)
+ {
+ snprintf(tmp, sizeof(tmp), "\"%s\" %d bytes\n", path, sb.st_size);
+ addstr(tmp);
+ }
+ fclose (f);
+ }
+ else
+ {
+ snprintf(tmp, sizeof(tmp), "%s: %s\n", path, strerror(errno));
+ addstr(tmp);
+ }
+ return (buf);
+}
+
+static int be_barf_file (const char *path, char **buf, int buflen)
+{
+ FILE *f;
+ int i;
+
+ if ((f = fopen (path, "w")) == NULL)
+ {
+ addstr (strerror (errno));
+ addch ('\n');
+ return (-1);
+ }
+ for (i = 0; i < buflen; i++) fputs (buf[i], f);
+ if (fclose (f) == 0) return 0;
+ printw ("fclose: %s\n", strerror (errno));
+ return (-1);
+}
+
+static void be_free_memory (char **buf, int buflen)
+{
+ while (buflen-- > 0)
+ free (buf[buflen]);
+ if (buf)
+ free (buf);
+}
+
+static char **
+be_include_messages (char *msg, char **buf, int *bufmax, int *buflen,
+ int pfx, int inc_hdrs)
+{
+ int offset, bytes, n;
+ char tmp[LONG_STRING];
+
+ while ((msg = strtok (msg, " ,")) != NULL)
+ {
+ n = atoi (msg);
+ if (n > 0 && n <= Context->msgcount)
+ {
+ n--;
+
+ /* add the attribution */
+ if (Attribution)
+ {
+ mutt_make_string (tmp, sizeof (tmp) - 1, Attribution, Context->hdrs[n]);
+ strcat (tmp, "\n");
+ }
+
+ if (*bufmax == *buflen)
+ safe_realloc ((void **) &buf, sizeof (char *) * (*bufmax += 25));
+ buf[(*buflen)++] = safe_strdup (tmp);
+
+ bytes = Context->hdrs[n]->content->length;
+ if (inc_hdrs)
+ {
+ offset = Context->hdrs[n]->offset;
+ bytes += Context->hdrs[n]->content->offset - offset;
+ }
+ else
+ offset = Context->hdrs[n]->content->offset;
+ buf = be_snarf_data (Context->fp, buf, bufmax, buflen, offset, bytes,
+ pfx);
+
+ if (*bufmax == *buflen)
+ safe_realloc ((void **)&buf, sizeof (char *) * (*bufmax += 25));
+ buf[(*buflen)++] = safe_strdup ("\n");
+ }
+ else
+ printw ("%d: invalid message number.\n", n);
+ msg = NULL;
+ }
+ return (buf);
+}
+
+static void be_print_header (ENVELOPE *env)
+{
+ char tmp[HUGE_STRING];
+
+ if (env->to)
+ {
+ addstr ("To: ");
+ tmp[0] = 0;
+ rfc822_write_address (tmp, sizeof (tmp), env->to);
+ addstr (tmp);
+ addch ('\n');
+ }
+ if (env->cc)
+ {
+ addstr ("Cc: ");
+ tmp[0] = 0;
+ rfc822_write_address (tmp, sizeof (tmp), env->cc);
+ addstr (tmp);
+ addch ('\n');
+ }
+ if (env->bcc)
+ {
+ addstr ("Bcc: ");
+ tmp[0] = 0;
+ rfc822_write_address (tmp, sizeof (tmp), env->bcc);
+ addstr (tmp);
+ addch ('\n');
+ }
+ if (env->subject)
+ {
+ addstr ("Subject: ");
+ addstr (env->subject);
+ addch ('\n');
+ }
+ addch ('\n');
+}
+
+/* args:
+ * force override the $ask* vars (used for the ~h command)
+ */
+static void be_edit_header (ENVELOPE *e, int force)
+{
+ char tmp[HUGE_STRING];
+
+ move (LINES-1, 0);
+
+ addstr ("To: ");
+ tmp[0] = 0;
+ rfc822_write_address (tmp, sizeof (tmp), e->to);
+ if (!e->to || force)
+ {
+ if (mutt_enter_string ((unsigned char *) tmp, sizeof (tmp), LINES-1, 4, 0) == 0)
+ {
+ rfc822_free_address (&e->to);
+ e->to = mutt_parse_adrlist (e->to, tmp);
+ e->to = mutt_expand_aliases (e->to);
+ tmp[0] = 0;
+ rfc822_write_address (tmp, sizeof (tmp), e->to);
+ mvaddstr (LINES - 1, 4, tmp);
+ }
+ }
+ else
+ {
+ addstr (tmp);
+ }
+ addch ('\n');
+
+ if (!e->subject || force)
+ {
+ addstr ("Subject: ");
+ strfcpy (tmp, e->subject ? e->subject: "", sizeof (tmp));
+ if (mutt_enter_string ((unsigned char *) tmp, sizeof (tmp), LINES-1, 9, 0) == 0)
+ {
+ safe_free ((void **) &e->subject);
+ e->subject = safe_strdup (tmp);
+ }
+ addch ('\n');
+ }
+
+ if ((!e->cc && option (OPTASKCC)) || force)
+ {
+ addstr ("Cc: ");
+ tmp[0] = 0;
+ rfc822_write_address (tmp, sizeof (tmp), e->cc);
+ if (mutt_enter_string ((unsigned char *) tmp, sizeof (tmp), LINES-1, 4, 0) == 0)
+ {
+ rfc822_free_address (&e->cc);
+ e->cc = mutt_parse_adrlist (e->cc, tmp);
+ e->cc = mutt_expand_aliases (e->cc);
+ tmp[0] = 0;
+ rfc822_write_address (tmp, sizeof (tmp), e->cc);
+ mvaddstr (LINES - 1, 4, tmp);
+ }
+ addch ('\n');
+ }
+
+ if (option (OPTASKBCC) || force)
+ {
+ addstr ("Bcc: ");
+ tmp[0] = 0;
+ rfc822_write_address (tmp, sizeof (tmp), e->bcc);
+ if (mutt_enter_string ((unsigned char *) tmp, sizeof (tmp), LINES-1, 5, 0) == 0)
+ {
+ rfc822_free_address (&e->bcc);
+ e->bcc = mutt_parse_adrlist (e->bcc, tmp);
+ e->bcc = mutt_expand_aliases (e->bcc);
+ tmp[0] = 0;
+ rfc822_write_address (tmp, sizeof (tmp), e->bcc);
+ mvaddstr (LINES - 1, 5, tmp);
+ }
+ addch ('\n');
+ }
+}
+
+int mutt_builtin_editor (const char *path, HEADER *msg, HEADER *cur)
+{
+ char **buf = NULL;
+ int bufmax = 0, buflen = 0;
+ char tmp[LONG_STRING];
+ int abort = 0;
+ int done = 0;
+ int i;
+ char *p;
+
+ scrollok (stdscr, TRUE);
+
+ be_edit_header (msg->env, 0);
+
+ addstr ("(End message with a . on a line by itself)\n");
+
+ buf = be_snarf_file (path, buf, &bufmax, &buflen, 0);
+
+ tmp[0] = 0;
+ while (!done)
+ {
+ if (mutt_enter_string ((unsigned char *) tmp, sizeof (tmp), LINES-1, 0, 0) == -1)
+ {
+ tmp[0] = 0;
+ continue;
+ }
+ addch ('\n');
+
+ if (tmp[0] == EscChar[0] && tmp[1] != EscChar[0])
+ {
+ /* remove trailing whitespace from the line */
+ p = tmp + strlen (tmp) - 1;
+ while (p >= tmp && ISSPACE (*p))
+ *p-- = 0;
+
+ p = tmp + 2;
+ SKIPWS (p);
+
+ switch (tmp[1])
+ {
+ case '?':
+ addstr (EditorHelp);
+ break;
+ case 'b':
+ msg->env->bcc = mutt_parse_adrlist (msg->env->bcc, p);
+ msg->env->bcc = mutt_expand_aliases (msg->env->bcc);
+ break;
+ case 'c':
+ msg->env->cc = mutt_parse_adrlist (msg->env->cc, p);
+ msg->env->cc = mutt_expand_aliases (msg->env->cc);
+ break;
+ case 'h':
+ be_edit_header (msg->env, 1);
+ break;
+ case 'F':
+ case 'f':
+ case 'm':
+ case 'M':
+ if (Context)
+ {
+ if (!*p && cur)
+ {
+ /* include the current message */
+ p = tmp + strlen (tmp) + 1;
+ snprintf (tmp + strlen (tmp), sizeof (tmp) - strlen (tmp), " %d",
+ cur->msgno + 1);
+ }
+ buf = be_include_messages (p, buf, &bufmax, &buflen,
+ (tolower (tmp[1]) == 'm'),
+ (isupper (tmp[1])));
+ }
+ else
+ addstr ("No mailbox.\n");
+ break;
+ case 'p':
+ addstr ("-----\n");
+ addstr ("Message contains:\n");
+ be_print_header (msg->env);
+ for (i = 0; i < buflen; i++)
+ addstr (buf[i]);
+ addstr ("(continue)\n");
+ break;
+ case 'q':
+ done = 1;
+ break;
+ case 'r':
+ if (*p)
+ buf = be_snarf_file (p, buf, &bufmax, &buflen, 1);
+ else
+ addstr ("missing filename.\n");
+ break;
+ case 's':
+ safe_free ((void **) &msg->env->subject);
+ msg->env->subject = safe_strdup (p);
+ break;
+ case 't':
+ msg->env->to = rfc822_parse_adrlist (msg->env->to, p);
+ msg->env->to = mutt_expand_aliases (msg->env->to);
+ break;
+ case 'u':
+ if (buflen)
+ {
+ buflen--;
+ strfcpy (tmp, buf[buflen], sizeof (tmp));
+ tmp[strlen (tmp)-1] = 0;
+ free (buf[buflen]);
+ buf[buflen] = NULL;
+ continue;
+ }
+ else
+ addstr ("No lines in message.\n");
+ break;
+
+ case 'e':
+ case 'v':
+ if (be_barf_file (path, buf, buflen) == 0)
+ {
+ be_free_memory (buf, buflen);
+ buf = NULL;
+ bufmax = buflen = 0;
+
+ if (option (OPTEDITHDRS))
+ mutt_edit_headers (Visual, path, msg, NULL, 0);
+ else
+ mutt_edit_file (Visual, path);
+
+ buf = be_snarf_file (path, buf, &bufmax, &buflen, 0);
+
+ addstr ("(continue)\n");
+ }
+ break;
+ case 'w':
+ be_barf_file (*p ? p : path, buf, buflen);
+ break;
+ case 'x':
+ abort = 1;
+ done = 1;
+ break;
+ default:
+ printw ("%s: unknown editor command (~? for help)\n", tmp);
+ break;
+ }
+ }
+ else if (strcmp (".", tmp) == 0)
+ done = 1;
+ else
+ {
+ strcat (tmp, "\n");
+ if (buflen == bufmax)
+ safe_realloc ((void **)&buf, sizeof (char *) * (bufmax += 25));
+ buf[buflen++] = safe_strdup (tmp[1] == '~' ? tmp + 1 : tmp);
+ }
+
+ tmp[0] = 0;
+ }
+
+ if (!abort) be_barf_file (path, buf, buflen);
+ be_free_memory (buf, buflen);
+
+ return (abort ? -1 : 0);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_menu.h"
+#include "mutt_curses.h"
+#include "keymap.h"
+
+#include <termios.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+/* macro to print control chars in reverse-video */
+#define ADDCH(x) addch (IsPrint (x) ? x : (ColorDefs[MT_COLOR_MARKERS] | (x + '@')))
+
+/* global vars used for the string-history routines */
+static char **Hist = NULL;
+static short HistCur = 0;
+static short HistLast = 0;
+
+void mutt_init_history (void)
+{
+ int i;
+ static int OldSize = 0;
+
+ if (Hist)
+ {
+ for (i = 0 ; i < OldSize ; i ++)
+ safe_free ((void **) &Hist[i]);
+ safe_free ((void **) &Hist);
+ }
+
+ if (HistSize)
+ Hist = safe_calloc (HistSize, sizeof (char *));
+ HistCur = 0;
+ HistLast = 0;
+ OldSize = HistSize;
+}
+
+static void sh_add (char *s)
+{
+ int prev;
+
+ if (!HistSize)
+ return; /* disabled */
+
+ if (*s)
+ {
+ prev = HistLast - 1;
+ if (prev < 0) prev = HistSize - 1;
+ if (!Hist[prev] || strcmp (Hist[prev], s) != 0)
+ {
+ safe_free ((void **) &Hist[HistLast]);
+ Hist[HistLast++] = safe_strdup (s);
+ if (HistLast > HistSize - 1)
+ HistLast = 0;
+ }
+ }
+ HistCur = HistLast; /* reset to the last entry */
+}
+
+static char *sh_next (void)
+{
+ int next;
+
+ if (!HistSize)
+ return (""); /* disabled */
+
+ next = HistCur + 1;
+ if (next > HistLast - 1)
+ next = 0;
+ if (Hist[next])
+ HistCur = next;
+ return (Hist[HistCur] ? Hist[HistCur] : "");
+}
+
+static char *sh_prev (void)
+{
+ int prev;
+
+ if (!HistSize)
+ return (""); /* disabled */
+
+ prev = HistCur - 1;
+ if (prev < 0)
+ {
+ prev = HistLast - 1;
+ if (prev < 0)
+ {
+ prev = HistSize - 1;
+ while (prev > 0 && Hist[prev] == NULL)
+ prev--;
+ }
+ }
+ if (Hist[prev])
+ HistCur = prev;
+ return (Hist[HistCur] ? Hist[HistCur] : "");
+}
+
+/* redraw flags for mutt_enter_string() */
+enum
+{
+ M_REDRAW_INIT = 1, /* recalculate lengths */
+ M_REDRAW_LINE, /* redraw entire line */
+ M_REDRAW_EOL, /* redraw from current position to eol */
+ M_REDRAW_PREV_EOL /* redraw from curpos-1 to eol */
+};
+
+/* Returns:
+ * 1 need to redraw the screen and call me again
+ * 0 if input was given
+ * -1 if abort.
+ *
+ */
+int mutt_enter_string (unsigned char *buf, size_t buflen, int y, int x,
+ int flags)
+{
+ int curpos = 0; /* the location of the cursor */
+ int lastchar = 0; /* offset of the last char in the string */
+ int begin = 0; /* first character displayed on the line */
+ int ch; /* last typed character */
+ int width = COLS - x - 1; /* width of field */
+ int redraw = M_REDRAW_INIT; /* when/what to redraw */
+ int pass = (flags == M_PASS);
+ int first = 1;
+ int j;
+ char tempbuf[_POSIX_PATH_MAX] = "";
+
+ FOREVER
+ {
+ if (redraw)
+ {
+ if (redraw == M_REDRAW_INIT)
+ {
+ /* full redraw */
+ lastchar = curpos = strlen ((char *) buf);
+ begin = lastchar - width;
+ }
+ if (begin < 0)
+ begin = 0;
+ switch (redraw)
+ {
+ case M_REDRAW_PREV_EOL:
+ j = curpos - 1;
+ break;
+ case M_REDRAW_EOL:
+ j = curpos;
+ break;
+ default:
+ j = begin;
+ }
+ move (y, x + j - begin);
+ for (; j < lastchar && j < begin + width; j++)
+ ADDCH (buf[j]);
+ clrtoeol ();
+ if (redraw != M_REDRAW_INIT)
+ move (y, x + curpos - begin);
+ redraw = 0;
+ }
+ mutt_refresh ();
+
+ /* first look to see if a keypress is an editor operation. km_dokey()
+ * returns 0 if there is no entry in the keymap, so restore the last
+ * keypress and continue normally.
+ */
+ if ((ch = km_dokey (MENU_EDITOR)) == -1)
+ {
+ buf[curpos] = 0;
+ return (-1);
+ }
+
+ if (ch != 0)
+ {
+ first = 0; /* make sure not to clear the buffer */
+ switch (ch)
+ {
+ case OP_EDITOR_HISTORY_UP:
+ if (!pass)
+ {
+ strfcpy ((char *) buf, sh_prev (), buflen);
+ redraw = M_REDRAW_INIT;
+ }
+ break;
+ case OP_EDITOR_HISTORY_DOWN:
+ if (!pass)
+ {
+ strfcpy ((char *) buf, sh_next (), buflen);
+ redraw = M_REDRAW_INIT;
+ }
+ break;
+ case OP_EDITOR_BACKSPACE:
+ if (curpos == 0)
+ {
+ BEEP ();
+ break;
+ }
+ for (j = curpos ; j < lastchar ; j++)
+ buf[j - 1] = buf[j];
+ curpos--;
+ lastchar--;
+ if (!pass)
+ {
+ if (curpos > begin)
+ {
+ if (lastchar == curpos)
+ {
+ move (y, x + curpos - begin);
+ delch ();
+ }
+ else
+ redraw = M_REDRAW_EOL;
+ }
+ else
+ {
+ begin -= width / 2;
+ redraw = M_REDRAW_LINE;
+ }
+ }
+ break;
+ case OP_EDITOR_BOL:
+ /* reposition the cursor at the begininning of the line */
+ curpos = 0;
+ if (!pass)
+ {
+ if (begin)
+ {
+ /* the first char is not displayed, so readjust */
+ begin = 0;
+ redraw = M_REDRAW_LINE;
+ }
+ else
+ move (y, x);
+ }
+ break;
+ case OP_EDITOR_EOL:
+ curpos = lastchar;
+ if (!pass)
+ {
+ if (lastchar < begin + width)
+ move (y, x + lastchar - begin);
+ else
+ {
+ begin = lastchar - width / 2;
+ redraw = M_REDRAW_LINE;
+ }
+ }
+ break;
+ case OP_EDITOR_KILL_LINE:
+ lastchar = curpos = 0;
+ if (!pass)
+ {
+ begin = 0;
+ redraw = M_REDRAW_LINE;
+ }
+ break;
+ case OP_EDITOR_KILL_EOL:
+ lastchar = curpos;
+ if (!pass)
+ clrtoeol ();
+ break;
+ case OP_EDITOR_BACKWARD_CHAR:
+ if (curpos == 0)
+ {
+ BEEP ();
+ }
+ else
+ {
+ curpos--;
+ if (!pass)
+ {
+ if (curpos < begin)
+ {
+ begin -= width / 2;
+ redraw = M_REDRAW_LINE;
+ }
+ else
+ move (y, x + curpos - begin);
+ }
+ }
+ break;
+ case OP_EDITOR_FORWARD_CHAR:
+ if (curpos == lastchar)
+ {
+ BEEP ();
+ }
+ else
+ {
+ curpos++;
+ if (!pass)
+ {
+ if (curpos >= begin + width)
+ {
+ begin = curpos - width / 2;
+ redraw = M_REDRAW_LINE;
+ }
+ else
+ move (y, x + curpos - begin);
+ }
+ }
+ break;
+ case OP_EDITOR_DELETE_CHAR:
+ if (curpos != lastchar)
+ {
+ for (j = curpos; j < lastchar; j++)
+ buf[j] = buf[j + 1];
+ lastchar--;
+ if (!pass)
+ redraw = M_REDRAW_EOL;
+ }
+ else
+ BEEP ();
+ break;
+ case OP_EDITOR_KILL_WORD:
+ /* delete to begining of word */
+ if (curpos != 0)
+ {
+ j = curpos;
+ while (j > 0 && ISSPACE (buf[j - 1]))
+ j--;
+ if (j > 0)
+ {
+ if (isalnum (buf[j - 1]))
+ {
+ for (j--; j > 0 && isalnum (buf[j - 1]); j--)
+ ;
+ }
+ else
+ j--;
+ }
+ ch = j; /* save current position */
+ while (curpos < lastchar)
+ buf[j++] = buf[curpos++];
+ lastchar = j;
+ curpos = ch; /* restore current position */
+ /* update screen */
+ if (!pass)
+ {
+ if (curpos < begin)
+ {
+ begin = curpos - width / 2;
+ redraw = M_REDRAW_LINE;
+ }
+ else
+ redraw = M_REDRAW_EOL;
+ }
+ }
+ break;
+ case OP_EDITOR_BUFFY_CYCLE:
+ if (flags & M_EFILE)
+ {
+ first = 1; /* clear input if user types a real key later */
+ buf[curpos] = 0;
+ mutt_buffy ((char *) buf);
+ redraw = M_REDRAW_INIT;
+ break;
+ }
+ else if (!(flags & M_FILE))
+ goto self_insert;
+ /* fall through to completion routine (M_FILE) */
+
+ case OP_EDITOR_COMPLETE:
+ if (flags & M_CMD)
+ {
+ buf[curpos] = 0;
+ for (j = curpos - 1; j >= 0 && buf[j] != ' '; j--);
+ if (strcmp (tempbuf, (char *) buf) == 0)
+ {
+ mutt_select_file ((char *) buf + j + 1, buflen - j - 1, 0);
+ set_option (OPTNEEDREDRAW);
+ return (1);
+ }
+ if (mutt_complete ((char *) buf + j + 1) == 0)
+ strfcpy (tempbuf, (char *) buf + j + 1, sizeof (tempbuf));
+ else
+ BEEP ();
+ redraw = M_REDRAW_INIT;
+ }
+ else if (flags & M_ALIAS)
+ {
+ /* invoke the alias-menu to get more addresses */
+ buf[curpos] = 0;
+ if (curpos)
+ {
+ for (j = curpos - 1 ; j >= 0 && buf[j] != ' ' && buf[j] != ',' ; j--);
+ if (mutt_alias_complete ((char *) buf + j + 1, buflen - j - 1))
+ {
+ redraw = M_REDRAW_INIT;
+ continue;
+ }
+ }
+ else
+ mutt_alias_menu ((char *) buf, buflen, Aliases);
+ return (1);
+ }
+ else if (flags & (M_FILE | M_EFILE))
+ {
+ buf[curpos] = 0;
+
+ /* see if the path has changed from the last time */
+ if (strcmp (tempbuf, (char *) buf) == 0)
+ {
+ mutt_select_file ((char *) buf, buflen, 0);
+ set_option (OPTNEEDREDRAW);
+ if (buf[0])
+ {
+ mutt_pretty_mailbox ((char *) buf);
+ sh_add ((char *) buf);
+ return (0);
+ }
+ return (-1);
+ }
+
+ if (mutt_complete ((char *) buf) == 0)
+ strfcpy (tempbuf, (char *) buf, sizeof (tempbuf));
+ else
+ BEEP (); /* let the user know that nothing matched */
+ redraw = M_REDRAW_INIT;
+ }
+ else
+ goto self_insert;
+ break;
+
+ case OP_EDITOR_COMPLETE_QUERY:
+ if (flags & M_ALIAS)
+ {
+ /* invoke the query-menu to get more addresses */
+ buf[curpos] = 0;
+ if (curpos)
+ {
+ for (j = curpos - 1 ; j >= 0 && buf[j] != ' ' && buf[j] != ',' ; j--);
+ mutt_query_complete ((char *) buf + j + 1, buflen - j - 1);
+ }
+ else
+ mutt_query_menu ((char *) buf, buflen);
+ return (1);
+ }
+ else
+ goto self_insert;
+
+ case OP_EDITOR_QUOTE_CHAR:
+ ADDCH (LastKey);
+ LastKey = mutt_getch ();
+ move (y, x + curpos - begin);
+ goto self_insert;
+
+ default:
+ BEEP ();
+ }
+ }
+ else
+ {
+
+self_insert:
+
+ /* use the raw keypress */
+ ch = LastKey;
+
+ if (first && (flags & M_CLEAR))
+ {
+ first = 0;
+ if (IsPrint (ch))
+ {
+ mutt_ungetch (ch);
+ buf[0] = 0;
+ redraw = M_REDRAW_INIT;
+ continue;
+ }
+ }
+
+ if (CI_is_return (ch))
+ {
+ buf[lastchar] = 0;
+ if (!pass)
+ sh_add ((char *) buf);
+ return (0);
+ }
+ else if ((ch < ' ' || IsPrint (ch)) && (lastchar + 1 < buflen))
+ {
+ for (j = lastchar; j > curpos; j--)
+ buf[j] = buf[j - 1];
+ buf[curpos++] = ch;
+ lastchar++;
+
+ if (!pass)
+ {
+ if (curpos >= begin + width)
+ {
+ begin = curpos - width / 2;
+ redraw = M_REDRAW_LINE;
+ }
+ else if (curpos == lastchar)
+ ADDCH (ch);
+ else
+ redraw = M_REDRAW_PREV_EOL;
+ }
+ }
+ else
+ {
+ mutt_flushinp ();
+ BEEP ();
+ }
+ }
+ }
+ /* not reached */
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+/* Invokes a commmand on a pipe and optionally connects its stdin and stdout
+ * to the specified handles.
+ */
+pid_t
+mutt_create_filter_fd (const char *cmd, FILE **in, FILE **out, FILE **err,
+ int fdin, int fdout, int fderr)
+{
+ int pin[2], pout[2], perr[2], thepid;
+
+ if (in)
+ {
+ *in = 0;
+ if (pipe (pin) == -1)
+ return (-1);
+ }
+
+ if (out)
+ {
+ *out = 0;
+ if (pipe (pout) == -1)
+ {
+ if (in)
+ {
+ close (pin[0]);
+ close (pin[1]);
+ }
+ return (-1);
+ }
+ }
+
+ if (err)
+ {
+ *err = 0;
+ if (pipe (perr) == -1)
+ {
+ if (in)
+ {
+ close (pin[0]);
+ close (pin[1]);
+ }
+ if (out)
+ {
+ close (pout[0]);
+ close (pout[1]);
+ }
+ return (-1);
+ }
+ }
+
+ mutt_block_signals_system ();
+
+ if ((thepid = fork ()) == 0)
+ {
+ mutt_unblock_signals_system (0);
+
+ if (in)
+ {
+ close (pin[1]);
+ dup2 (pin[0], 0);
+ close (pin[0]);
+ }
+ else if (fdin != -1)
+ {
+ dup2 (fdin, 0);
+ close (fdin);
+ }
+
+ if (out)
+ {
+ close (pout[0]);
+ dup2 (pout[1], 1);
+ close (pout[1]);
+ }
+ else if (fdout != -1)
+ {
+ dup2 (fdout, 1);
+ close (fdout);
+ }
+
+ if (err)
+ {
+ close (perr[0]);
+ dup2 (perr[1], 2);
+ close (perr[1]);
+ }
+ else if (fderr != -1)
+ {
+ dup2 (fderr, 2);
+ close (fderr);
+ }
+
+ execl (EXECSHELL, "sh", "-c", cmd, NULL);
+ _exit (127);
+ }
+ else if (thepid == -1)
+ {
+ mutt_unblock_signals_system (1);
+
+ if (in)
+ {
+ close (pin[0]);
+ close (pin[1]);
+ }
+
+ if (out)
+ {
+ close (pout[0]);
+ close (pout[1]);
+ }
+
+ if (err)
+ {
+ close (perr[0]);
+ close (perr[1]);
+ }
+
+ return (-1);
+ }
+
+ if (out)
+ {
+ close (pout[1]);
+ *out = fdopen (pout[0], "r");
+ }
+
+ if (in)
+ {
+ close (pin[0]);
+ *in = fdopen (pin[1], "w");
+ }
+
+ if (err)
+ {
+ close (perr[1]);
+ *err = fdopen (perr[0], "r");
+ }
+
+ return (thepid);
+}
+
+pid_t mutt_create_filter (const char *s, FILE **in, FILE **out, FILE **err)
+{
+ return (mutt_create_filter_fd (s, in, out, err, -1, -1, -1));
+}
+
+int mutt_wait_filter (pid_t pid)
+{
+ int rc;
+
+ waitpid (pid, &rc, 0);
+ mutt_unblock_signals_system (1);
+ rc = WIFEXITED (rc) ? WEXITSTATUS (rc) : -1;
+
+ return rc;
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "sort.h"
+
+void mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf)
+{
+ int changed = h->changed;
+ int deleted = ctx->deleted;
+ int tagged = ctx->tagged;
+
+ if (ctx->readonly && flag != M_TAG)
+ return; /* don't modify anything if we are read-only */
+
+ switch (flag)
+ {
+ case M_DELETE:
+ if (bf)
+ {
+ if (!h->deleted)
+ {
+ h->deleted = 1;
+ ctx->deleted++;
+ }
+ }
+ else if (h->deleted)
+ {
+ h->deleted = 0;
+ ctx->deleted--;
+ }
+ break;
+
+ case M_NEW:
+ if (bf)
+ {
+ if (h->read || h->old)
+ {
+ h->old = 0;
+ ctx->new++;
+ if (h->read)
+ {
+ h->read = 0;
+ ctx->unread++;
+ }
+ h->changed = 1;
+ ctx->changed = 1;
+ }
+ }
+ else if (!h->read)
+ {
+ if (!h->old)
+ ctx->new--;
+ h->read = 1;
+ ctx->unread--;
+ h->changed = 1;
+ ctx->changed = 1;
+ }
+ break;
+
+ case M_OLD:
+ if (bf)
+ {
+ if (!h->old)
+ {
+ h->old = 1;
+ if (!h->read)
+ ctx->new--;
+ h->changed = 1;
+ ctx->changed = 1;
+ }
+ }
+ else if (h->old)
+ {
+ h->old = 0;
+ if (!h->read)
+ ctx->new++;
+ h->changed = 1;
+ ctx->changed = 1;
+ }
+ break;
+
+ case M_READ:
+ if (bf)
+ {
+ if (!h->read)
+ {
+ h->read = 1;
+ ctx->unread--;
+ if (!h->old)
+ ctx->new--;
+ h->changed = 1;
+ ctx->changed = 1;
+ }
+ }
+ else if (h->read)
+ {
+ h->read = 0;
+ ctx->unread++;
+ if (!h->old)
+ ctx->new++;
+ h->changed = 1;
+ ctx->changed = 1;
+ }
+ break;
+
+ case M_REPLIED:
+ if (bf)
+ {
+ if (!h->replied)
+ {
+ h->replied = 1;
+ if (!h->read)
+ {
+ h->read = 1;
+ ctx->unread--;
+ if (!h->old)
+ ctx->new--;
+ }
+ h->changed = 1;
+ ctx->changed = 1;
+ }
+ }
+ else if (h->replied)
+ {
+ h->replied = 0;
+ h->changed = 1;
+ ctx->changed = 1;
+ }
+ break;
+
+ case M_FLAG:
+ if (bf)
+ {
+ if (!h->flagged)
+ {
+ h->flagged = bf;
+ ctx->flagged++;
+ h->changed = 1;
+ ctx->changed = 1;
+ }
+ }
+ else if (h->flagged)
+ {
+ h->flagged = 0;
+ ctx->flagged--;
+ h->changed = 1;
+ ctx->changed = 1;
+ }
+ break;
+
+ case M_TAG:
+ if (bf)
+ {
+ if (!h->tagged)
+ {
+ h->tagged = 1;
+ ctx->tagged++;
+ }
+ }
+ else if (h->tagged)
+ {
+ h->tagged = 0;
+ ctx->tagged--;
+ }
+ break;
+ }
+
+ mutt_set_header_color(ctx, h);
+
+ /* if the message status has changed, we need to invalidate the cached
+ * search results so that any future search will match the current status
+ * of this message and not what it was at the time it was last searched.
+ */
+ if (h->searched && (changed != h->changed || deleted != ctx->deleted || tagged != ctx->tagged))
+ h->searched = 0;
+}
+
+void mutt_tag_set_flag (int flag, int bf)
+{
+ int j;
+
+ for (j = 0; j < Context->vcount; j++)
+ if (Context->hdrs[Context->v2r[j]]->tagged)
+ mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], flag, bf);
+}
+
+int mutt_thread_set_flag (HEADER *cur, int flag, int bf, int subthread)
+{
+ HEADER *start;
+
+ if ((Sort & SORT_MASK) != SORT_THREADS)
+ {
+ mutt_error ("Threading is not enabled.");
+ return (-1);
+ }
+
+ if (!subthread)
+ while (cur->parent)
+ cur = cur->parent;
+ start = cur;
+
+ mutt_set_flag (Context, cur, flag, bf);
+ if ((cur = cur->child) == NULL)
+ return (0);
+ FOREVER
+ {
+ mutt_set_flag (Context, cur, flag, bf);
+ if (cur->child)
+ cur = cur->child;
+ else if (cur->next)
+ cur = cur->next;
+ else
+ {
+ while (!cur->next)
+ {
+ cur = cur->parent;
+ if (cur == start)
+ return (0);
+ }
+ cur = cur->next;
+ }
+ }
+ /* not reached */
+}
+
+int mutt_change_flag (HEADER *h, int bf)
+{
+ int i, flag;
+
+ mvprintw (LINES - 1, 0, "%s flag? (D/N/O/r/*/!): ", bf ? "Set" : "Clear");
+ clrtoeol ();
+
+ if ((i = mutt_getch ()) == ERR)
+ {
+ CLEARLINE (LINES-1);
+ return (-1);
+ }
+
+ CLEARLINE (LINES-1);
+
+ switch (i)
+ {
+ case 'd':
+ case 'D':
+ flag = M_DELETE;
+ break;
+
+ case 'N':
+ case 'n':
+ flag = M_NEW;
+ break;
+
+ case 'o':
+ case 'O':
+ if (h)
+ mutt_set_flag (Context, h, M_READ, !bf);
+ else
+ mutt_tag_set_flag (M_READ, !bf);
+ flag = M_OLD;
+ break;
+
+ case 'r':
+ case 'R':
+ flag = M_REPLIED;
+ break;
+
+ case '*':
+ flag = M_TAG;
+ break;
+
+ case '!':
+ flag = M_FLAG;
+ break;
+
+ default:
+ BEEP ();
+ return (-1);
+ }
+
+ if (h)
+ mutt_set_flag (Context, h, flag, bf);
+ else
+ mutt_tag_set_flag (flag, bf);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+
+#include <ctype.h>
+#include <string.h>
+
+static const char *next_word (const char *s)
+{
+ while (*s && !ISSPACE (*s))
+ s++;
+ SKIPWS (s);
+ return s;
+}
+
+int mutt_check_month (const char *s)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ if (strncasecmp (s, Months[i], 3) == 0)
+ return (i);
+ return (-1); /* error */
+}
+
+static int is_day_name (const char *s)
+{
+ int i;
+
+ if (!ISSPACE (*(s+3)))
+ return 0;
+ for (i=0; i<7; i++)
+ if (strncasecmp (s, Weekdays[i], 3) == 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * A valid message separator looks like:
+ *
+ * From [ <return-path> ] <weekday> <month> <day> <time> [ <timezone> ] <year>
+ */
+
+time_t is_from (const char *s, char *path, size_t pathlen)
+{
+ struct tm tm;
+ int yr;
+
+ *path = 0;
+
+ if (strncmp ("From ", s, 5) != 0)
+ return 0;
+
+ s = next_word (s); /* skip over the From part. */
+ if (!*s)
+ return 0;
+
+ dprint (3, (debugfile, "\nis_from(): parsing: %s", s));
+
+ if (!is_day_name (s))
+ {
+ const char *p;
+ size_t len;
+
+ /* looks like we got the return-path, so extract it */
+ if (*s == '"')
+ {
+ /* sometimes we see bogus addresses like
+ * From "/foo/bar baz/"@dumbdar.com Sat Nov 22 15:29:32 PST 1997
+ */
+ p = s;
+ p++; /* skip over the quote */
+ do
+ {
+ if (!(p = strpbrk (p, "\\\"")))
+ return 0;
+ if (*p == '\\')
+ p += 2;
+ }
+ while (*p != '"');
+ while (*p && !ISSPACE (*p))
+ p++;
+ }
+ else
+ {
+ if ((p = strchr (s, ' ')) == NULL)
+ return 0;
+ }
+ len = (size_t) (p - s);
+ if (len + 1 > pathlen)
+ len = pathlen - 1;
+ memcpy (path, s, len);
+ path[len] = 0;
+
+ s = p + 1;
+ SKIPWS (s);
+ if (!*s)
+ return 0;
+
+ if (!is_day_name (s))
+ {
+ dprint(1, (debugfile, "is_from(): expected weekday, got: %s\n", s));
+ return 0;
+ }
+ }
+
+ s = next_word (s);
+ if (!*s) return 0;
+
+ /* do a quick check to make sure that this isn't really the day of the week.
+ * this could happen when receiving mail from a local user whose login name
+ * is the same as a three-letter abbreviation of the day of the week.
+ */
+ if (is_day_name (s))
+ {
+ s = next_word (s);
+ if (!*s) return 0;
+ }
+
+ /* now we should be on the month. */
+ if ((tm.tm_mon = mutt_check_month (s)) < 0) return 0;
+
+ /* day */
+ s = next_word (s);
+ if (!*s) return 0;
+ if (sscanf (s, "%d", &tm.tm_mday) != 1) return 0;
+
+ /* time */
+ s = next_word (s);
+ if (!*s) return 0;
+
+ /* Accept either HH:MM or HH:MM:SS */
+ if (sscanf (s, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 3);
+ else if (sscanf (s, "%d:%d", &tm.tm_hour, &tm.tm_min) == 2)
+ tm.tm_sec = 0;
+ else
+ return 0;
+
+ s = next_word (s);
+ if (!*s) return 0;
+
+ /* timezone? */
+ if (isalpha (*s) || *s == '+' || *s == '-')
+ {
+ s = next_word (s);
+ if (!*s) return 0;
+
+ /*
+ * some places have two timezone fields after the time, e.g.
+ * From xxxx@yyyyyyy.fr Wed Aug 2 00:39:12 MET DST 1995
+ */
+ if (isalpha (*s))
+ {
+ s = next_word (s);
+ if (!*s) return 0;
+ }
+ }
+
+ /* year */
+ if (sscanf (s, "%d", &yr) != 1) return 0;
+ tm.tm_year = yr > 1900 ? yr - 1900 : yr;
+
+ dprint (3,(debugfile, "is_from(): month=%d, day=%d, hr=%d, min=%d, sec=%d, yr=%d.\n",
+ tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year));
+
+ tm.tm_isdst = 0;
+ tm.tm_yday = 0;
+ tm.tm_wday = 0;
+
+ return (mutt_mktime (&tm, 0));
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file contains the structures needed to parse ``bind'' commands, as
+ * well as the default bindings for each menu.
+ *
+ * Notes:
+ *
+ * - If you want to bind \n or \r, use M_ENTER_S so that it will work
+ * correctly under both ncurses and S-Lang
+ *
+ * - If you need to bind a control char, use the octal value because the \cX
+ * construct does not work at this level.
+ *
+ */
+
+struct binding_t OpGeneric[] = {
+ { "top-page", OP_TOP_PAGE, "H" },
+ { "next-entry", OP_NEXT_ENTRY, "j" },
+ { "previous-entry", OP_PREV_ENTRY, "k" },
+ { "bottom-page", OP_BOTTOM_PAGE, "L" },
+ { "refresh", OP_REDRAW, "\014" },
+ { "middle-page", OP_MIDDLE_PAGE, "M" },
+ { "search-next", OP_SEARCH_NEXT, "n" },
+ { "exit", OP_EXIT, "q" },
+ { "tag-entry", OP_TAG, "t" },
+ { "next-page", OP_NEXT_PAGE, "z" },
+ { "previous-page", OP_PREV_PAGE, "Z" },
+ { "last-entry", OP_LAST_ENTRY, "*" },
+ { "first-entry", OP_FIRST_ENTRY, "=" },
+ { "enter-command", OP_ENTER_COMMAND, ":" },
+ { "next-line", OP_NEXT_LINE, ">" },
+ { "previous-line", OP_PREV_LINE, "<" },
+ { "half-up", OP_HALF_UP, "[" },
+ { "half-down", OP_HALF_DOWN, "]" },
+ { "help", OP_HELP, "?" },
+ { "tag-prefix", OP_TAG_PREFIX, ";" },
+ { "shell-escape", OP_SHELL_ESCAPE, "!" },
+ { "select-entry", OP_GENERIC_SELECT_ENTRY,M_ENTER_S },
+ { "search", OP_SEARCH, "/" },
+ { "search-reverse", OP_SEARCH_REVERSE, "\033/" },
+ { "search-opposite", OP_SEARCH_OPPOSITE, NULL },
+ { "jump", OP_JUMP, NULL },
+ { "current-top", OP_CURRENT_TOP, NULL },
+ { "current-middle", OP_CURRENT_MIDDLE, NULL },
+ { "current-bottom", OP_CURRENT_BOTTOM, NULL },
+ { NULL, 0, NULL }
+};
+
+struct binding_t OpMain[] = {
+ { "create-alias", OP_CREATE_ALIAS, "a" },
+ { "bounce-message", OP_BOUNCE_MESSAGE, "b" },
+ { "change-folder", OP_MAIN_CHANGE_FOLDER, "c" },
+ { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" },
+ { "copy-message", OP_COPY_MESSAGE, "C" },
+ { "decode-copy", OP_DECODE_COPY, "\033C" },
+ { "decode-save", OP_DECODE_SAVE, "\033s" },
+ { "delete-message", OP_DELETE, "d" },
+ { "delete-pattern", OP_MAIN_DELETE_PATTERN, "D" },
+ { "delete-thread", OP_DELETE_THREAD, "\004" },
+ { "delete-subthread", OP_DELETE_SUBTHREAD, "\033d" },
+ { "forward-message", OP_FORWARD_MESSAGE, "f" },
+ { "flag-message", OP_FLAG_MESSAGE, "F" },
+ { "group-reply", OP_GROUP_REPLY, "g" },
+#ifdef USE_POP
+ { "fetch-mail", OP_MAIN_FETCH_MAIL, "G" },
+#endif
+ { "display-headers", OP_DISPLAY_HEADERS, "h" },
+ { "next-undeleted", OP_MAIN_NEXT_UNDELETED, "j" },
+ { "previous-undeleted", OP_MAIN_PREV_UNDELETED, "k" },
+ { "limit", OP_MAIN_LIMIT, "l" },
+ { "list-reply", OP_LIST_REPLY, "L" },
+ { "mail", OP_MAIL, "m" },
+ { "toggle-new", OP_TOGGLE_NEW, "N" },
+ { "toggle-write", OP_TOGGLE_WRITE, "%" },
+ { "next-thread", OP_MAIN_NEXT_THREAD, "\016" },
+ { "next-subthread", OP_MAIN_NEXT_SUBTHREAD, "\033n" },
+ { "query", OP_QUERY, "Q" },
+ { "quit", OP_QUIT, "q" },
+ { "reply", OP_REPLY, "r" },
+ { "show-limit", OP_MAIN_SHOW_LIMIT, "\033l" },
+ { "sort-mailbox", OP_SORT, "o" },
+ { "sort-reverse", OP_SORT_REVERSE, "O" },
+ { "print-message", OP_PRINT, "p" },
+ { "previous-thread", OP_MAIN_PREV_THREAD, "\020" },
+ { "previous-subthread", OP_MAIN_PREV_SUBTHREAD, "\033p" },
+ { "recall-message", OP_RECALL_MESSAGE, "R" },
+ { "read-thread", OP_MAIN_READ_THREAD, "\022" },
+ { "read-subthread", OP_MAIN_READ_SUBTHREAD, "\033r" },
+ { "save-message", OP_SAVE, "s" },
+ { "tag-pattern", OP_MAIN_TAG_PATTERN, "T" },
+ { "tag-subthread", OP_TAG_SUBTHREAD, NULL },
+ { "tag-thread", OP_TAG_THREAD, "\033t" },
+ { "untag-pattern", OP_MAIN_UNTAG_PATTERN, "\024" },
+ { "undelete-message", OP_UNDELETE, "u" },
+ { "undelete-pattern", OP_MAIN_UNDELETE_PATTERN, "U"},
+ { "undelete-subthread", OP_UNDELETE_SUBTHREAD, "\033u" },
+ { "undelete-thread", OP_UNDELETE_THREAD, "\025" },
+ { "view-attachments", OP_VIEW_ATTACHMENTS, "v" },
+ { "show-version", OP_VERSION, "V" },
+ { "set-flag", OP_MAIN_SET_FLAG, "w" },
+ { "clear-flag", OP_MAIN_CLEAR_FLAG, "W" },
+ { "display-message", OP_DISPLAY_MESSAGE, M_ENTER_S },
+ { "sync-mailbox", OP_MAIN_SYNC_FOLDER, "$" },
+ { "display-address", OP_DISPLAY_ADDRESS, "@" },
+ { "pipe-message", OP_PIPE, "|" },
+ { "next-new", OP_MAIN_NEXT_NEW, "\t" },
+ { "previous-new", OP_MAIN_PREV_NEW, "\033\t" },
+ { "next-unread", OP_MAIN_NEXT_UNREAD, NULL },
+ { "previous-unread", OP_MAIN_PREV_UNREAD, NULL },
+
+
+
+#ifdef _PGPPATH
+ { "extract-keys", OP_EXTRACT_KEYS, "\013" },
+ { "forget-passphrase", OP_FORGET_PASSPHRASE, "\006" },
+ { "mail-key", OP_MAIL_KEY, "\033k" },
+#endif
+
+
+
+ { NULL, 0, NULL }
+};
+
+struct binding_t OpPager[] = {
+ { "create-alias", OP_CREATE_ALIAS, "a" },
+ { "bounce-message", OP_BOUNCE_MESSAGE, "b" },
+ { "change-folder", OP_MAIN_CHANGE_FOLDER, "c" },
+ { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" },
+ { "copy-message", OP_COPY_MESSAGE, "C" },
+ { "decode-copy", OP_DECODE_COPY, "\033C" },
+ { "delete-message", OP_DELETE, "d" },
+ { "delete-thread", OP_DELETE_THREAD, "\004" },
+ { "delete-subthread", OP_DELETE_SUBTHREAD, "\033d" },
+ { "forward-message", OP_FORWARD_MESSAGE, "f" },
+ { "flag-message", OP_FLAG_MESSAGE, "F" },
+ { "group-reply", OP_GROUP_REPLY, "g" },
+ { "display-headers", OP_DISPLAY_HEADERS, "h" },
+ { "exit", OP_PAGER_EXIT, "i" },
+ { "next-undeleted", OP_MAIN_NEXT_UNDELETED, "j" },
+ { "next-entry", OP_NEXT_ENTRY, "J" },
+ { "previous-undeleted",OP_MAIN_PREV_UNDELETED, "k" },
+ { "previous-entry", OP_PREV_ENTRY, "K" },
+ { "list-reply", OP_LIST_REPLY, "L" },
+ { "redraw-screen", OP_REDRAW, "\014" },
+ { "mail", OP_MAIL, "m" },
+ { "mark-as-new", OP_TOGGLE_NEW, "N" },
+ { "search-next", OP_SEARCH_NEXT, "n" },
+ { "next-thread", OP_MAIN_NEXT_THREAD, "\016" },
+ { "next-subthread", OP_MAIN_NEXT_SUBTHREAD, "\033n" },
+ { "print-message", OP_PRINT, "p" },
+ { "previous-thread", OP_MAIN_PREV_THREAD, "\020" },
+ { "previous-subthread",OP_MAIN_PREV_SUBTHREAD, "\033p" },
+ { "quit", OP_QUIT, "Q" },
+ { "reply", OP_REPLY, "r" },
+ { "recall-message", OP_RECALL_MESSAGE, "R" },
+ { "read-thread", OP_MAIN_READ_THREAD, "\022" },
+ { "read-subthread", OP_MAIN_READ_SUBTHREAD, "\033r" },
+ { "save-message", OP_SAVE, "s" },
+ { "skip-quoted", OP_PAGER_SKIP_QUOTED, "S" },
+ { "decode-save", OP_DECODE_SAVE, "\033s" },
+ { "tag-message", OP_TAG, "t" },
+ { "toggle-quoted", OP_PAGER_HIDE_QUOTED, "T" },
+ { "undelete-message", OP_UNDELETE, "u" },
+ { "undelete-subthread",OP_UNDELETE_SUBTHREAD, "\033u" },
+ { "undelete-thread", OP_UNDELETE_THREAD, "\025" },
+ { "view-attachments", OP_VIEW_ATTACHMENTS, "v" },
+ { "show-version", OP_VERSION, "V" },
+ { "search-toggle", OP_SEARCH_TOGGLE, "\\" },
+ { "display-address", OP_DISPLAY_ADDRESS, "@" },
+ { "next-new", OP_MAIN_NEXT_NEW, "\t" },
+ { "pipe-message", OP_PIPE, "|" },
+ { "help", OP_HELP, "?" },
+ { "next-page", OP_NEXT_PAGE, " " },
+ { "previous-page", OP_PREV_PAGE, "-" },
+ { "top", OP_PAGER_TOP, "^" },
+ { "bottom", OP_PAGER_BOTTOM, "$" },
+ { "shell-escape", OP_SHELL_ESCAPE, "!" },
+ { "enter-command", OP_ENTER_COMMAND, ":" },
+ { "search", OP_SEARCH, "/" },
+ { "search-reverse", OP_SEARCH_REVERSE, "\033/" },
+ { "search-opposite", OP_SEARCH_OPPOSITE, NULL },
+ { "next-line", OP_NEXT_LINE, M_ENTER_S },
+ { "jump", OP_JUMP, NULL },
+ { "next-unread", OP_MAIN_NEXT_UNREAD, NULL },
+ { "previous-new", OP_MAIN_PREV_NEW, NULL },
+ { "previous-unread", OP_MAIN_PREV_UNREAD, NULL },
+ { "half-up", OP_HALF_UP, NULL },
+ { "half-down", OP_HALF_DOWN, NULL },
+ { "previous-line", OP_PREV_LINE, NULL },
+
+
+
+
+
+
+
+
+
+
+
+
+#ifdef _PGPPATH
+ { "extract-keys", OP_EXTRACT_KEYS, "\013" },
+ { "forget-passphrase",OP_FORGET_PASSPHRASE, "\006" },
+ { "mail-key", OP_MAIL_KEY, "\033k" },
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { NULL, 0, NULL }
+};
+
+struct binding_t OpAttach[] = {
+ { "bounce-message", OP_BOUNCE_MESSAGE, "b" },
+ { "display-headers", OP_DISPLAY_HEADERS, "h" },
+ { "print-entry", OP_PRINT, "p" },
+ { "save-entry", OP_SAVE, "s" },
+ { "pipe-entry", OP_PIPE, "|" },
+ { "view-mailcap", OP_ATTACH_VIEW_MAILCAP, "m" },
+ { "reply", OP_REPLY, "r" },
+ { "group-reply", OP_GROUP_REPLY, "g" },
+ { "list-reply", OP_LIST_REPLY, "L" },
+ { "forward-message", OP_FORWARD_MESSAGE, "f" },
+ { "view-text", OP_ATTACH_VIEW_TEXT, "T" },
+ { "view-attach", OP_VIEW_ATTACH, M_ENTER_S },
+ { "delete-entry", OP_DELETE, "d" },
+ { "undelete-entry", OP_UNDELETE, "u" },
+
+
+
+#ifdef _PGPPATH
+ { "extract-keys", OP_EXTRACT_KEYS, "\013" },
+#endif
+
+
+
+ { NULL, 0, NULL }
+};
+
+struct binding_t OpCompose[] = {
+ { "attach-file", OP_COMPOSE_ATTACH_FILE, "a" },
+ { "edit-bcc", OP_COMPOSE_EDIT_BCC, "b" },
+ { "edit-cc", OP_COMPOSE_EDIT_CC, "c" },
+ { "copy-file", OP_SAVE, "C" },
+ { "detach-file", OP_DELETE, "D" },
+ { "display-headers", OP_DISPLAY_HEADERS, "h" },
+ { "edit-description", OP_COMPOSE_EDIT_DESCRIPTION, "d" },
+ { "edit-message", OP_COMPOSE_EDIT_MESSAGE, "e" },
+ { "edit-headers", OP_COMPOSE_EDIT_HEADERS, "E" },
+ { "edit-file", OP_COMPOSE_EDIT_FILE, "\030e" },
+ { "edit-encoding", OP_COMPOSE_EDIT_ENCODING, "\005" },
+ { "edit-from", OP_COMPOSE_EDIT_FROM, "\033f" },
+ { "edit-fcc", OP_COMPOSE_EDIT_FCC, "f" },
+ { "filter-entry", OP_FILTER, "F" },
+ { "ispell", OP_COMPOSE_ISPELL, "i" },
+ { "print-entry", OP_PRINT, "l" },
+ { "redraw-screen", OP_REDRAW, "\014" },
+ { "edit-mime", OP_COMPOSE_EDIT_MIME, "m" },
+ { "new-mime", OP_COMPOSE_NEW_MIME, "n" },
+ { "postpone-message", OP_COMPOSE_POSTPONE_MESSAGE, "P" },
+ { "edit-reply-to", OP_COMPOSE_EDIT_REPLY_TO, "r" },
+ { "rename-file", OP_COMPOSE_RENAME_FILE, "R" },
+ { "edit-subject", OP_COMPOSE_EDIT_SUBJECT, "s" },
+ { "edit-to", OP_COMPOSE_EDIT_TO, "t" },
+ { "edit-type", OP_COMPOSE_EDIT_TYPE, "\024" },
+ { "toggle-unlink", OP_COMPOSE_TOGGLE_UNLINK, "u" },
+ { "view-attach", OP_VIEW_ATTACH, M_ENTER_S },
+ { "send-message", OP_COMPOSE_SEND_MESSAGE, "y" },
+ { "pipe-entry", OP_PIPE, "|" },
+
+#ifdef _PGPPATH
+ { "attach-key", OP_COMPOSE_ATTACH_KEY, "\033k" },
+ { "forget-passphrase",OP_FORGET_PASSPHRASE, "\006" },
+ { "pgp-menu", OP_COMPOSE_PGP_MENU, "p" },
+#endif
+
+ { NULL, 0, NULL }
+};
+
+struct binding_t OpPost[] = {
+ { "delete-entry", OP_DELETE, "d" },
+ { "undelete-entry", OP_UNDELETE, "u" },
+ { NULL, 0, NULL }
+};
+
+/* The file browser */
+struct binding_t OpBrowser[] = {
+ { "change-dir", OP_CHANGE_DIRECTORY, "c" },
+ { "enter-mask", OP_ENTER_MASK, "m" },
+ { "sort", OP_SORT, "o" },
+ { "sort-reverse", OP_SORT_REVERSE, "O" },
+ { "select-new", OP_BROWSER_NEW_FILE, "N" },
+ { "check-new", OP_CHECK_NEW, "\t" },
+ { NULL, 0, NULL }
+};
+
+/* External Query Menu */
+struct binding_t OpQuery[] = {
+ { "create-alias", OP_CREATE_ALIAS, "a" },
+ { "search", OP_SEARCH, "/" },
+ { "search-reverse", OP_SEARCH_REVERSE, "\033/" },
+ { "search-opposite", OP_SEARCH_OPPOSITE, NULL },
+ { "mail", OP_MAIL, "m" },
+ { "query", OP_QUERY, "Q" },
+ { "query-append", OP_QUERY_APPEND, "A" },
+ { NULL, 0, NULL }
+};
+
+struct binding_t OpEditor[] = {
+ { "bol", OP_EDITOR_BOL, "\001" },
+ { "backward-char", OP_EDITOR_BACKWARD_CHAR, "\002" },
+ { "delete-char", OP_EDITOR_DELETE_CHAR, "\004" },
+ { "eol", OP_EDITOR_EOL, "\005" },
+ { "forward-char", OP_EDITOR_FORWARD_CHAR, "\006" },
+ { "backspace", OP_EDITOR_BACKSPACE, "\010" },
+ { "kill-eol", OP_EDITOR_KILL_EOL, "\013" },
+ { "kill-line", OP_EDITOR_KILL_LINE, "\025" },
+ { "quote-char", OP_EDITOR_QUOTE_CHAR, "\026" },
+ { "kill-word", OP_EDITOR_KILL_WORD, "\027" },
+ { "complete", OP_EDITOR_COMPLETE, "\t" },
+ { "complete-query", OP_EDITOR_COMPLETE_QUERY, "\024" },
+ { "buffy-cycle", OP_EDITOR_BUFFY_CYCLE, " " },
+ { "history-up", OP_EDITOR_HISTORY_UP, NULL },
+ { "history-down", OP_EDITOR_HISTORY_DOWN, NULL },
+ { NULL, 0, NULL }
+};
+
+
+
+#ifdef _PGPPATH
+struct binding_t OpPgp[] = {
+ { "verify-key", OP_VERIFY_KEY, "c" },
+ { "view-name", OP_VIEW_ID, "%" },
+ { "search-next", OP_SEARCH_NEXT, "n" },
+ { "search", OP_SEARCH, "/" },
+ { "search-reverse", OP_SEARCH_REVERSE, "\033/" },
+ { NULL, 0, NULL }
+};
+#endif /* _PGPPATH */
--- /dev/null
+#!/bin/sh
+
+echo '/* Automatically generated by gen_defs. Do not edit! */'
+echo ''
+
+for mode in help defs; do
+ case $mode in
+ help)
+ echo "#ifdef HELP_C"
+ echo "const char *HelpStrings[] = {"
+ expr='s;^[^ ]* *\(.*\); \1,;'
+ ;;
+ *)
+ echo "enum {"
+ expr='s;^\([^ ]*\).*; \1,;'
+ ;;
+ esac
+ for i in $*; do
+ sed -e "$expr" < $i
+ done
+ if test $mode = help; then
+ echo ' NULL'
+ else
+ echo ' OP_MAX'
+ fi
+ echo "};"
+ if test $mode = help; then
+ echo "#endif /* MAIN_C */"
+ echo ''
+ fi
+done
--- /dev/null
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "mutt.h"
+
+#ifndef STDC_HEADERS
+int fclose ();
+#endif
+
+/* poor man's version of getdomainname() for systems where it does not return
+ * return the DNS domain, but the NIS domain.
+ */
+
+int getdnsdomainname (char *s, size_t l)
+{
+ FILE *f;
+ char tmp[1024];
+ char *p = NULL;
+
+ if ((f = fopen ("/etc/resolv.conf", "r")) == NULL) return (-1);
+
+ tmp[sizeof (tmp) - 1] = 0;
+
+ l--; /* save room for the terminal \0 */
+
+ while (fgets (tmp, sizeof (tmp) - 1, f) != NULL)
+ {
+ p = tmp;
+ while (ISSPACE (*p)) p++;
+ if (strncmp ("domain", p, 6) == 0 || strncmp ("search", p, 6) == 0)
+ {
+ p += 6;
+ while (ISSPACE (*p)) p++;
+
+ if (*p)
+ {
+ while (*p && !ISSPACE (*p) && l > 0)
+ {
+ *s++ = *p++;
+ l--;
+ }
+ if (*(s-1) == '.') s--;
+ *s = 0;
+
+ fclose (f);
+ return (0);
+ }
+ }
+ }
+
+ fclose (f);
+ return (-1);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+WHERE void (*mutt_error) (const char *, ...);
+
+WHERE CONTEXT *Context;
+
+WHERE char Errorbuf[SHORT_STRING];
+
+WHERE char *AliasFile;
+WHERE char *AliasFmt;
+WHERE char *Attribution;
+WHERE char *Charset;
+WHERE char *DefaultHook;
+WHERE char *DateFmt;
+WHERE char *DeleteFmt;
+WHERE char *DsnNotify;
+WHERE char *DsnReturn;
+WHERE char *Editor;
+WHERE char *EmptyTo;
+WHERE char *EscChar;
+WHERE char *FolderFormat;
+WHERE char *ForwFmt;
+WHERE char *Fqdn;
+WHERE char *HdrFmt;
+WHERE char *Homedir;
+WHERE char *Hostname;
+WHERE char *InReplyTo;
+WHERE char *Inbox;
+WHERE char *Ispell;
+WHERE char *Locale;
+WHERE char *MailcapPath;
+WHERE char *Maildir;
+WHERE char *MsgFmt;
+WHERE char *Muttrc INITVAL (NULL);
+WHERE char *Outbox;
+WHERE char *Pager;
+WHERE char *PagerFmt;
+WHERE char *PipeSep;
+#ifdef USE_POP
+WHERE char *PopHost;
+WHERE char *PopPass;
+WHERE char *PopUser;
+#endif
+WHERE char *PostIndentString;
+WHERE char *Postponed;
+WHERE char *Prefix;
+WHERE char *PrintCmd;
+WHERE char *QueryCmd;
+WHERE char *Realname;
+WHERE char *Sendmail;
+WHERE char *Shell;
+WHERE char *Signature;
+WHERE char *SimpleSearch;
+WHERE char *Spoolfile;
+WHERE char *StChars;
+WHERE char *Status;
+WHERE char *Tempdir;
+WHERE char *Tochars;
+WHERE char *Username;
+WHERE char *Visual;
+
+WHERE char *LastFolder;
+
+WHERE LIST *AutoViewList INITVAL(0);
+WHERE LIST *AlternativeOrderList INITVAL(0);
+WHERE LIST *HeaderOrderList INITVAL(0);
+WHERE LIST *Ignore INITVAL(0);
+WHERE LIST *UnIgnore INITVAL(0);
+WHERE LIST *MailLists INITVAL(0);
+
+/* bit vector for boolean variables */
+#ifdef MAIN_C
+unsigned char Options[(OPTMAX + 7)/8];
+#else
+extern unsigned char Options[];
+#endif
+
+/* bit vector for the yes/no/ask variable type */
+WHERE unsigned long QuadOptions INITVAL (0);
+
+WHERE unsigned short Counter INITVAL (0);
+
+WHERE short HistSize;
+WHERE short PagerContext;
+WHERE short PagerIndexLines;
+WHERE short PopPort;
+WHERE short ReadInc;
+WHERE short SendmailWait;
+WHERE short Timeout;
+WHERE short WriteInc;
+
+/* vector to store received signals */
+WHERE short Signals INITVAL (0);
+
+WHERE ALIAS *Aliases INITVAL (0);
+WHERE LIST *UserHeader INITVAL (0);
+
+#ifdef DEBUG
+WHERE FILE *debugfile INITVAL (0);
+WHERE int debuglevel INITVAL (0);
+#endif
+
+#ifdef USE_SETGID
+WHERE gid_t MailGid;
+WHERE gid_t UserGid;
+#endif /* USE_SETGID */
+
+#ifdef MAIN_C
+const char *Weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+const char *Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "ERR" };
+
+const char *BodyTypes[] = { "x-unknown", "audio", "application", "image", "message", "multipart", "text", "video" };
+const char *BodyEncodings[] = { "x-unknown", "7bit", "8bit", "quoted-printable", "base64", "binary" };
+#else
+extern const char *Weekdays[];
+extern const char *Months[];
+#endif
+
+#ifdef MAIN_C
+/* so that global vars get included */
+#include "mx.h"
+#include "mutt_regex.h"
+#include "buffy.h"
+#include "sort.h"
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+#endif /* MAIN_C */
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "rfc1524.h"
+#include "keymap.h"
+#include "mime.h"
+#include "copy.h"
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+typedef void handler_f (BODY *, STATE *);
+typedef handler_f *handler_t;
+
+int Index_hex[128] = {
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
+ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
+};
+
+int Index_64[128] = {
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+ 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+ 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+ 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+
+void mutt_decode_xbit (STATE *s, long len, int istext)
+{
+ int linelen;
+ char buffer[LONG_STRING];
+
+ if (istext)
+ {
+ while (len > 0)
+ {
+ if (fgets (buffer, LONG_STRING, s->fpin) == 0) return;
+ linelen = strlen (buffer);
+ len -= linelen;
+ if (linelen >= 2 && buffer[linelen-2] == '\r')
+ {
+ buffer[linelen-2] = '\n';
+ buffer[linelen-1] = 0;
+ }
+ if (s->prefix) state_puts (s->prefix, s);
+ state_puts (buffer, s);
+ }
+ }
+ else
+ mutt_copy_bytes (s->fpin, s->fpout, len);
+}
+
+void mutt_decode_quoted (STATE *s, long len, int istext)
+{
+ char *c, buffer[LONG_STRING];
+ int ch, soft = 0;
+
+ while (len > 0)
+ {
+ if (fgets (buffer, LONG_STRING, s->fpin) == NULL)
+ {
+ dprint (1, (debugfile, "mutt_decode_quoted: unexpected EOF.\n"));
+ state_puts ("[-- Error: unexpected end of file! --]\n", s);
+ break;
+ }
+ c = buffer;
+ len -= strlen (buffer);
+ if (s->prefix && !soft) state_puts (s->prefix, s);
+ soft = 0;
+ while (*c)
+ {
+ if (*c == '=')
+ {
+ if (c[1] == '\n' || c[1] == '\r' || c[1] == ' ' || c[1] == '\t')
+ {
+ /* Skip whitespace at the end of the line since MIME does not
+ * allow for it
+ */
+ soft = 1;
+ break;
+ }
+ ch = hexval ((int) c[1]) << 4;
+ ch |= hexval ((int) c[2]);
+ state_putc (ch, s);
+ c += 3;
+ }
+ else if (istext && c[0] == '\r' && c[1] == '\n')
+ {
+ state_putc ('\n', s);
+ break;
+ }
+ else
+ {
+ state_putc (*c, s);
+ c++;
+ }
+ }
+ }
+}
+
+void mutt_decode_base64 (STATE *s, long len, int istext)
+{
+ char buf[5];
+ int c1, c2, c3, c4, ch, cr = 0, i;
+
+ buf[4] = 0;
+
+ if (s->prefix) state_puts (s->prefix, s);
+
+ while (len > 0)
+ {
+ for (i = 0 ; i < 4 && len > 0 ; len--)
+ {
+ if ((ch = fgetc (s->fpin)) == EOF)
+ return;
+ if (!ISSPACE (ch))
+ buf[i++] = ch;
+ }
+ if (i != 4)
+ return; /* didn't get a multiple of four chars! */
+
+ c1 = base64val ((int) buf[0]);
+ c2 = base64val ((int) buf[1]);
+ ch = (c1 << 2) | (c2 >> 4);
+
+ if (cr && ch != '\n') state_putc ('\r', s);
+ cr = 0;
+
+ if (istext && ch == '\r')
+ cr = 1;
+ else
+ {
+ state_putc (ch, s);
+ if (ch == '\n' && s->prefix) state_puts (s->prefix, s);
+ }
+
+ if (buf[2] == '=')
+ break;
+ c3 = base64val ((int) buf[2]);
+ ch = ((c2 & 0xf) << 4) | (c3 >> 2);
+
+ if (cr && ch != '\n')
+ state_putc ('\r', s);
+ cr = 0;
+
+ if (istext && ch == '\r')
+ cr = 1;
+ else
+ {
+ state_putc (ch, s);
+ if (ch == '\n' && s->prefix)
+ state_puts (s->prefix, s);
+ }
+
+ if (buf[3] == '=') break;
+ c4 = base64val ((int) buf[3]);
+ ch = ((c3 & 0x3) << 6) | c4;
+
+ if (cr && ch != '\n')
+ state_putc ('\r', s);
+ cr = 0;
+
+ if (istext && ch == '\r')
+ cr = 1;
+ else
+ {
+ state_putc (ch, s);
+ if (ch == '\n' && s->prefix)
+ state_puts (s->prefix, s);
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------------
+ * A (not so) minimal implementation of RFC1563.
+ */
+
+#define IndentSize (4)
+
+enum { RICH_PARAM=0, RICH_BOLD, RICH_UNDERLINE, RICH_ITALIC, RICH_NOFILL,
+ RICH_INDENT, RICH_INDENT_RIGHT, RICH_EXCERPT, RICH_CENTER, RICH_FLUSHLEFT,
+ RICH_FLUSHRIGHT, RICH_COLOR, RICH_LAST_TAG };
+
+static struct {
+ const char *tag_name;
+ int index;
+} EnrichedTags[] = {
+ { "param", RICH_PARAM },
+ { "bold", RICH_BOLD },
+ { "italic", RICH_ITALIC },
+ { "underline", RICH_UNDERLINE },
+ { "nofill", RICH_NOFILL },
+ { "excerpt", RICH_EXCERPT },
+ { "indent", RICH_INDENT },
+ { "indentright", RICH_INDENT_RIGHT },
+ { "center", RICH_CENTER },
+ { "flushleft", RICH_FLUSHLEFT },
+ { "flushright", RICH_FLUSHRIGHT },
+ { "flushboth", RICH_FLUSHLEFT },
+ { "color", RICH_COLOR },
+ { "x-color", RICH_COLOR },
+ { NULL, -1 }
+};
+
+struct enriched_state
+{
+ char *buffer;
+ char *line;
+ char *param;
+ size_t buff_len;
+ size_t line_len;
+ size_t line_used;
+ size_t line_max;
+ size_t indent_len;
+ size_t word_len;
+ size_t buff_used;
+ size_t param_len;
+ int tag_level[RICH_LAST_TAG];
+ int WrapMargin;
+ STATE *s;
+};
+
+static void enriched_wrap (struct enriched_state *stte)
+{
+ int x;
+ int extra;
+
+ if (stte->line_len)
+ {
+ if (stte->tag_level[RICH_CENTER] || stte->tag_level[RICH_FLUSHRIGHT])
+ {
+ /* Strip trailing white space */
+ size_t y = stte->line_used - 1;
+
+ while (y && ISSPACE (stte->line[y]))
+ {
+ stte->line[y] = '\0';
+ y--;
+ stte->line_used--;
+ stte->line_len--;
+ }
+ if (stte->tag_level[RICH_CENTER])
+ {
+ /* Strip leading whitespace */
+ y = 0;
+
+ while (stte->line[y] && ISSPACE (stte->line[y]))
+ y++;
+ if (y)
+ {
+ size_t z;
+
+ for (z = y ; z <= stte->line_used; z++)
+ {
+ stte->line[z - y] = stte->line[z];
+ }
+
+ stte->line_len -= y;
+ stte->line_used -= y;
+ }
+ }
+ }
+
+ extra = stte->WrapMargin - stte->line_len - stte->indent_len -
+ (stte->tag_level[RICH_INDENT_RIGHT] * IndentSize);
+ if (extra > 0)
+ {
+ if (stte->tag_level[RICH_CENTER])
+ {
+ x = extra / 2;
+ while (x)
+ {
+ state_putc (' ', stte->s);
+ x--;
+ }
+ }
+ else if (stte->tag_level[RICH_FLUSHRIGHT])
+ {
+ x = extra-1;
+ while (x)
+ {
+ state_putc (' ', stte->s);
+ x--;
+ }
+ }
+ }
+ state_puts (stte->line, stte->s);
+ }
+
+ state_putc ('\n', stte->s);
+ stte->line[0] = '\0';
+ stte->line_len = 0;
+ stte->line_used = 0;
+ stte->indent_len = 0;
+ if (stte->s->prefix)
+ {
+ state_puts (stte->s->prefix, stte->s);
+ stte->indent_len += strlen (stte->s->prefix);
+ }
+
+ if (stte->tag_level[RICH_EXCERPT])
+ {
+ x = stte->tag_level[RICH_EXCERPT];
+ while (x)
+ {
+ if (stte->s->prefix)
+ {
+ state_puts (stte->s->prefix, stte->s);
+ stte->indent_len += strlen (stte->s->prefix);
+ }
+ else
+ {
+ state_puts ("> ", stte->s);
+ stte->indent_len += strlen ("> ");
+ }
+ x--;
+ }
+ }
+ else
+ stte->indent_len = 0;
+ if (stte->tag_level[RICH_INDENT])
+ {
+ x = stte->tag_level[RICH_INDENT] * IndentSize;
+ stte->indent_len += x;
+ while (x)
+ {
+ state_putc (' ', stte->s);
+ x--;
+ }
+ }
+}
+
+static void enriched_flush (struct enriched_state *stte, int wrap)
+{
+ if (!stte->tag_level[RICH_NOFILL] && (stte->line_len + stte->word_len >
+ (stte->WrapMargin - (stte->tag_level[RICH_INDENT_RIGHT] * IndentSize) -
+ stte->indent_len)))
+ enriched_wrap (stte);
+
+ if (stte->buff_used)
+ {
+ stte->buffer[stte->buff_used] = '\0';
+ stte->line_used += stte->buff_used;
+ if (stte->line_used > stte->line_max)
+ {
+ stte->line_max = stte->line_used;
+ safe_realloc ((void **) &stte->line, stte->line_max + 1);
+ }
+ strcat (stte->line, stte->buffer);
+ stte->line_len += stte->word_len;
+ stte->word_len = 0;
+ stte->buff_used = 0;
+ }
+ if (wrap)
+ enriched_wrap(stte);
+}
+
+
+static void enriched_putc (int c, struct enriched_state *stte)
+{
+ if (stte->tag_level[RICH_PARAM])
+ {
+ if (stte->tag_level[RICH_COLOR])
+ {
+ stte->param[stte->param_len++] = c;
+ }
+ return; /* nothing to do */
+ }
+
+ /* see if more space is needed (plus extra for possible rich characters) */
+ if (stte->buff_len < stte->buff_used + 3)
+ {
+ stte->buff_len += LONG_STRING;
+ safe_realloc ((void **) &stte->buffer, stte->buff_len + 1);
+ }
+
+ if ((!stte->tag_level[RICH_NOFILL] && ISSPACE (c)) || c == '\0' )
+ {
+ if (c == '\t')
+ stte->word_len += 8 - (stte->line_len + stte->word_len) % 8;
+ else
+ stte->word_len++;
+
+ stte->buffer[stte->buff_used++] = c;
+ enriched_flush (stte, 0);
+ }
+ else
+ {
+ if (stte->s->flags & M_DISPLAY)
+ {
+ if (stte->tag_level[RICH_BOLD])
+ {
+ stte->buffer[stte->buff_used++] = c;
+ stte->buffer[stte->buff_used++] = '\010';
+ stte->buffer[stte->buff_used++] = c;
+ }
+ else if (stte->tag_level[RICH_UNDERLINE])
+ {
+
+ stte->buffer[stte->buff_used++] = '_';
+ stte->buffer[stte->buff_used++] = '\010';
+ stte->buffer[stte->buff_used++] = c;
+ }
+ else if (stte->tag_level[RICH_ITALIC])
+ {
+ stte->buffer[stte->buff_used++] = c;
+ stte->buffer[stte->buff_used++] = '\010';
+ stte->buffer[stte->buff_used++] = '_';
+ }
+ else
+ {
+ stte->buffer[stte->buff_used++] = c;
+ }
+ }
+ else
+ {
+ stte->buffer[stte->buff_used++] = c;
+ }
+ stte->word_len++;
+ }
+}
+
+static void enriched_puts (char *s, struct enriched_state *stte)
+{
+ char *c;
+
+ if (stte->buff_len < stte->buff_used + strlen(s))
+ {
+ stte->buff_len += LONG_STRING;
+ safe_realloc ((void **) &stte->buffer, stte->buff_len + 1);
+ }
+ c = s;
+ while (*c)
+ {
+ stte->buffer[stte->buff_used++] = *c;
+ c++;
+ }
+}
+
+static void enriched_set_flags (const char *tag, struct enriched_state *stte)
+{
+ const char *tagptr = tag;
+ int i, j;
+
+ if (*tagptr == '/')
+ tagptr++;
+
+ for (i = 0, j = -1; EnrichedTags[i].tag_name; i++)
+ if (strcasecmp (EnrichedTags[i].tag_name,tagptr) == 0)
+ {
+ j = EnrichedTags[i].index;
+ break;
+ }
+
+ if (j != -1)
+ {
+ if (j == RICH_CENTER || j == RICH_FLUSHLEFT || j == RICH_FLUSHRIGHT)
+ enriched_flush (stte, 1);
+
+ if (*tag == '/')
+ {
+ if (stte->tag_level[j]) /* make sure not to go negative */
+ stte->tag_level[j]--;
+ if ((stte->s->flags & M_DISPLAY) && j == RICH_PARAM && stte->tag_level[RICH_COLOR])
+ {
+ stte->param[stte->param_len] = '\0';
+ if (!strcasecmp(stte->param, "black"))
+ {
+ enriched_puts("\033[30m", stte);
+ }
+ else if (!strcasecmp(stte->param, "red"))
+ {
+ enriched_puts("\033[31m", stte);
+ }
+ else if (!strcasecmp(stte->param, "green"))
+ {
+ enriched_puts("\033[32m", stte);
+ }
+ else if (!strcasecmp(stte->param, "yellow"))
+ {
+ enriched_puts("\033[33m", stte);
+ }
+ else if (!strcasecmp(stte->param, "blue"))
+ {
+ enriched_puts("\033[34m", stte);
+ }
+ else if (!strcasecmp(stte->param, "magenta"))
+ {
+ enriched_puts("\033[35m", stte);
+ }
+ else if (!strcasecmp(stte->param, "cyan"))
+ {
+ enriched_puts("\033[36m", stte);
+ }
+ else if (!strcasecmp(stte->param, "white"))
+ {
+ enriched_puts("\033[37m", stte);
+ }
+ stte->param_len = 0;
+ stte->param[0] = '\0';
+ }
+ if ((stte->s->flags & M_DISPLAY) && j == RICH_COLOR)
+ {
+ enriched_puts("\033[0m", stte);
+ }
+ }
+ else
+ stte->tag_level[j]++;
+
+ if (j == RICH_EXCERPT)
+ enriched_flush(stte, 1);
+ }
+}
+
+void text_enriched_handler (BODY *a, STATE *s)
+{
+ enum {
+ TEXT, LANGLE, TAG, BOGUS_TAG, NEWLINE, ST_EOF, DONE
+ } state = TEXT;
+
+ long bytes = a->length;
+ struct enriched_state stte;
+ int c = 0;
+ int tag_len = 0;
+ char tag[LONG_STRING + 1];
+
+ memset (&stte, 0, sizeof (stte));
+ stte.s = s;
+ stte.WrapMargin = ((s->flags & M_DISPLAY) ? (COLS-4) : ((COLS-4)<72)?(COLS-4):72);
+ stte.line_max = stte.WrapMargin * 4;
+ stte.line = (char *) safe_calloc (1, stte.line_max + 1);
+ stte.param = (char *) safe_calloc (1, STRING);
+
+ if (s->prefix)
+ {
+ state_puts (s->prefix, s);
+ stte.indent_len += strlen (s->prefix);
+ }
+
+ while (state != DONE)
+ {
+ if (state != ST_EOF)
+ {
+ if (!bytes || (c = fgetc (s->fpin)) == EOF)
+ state = ST_EOF;
+ else
+ bytes--;
+ }
+
+ switch (state)
+ {
+ case TEXT :
+ switch (c)
+ {
+ case '<' :
+ state = LANGLE;
+ break;
+
+ case '\n' :
+ if (stte.tag_level[RICH_NOFILL])
+ {
+ enriched_flush (&stte, 1);
+ }
+ else
+ {
+ enriched_putc (' ', &stte);
+ state = NEWLINE;
+ }
+ break;
+
+ default:
+ enriched_putc (c, &stte);
+ }
+ break;
+
+ case LANGLE :
+ if (c == '<')
+ {
+ enriched_putc (c, &stte);
+ state = TEXT;
+ break;
+ }
+ else
+ {
+ tag_len = 0;
+ state = TAG;
+ }
+ /* Yes, fall through (it wasn't a <<, so this char is first in TAG) */
+ case TAG :
+ if (c == '>')
+ {
+ tag[tag_len] = '\0';
+ enriched_set_flags (tag, &stte);
+ state = TEXT;
+ }
+ else if (tag_len < LONG_STRING) /* ignore overly long tags */
+ tag[tag_len++] = c;
+ else
+ state = BOGUS_TAG;
+ break;
+
+ case BOGUS_TAG :
+ if (c == '>')
+ state = TEXT;
+ break;
+
+ case NEWLINE :
+ if (c == '\n')
+ enriched_flush (&stte, 1);
+ else
+ {
+ ungetc (c, s->fpin);
+ bytes++;
+ state = TEXT;
+ }
+ break;
+
+ case ST_EOF :
+ enriched_putc ('\0', &stte);
+ state = DONE;
+ break;
+
+ case DONE: /* not reached, but gcc complains if this is absent */
+ break;
+ }
+ }
+
+ state_putc ('\n', s); /* add a final newline */
+
+ if (stte.buffer)
+ free (stte.buffer);
+ free (stte.line);
+ free (stte.param);
+}
+
+#define TXTPLAIN 1
+#define TXTENRICHED 2
+#define TXTHTML 3
+
+void alternative_handler (BODY *a, STATE *s)
+{
+ BODY *choice = NULL;
+ BODY *b;
+ LIST *t;
+ char buf[STRING];
+ int type = 0;
+
+ /* First, search list of prefered types */
+ t = AlternativeOrderList;
+ while (t && !choice)
+ {
+ if (a && a->parts)
+ b = a->parts;
+ else
+ b = a;
+ while (b && !choice)
+ {
+ int i;
+
+ /* catch base only matches */
+ i = strlen (t->data) - 1;
+ if (!strchr(t->data, '/') ||
+ (i > 0 && t->data[i-1] == '/' && t->data[i] == '*'))
+ {
+ if (!strcasecmp(t->data, TYPE(b->type)))
+ {
+ choice = b;
+ }
+ }
+ else
+ {
+ snprintf (buf, sizeof (buf), "%s/%s", TYPE (b->type), b->subtype);
+ if (!strcasecmp(t->data, buf))
+ {
+ choice = b;
+ }
+ }
+ b = b->next;
+ }
+ t = t->next;
+ }
+ /* Next, look for an autoviewable type */
+ if (a && a->parts)
+ b = a->parts;
+ else
+ b = a;
+ while (b && !choice)
+ {
+ snprintf (buf, sizeof (buf), "%s/%s", TYPE (b->type), b->subtype);
+ if (mutt_is_autoview (buf))
+ {
+ rfc1524_entry *entry = rfc1524_new_entry ();
+
+ if (rfc1524_mailcap_lookup (b, buf, entry, M_AUTOVIEW))
+ {
+ choice = b;
+ }
+ rfc1524_free_entry (&entry);
+ }
+ b = b->next;
+ }
+
+ /* Then, look for a text entry */
+ if (!choice)
+ {
+ if (a && a->parts)
+ b = a->parts;
+ else
+ b = a;
+ while (b)
+ {
+ if (b->type == TYPETEXT)
+ {
+ if (strcasecmp ("plain", b->subtype) == 0)
+ {
+ choice = b;
+ type = TXTPLAIN;
+ }
+ else if (strcasecmp ("enriched", b->subtype) == 0)
+ {
+ if (type == 0 || type > TXTENRICHED)
+ {
+ choice = b;
+ type = TXTENRICHED;
+ }
+ }
+ else if (strcasecmp ("html", b->subtype) == 0)
+ {
+ if (type == 0)
+ {
+ choice = b;
+ type = TXTHTML;
+ }
+ }
+ }
+ b = b->next;
+ }
+ }
+ /* Finally, look for other possibilities */
+ if (!choice)
+ {
+ if (a && a->parts)
+ b = a->parts;
+ else
+ b = a;
+ while (b && !choice)
+ {
+ if (mutt_can_decode (b))
+ choice = b;
+ b = b->next;
+ }
+ }
+ if (choice)
+ {
+ if (s->flags & M_DISPLAY && !option (OPTWEED))
+ {
+ fseek (s->fpin, choice->hdr_offset, 0);
+ mutt_copy_bytes(s->fpin, s->fpout, choice->offset-choice->hdr_offset);
+ }
+ mutt_body_handler (choice, s);
+ }
+ else if (s->flags & M_DISPLAY)
+ {
+ /* didn't find anything that we could display! */
+ state_puts("[-- Error: Could not display any parts of Multipart/Alternative! --]\n", s);
+ }
+}
+
+/* handles message/rfc822 body parts */
+void message_handler (BODY *a, STATE *s)
+{
+ struct stat st;
+ BODY *b;
+ long off_start;
+
+ off_start = ftell (s->fpin);
+ if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE)
+ {
+ fstat (fileno (s->fpin), &st);
+ b = mutt_new_body ();
+ b->length = (long) st.st_size;
+ b->parts = mutt_parse_messageRFC822 (s->fpin, b);
+ }
+ else
+ b = a;
+
+ if (b->parts)
+ {
+ mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset,
+ (((s->flags & M_DISPLAY) && option (OPTWEED)) ? (CH_WEED | CH_REORDER) : 0) |
+ (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM, s->prefix);
+
+ if (s->prefix)
+ state_puts (s->prefix, s);
+ state_putc ('\n', s);
+
+ mutt_body_handler (b->parts, s);
+ }
+
+ if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE)
+ mutt_free_body (&b);
+}
+
+/* returns 1 if decoding the attachment will produce output */
+int mutt_can_decode (BODY *a)
+{
+ char type[STRING];
+
+ snprintf (type, sizeof (type), "%s/%s", TYPE (a->type), a->subtype);
+ if (mutt_is_autoview (type))
+ return (rfc1524_mailcap_lookup (a, type, NULL, M_AUTOVIEW));
+ else if (a->type == TYPETEXT)
+ return (1);
+ else if (a->type == TYPEMESSAGE)
+ return (1);
+ else if (a->type == TYPEMULTIPART)
+ {
+
+
+
+#ifdef _PGPPATH
+ if (strcasecmp (a->subtype, "signed") == 0 ||
+ strcasecmp (a->subtype, "encrypted") == 0)
+ return (1);
+ else
+#endif
+
+
+
+ {
+ BODY *p;
+
+ for (p = a->parts; p; p = p->next)
+ {
+ if (mutt_can_decode (p))
+ return (1);
+ }
+ }
+ }
+
+
+
+#ifdef _PGPPATH
+ else if (a->type == TYPEAPPLICATION)
+ {
+ if (strcasecmp (a->subtype, "pgp") == 0 ||
+ strcasecmp (a->subtype, "x-pgp-message") == 0 ||
+ strcasecmp (a->subtype, "pgp-signed") == 0 ||
+ strcasecmp (a->subtype, "pgp-keys") == 0)
+ return (1);
+ }
+#endif
+
+
+
+ return (0);
+}
+
+void multipart_handler (BODY *a, STATE *s)
+{
+ BODY *b, *p;
+ char buffer[STRING];
+ char length[5];
+ struct stat st;
+ int count;
+
+ if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE)
+ {
+ fstat (fileno (s->fpin), &st);
+ b = mutt_new_body ();
+ b->length = (long) st.st_size;
+ b->parts = mutt_parse_multipart (s->fpin,
+ mutt_get_parameter ("boundary", a->parameter),
+ (long) st.st_size, strcasecmp ("digest", a->subtype) == 0);
+ }
+ else
+ b = a;
+
+ for (p = b->parts, count = 1; p; p = p->next, count++)
+ {
+ if (s->flags & M_DISPLAY)
+ {
+ snprintf (buffer, sizeof (buffer), "[-- Attachment #%d", count);
+ state_puts (buffer, s);
+ if (p->description || p->filename || p->form_name)
+ {
+ state_puts (": ", s);
+ state_puts (p->description ? p->description :
+ p->filename ? p->filename : p->form_name, s);
+ }
+ state_puts (" --]\n", s);
+
+ mutt_pretty_size (length, sizeof (length), p->length);
+
+ snprintf (buffer, sizeof (buffer),
+ "[-- Type: %s/%s, Encoding: %s, Size: %s --]\n",
+ TYPE (p->type), p->subtype, ENCODING (p->encoding), length);
+ state_puts (buffer, s);
+ if (!option (OPTWEED))
+ {
+ fseek (s->fpin, p->hdr_offset, 0);
+ mutt_copy_bytes(s->fpin, s->fpout, p->offset-p->hdr_offset);
+ }
+ else
+ state_putc ('\n', s);
+ }
+ else
+ {
+ if (p->description && mutt_can_decode (p))
+ {
+ state_puts ("Content-Description: ", s);
+ state_puts (p->description, s);
+ state_putc ('\n', s);
+ }
+ if (p->form_name)
+ {
+ state_puts (p->form_name, s);
+ state_puts (": \n", s);
+ }
+ }
+ mutt_body_handler (p, s);
+ state_putc ('\n', s);
+ }
+
+ if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE)
+ mutt_free_body (&b);
+}
+
+void autoview_handler (BODY *a, STATE *s)
+{
+ rfc1524_entry *entry = rfc1524_new_entry ();
+ char buffer[LONG_STRING];
+ char type[STRING];
+ char command[LONG_STRING];
+ char tempfile[_POSIX_PATH_MAX] = "";
+ FILE *fpin = NULL;
+ FILE *fpout = NULL;
+ FILE *fperr = NULL;
+ int piped = FALSE;
+ pid_t thepid;
+
+ snprintf (type, sizeof (type), "%s/%s", TYPE (a->type), a->subtype);
+ rfc1524_mailcap_lookup (a, type, entry, M_AUTOVIEW);
+
+ rfc1524_expand_filename (entry->nametemplate, a->filename, tempfile, sizeof (tempfile));
+
+ if (entry->command)
+ {
+ strfcpy (command, entry->command, sizeof (command));
+
+ /* rfc1524_expand_command returns 0 if the file is required */
+ piped = rfc1524_expand_command (a, tempfile, type, command, sizeof (command));
+
+ if (s->flags & M_DISPLAY)
+ {
+ char mesg[STRING];
+
+ snprintf (mesg, sizeof (buffer), "[-- Autoview using %s --]\n", command);
+ state_puts (mesg, s);
+ mutt_message("Invoking autoview command: %s",command);
+ }
+
+ if (piped)
+ {
+ thepid = mutt_create_filter (command, &fpin, &fpout, &fperr);
+ mutt_copy_bytes (s->fpin, fpin, a->length);
+ fclose (fpin);
+ }
+ else
+ {
+ if ((fpin = safe_fopen (tempfile, "w")) == NULL)
+ {
+ mutt_perror ("fopen");
+ rfc1524_free_entry (&entry);
+ return;
+ }
+
+ mutt_copy_bytes (s->fpin, fpin, a->length);
+ fclose (fpin);
+ thepid = mutt_create_filter (command, NULL, &fpout, &fperr);
+ }
+
+ if (s->prefix)
+ {
+ while (fgets (buffer, sizeof(buffer), fpout) != NULL)
+ {
+ state_puts (s->prefix, s);
+ state_puts (buffer, s);
+ }
+ /* check for data on stderr */
+ if (fgets (buffer, sizeof(buffer), fperr))
+ {
+ if (s->flags & M_DISPLAY)
+ {
+ char mesg[STRING];
+
+ snprintf (mesg, sizeof (buffer), "[-- Autoview stderr of %s --]\n",
+ command);
+ state_puts (mesg, s);
+ }
+ state_puts (s->prefix, s);
+ state_puts (buffer, s);
+ while (fgets (buffer, sizeof(buffer), fperr) != NULL)
+ {
+ state_puts (s->prefix, s);
+ state_puts (buffer, s);
+ }
+ }
+ }
+ else
+ {
+ mutt_copy_stream (fpout, s->fpout);
+ /* Check for stderr messages */
+ if (fgets (buffer, sizeof(buffer), fperr))
+ {
+ if (s->flags & M_DISPLAY)
+ {
+ char mesg[STRING];
+
+ snprintf (mesg, sizeof (buffer), "[-- Autoview stderr of %s --]\n",
+ command);
+ state_puts (mesg, s);
+ }
+ state_puts (buffer, s);
+ mutt_copy_stream (fperr, s->fpout);
+ }
+ }
+
+ fclose (fpout);
+ fclose (fperr);
+ mutt_wait_filter (thepid);
+ mutt_unlink (tempfile);
+ if (s->flags & M_DISPLAY)
+ mutt_clear_error ();
+ }
+ rfc1524_free_entry (&entry);
+}
+
+void mutt_decode_attachment (BODY *b, STATE *s)
+{
+ fseek (s->fpin, b->offset, 0);
+ switch (b->encoding)
+ {
+ case ENCQUOTEDPRINTABLE:
+ mutt_decode_quoted (s, b->length, mutt_is_text_type (b->type, b->subtype));
+ break;
+ case ENCBASE64:
+ mutt_decode_base64 (s, b->length, mutt_is_text_type (b->type, b->subtype));
+ break;
+ default:
+ mutt_decode_xbit (s, b->length, mutt_is_text_type (b->type, b->subtype));
+ break;
+ }
+}
+
+void mutt_body_handler (BODY *b, STATE *s)
+{
+ int decode = 0;
+ int plaintext = 0;
+ FILE *fp = NULL;
+ char tempfile[_POSIX_PATH_MAX];
+ handler_t handler = NULL;
+ long tmpoffset = 0;
+ size_t tmplength = 0;
+ char type[STRING];
+
+ /* first determine which handler to use to process this part */
+
+ snprintf (type, sizeof (type), "%s/%s", TYPE (b->type), b->subtype);
+ if (mutt_is_autoview (type))
+ {
+ rfc1524_entry *entry = rfc1524_new_entry ();
+
+ if (rfc1524_mailcap_lookup (b, type, entry, M_AUTOVIEW))
+ handler = autoview_handler;
+ rfc1524_free_entry (&entry);
+ }
+ else if (b->type == TYPETEXT)
+ {
+ if (strcasecmp ("plain", b->subtype) == 0)
+ {
+ /* avoid copying this part twice since removing the transfer-encoding is
+ * the only operation needed.
+ */
+ plaintext = 1;
+ }
+ else if (strcasecmp ("enriched", b->subtype) == 0)
+ handler = text_enriched_handler;
+ else if (strcasecmp ("rfc822-headers", b->subtype) == 0)
+ plaintext = 1;
+ }
+ else if (b->type == TYPEMESSAGE)
+ {
+ if (!strcasecmp ("rfc822", b->subtype) || !strcasecmp ("news", b->subtype))
+ handler = message_handler;
+ else if (!strcasecmp ("delivery-status", b->subtype))
+ plaintext = 1;
+ }
+ else if (b->type == TYPEMULTIPART)
+ {
+
+
+
+#ifdef _PGPPATH
+ char *p;
+#endif /* _PGPPATH */
+
+
+
+ if (strcasecmp ("alternative", b->subtype) == 0)
+ handler = alternative_handler;
+
+
+
+#ifdef _PGPPATH
+ else if (strcasecmp ("signed", b->subtype) == 0)
+ {
+ p = mutt_get_parameter ("protocol", b->parameter);
+
+ if (!p)
+ mutt_error ("Error: Multipart/Signed has no protocol.");
+ else if (strcasecmp ("application/pgp-signature", p) == 0)
+ {
+ if (s->flags & M_VERIFY)
+ handler = pgp_signed_handler;
+ }
+ }
+ else if (strcasecmp ("encrypted", b->subtype) == 0)
+ {
+ p = mutt_get_parameter ("protocol", b->parameter);
+
+ if (!p)
+ mutt_error ("Error: Multipart/Encrypted has no protocol parameter!");
+ else if (strcasecmp ("application/pgp-encrypted", p) == 0)
+ handler = pgp_encrypted_handler;
+ }
+#endif /* _PGPPATH */
+
+
+
+ if (!handler)
+ handler = multipart_handler;
+ }
+
+
+
+#ifdef _PGPPATH
+ else if (b->type == TYPEAPPLICATION)
+ {
+ if (strcasecmp ("pgp", b->subtype) == 0 ||
+ strcasecmp ("x-pgp-message", b->subtype) == 0 ||
+ strcasecmp ("pgp-signed", b->subtype) == 0 ||
+ strcasecmp ("pgp-keys", b->subtype) == 0)
+
+ handler = application_pgp_handler;
+ }
+#endif /* _PGPPATH */
+
+
+
+ if (plaintext || handler)
+ {
+ fseek (s->fpin, b->offset, 0);
+
+ /* see if we need to decode this part before processing it */
+ if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE ||
+ (s->prefix && plaintext))
+ {
+ int origType = b->type;
+ char *savePrefix = NULL;
+
+ if (!plaintext)
+ {
+ /* decode to a tempfile, saving the original destination */
+ fp = s->fpout;
+ mutt_mktemp (tempfile);
+ if ((s->fpout = safe_fopen (tempfile, "w")) == NULL)
+ {
+ mutt_error ("Unable to open temporary file!");
+ return;
+ }
+ /* decoding the attachment changes the size and offset, so save a copy
+ * of the "real" values now, and restore them after processing
+ */
+ tmplength = b->length;
+ tmpoffset = b->offset;
+
+ /* if we are decoding binary bodies, we don't want to prefix each
+ * line with the prefix or else the data will get corrupted.
+ */
+ savePrefix = s->prefix;
+ s->prefix = NULL;
+
+ decode = 1;
+ }
+ else
+ b->type = TYPETEXT;
+
+ mutt_decode_attachment (b, s);
+
+ if (decode)
+ {
+ b->length = ftell (s->fpout);
+ b->offset = 0;
+ fclose (s->fpout);
+
+ /* restore final destination and substitute the tempfile for input */
+ s->fpout = fp;
+ fp = s->fpin;
+ s->fpin = fopen (tempfile, "r");
+ unlink (tempfile);
+
+ /* restore the prefix */
+ s->prefix = savePrefix;
+ }
+
+ b->type = origType;
+ }
+ else if (plaintext)
+ mutt_copy_bytes (s->fpin, s->fpout, b->length);
+
+ /* process the (decoded) body part */
+ if (handler)
+ {
+ handler (b, s);
+
+ if (decode)
+ {
+ b->length = tmplength;
+ b->offset = tmpoffset;
+
+ /* restore the original source stream */
+ fclose (s->fpin);
+ s->fpin = fp;
+ }
+ }
+ }
+ else if (s->flags & M_DISPLAY)
+ {
+ fprintf (s->fpout, "[-- %s/%s is unsupported ", TYPE (b->type), b->subtype);
+ if (!option (OPTVIEWATTACH))
+ {
+ if (km_expand_key (type, sizeof(type),
+ km_find_func (MENU_PAGER, OP_VIEW_ATTACHMENTS)))
+ fprintf (s->fpout, "(use '%s' to view this part)", type);
+ else
+ fputs ("(need 'view-attachments' bound to key!)", s->fpout);
+ }
+ fputs (" --]\n", s->fpout);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "mutt.h"
+
+int hash_string (const unsigned char *s, int n)
+{
+ int h = 0;
+ while (*s)
+ h += *s++;
+ return (h % n);
+}
+
+HASH *hash_create (int nelem)
+{
+ HASH *table = safe_malloc (sizeof (HASH));
+ table->nelem = nelem;
+ table->table = safe_calloc (nelem, sizeof (struct hash_elem *));
+ return table;
+}
+
+/* table hash table to update
+ key key to hash on
+ data data to associate with `key'
+ allow_dup if nonzero, duplicate keys are allowed in the table */
+int hash_insert (HASH * table, const char *key, void *data, int allow_dup)
+{
+ struct hash_elem *ptr = malloc (sizeof (struct hash_elem));
+ int h = hash_string ((unsigned char *) key, table->nelem);
+
+ ptr->key = key;
+ ptr->data = data;
+
+ if (allow_dup)
+ {
+ ptr->next = table->table[h];
+ table->table[h] = ptr;
+ }
+ else
+ {
+ struct hash_elem *tmp, *last;
+ int r;
+
+ for (tmp = table->table[h], last = NULL; tmp; last = tmp, tmp = tmp->next)
+ {
+ r = strcmp (tmp->key, key);
+ if (r == 0)
+ {
+ free (ptr);
+ return (-1);
+ }
+ if (r > 0)
+ break;
+ }
+ if (last)
+ last->next = ptr;
+ else
+ table->table[h] = ptr;
+ ptr->next = tmp;
+ }
+ return h;
+}
+
+void *hash_find_hash (const HASH * table, int hash, const char *key)
+{
+ struct hash_elem *ptr = table->table[hash];
+ for (; ptr; ptr = ptr->next)
+ {
+ if (strcmp (key, ptr->key) == 0)
+ return (ptr->data);
+ }
+ return NULL;
+}
+
+void hash_delete_hash (HASH * table, int hash, const char *key, const void *data,
+ void (*destroy) (void *))
+{
+ struct hash_elem *ptr = table->table[hash];
+ struct hash_elem *last = NULL;
+ for (; ptr; last = ptr, ptr = ptr->next)
+ {
+ /* if `data' is given, look for a matching ->data member. this is
+ required for the case where we have multiple entries with the same
+ key */
+ if (data == ptr->data || (!data && strcmp (ptr->key, key) == 0))
+ {
+ if (last)
+ last->next = ptr->next;
+ else
+ table->table[hash] = ptr->next;
+ if (destroy)
+ destroy (ptr->data);
+ memset (ptr, 0, sizeof (struct hash_elem));
+ free (ptr);
+ ptr = 0;
+ return;
+ }
+ }
+}
+
+/* ptr pointer to the hash table to be freed
+ destroy() function to call to free the ->data member (optional) */
+void hash_destroy (HASH **ptr, void (*destroy) (void *))
+{
+ int i;
+ HASH *pptr = *ptr;
+ struct hash_elem *elem, *tmp;
+
+ for (i = 0 ; i < pptr->nelem; i++)
+ {
+ for (elem = pptr->table[i]; elem; )
+ {
+ tmp = elem;
+ elem = elem->next;
+ if (destroy)
+ destroy (tmp->data);
+ safe_free ((void **) &tmp);
+ }
+ }
+ safe_free ((void **) &pptr->table);
+ safe_free ((void **) ptr);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+struct hash_elem
+{
+ const char *key;
+ void *data;
+ struct hash_elem *next;
+};
+
+typedef struct
+{
+ int nelem;
+ struct hash_elem **table;
+}
+HASH;
+
+#define hash_find(table, key) hash_find_hash(table, hash_string ((unsigned char *)key, table->nelem), key)
+
+#define hash_delete(table,key,data,destroy) hash_delete_hash(table, hash_string ((unsigned char *)key, table->nelem), key, data, destroy)
+
+HASH *hash_create (int nelem);
+int hash_string (const unsigned char *s, int n);
+int hash_insert (HASH * table, const char *key, void *data, int allow_dup);
+void *hash_find_hash (const HASH * table, int hash, const char *key);
+void hash_delete_hash (HASH * table, int hash, const char *key, const void *data,
+ void (*destroy) (void *));
+void hash_destroy (HASH ** hash, void (*destroy) (void *));
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+int mutt_is_mail_list (ADDRESS *addr)
+{
+ LIST *p;
+
+ if (addr->mailbox)
+ {
+ for (p = MailLists; p; p = p->next)
+ if (strncasecmp (addr->mailbox, p->data, strlen (p->data)) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+check_for_mailing_list (ADDRESS *adr, char *pfx, char *buf, int buflen)
+{
+ for (; adr; adr = adr->next)
+ {
+ if (mutt_is_mail_list (adr))
+ {
+ if (pfx && buf && buflen)
+ snprintf (buf, buflen, "%s%s", pfx, mutt_get_name (adr));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int first_mailing_list (char *buf, size_t buflen, ADDRESS *a)
+{
+ for (; a; a = a->next)
+ {
+ if (mutt_is_mail_list (a))
+ {
+ mutt_save_path (buf, buflen, a);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void make_from (ENVELOPE *hdr, char *buf, size_t len, int do_lists)
+{
+ int me;
+
+ me = mutt_addr_is_user (hdr->from);
+
+ if (do_lists || me)
+ {
+ if (check_for_mailing_list (hdr->to, "To ", buf, len))
+ return;
+ if (check_for_mailing_list (hdr->cc, "Cc ", buf, len))
+ return;
+ }
+
+ if (me && hdr->to)
+ snprintf (buf, len, "To %s", mutt_get_name (hdr->to));
+ else if (me && hdr->cc)
+ snprintf (buf, len, "Cc %s", mutt_get_name (hdr->cc));
+ else if (hdr->from)
+ strfcpy (buf, mutt_get_name (hdr->from), len);
+ else
+ *buf = 0;
+}
+
+int mutt_user_is_recipient (ADDRESS *a)
+{
+ for (; a; a = a->next)
+ if (mutt_addr_is_user (a))
+ return 1;
+ return 0;
+}
+
+/* Return values:
+ * 0: user is not in list
+ * 1: user is unique recipient
+ * 2: user is in the TO list
+ * 3: user is in the CC list
+ * 4: user is originator
+ */
+static int user_is_recipient (ENVELOPE *hdr)
+{
+ if (mutt_addr_is_user (hdr->from))
+ return 4;
+
+ if (mutt_user_is_recipient (hdr->to))
+ {
+ if (hdr->to->next || hdr->cc)
+ return 2; /* non-unique recipient */
+ else
+ return 1; /* unique recipient */
+ }
+
+ if (mutt_user_is_recipient (hdr->cc))
+ return 3;
+
+ return (0);
+}
+
+/* %a = address of author
+ * %b = filename of the originating folder
+ * %B = the list to which the letter was sent
+ * %c = size of message in bytes
+ * %C = current message number
+ * %d = date and time of message (using strftime)
+ * %f = entire from line
+ * %F = like %n, unless from self
+ * %i = message-id
+ * %l = number of lines in the message
+ * %L = like %F, except `lists' are displayed first
+ * %m = number of messages in the mailbox
+ * %n = name of author
+ * %N = score
+ * %s = subject
+ * %S = short message status (e.g., N/O/D/!/r/-)
+ * %t = `to:' field (recipients)
+ * %T = $to_chars
+ * %u = user (login) name of author
+ * %Z = status flags */
+
+static const char *
+hdr_format_str (char *dest,
+ size_t destlen,
+ char op,
+ const char *src,
+ const char *prefix,
+ const char *ifstring,
+ const char *elsestring,
+ unsigned long data,
+ format_flag flags)
+{
+ HEADER *hdr = (HEADER *) data;
+ char fmt[SHORT_STRING], buf2[SHORT_STRING], ch, *p;
+ int do_locales, i;
+ int optional = (flags & M_FORMAT_OPTIONAL);
+ size_t len;
+
+ dest[0] = 0;
+ switch (op)
+ {
+ case 'a':
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (dest, destlen, fmt, hdr->env->from->mailbox);
+ break;
+
+ case 'B':
+ if (!first_mailing_list (dest, destlen, hdr->env->to) &&
+ !first_mailing_list (dest, destlen, hdr->env->cc))
+ dest[0] = 0;
+ break;
+
+ case 'b':
+ if ((p = strrchr (Context->path, '/')))
+ strncpy (dest, p + 1, destlen);
+ else
+ strncpy (dest, Context->path, destlen);
+ break;
+
+ case 'c':
+ mutt_pretty_size (buf2, sizeof (buf2), (long) hdr->content->length);
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (dest, destlen, fmt, buf2);
+ break;
+
+ case 'C':
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (dest, destlen, fmt, hdr->msgno + 1);
+ break;
+
+ case 'd':
+ case '{':
+ case '[':
+ case '(':
+ case '<':
+
+ /* preprocess $date_format to handle %Z */
+ {
+ const char *cp;
+ struct tm *tm;
+ time_t T;
+
+ p = dest;
+
+ cp = (op == 'd') ? (NONULL (DateFmt)) : src;
+ if (*cp == '!')
+ {
+ do_locales = 0;
+ cp++;
+ }
+ else
+ do_locales = 1;
+
+ len = destlen - 1;
+ while (len > 0 && ((op == 'd' && *cp) ||
+ (op == '{' && *cp != '}') ||
+ (op == '[' && *cp != ']') ||
+ (op == '(' && *cp != ')') ||
+ (op == '<' && *cp != '>')))
+ {
+ if (*cp == '%')
+ {
+ cp++;
+ if (*cp == 'Z' && (op == 'd' || op == '{'))
+ {
+ if (len >= 5)
+ {
+ sprintf (p, "%c%02d%02d", hdr->zoccident ? '-' : '+',
+ hdr->zhours, hdr->zminutes);
+ p += 5;
+ len -= 5;
+ }
+ else
+ break; /* not enough space left */
+ }
+ else
+ {
+ if (len >= 2)
+ {
+ *p++ = '%';
+ *p++ = *cp;
+ len -= 2;
+ }
+ else
+ break; /* not enough space */
+ }
+ cp++;
+ }
+ else
+ {
+ *p++ = *cp++;
+ len--;
+ }
+ }
+ *p = 0;
+
+ if (do_locales && Locale)
+ setlocale (LC_TIME, Locale);
+
+ if (op == '[')
+ tm = localtime (&hdr->date_sent);
+ else if (op == '(')
+ tm = localtime (&hdr->received);
+ else if (op == '<')
+ {
+ T = time (NULL);
+ tm = localtime (&T);
+ }
+ else
+ {
+ /* restore sender's time zone */
+ T = hdr->date_sent;
+ if (hdr->zoccident)
+ T -= (hdr->zhours * 3600 + hdr->zminutes * 60);
+ else
+ T += (hdr->zhours * 3600 + hdr->zminutes * 60);
+ tm = gmtime (&T);
+ }
+
+ strftime (buf2, sizeof (buf2), dest, tm);
+
+ if (do_locales)
+ setlocale (LC_TIME, "C");
+
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (dest, destlen, fmt, buf2);
+ if (len > 0 && op != 'd')
+ src = cp + 1;
+ }
+ break;
+
+ case 'f':
+ buf2[0] = 0;
+ rfc822_write_address (buf2, sizeof (buf2), hdr->env->from);
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (dest, destlen, fmt, buf2);
+ break;
+
+ case 'F':
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ make_from (hdr->env, buf2, sizeof (buf2), 0);
+ snprintf (dest, destlen, fmt, buf2);
+ break;
+
+ case 'i':
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (dest, destlen, fmt, hdr->env->message_id ? hdr->env->message_id : "<no.id>");
+ break;
+
+ case 'l':
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (dest, destlen, fmt, (int) hdr->lines);
+ break;
+
+ case 'L':
+ if (!optional)
+ {
+ make_from (hdr->env, buf2, sizeof (buf2), 1);
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (dest, destlen, fmt, buf2);
+ }
+ else if (!check_for_mailing_list (hdr->env->to, NULL, NULL, 0) &&
+ !check_for_mailing_list (hdr->env->cc, NULL, NULL, 0))
+ {
+ optional = 0;
+ }
+ break;
+
+ case 'm':
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (dest, destlen, fmt, Context->msgcount);
+ break;
+
+ case 'n':
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (dest, destlen, fmt, mutt_get_name (hdr->env->from));
+ break;
+
+ case 'N':
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (dest, destlen, fmt, hdr->score);
+ break;
+
+ case 's':
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ if (flags & M_FORMAT_TREE)
+ {
+ if (flags & M_FORMAT_FORCESUBJ)
+ {
+ snprintf (buf2, sizeof (buf2), "%s%s", hdr->tree,
+ hdr->env->subject ? hdr->env->subject : "");
+ snprintf (dest, destlen, fmt, buf2);
+ }
+ else
+ snprintf (dest, destlen, fmt, hdr->tree);
+ }
+ else
+ {
+ snprintf (dest, destlen, fmt, hdr->env->subject ? hdr->env->subject : "");
+ }
+ break;
+
+ case 'S':
+ if (hdr->deleted)
+ ch = 'D';
+ else if (hdr->attach_del)
+ ch = 'd';
+ else if (hdr->tagged)
+ ch = '*';
+ else if (hdr->flagged)
+ ch = '!';
+ else if (hdr->replied)
+ ch = 'r';
+ else if (hdr->read && (Context->msgnotreadyet != hdr->msgno))
+ ch = '-';
+ else if (hdr->old)
+ ch = 'O';
+ else
+ ch = 'N';
+
+ /* FOO - this is probably unsafe, but we are not likely to have such
+ a short string passed into this routine */
+ *dest = ch;
+ *(dest + 1) = 0;
+ break;
+
+ case 't':
+ buf2[0] = 0;
+ if (!check_for_mailing_list (hdr->env->to, "To ", buf2, sizeof (buf2)) &&
+ !check_for_mailing_list (hdr->env->cc, "Cc ", buf2, sizeof (buf2)))
+ {
+ if (hdr->env->to)
+ snprintf (buf2, sizeof (buf2), "To %s", mutt_get_name (hdr->env->to));
+ else if (hdr->env->cc)
+ snprintf (buf2, sizeof (buf2), "Cc %s", mutt_get_name (hdr->env->cc));
+ }
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (dest, destlen, fmt, buf2);
+ break;
+
+ case 'T':
+ snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
+ snprintf (dest, destlen, fmt,
+ (Tochars && ((i = user_is_recipient (hdr->env))) < strlen (Tochars)) ? Tochars[i] : ' ');
+ break;
+
+ case 'u':
+ if (hdr->env->from && hdr->env->from->mailbox)
+ {
+ strfcpy (buf2, hdr->env->from->mailbox, sizeof (buf2));
+ if ((p = strpbrk (buf2, "%@")))
+ *p = 0;
+ }
+ else
+ buf2[0] = 0;
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (dest, destlen, fmt, buf2);
+ break;
+
+ case 'Z':
+ if (hdr->mailcap)
+ ch = 'M';
+
+
+
+#ifdef _PGPPATH
+ else if (hdr->pgp & PGPENCRYPT)
+ ch = 'P';
+ else if (hdr->pgp & PGPSIGN)
+ ch = 'S';
+ else if (hdr->pgp & PGPKEY)
+ ch = 'K';
+#endif
+
+
+
+ else
+ ch = ' ';
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (buf2, sizeof (buf2),
+ "%c%c%c",
+ (hdr->read && (Context->msgnotreadyet != hdr->msgno))
+ ? (hdr->replied ? 'r' : ' ') : (hdr->old ? 'O' : 'N'),
+ hdr->deleted ? 'D' : (hdr->attach_del ? 'd' : ch),
+ hdr->tagged ? '*' :
+ (hdr->flagged ? '!' :
+ (Tochars && ((i = user_is_recipient (hdr->env)) < strlen (Tochars)) ? Tochars[user_is_recipient (hdr->env)] : ' ')));
+ snprintf (dest, destlen, fmt, buf2);
+ break;
+
+ default:
+ snprintf (dest, destlen, "%%%s%c", prefix, op);
+ break;
+ }
+
+ if (optional)
+ mutt_FormatString (dest, destlen, ifstring, hdr_format_str, (unsigned long) hdr, flags);
+ else if (flags & M_FORMAT_OPTIONAL)
+ mutt_FormatString (dest, destlen, elsestring, hdr_format_str, (unsigned long) hdr, flags);
+
+ return (src);
+}
+
+void
+_mutt_make_string (char *dest, size_t destlen, const char *s, HEADER *hdr, format_flag flags)
+{
+ mutt_FormatString (dest, destlen, s, hdr_format_str, (unsigned long) hdr, flags);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#include <sys/stat.h>
+#include <string.h>
+#include <ctype.h>
+
+void mutt_edit_headers (const char *editor,
+ const char *body,
+ HEADER *msg,
+ char *fcc,
+ size_t fcclen)
+{
+ char path[_POSIX_PATH_MAX]; /* tempfile used to edit headers + body */
+ char buffer[LONG_STRING];
+ char *p;
+ FILE *ifp, *ofp;
+ int i, keep;
+ int in_reply_to = 0; /* did we see the in-reply-to field ? */
+ ENVELOPE *n;
+ time_t mtime;
+ struct stat st;
+ LIST *cur, *last = NULL, *tmp;
+
+ mutt_mktemp (path);
+ if ((ofp = safe_fopen (path, "w")) == NULL)
+ {
+ mutt_perror (path);
+ return;
+ }
+
+ mutt_write_rfc822_header (ofp, msg->env, NULL, 1);
+ fputc ('\n', ofp); /* tie off the header. */
+
+ /* now copy the body of the message. */
+ if ((ifp = fopen (body, "r")) == NULL)
+ {
+ mutt_perror (body);
+ return;
+ }
+
+ mutt_copy_stream (ifp, ofp);
+
+ fclose (ifp);
+ fclose (ofp);
+
+ if (stat (path, &st) == -1)
+ {
+ mutt_perror (path);
+ return;
+ }
+
+ mtime = st.st_mtime;
+ mutt_edit_file (editor, path);
+ stat (path, &st);
+ if (mtime == st.st_mtime)
+ {
+ dprint (1, (debugfile, "ci_edit_headers(): temp file was not modified.\n"));
+ /* the file has not changed! */
+ mutt_unlink (path);
+ return;
+ }
+
+ mutt_unlink (body);
+ mutt_free_list (&msg->env->userhdrs);
+
+ /* Read the temp file back in */
+ ifp = fopen (path, "r");
+ ofp = safe_fopen (body, "w");
+ n = mutt_read_rfc822_header (ifp, NULL);
+ while ((i = fread (buffer, 1, sizeof (buffer), ifp)) > 0)
+ fwrite (buffer, 1, i, ofp);
+ fclose (ofp);
+ fclose (ifp);
+ mutt_unlink (path);
+
+ /* restore old info. */
+ n->references = msg->env->references;
+ msg->env->references = NULL;
+ mutt_free_envelope (&msg->env);
+ msg->env = n;
+
+ msg->env->to = mutt_expand_aliases (msg->env->to);
+ msg->env->cc = mutt_expand_aliases (msg->env->cc);
+ msg->env->bcc = mutt_expand_aliases (msg->env->bcc);
+ msg->env->reply_to = mutt_expand_aliases (msg->env->reply_to);
+ msg->env->mail_followup_to = mutt_expand_aliases (msg->env->mail_followup_to);
+
+ /* search through the user defined headers added to see if either a * fcc:
+ or attach-file: field was specified. */
+ cur = msg->env->userhdrs;
+ while (cur)
+ {
+ keep = 1;
+
+ /* keep track of whether or not we see the in-reply-to field. if we did
+ * not, remove the references: field later so that we can generate a new
+ * message based upon this one.
+ */
+ if (strncasecmp ("in-reply-to:", cur->data, 12) == 0)
+ in_reply_to = 1;
+ else if (fcc && strncasecmp ("fcc:", cur->data, 4) == 0)
+ {
+ p = cur->data + 4;
+ SKIPWS (p);
+ if (*p)
+ {
+ strfcpy (fcc, p, fcclen);
+ mutt_pretty_mailbox (fcc);
+ }
+ keep = 0;
+ }
+ else if (strncasecmp ("attach:", cur->data, 7) == 0)
+ {
+ BODY *body;
+ BODY *parts;
+ char *q;
+
+ p = cur->data + 7;
+ SKIPWS (p);
+ if (*p)
+ {
+ if ((q = strpbrk (p, " \t")))
+ {
+ mutt_substrcpy (path, p, q, sizeof (path));
+ SKIPWS (q);
+ }
+ else
+ strfcpy (path, p, sizeof (path));
+ mutt_expand_path (path, sizeof (path));
+ if ((body = mutt_make_attach (path)))
+ {
+ body->description = safe_strdup (q);
+ for (parts = msg->content; parts->next; parts = parts->next) ;
+ parts->next = body;
+ }
+ else
+ {
+ mutt_pretty_mailbox (path);
+ mutt_error ("%s: unable to attach file", path);
+ }
+ }
+ keep = 0;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#ifdef _PGPPATH
+ else if (strncasecmp ("pgp:", cur->data, 4) == 0)
+ {
+ msg->pgp = mutt_parse_pgp_hdr (cur->data + 4, 0);
+ keep = 0;
+ }
+#endif
+
+
+
+
+
+
+
+
+ if (keep)
+ {
+ last = cur;
+ cur = cur->next;
+ }
+ else
+ {
+ if (last)
+ last->next = cur->next;
+ else
+ msg->env->userhdrs = cur->next;
+ tmp = cur;
+ cur = cur->next;
+ tmp->next = NULL;
+ mutt_free_list (&tmp);
+ }
+ }
+
+ if (!in_reply_to)
+ mutt_free_list (&msg->env->references);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define HELP_C
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "keymap.h"
+#include "pager.h"
+#include "mapping.h"
+
+#include <ctype.h>
+#include <string.h>
+
+static struct binding_t *help_lookupFunction (int op, int menu)
+{
+ int i;
+ struct binding_t *map;
+
+ if (menu != MENU_PAGER)
+ {
+ /* first look in the generic map for the function */
+ for (i = 0; OpGeneric[i].name; i++)
+ if (OpGeneric[i].op == op)
+ return (&OpGeneric[i]);
+ }
+
+ if ((map = km_get_table(menu)))
+ {
+ for (i = 0; map[i].name; i++)
+ if (map[i].op == op)
+ return (&map[i]);
+ }
+
+ return (NULL);
+}
+
+void mutt_make_help (char *d, size_t dlen, char *txt, int menu, int op)
+{
+ char buf[SHORT_STRING];
+
+ if (km_expand_key (buf, sizeof (buf), km_find_func (menu, op)) ||
+ km_expand_key (buf, sizeof (buf), km_find_func (MENU_GENERIC, op)))
+ snprintf (d, dlen, "%s:%s", buf, txt);
+ else
+ d[0] = 0;
+}
+
+char *
+mutt_compile_help (char *buf, size_t buflen, int menu, struct mapping_t *items)
+{
+ int i;
+ size_t len;
+ char *pbuf = buf;
+
+ for (i = 0; items[i].name && buflen > 2; i++)
+ {
+ if (i)
+ {
+ *pbuf++ = ' ';
+ *pbuf++ = ' ';
+ buflen -= 2;
+ }
+ mutt_make_help (pbuf, buflen, items[i].name, menu, items[i].value);
+ len = strlen (pbuf);
+ pbuf += len;
+ buflen -= len;
+ }
+ return buf;
+}
+
+static void print_macro (FILE *f, const char *macro)
+{
+ int i;
+
+ for (i = 0; *macro && i < COLS - 34; macro++, i++)
+ {
+ switch (*macro)
+ {
+ case '\033':
+ fputs ("\\e", f);
+ i++;
+ break;
+ case '\n':
+ fputs ("\\n", f);
+ i++;
+ break;
+ case '\r':
+ fputs ("\\r", f);
+ i++;
+ break;
+ case '\t':
+ fputs ("\\t", f);
+ i++;
+ break;
+ default:
+ fputc (*macro, f);
+ break;
+ }
+ }
+}
+
+static void dump_menu (FILE *f, int menu)
+{
+ struct keymap_t *map;
+ struct binding_t *b;
+ char buf[SHORT_STRING];
+
+ /* browse through the keymap table */
+ for (map = Keymaps[menu]; map; map = map->next)
+ {
+ km_expand_key (buf, sizeof (buf), map);
+
+ if (map->op == OP_MACRO)
+ {
+ fprintf (f, "%s\t%-20s\t", buf, "macro");
+ print_macro (f, map->macro);
+ fputc ('\n', f);
+ }
+ else if (map->op != OP_NULL)
+ {
+ b = help_lookupFunction (map->op, menu);
+ fprintf (f, "%s\t%-20s\t%s\n", buf,
+ b ? b->name : "UNKNOWN",
+ b ? HelpStrings[b->op] : "ERROR: please report this bug");
+ }
+ }
+}
+
+static int is_bound (struct keymap_t *map, int op)
+{
+ for (; map; map = map->next)
+ if (map->op == op)
+ return 1;
+ return 0;
+}
+
+static void dump_unbound (FILE *f,
+ struct binding_t *funcs,
+ struct keymap_t *map,
+ struct keymap_t *aux)
+{
+ int i;
+
+ for (i = 0; funcs[i].name; i++)
+ {
+ if (! is_bound (map, funcs[i].op) &&
+ (!aux || ! is_bound (aux, funcs[i].op)))
+ fprintf (f, "%s\t\t%s\n", funcs[i].name, HelpStrings[funcs[i].op]);
+ }
+}
+
+void mutt_help (int menu)
+{
+ char t[_POSIX_PATH_MAX];
+ char buf[SHORT_STRING];
+ char *desc;
+ FILE *f;
+ struct binding_t *funcs;
+
+ mutt_mktemp (t);
+ if ((f = safe_fopen (t, "w")) == NULL)
+ {
+ mutt_perror (t);
+ return;
+ }
+
+ funcs = km_get_table (menu);
+ desc = mutt_getnamebyvalue (menu, Menus);
+ if (!desc)
+ desc = "<UNKNOWN>";
+
+ dump_menu (f, menu);
+ if (menu != MENU_EDITOR && menu != MENU_PAGER)
+ {
+ fputs ("\nGeneric bindings:\n\n", f);
+ dump_menu (f, MENU_GENERIC);
+ }
+
+ fputs ("\nUnbound functions:\n\n", f);
+ if (funcs)
+ dump_unbound (f, funcs, Keymaps[menu], NULL);
+ if (menu != MENU_PAGER)
+ dump_unbound (f, OpGeneric, Keymaps[MENU_GENERIC], Keymaps[menu]);
+
+ fclose (f);
+
+ snprintf (buf, sizeof (buf), "Help for %s", desc);
+ mutt_do_pager (buf, t, 0, NULL);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+
+typedef struct hook
+{
+ int type; /* hook type */
+ REGEXP rx; /* regular expression */
+ char *command; /* filename, command or pattern to execute */
+ pattern_t *pattern; /* used for fcc,save,send-hook */
+ struct hook *next;
+} HOOK;
+
+static HOOK *Hooks = NULL;
+
+int mutt_parse_hook (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ HOOK *ptr;
+ BUFFER command, pattern;
+ int rc, not = 0;
+ regex_t *rx = NULL;
+ pattern_t *pat = NULL;
+ char path[_POSIX_PATH_MAX];
+
+ memset (&pattern, 0, sizeof (pattern));
+ memset (&command, 0, sizeof (command));
+
+ if (*s->dptr == '!')
+ {
+ s->dptr++;
+ SKIPWS (s->dptr);
+ not = 1;
+ }
+
+ mutt_extract_token (&pattern, s, 0);
+
+ if (!MoreArgs (s))
+ {
+ strfcpy (err->data, "too few arguments", err->dsize);
+ goto error;
+ }
+
+ mutt_extract_token (&command, s, (data & (M_FOLDERHOOK | M_SENDHOOK)) ? M_TOKEN_SPACE : 0);
+
+ if (!command.data)
+ {
+ strfcpy (err->data, "too few arguments", err->dsize);
+ goto error;
+ }
+
+ if (MoreArgs (s))
+ {
+ strfcpy (err->data, "too many arguments", err->dsize);
+ goto error;
+ }
+
+ if (data & (M_FOLDERHOOK | M_MBOXHOOK))
+ {
+ strfcpy (path, pattern.data, sizeof (path));
+ mutt_expand_path (path, sizeof (path));
+ FREE (&pattern.data);
+ memset (&pattern, 0, sizeof (pattern));
+ pattern.data = safe_strdup (path);
+ }
+ else if (DefaultHook)
+ {
+ char tmp[HUGE_STRING];
+
+ strfcpy (tmp, pattern.data, sizeof (tmp));
+ mutt_check_simple (tmp, sizeof (tmp), DefaultHook);
+ FREE (&pattern.data);
+ memset (&pattern, 0, sizeof (pattern));
+ pattern.data = safe_strdup (tmp);
+ }
+
+ if (data & (M_MBOXHOOK | M_SAVEHOOK | M_FCCHOOK))
+ {
+ strfcpy (path, command.data, sizeof (path));
+ mutt_expand_path (path, sizeof (path));
+ FREE (&command.data);
+ memset (&command, 0, sizeof (command));
+ command.data = safe_strdup (path);
+ }
+
+ /* check to make sure that a matching hook doesn't already exist */
+ for (ptr = Hooks; ptr; ptr = ptr->next)
+ {
+ if (ptr->type == data &&
+ ptr->rx.not == not &&
+ !strcmp (pattern.data, ptr->rx.pattern))
+ {
+ if (data & (M_FOLDERHOOK | M_SENDHOOK))
+ {
+ /* folder-hook and send-hook allow multiple commands with the same
+ pattern, so if we've already seen this pattern/command pair, just
+ ignore it instead of creating a duplicate */
+ if (!strcmp (ptr->command, command.data))
+ {
+ FREE (&command.data);
+ FREE (&pattern.data);
+ return 0;
+ }
+ }
+ else
+ {
+ /* other hooks only allow one command per pattern, so update the
+ entry with the new command. this currently does not change the
+ order of execution of the hooks, which i think is desirable since
+ a common action to perform is to change the default (.) entry
+ based upon some other information. */
+ FREE (&ptr->command);
+ ptr->command = command.data;
+ FREE (&pattern.data);
+ return 0;
+ }
+ }
+ if (!ptr->next)
+ break;
+ }
+
+ if (data & (M_SENDHOOK | M_SAVEHOOK | M_FCCHOOK))
+ {
+ if ((pat = mutt_pattern_comp (pattern.data, (data & M_SENDHOOK) ? M_FULL_MSG : 0, err)) == NULL)
+ goto error;
+ }
+ else
+ {
+ rx = safe_malloc (sizeof (regex_t));
+ if ((rc = REGCOMP (rx, pattern.data, 0)) != 0)
+ {
+ regerror (rc, rx, err->data, err->dsize);
+ regfree (rx);
+ safe_free ((void **) &rx);
+ goto error;
+ }
+ }
+
+ if (ptr)
+ {
+ ptr->next = safe_calloc (1, sizeof (HOOK));
+ ptr = ptr->next;
+ }
+ else
+ Hooks = ptr = safe_calloc (1, sizeof (HOOK));
+ ptr->type = data;
+ ptr->command = command.data;
+ ptr->pattern = pat;
+ ptr->rx.pattern = pattern.data;
+ ptr->rx.rx = rx;
+ ptr->rx.not = not;
+ return 0;
+
+error:
+ FREE (&pattern.data);
+ FREE (&command.data);
+ return (-1);
+}
+
+void mutt_folder_hook (char *path)
+{
+ HOOK *tmp = Hooks;
+ BUFFER err, token;
+ char buf[STRING];
+
+ err.data = buf;
+ err.dsize = sizeof (buf);
+ memset (&token, 0, sizeof (token));
+ for (; tmp; tmp = tmp->next)
+ if (tmp->type & M_FOLDERHOOK)
+ {
+ if ((regexec (tmp->rx.rx, path, 0, NULL, 0) == 0) ^ tmp->rx.not)
+ {
+ if (mutt_parse_rc_line (tmp->command, &token, &err) == -1)
+ {
+ mutt_error ("%s", err.data);
+ FREE (&token.data);
+ sleep (1); /* pause a moment to let the user see the error */
+ return;
+ }
+ }
+ }
+ FREE (&token.data);
+}
+
+char *mutt_find_hook (int type, const char *pat)
+{
+ HOOK *tmp = Hooks;
+
+ for (; tmp; tmp = tmp->next)
+ if (tmp->type & type)
+ {
+ if (regexec (tmp->rx.rx, pat, 0, NULL, 0) == 0)
+ return (tmp->command);
+ }
+ return (NULL);
+}
+
+void mutt_send_hook (HEADER *hdr)
+{
+ BUFFER err, token;
+ HOOK *hook;
+ char buf[STRING];
+
+ err.data = buf;
+ err.dsize = sizeof (buf);
+ memset (&token, 0, sizeof (token));
+ for (hook = Hooks; hook; hook = hook->next)
+ if (hook->type & M_SENDHOOK)
+ if ((mutt_pattern_exec (hook->pattern, 0, NULL, hdr) > 0) ^ hook->rx.not)
+ if (mutt_parse_rc_line (hook->command, &token, &err) != 0)
+ {
+ FREE (&token.data);
+ mutt_error ("%s", err.data);
+ sleep (1);
+ return;
+ }
+ FREE (&token.data);
+}
+
+static int
+mutt_addr_hook (char *path, size_t pathlen, int type, CONTEXT *ctx, HEADER *hdr)
+{
+ HOOK *hook;
+
+ /* determine if a matching hook exists */
+ for (hook = Hooks; hook; hook = hook->next)
+ if (hook->type & type)
+ if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr) > 0) ^ hook->rx.not)
+ {
+ mutt_make_string (path, pathlen, hook->command, hdr);
+ return 0;
+ }
+
+ return -1;
+}
+
+void mutt_default_save (char *path, size_t pathlen, HEADER *hdr)
+{
+ *path = 0;
+ if (mutt_addr_hook (path, pathlen, M_SAVEHOOK, Context, hdr) != 0)
+ {
+ char tmp[_POSIX_PATH_MAX];
+ ADDRESS *adr;
+ ENVELOPE *env = hdr->env;
+ int fromMe = mutt_addr_is_user (env->from);
+
+ if (!fromMe && env->reply_to && env->reply_to->mailbox)
+ adr = env->reply_to;
+ else if (!fromMe && env->from && env->from->mailbox)
+ adr = env->from;
+ else if (env->to && env->to->mailbox)
+ adr = env->to;
+ else if (env->cc && env->cc->mailbox)
+ adr = env->cc;
+ else
+ adr = NULL;
+ if (adr)
+ {
+ mutt_safe_path (tmp, sizeof (tmp), adr);
+ snprintf (path, pathlen, "=%s", tmp);
+ }
+ }
+}
+
+void mutt_select_fcc (char *path, size_t pathlen, HEADER *hdr)
+{
+ ADDRESS *adr;
+ char buf[_POSIX_PATH_MAX];
+ ENVELOPE *env = hdr->env;
+
+ if (mutt_addr_hook (path, pathlen, M_FCCHOOK, NULL, hdr) != 0)
+ {
+ if ((option (OPTSAVENAME) || option (OPTFORCENAME)) &&
+ (env->to || env->cc || env->bcc))
+ {
+ adr = env->to ? env->to : (env->cc ? env->cc : env->bcc);
+ mutt_safe_path (buf, sizeof (buf), adr);
+ snprintf (path, pathlen, "%s/%s", NONULL (Maildir), buf);
+ if (!option (OPTFORCENAME) && access (path, W_OK) != 0)
+ strfcpy (path, NONULL (Outbox), pathlen);
+ }
+ else
+ strfcpy (path, NONULL (Outbox), pathlen);
+ }
+ mutt_pretty_mailbox (path);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mx.h"
+#include "mailbox.h"
+
+#include <unistd.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+/* Minimal support for IMAP 4rev1 */
+
+#define IMAP_PORT 143
+
+#define SEQLEN 5
+
+/* number of entries in the hash table */
+#define IMAP_CACHE_LEN 10
+
+enum
+{
+ IMAP_FATAL = 1,
+ IMAP_NEW_MAIL,
+ IMAP_BYE
+};
+
+typedef struct
+{
+ int index;
+ char *path;
+} IMAP_CACHE;
+
+typedef struct
+{
+ short status;
+ unsigned short sequence;
+ unsigned short newMailCount;
+ short xxx;
+ IMAP_CACHE cache[IMAP_CACHE_LEN];
+} IMAP_DATA;
+
+static char ImapUser[SHORT_STRING] = { 0 };
+static char ImapPass[SHORT_STRING] = { 0 };
+
+static int imap_read_line (char *buf, size_t buflen, int fd)
+{
+ char ch;
+ int i;
+
+ for (i = 0; i < buflen; i++)
+ {
+ if (read (fd, &ch, 1) != 1)
+ return (-1);
+ if (ch == '\n')
+ break;
+ buf[i] = ch;
+ }
+ buf[i-1] = 0;
+ return (i + 1);
+}
+
+static int imap_read_line_d (char *buf, size_t buflen, int fd)
+{
+ int r = imap_read_line (buf, buflen, fd);
+ dprint (1,(debugfile,"imap_read_line_d():%s\n", buf));
+ return r;
+}
+
+static void imap_make_sequence (char *buf, size_t buflen, CONTEXT *ctx)
+{
+ snprintf (buf, buflen, "a%04d", ((IMAP_DATA *) ctx->data)->sequence++);
+}
+
+static int imap_write (int fd, const char *buf)
+{
+ dprint (1,(debugfile,"imap_write():%s", buf));
+ return (write (fd, buf, strlen (buf)));
+}
+
+static void imap_error (const char *where, const char *msg)
+{
+ dprint (1, (debugfile, "imap_error(): unexpected response in %s: %s\n", where, msg));
+}
+
+/* date is of the form: DD-MMM-YYYY HH:MM:SS +ZZzz */
+static time_t imap_parse_date (char *s)
+{
+ struct tm t;
+ time_t tz;
+
+ t.tm_mday = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != '-')
+ return 0;
+ s++;
+ t.tm_mon = mutt_check_month (s);
+ s += 3;
+ if (*s != '-')
+ return 0;
+ s++;
+ t.tm_year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0') - 1900;
+ s += 4;
+ if (*s != ' ')
+ return 0;
+ s++;
+
+ /* time */
+ t.tm_hour = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != ':')
+ return 0;
+ s++;
+ t.tm_min = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != ':')
+ return 0;
+ s++;
+ t.tm_sec = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != ' ')
+ return 0;
+ s++;
+
+ /* timezone */
+ tz = ((s[1] - '0') * 10 + (s[2] - '0')) * 3600 +
+ ((s[3] - '0') * 10 + (s[4] - '0')) * 60;
+ if (s[0] == '+')
+ tz = -tz;
+
+ return (mutt_mktime (&t, 0) + tz);
+}
+
+static int imap_parse_fetch (HEADER *h, char *s)
+{
+ char tmp[SHORT_STRING];
+ char *ptmp;
+ int state = 0;
+
+ if (!s)
+ return (-1);
+
+ h->read = 0;
+ h->old = 0;
+
+ while (*s)
+ {
+ SKIPWS (s);
+
+ switch (state)
+ {
+ case 0:
+ if (strncasecmp ("FLAGS", s, 5) == 0)
+ {
+ s += 5;
+ SKIPWS (s);
+ if (*s != '(')
+ {
+ dprint (1, (debugfile, "imap_parse_fetch(): bogus FLAGS entry: %s\n", s));
+ return (-1); /* parse error */
+ }
+ s++;
+ state = 1;
+ }
+ else if (strncasecmp ("INTERNALDATE", s, 12) == 0)
+ {
+ s += 12;
+ SKIPWS (s);
+ if (*s != '\"')
+ {
+ dprint (1, (debugfile, "imap_parse_fetch(): bogus INTERNALDATE entry: %s\n", s));
+ return (-1);
+ }
+ s++;
+ ptmp = tmp;
+ while (*s && *s != '\"')
+ *ptmp++ = *s++;
+ if (*s != '\"')
+ return (-1);
+ s++; /* skip past the trailing " */
+ *ptmp = 0;
+ h->received = imap_parse_date (tmp);
+ }
+ else if (strncasecmp ("RFC822.SIZE", s, 11) == 0)
+ {
+ s += 11;
+ SKIPWS (s);
+ ptmp = tmp;
+ while (isdigit (*s))
+ *ptmp++ = *s++;
+ *ptmp = 0;
+ }
+ else if (*s == ')')
+ s++; /* end of request */
+ else
+ {
+ /* got something i don't understand */
+ imap_error ("imap_parse_fetch()", s);
+ return (-1);
+ }
+ break;
+ case 1: /* flags */
+ if (*s == ')')
+ {
+ s++;
+ state = 0;
+ }
+ else if (strncasecmp ("\\deleted", s, 8) == 0)
+ {
+ s += 8;
+ h->deleted = 1;
+ }
+ else if (strncasecmp ("\\flagged", s, 8) == 0)
+ {
+ s += 8;
+ h->flagged = 1;
+ }
+ else if (strncasecmp ("\\answered", s, 9) == 0)
+ {
+ s += 9;
+ h->replied = 1;
+ }
+ else if (strncasecmp ("\\seen", s, 5) == 0)
+ {
+ s += 5;
+ h->read = 1;
+ }
+ else
+ {
+ while (*s && !ISSPACE (*s) && *s != ')')
+ s++;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int imap_read_bytes (FILE *fp, int fd, long bytes)
+{
+ long pos;
+ long len;
+ char buf[LONG_STRING];
+
+ for (pos = 0; pos < bytes; )
+ {
+ len = imap_read_line (buf, sizeof (buf), fd);
+ if (len < 0)
+ return (-1);
+ pos += len;
+ fputs (buf, fp);
+ fputs ("\n", fp);
+ }
+
+ return 0;
+}
+
+/* returns 1 if the command result was OK, or 0 if NO or BAD */
+static int imap_code (const char *s)
+{
+ s += SEQLEN;
+ SKIPWS (s);
+ return (strncasecmp ("OK", s, 2) == 0);
+}
+
+static char *imap_next_word (char *s)
+{
+ while (*s && !ISSPACE (*s))
+ s++;
+ SKIPWS (s);
+ return s;
+}
+
+static int imap_handle_untagged (CONTEXT *ctx, char *s)
+{
+ char *pn;
+ int count;
+
+ s = imap_next_word (s);
+
+ if (isdigit (*s))
+ {
+ pn = s;
+ s = imap_next_word (s);
+
+ if (strncasecmp ("EXISTS", s, 6) == 0)
+ {
+ /* new mail arrived */
+ count = atoi (pn);
+
+ if (count <= ctx->msgcount)
+ {
+ /* something is wrong because the server reported fewer messages
+ * than we previously saw
+ */
+ mutt_error ("Fatal error. Message count is out of sync!");
+ ((IMAP_DATA *) ctx->data)->status = IMAP_FATAL;
+ mx_fastclose_mailbox (ctx);
+ return (-1);
+ }
+ else
+ {
+ ((IMAP_DATA *) ctx->data)->status = IMAP_NEW_MAIL;
+ ((IMAP_DATA *) ctx->data)->newMailCount = count;
+ }
+ }
+ }
+ else if (strncasecmp ("BYE", s, 3) == 0)
+ {
+ /* server shut down our connection */
+ s += 3;
+ SKIPWS (s);
+ mutt_error (s);
+ ((IMAP_DATA *) ctx->data)->status = IMAP_BYE;
+ mx_fastclose_mailbox (ctx);
+ return (-1);
+ }
+ else
+ {
+ dprint (1, (debugfile, "imap_unhandle_untagged(): unhandled request: %s\n",
+ s));
+ }
+
+ return 0;
+}
+
+static int imap_read_header (CONTEXT *ctx, int msgno)
+{
+ char buf[LONG_STRING];
+ FILE *fp;
+ char tempfile[_POSIX_PATH_MAX];
+ char seq[8];
+ char *pc;
+ char *pn;
+ long bytes;
+
+ ctx->hdrs[ctx->msgcount]->index = ctx->msgcount;
+
+ mutt_mktemp (tempfile);
+ if (!(fp = safe_fopen (tempfile, "w+")))
+ {
+ return (-1);
+ }
+
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s FETCH %d RFC822.HEADER\r\n", seq, msgno + 1);
+ imap_write (ctx->fd, buf);
+
+ do
+ {
+ if (imap_read_line (buf, sizeof (buf), ctx->fd) < 0)
+ {
+ return (-1);
+ }
+
+ if (buf[0] == '*')
+ {
+ pc = buf;
+ pc = imap_next_word (pc);
+ pc = imap_next_word (pc);
+ if (strncasecmp ("FETCH", pc, 5) == 0)
+ {
+ if (!(pc = strchr (pc, '{')))
+ {
+ imap_error ("imap_read_header()", buf);
+ return (-1);
+ }
+ pc++;
+ pn = pc;
+ while (isdigit (*pc))
+ pc++;
+ *pc = 0;
+ bytes = atoi (pn);
+
+ imap_read_bytes (fp, ctx->fd, bytes);
+ }
+ else if (imap_handle_untagged (ctx, buf) != 0)
+ return (-1);
+ }
+ }
+ while (strncmp (seq, buf, SEQLEN) != 0);
+
+ rewind (fp);
+ ctx->hdrs[msgno]->env = mutt_read_rfc822_header (fp, ctx->hdrs[msgno]);
+
+ fclose (fp);
+ unlink (tempfile);
+
+ /* get the status of this message */
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s FETCH %d FAST\r\n", seq, msgno + 1);
+ imap_write (ctx->fd, buf);
+ do
+ {
+ if (imap_read_line_d (buf, sizeof (buf), ctx->fd) < 0)
+ break;
+ if (buf[0] == '*')
+ {
+ pc = buf;
+ pc = imap_next_word (pc);
+ pc = imap_next_word (pc);
+ if (strncasecmp ("FETCH", pc, 5) == 0)
+ {
+ if (!(pc = strchr (pc, '(')))
+ {
+ imap_error ("imap_read_header()", buf);
+ return (-1);
+ }
+ if (imap_parse_fetch (ctx->hdrs[msgno], pc + 1) != 0)
+ return (-1);
+ }
+ else if (imap_handle_untagged (ctx, buf) != 0)
+ return (-1);
+ }
+ }
+ while (strncmp (seq, buf, SEQLEN) != 0)
+ ;
+
+ return 0;
+}
+
+static int imap_exec (char *buf, size_t buflen,
+ CONTEXT *ctx, const char *seq, const char *cmd)
+{
+ int count;
+
+ imap_write (ctx->fd, cmd);
+
+ do
+ {
+ if (imap_read_line_d (buf, buflen, ctx->fd) < 0)
+ return (-1);
+
+ if (buf[0] == '*' && imap_handle_untagged (ctx, buf) != 0)
+ return (-1);
+ }
+ while (strncmp (buf, seq, SEQLEN) != 0);
+
+ if (((IMAP_DATA *) ctx->data)->status == IMAP_NEW_MAIL)
+ {
+ /* read new mail messages */
+
+ dprint (1, (debugfile, "imap_exec(): new mail detected\n"));
+ mutt_message ("Fetching headers for new mail...");
+
+ ((IMAP_DATA *) ctx->data)->status = 0;
+
+ count = ((IMAP_DATA *) ctx->data)->newMailCount;
+ while (count > ctx->hdrmax)
+ mx_alloc_memory (ctx);
+
+ while (ctx->msgcount < count)
+ {
+ ctx->hdrs[ctx->msgcount] = mutt_new_header ();
+ imap_read_header (ctx, ctx->msgcount);
+ mx_update_context (ctx); /* incremements ->msgcount */
+
+ /* check to make sure that new mail hasn't arrived in the middle of
+ * checking for new mail (sigh)
+ */
+ if (((IMAP_DATA *) ctx->data)->status == IMAP_NEW_MAIL)
+ {
+ count = ((IMAP_DATA *) ctx->data)->newMailCount;
+ while (count > ctx->hdrmax)
+ mx_alloc_memory (ctx);
+ ((IMAP_DATA *) ctx->data)->status = 0;
+ }
+ }
+
+ mutt_clear_error ();
+ }
+
+ if (!imap_code (buf))
+ {
+ char *pc;
+
+ dprint (1, (debugfile, "imap_exec(): command failed: %s\n", buf));
+ pc = buf + SEQLEN;
+ SKIPWS (pc);
+ pc = imap_next_word (pc);
+ mutt_error (pc);
+ sleep (1);
+ return (-1);
+ }
+
+ return 0;
+}
+
+int imap_open_mailbox (CONTEXT *ctx)
+{
+ struct sockaddr_in sin;
+ struct hostent *he;
+ char buf[LONG_STRING];
+ char bufout[LONG_STRING];
+ char host[SHORT_STRING];
+ char mailbox[_POSIX_PATH_MAX];
+ char seq[16];
+ int count = 0;
+ int n;
+ char *pc;
+
+ pc = ctx->path;
+ if (*pc != '{')
+ return (-1);
+ pc++;
+ n = 0;
+ while (*pc && *pc != '}')
+ host[n++] = *pc++;
+ host[n] = 0;
+ if (!*pc)
+ return (-1);
+ pc++;
+ strfcpy (mailbox, pc, sizeof (mailbox));
+
+ if (!ImapUser[0])
+ strfcpy (ImapUser, Username, sizeof (ImapUser));
+ if (mutt_get_field ("IMAP Username: ", ImapUser, sizeof (ImapUser), 0) != 0 ||
+ !ImapUser[0])
+ {
+ ImapUser[0] = 0;
+ return (-1);
+ }
+
+ snprintf (buf, sizeof (buf), "Password for %s@%s: ", ImapUser, host);
+ ImapPass[0] = 0;
+ if (mutt_get_field (buf, ImapPass, sizeof (ImapPass), M_PASS) != 0 ||
+ !ImapPass[0])
+ {
+ return (-1);
+ }
+
+ memset (&sin, 0, sizeof (sin));
+ sin.sin_port = htons (IMAP_PORT);
+ sin.sin_family = AF_INET;
+ if ((he = gethostbyname (host)) == NULL)
+ {
+ mutt_perror (host);
+ return (-1);
+ }
+ memcpy (&sin.sin_addr, he->h_addr_list[0], he->h_length);
+
+ if ((ctx->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
+ {
+ mutt_perror ("socket");
+ return (-1);
+ }
+
+ mutt_message ("Connecting to %s...", host);
+
+ if (connect (ctx->fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)
+ {
+ mutt_perror ("connect");
+ close (ctx->fd);
+ }
+
+ if (imap_read_line_d (buf, sizeof (buf), ctx->fd) < 0)
+ {
+ close (ctx->fd);
+ return (-1);
+ }
+
+ if (strncmp ("* OK", buf, 4) != 0)
+ {
+ imap_error ("imap_open_mailbox()", buf);
+ close (ctx->fd);
+ return (-1);
+ }
+
+ /* create IMAP-specific state struct */
+ ctx->data = safe_malloc (sizeof (IMAP_DATA));
+ memset (ctx->data, 0, sizeof (IMAP_DATA));
+
+ mutt_message ("Logging in...");
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s LOGIN %s %s\r\n", seq, ImapUser, ImapPass);
+ if (imap_exec (buf, sizeof (buf), ctx, seq, buf) != 0)
+ {
+ imap_error ("imap_open_mailbox()", buf);
+ return (-1);
+ }
+
+ mutt_message ("Selecting %s...", mailbox);
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (bufout, sizeof (bufout), "%s SELECT %s\r\n", seq, mailbox);
+ imap_write (ctx->fd, bufout);
+
+ do
+ {
+ if (imap_read_line_d (buf, sizeof (buf), ctx->fd) < 0)
+ break;
+
+ if (buf[0] == '*')
+ {
+ pc = buf + 2;
+
+ if (isdigit (*pc))
+ {
+ char *pn = pc;
+
+ while (*pc && isdigit (*pc))
+ pc++;
+ *pc++ = 0;
+ n = atoi (pn);
+ SKIPWS (pc);
+ if (strncasecmp ("EXISTS", pc, 6) == 0)
+ count = n;
+ }
+ else if (imap_handle_untagged (ctx, buf) != 0)
+ return (-1);
+ }
+ }
+ while (strncmp (seq, buf, strlen (seq)) != 0);
+
+ mutt_message ("Fetching message headers...");
+ ctx->hdrmax = count;
+ ctx->hdrs = safe_malloc (count * sizeof (HEADER *));
+ ctx->v2r = safe_malloc (count * sizeof (int));
+ for (ctx->msgcount = 0; ctx->msgcount < count; )
+ {
+ ctx->hdrs[ctx->msgcount] = mutt_new_header ();
+
+ /* `count' can get modified if new mail arrives while fetching the
+ * header for this message
+ */
+ if (imap_read_header (ctx, ctx->msgcount) != 0)
+ {
+ mx_fastclose_mailbox (ctx);
+ return (-1);
+ }
+ mx_update_context (ctx); /* increments ->msgcount */
+
+ /* in case we get new mail while fetching the headers */
+ if (((IMAP_DATA *) ctx->data)->status == IMAP_NEW_MAIL)
+ {
+ count = ((IMAP_DATA *) ctx->data)->newMailCount;
+ while (count > ctx->hdrmax)
+ mx_alloc_memory (ctx);
+ ((IMAP_DATA *) ctx->data)->status = 0;
+ }
+ }
+
+ return 0;
+}
+
+int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
+{
+ char seq[8];
+ char buf[LONG_STRING];
+ char path[_POSIX_PATH_MAX];
+ char *pc;
+ char *pn;
+ long bytes;
+ IMAP_CACHE *cache;
+
+ /* see if we already have the message in our cache */
+ cache = &((IMAP_DATA *) ctx->data)->cache[ctx->hdrs[msgno]->index % IMAP_CACHE_LEN];
+
+ if (cache->path)
+ {
+ if (cache->index == ctx->hdrs[msgno]->index)
+ {
+ /* yes, so just return a pointer to the message */
+ if (!(msg->fp = fopen (cache->path, "r")))
+ {
+ mutt_perror (cache->path);
+ return (-1);
+ }
+ return 0;
+ }
+ else
+ {
+ /* clear the previous entry */
+ unlink (cache->path);
+ free (cache->path);
+ }
+ }
+
+ mutt_message ("Fetching message...");
+
+ cache->index = ctx->hdrs[msgno]->index;
+ mutt_mktemp (path);
+ cache->path = safe_strdup (path);
+ if (!(msg->fp = safe_fopen (path, "w+")))
+ {
+ safe_free ((void **) &cache->path);
+ return (-1);
+ }
+
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s FETCH %d RFC822\r\n", seq,
+ ctx->hdrs[msgno]->index + 1);
+ imap_write (ctx->fd, buf);
+ do
+ {
+ if (imap_read_line (buf, sizeof (buf), ctx->fd) < 0)
+ {
+ return (-1);
+ }
+
+ if (buf[0] == '*')
+ {
+ pc = buf;
+ pc = imap_next_word (pc);
+ pc = imap_next_word (pc);
+ if (strncasecmp ("FETCH", pc, 5) == 0)
+ {
+ if (!(pc = strchr (buf, '{')))
+ {
+ imap_error ("imap_fetch_message()", buf);
+ return (-1);
+ }
+ pc++;
+ pn = pc;
+ while (isdigit (*pc))
+ pc++;
+ *pc = 0;
+ bytes = atoi (pn);
+ imap_read_bytes (msg->fp, ctx->fd, bytes);
+ }
+ else if (imap_handle_untagged (ctx, buf) != 0)
+ return (-1);
+ }
+ }
+ while (strncmp (buf, seq, SEQLEN) != 0)
+ ;
+
+ if (!imap_code (buf))
+ {
+ return (-1);
+ }
+
+ return 0;
+}
+
+int imap_close_connection (CONTEXT *ctx)
+{
+ char buf[LONG_STRING];
+ char seq[8];
+
+ /* if the server didn't shut down on us, close the connection gracefully */
+ if (((IMAP_DATA *) ctx->data)->status != IMAP_BYE)
+ {
+ mutt_message ("Closing connection to IMAP server...");
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s LOGOUT\r\n", seq);
+ imap_write (ctx->fd, buf);
+ do
+ {
+ if (imap_read_line_d (buf, sizeof (buf), ctx->fd) < 0)
+ break;
+ }
+ while (strncmp (seq, buf, SEQLEN) != 0);
+ mutt_clear_error ();
+ }
+ close (ctx->fd);
+ return 0;
+}
+
+static int make_delete_list (char *list, size_t listlen, CONTEXT *ctx)
+{
+ int first = -1, last = -1;
+ int n;
+ char tmp[LONG_STRING];
+
+ *list = 0;
+ for (n=0; n<ctx->msgcount; n++)
+ {
+ if (ctx->hdrs[n]->deleted)
+ {
+ if (first < 0)
+ {
+ first = n;
+ last = n;
+ }
+ else if (last != n - 1)
+ {
+ if (first != last)
+ snprintf (tmp, sizeof (tmp), "%d:%d", first + 1, last + 1);
+ else
+ snprintf (tmp, sizeof (tmp), "%d", first + 1);
+ if (list[0])
+ strcat (list, ",");
+ strcat (list, tmp);
+ first = last = n;
+ }
+ else
+ last = n;
+ }
+ }
+
+ if (first >= 0)
+ {
+ if (first != last)
+ snprintf (tmp, sizeof (tmp), "%d:%d", first + 1, last + 1);
+ else
+ snprintf (tmp, sizeof (tmp), "%d", first + 1);
+ if (list[0])
+ strcat (list, ",");
+ strcat (list, tmp);
+ }
+
+ return 0;
+}
+
+int imap_sync_mailbox (CONTEXT *ctx)
+{
+ char seq[8];
+ char buf[LONG_STRING];
+ char tmp[LONG_STRING];
+ int n;
+
+ /* save status changes */
+ mutt_message ("Saving message status flags...");
+ for (n = 0; n < ctx->msgcount; n++)
+ {
+ if (!ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed)
+ {
+ if (ctx->hdrs[n]->read)
+ strcat (tmp, "\\Seen ");
+ if (ctx->hdrs[n]->flagged)
+ strcat (tmp, "\\Flagged ");
+ if (ctx->hdrs[n]->replied)
+ strcat (tmp, "\\Answered");
+ mutt_remove_trailing_ws (tmp);
+
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s STORE %d FLAGS.SILENT (%s)\r\n", seq, tmp);
+ if (imap_exec (buf, sizeof (buf), ctx, seq, buf) != 0)
+ {
+ imap_error ("imap_sync_mailbox()", buf);
+ return (-1);
+ }
+ }
+ }
+
+ mutt_message ("Marking messages as deleted...");
+ make_delete_list (tmp, sizeof (tmp), ctx);
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s STORE %s +FLAGS.SILENT (\\Deleted)\r\n", seq, tmp);
+ if (imap_exec (buf, sizeof (buf), ctx, seq, buf) != 0)
+ {
+ imap_error ("imap_sync_mailbox()", buf);
+ return (-1);
+ }
+
+ return 0;
+}
+
+void imap_fastclose_mailbox (CONTEXT *ctx)
+{
+ int i;
+
+ imap_close_connection (ctx);
+ for (i = 0; i < IMAP_CACHE_LEN; i++)
+ {
+ if (((IMAP_DATA *) ctx->data)->cache[i].path)
+ {
+ unlink (((IMAP_DATA *) ctx->data)->cache[i].path);
+ safe_free ((void **) &((IMAP_DATA *) ctx->data)->cache[i].path);
+ }
+ }
+ safe_free ((void **) &ctx->data);
+}
+
+/* commit changes and terminate connection */
+int imap_close_mailbox (CONTEXT *ctx)
+{
+ char seq[8];
+ char buf[LONG_STRING];
+
+ /* tell the server to commit changes */
+ mutt_message ("Closing mailbox...");
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s CLOSE\r\n", seq);
+ if (imap_exec (buf, sizeof (buf), ctx, seq, buf) != 0)
+ {
+ imap_error ("imap_close_mailbox()", buf);
+ return (-1);
+ }
+ return 0;
+}
+
+/* use the NOOP command to poll for new mail */
+int imap_check_mailbox (CONTEXT *ctx, int *index_hint)
+{
+ char seq[8];
+ char buf[LONG_STRING];
+ int msgcount = ctx->msgcount;
+
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s NOOP\r\n", seq);
+ if (imap_exec (buf, sizeof (buf), ctx, seq, buf) != 0)
+ {
+ imap_error ("imap_check_mailbox()", buf);
+ return (-1);
+ }
+
+ return (msgcount != ctx->msgcount);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mutt_regex.h"
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+#include "mx.h"
+#include "init.h"
+#include "mailbox.h"
+
+#include <pwd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#define toggle_quadoption(opt) QuadOptions ^= (1 << (2 * opt))
+
+void set_quadoption (int opt, int flag)
+{
+ QuadOptions &= ~(0x3 << (2 * opt)); /* first clear the bits */
+ QuadOptions |= (flag & 0x3) << (2 * opt); /* now set them */
+}
+
+int quadoption (int opt)
+{
+ return ((QuadOptions >> (opt * 2)) & 0x3);
+}
+
+int query_quadoption (int opt, const char *prompt)
+{
+ int v = quadoption (opt);
+
+ switch (v)
+ {
+ case M_YES:
+ case M_NO:
+ return (v);
+
+ default:
+ v = mutt_yesorno (prompt, (v == M_ASKYES));
+ CLEARLINE (LINES - 1);
+ return (v);
+ }
+
+ /* not reached */
+}
+
+/* given the variable ``s'', return the index into the rc_vars array which
+ matches, or -1 if the variable is not found. */
+int mutt_option_index (char *s)
+{
+ int i;
+
+ for (i = 0; MuttVars[i].option; i++)
+ if (strcmp (s, MuttVars[i].option) == 0)
+ return (MuttVars[i].type == DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
+ return (-1);
+}
+
+static void add_char (BUFFER *buf, char ch)
+{
+ size_t offset;
+
+ if (buf->dptr >= buf->data + buf->dsize)
+ {
+ offset = buf->dptr - buf->data;
+ buf->dsize += 4;
+ safe_realloc ((void **) &buf->data, buf->dsize);
+ buf->dptr = buf->data + offset;
+ }
+ *buf->dptr++ = ch;
+}
+
+static void add_str (BUFFER *buf, const char *s)
+{
+ size_t slen = strlen (s);
+ size_t offset;
+
+ if (buf->dptr + slen > buf->data + buf->dsize)
+ {
+ offset = buf->dptr - buf->data;
+ buf->dsize += slen;
+ safe_realloc ((void **) &buf->data, buf->dsize);
+ buf->dptr = buf->data + offset;
+ }
+ memcpy (buf->dptr, s, slen);
+ buf->dptr += slen;
+}
+
+int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags)
+{
+ char ch;
+ char qc = 0; /* quote char */
+ char *pc;
+
+ /* reset the destination pointer to the beginning of the buffer */
+ dest->dptr = dest->data;
+
+ SKIPWS (tok->dptr);
+ while ((ch = *tok->dptr))
+ {
+ if (!qc)
+ {
+ if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
+ (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
+ (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
+ (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
+ ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
+ break;
+ }
+
+ tok->dptr++;
+
+ if (ch == qc)
+ qc = 0; /* end of quote */
+ else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
+ qc = ch;
+ else if (ch == '\\' && qc != '\'')
+ {
+ switch (ch = *tok->dptr++)
+ {
+ case 'c':
+ case 'C':
+ add_char (dest, (toupper (*tok->dptr) - '@') & 0x7f);
+ tok->dptr++;
+ break;
+ case 'r':
+ add_char (dest, '\r');
+ break;
+ case 'n':
+ add_char (dest, '\n');
+ break;
+ case 't':
+ add_char (dest, '\t');
+ break;
+ case 'f':
+ add_char (dest, '\f');
+ break;
+ case 'e':
+ add_char (dest, '\033');
+ break;
+ default:
+ if (isdigit (ch) &&
+ isdigit (*tok->dptr) &&
+ isdigit (*(tok->dptr + 1)))
+ {
+
+ add_char (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504);
+ tok->dptr += 2;
+ }
+ else
+ add_char (dest, ch);
+ }
+ }
+ else if (ch == '^' && (flags & M_TOKEN_CONDENSE))
+ {
+ ch = *tok->dptr++;
+ if (ch == '^')
+ add_char (dest, ch);
+ else if (ch == '[')
+ add_char (dest, '\033');
+ else if (isalpha (ch))
+ add_char (dest, toupper (ch) - '@');
+ else
+ {
+ add_char (dest, '^');
+ add_char (dest, ch);
+ }
+ }
+ else if (ch == '`' && (!qc || qc == '"'))
+ {
+ FILE *fp;
+ pid_t pid;
+ char *cmd, *ptr;
+ size_t expnlen;
+ BUFFER expn;
+ int line = 0;
+
+ pc = tok->dptr;
+ do {
+ if ((pc = strpbrk (pc, "\\`")))
+ {
+ /* skip any quoted chars */
+ if (*pc == '\\')
+ pc += 2;
+ }
+ } while (pc && *pc != '`');
+ if (!pc)
+ {
+ dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
+ return (-1);
+ }
+ cmd = mutt_substrdup (tok->dptr, pc);
+ if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
+ {
+ dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd));
+ return (-1);
+ }
+ FREE (&cmd);
+
+ tok->dptr = pc + 1;
+
+ /* read line */
+ memset (&expn, 0, sizeof (expn));
+ expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
+ fclose (fp);
+ mutt_wait_filter (pid);
+
+ /* if we got output, make a new string consiting of the shell ouptput
+ plus whatever else was left on the original line */
+ if (expn.data)
+ {
+ expnlen = strlen (expn.data);
+ tok->dsize = expnlen + strlen (tok->dptr) + 1;
+ ptr = safe_malloc (tok->dsize);
+ memcpy (ptr, expn.data, expnlen);
+ strcpy (ptr + expnlen, tok->dptr);
+ if (tok->destroy)
+ FREE (&tok->data);
+ tok->data = ptr;
+ tok->dptr = ptr;
+ tok->destroy = 1; /* mark that the caller should destroy this data */
+ ptr = NULL;
+ FREE (&expn.data);
+ }
+ }
+ else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha (*tok->dptr)))
+ {
+ char *env, *var;
+
+ if (*tok->dptr == '{')
+ {
+ tok->dptr++;
+ if ((pc = strchr (tok->dptr, '}')))
+ {
+ var = mutt_substrdup (tok->dptr, pc);
+ tok->dptr = pc + 1;
+ }
+ }
+ else
+ {
+ for (pc = tok->dptr; isalpha (*pc) || *pc == '_'; pc++)
+ ;
+ var = mutt_substrdup (tok->dptr, pc);
+ tok->dptr = pc;
+ }
+ if ((env = getenv (var)))
+ add_str (dest, env);
+ FREE (&var);
+ }
+ else
+ add_char (dest, ch);
+ }
+ add_char (dest, 0); /* terminate the string */
+ SKIPWS (tok->dptr);
+ return 0;
+}
+
+void mutt_add_to_list (LIST **list, BUFFER *inp)
+{
+ LIST *t, *last = NULL;
+ BUFFER buf;
+
+ memset (&buf, 0, sizeof (buf));
+ do
+ {
+ mutt_extract_token (&buf, inp, 0);
+
+ /* check to make sure the item is not already on this list */
+ for (last = *list; last; last = last->next)
+ {
+ if (strcasecmp (buf.data, last->data) == 0)
+ {
+ /* already on the list, so just ignore it */
+ last = NULL;
+ break;
+ }
+ if (!last->next)
+ break;
+ }
+
+ if (!*list || last)
+ {
+ t = (LIST *) safe_calloc (1, sizeof (LIST));
+ t->data = buf.data;
+ memset (&buf, 0, sizeof (buf));
+ if (last)
+ {
+ last->next = t;
+ last = last->next;
+ }
+ else
+ *list = last = t;
+ }
+ }
+ while (MoreArgs (inp));
+ FREE (&buf.data);
+}
+
+static void remove_from_list (LIST **l, BUFFER *inp)
+{
+ LIST *p, *last = NULL;
+ BUFFER buf;
+
+ memset (&buf, 0, sizeof (buf));
+ do
+ {
+ mutt_extract_token (&buf, inp, 0);
+
+ if (strcmp ("*", buf.data) == 0)
+ mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
+ else
+ {
+ p = *l;
+ last = NULL;
+ while (p)
+ {
+ if (strcasecmp (buf.data, p->data) == 0)
+ {
+ safe_free ((void **) &p->data);
+ if (last)
+ last->next = p->next;
+ else
+ (*l) = p->next;
+ safe_free ((void **) &p);
+ }
+ else
+ {
+ last = p;
+ p = p->next;
+ }
+ }
+ }
+ }
+ while (MoreArgs (inp));
+ FREE (&buf.data);
+}
+
+static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ mutt_add_to_list (&UnIgnore, s);
+ remove_from_list (&Ignore, s);
+ return 0;
+}
+
+static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ mutt_add_to_list (&Ignore, s);
+ remove_from_list (&UnIgnore, s);
+ return 0;
+}
+
+static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ mutt_add_to_list ((LIST **) data, s);
+ return 0;
+}
+
+static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ remove_from_list ((LIST **) data, s);
+ return 0;
+}
+
+static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ ALIAS *tmp, *last = NULL;
+
+ do
+ {
+ mutt_extract_token (buf, s, 0);
+
+ tmp = Aliases;
+ for (tmp = Aliases; tmp; tmp = tmp->next)
+ {
+ if (strcasecmp (buf->data, tmp->name) == 0)
+ {
+ if (last)
+ last->next = tmp->next;
+ else
+ Aliases = tmp->next;
+ tmp->next = NULL;
+ mutt_free_alias (&tmp);
+ break;
+ }
+ last = tmp;
+ }
+ }
+ while (MoreArgs (s));
+ return 0;
+}
+
+static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ ALIAS *tmp = Aliases;
+ ALIAS *last = NULL;
+ char *p;
+ size_t len;
+
+ if ((p = strpbrk (s->dptr, " \t")) == NULL)
+ {
+ strfcpy (err->data, "alias: no address", err->dsize);
+ return (-1);
+ }
+
+ len = p - s->dptr;
+
+ /* check to see if an alias with this name already exists */
+ for (; tmp; tmp = tmp->next)
+ {
+ if (!strncasecmp (tmp->name, s->dptr, len) && *(tmp->name + len) == 0)
+ break;
+ last = tmp;
+ }
+
+ if (!tmp)
+ {
+ /* create a new alias */
+ tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
+ tmp->name = safe_malloc (len + 1);
+ memcpy (tmp->name, s->dptr, len);
+ tmp->name[len] = 0;
+ }
+ else
+ {
+ /* override the previous value */
+ rfc822_free_address (&tmp->addr);
+ }
+ s->dptr = p;
+
+ mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE);
+ tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
+ if (last)
+ last->next = tmp;
+ else
+ Aliases = tmp;
+ return 0;
+}
+
+static int
+parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ LIST *last = NULL;
+ LIST *tmp = UserHeader;
+ LIST *ptr;
+ size_t l;
+
+ do
+ {
+ mutt_extract_token (buf, s, 0);
+ if (strcmp ("*", buf->data) == 0)
+ mutt_free_list (&UserHeader);
+ else
+ {
+ tmp = UserHeader;
+ last = NULL;
+
+ l = strlen (buf->data);
+ if (buf->data[l - 1] == ':')
+ l--;
+
+ while (tmp)
+ {
+ if (strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
+ {
+ ptr = tmp;
+ if (last)
+ last->next = tmp->next;
+ else
+ UserHeader = tmp->next;
+ tmp = tmp->next;
+ ptr->next = NULL;
+ mutt_free_list (&ptr);
+ }
+ else
+ {
+ last = tmp;
+ tmp = tmp->next;
+ }
+ }
+ }
+ }
+ while (MoreArgs (s));
+ return 0;
+}
+
+static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ LIST *tmp;
+ size_t keylen;
+ char *p;
+
+ mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
+ if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
+ {
+ strfcpy (err->data, "invalid header field", err->dsize);
+ return (-1);
+ }
+ keylen = p - buf->data + 1;
+ p++;
+ SKIPWS (p);
+ if (!*p)
+ {
+ snprintf (err->data, err->dsize, "ignoring empty header field: %s", buf->data);
+ return (-1);
+ }
+
+ if (UserHeader)
+ {
+ for (tmp = UserHeader; ; tmp = tmp->next)
+ {
+ /* see if there is already a field by this name */
+ if (strncasecmp (buf->data, tmp->data, keylen) == 0)
+ {
+ /* replace the old value */
+ safe_free ((void **) &tmp->data);
+ tmp->data = buf->data;
+ memset (buf, 0, sizeof (BUFFER));
+ return 0;
+ }
+ if (!tmp->next)
+ break;
+ }
+ tmp->next = mutt_new_list ();
+ tmp = tmp->next;
+ }
+ else
+ {
+ tmp = mutt_new_list ();
+ UserHeader = tmp;
+ }
+ tmp->data = buf->data;
+ memset (buf, 0, sizeof (BUFFER));
+ return 0;
+}
+
+static int
+parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
+{
+ int i, flags = 0;
+
+ if (strncmp ("reverse-", s, 8) == 0)
+ {
+ s += 8;
+ flags = SORT_REVERSE;
+ }
+
+ if (strncmp ("last-", s, 5) == 0)
+ {
+ s += 5;
+ flags |= SORT_LAST;
+ }
+
+ if ((i = mutt_getvaluebyname (s, map)) == -1)
+ {
+ snprintf (err->data, err->dsize, "%s: unknown sorting method", s);
+ return (-1);
+ }
+
+ *val = i | flags;
+
+ return 0;
+}
+
+static void mutt_restore_default (struct option_t *p)
+{
+ switch (p->type & DT_MASK)
+ {
+ case DT_STR:
+ if (p->init)
+ {
+ FREE (p->data);
+ *((char **) p->data) = safe_strdup ((char *) p->init);
+ }
+ break;
+ case DT_PATH:
+ if (p->init)
+ {
+ char path[_POSIX_PATH_MAX];
+
+ FREE (p->data);
+ strfcpy (path, (char *) p->init, sizeof (path));
+ mutt_expand_path (path, sizeof (path));
+ *((char **) p->data) = safe_strdup (path);
+ }
+ break;
+ case DT_BOOL:
+ if (p->init)
+ set_option (p->data);
+ else
+ unset_option (p->data);
+ break;
+ case DT_QUAD:
+ set_quadoption (p->data, p->init);
+ break;
+ case DT_NUM:
+ case DT_SORT:
+ case DT_MAGIC:
+ *((short *) p->data) = p->init;
+ break;
+ case DT_RX:
+ {
+ REGEXP *pp = (REGEXP *) p->data;
+ FREE (&pp->pattern);
+ if (p->init)
+ {
+ if (pp->rx)
+ regfree (pp->rx);
+ else
+ pp->rx = safe_calloc (1, sizeof (regex_t));
+ pp->pattern = safe_strdup ((char *) p->init);
+ if (REGCOMP (pp->rx, pp->pattern, mutt_which_case (pp->pattern)) != 0)
+ {
+ fprintf (stderr, "mutt_restore_default: error in regexp: %s\n",
+ pp->pattern);
+ }
+ }
+ }
+ break;
+ }
+ if (p->flags & R_INDEX)
+ set_option (OPTFORCEREDRAWINDEX);
+ if (p->flags & R_PAGER)
+ set_option (OPTFORCEREDRAWPAGER);
+ if (p->flags & R_RESORT_SUB)
+ set_option (OPTSORTSUBTHREADS);
+ if (p->flags & R_RESORT)
+ set_option (OPTNEEDRESORT);
+}
+
+static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ int idx, query, unset, inv, reset, r = 0;
+ char *p, scratch[_POSIX_PATH_MAX];
+
+ while (MoreArgs (s))
+ {
+ /* reset state variables */
+ query = 0;
+ unset = data & M_SET_UNSET;
+ inv = data & M_SET_INV;
+ reset = data & M_SET_RESET;
+
+ if (*s->dptr == '?')
+ {
+ query = 1;
+ s->dptr++;
+ }
+ else if (strncmp ("no", s->dptr, 2) == 0)
+ {
+ s->dptr += 2;
+ unset = !unset;
+ }
+ else if (strncmp ("inv", s->dptr, 3) == 0)
+ {
+ s->dptr += 3;
+ inv = !inv;
+ }
+ else if (*s->dptr == '&')
+ {
+ reset = 1;
+ s->dptr++;
+ }
+
+ /* get the variable name */
+ mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
+
+ if ((idx = mutt_option_index (tmp->data)) == -1 &&
+ !(reset && !strcmp ("all", tmp->data)))
+ {
+ snprintf (err->data, err->dsize, "%s: unknown variable", tmp->data);
+ return (-1);
+ }
+ SKIPWS (s->dptr);
+
+ if (reset)
+ {
+ if (query || unset || inv)
+ {
+ snprintf (err->data, err->dsize, "prefix is illegal with reset");
+ return (-1);
+ }
+
+ if (s && *s->dptr == '=')
+ {
+ snprintf (err->data, err->dsize, "value is illegal with reset");
+ return (-1);
+ }
+
+ if (!strcmp ("all", tmp->data))
+ {
+ for (idx = 0; MuttVars[idx].option; idx++)
+ mutt_restore_default (&MuttVars[idx]);
+ return 0;
+ }
+ else
+ mutt_restore_default (&MuttVars[idx]);
+ }
+ else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
+ {
+ if (s && *s->dptr == '=')
+ {
+ snprintf (err->data, err->dsize, "%s is a boolean var!", tmp->data);
+ return (-1);
+ }
+
+ if (query)
+ {
+ snprintf (err->data, err->dsize, "%s is %sset", tmp->data,
+ option (MuttVars[idx].data) ? "" : "un");
+ return 0;
+ }
+
+ if (unset)
+ unset_option (MuttVars[idx].data);
+ else if (inv)
+ toggle_option (MuttVars[idx].data);
+ else
+ set_option (MuttVars[idx].data);
+ }
+ else if (DTYPE (MuttVars[idx].type) == DT_STR ||
+ DTYPE (MuttVars[idx].type) == DT_PATH)
+ {
+ if (query || *s->dptr != '=')
+ {
+ /* user requested the value of this variable */
+ snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
+ NONULL (*((char **) MuttVars[idx].data)));
+ break;
+ }
+
+ s->dptr++;
+
+ /* copy the value of the string */
+ FREE (MuttVars[idx].data);
+ mutt_extract_token (tmp, s, 0);
+ if (MuttVars[idx].type == DT_PATH)
+ {
+ strfcpy (scratch, tmp->data, sizeof (scratch));
+ mutt_expand_path (scratch, sizeof (scratch));
+ *((char **) MuttVars[idx].data) = safe_strdup (scratch);
+ }
+ else
+ {
+ *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
+ }
+ }
+ else if (DTYPE(MuttVars[idx].type) == DT_RX)
+ {
+ REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
+ regex_t *rx;
+ int e, flags = 0;
+
+ if (query || *s->dptr != '=')
+ {
+ /* user requested the value of this variable */
+ snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
+ NONULL (ptr->pattern));
+ break;
+ }
+
+ s->dptr++;
+
+ /* copy the value of the string */
+ mutt_extract_token (tmp, s, 0);
+
+ if (!ptr->pattern || strcmp (ptr->pattern, tmp->data) != 0)
+ {
+ /* $alternates is case-insensitive,
+ $mask is case-sensitive */
+ if (strcmp (MuttVars[idx].option, "alternates") == 0)
+ flags |= REG_ICASE;
+ else if (strcmp (MuttVars[idx].option, "mask") != 0)
+ flags |= mutt_which_case (tmp->data);
+
+ rx = (regex_t *) safe_malloc (sizeof (regex_t));
+ if ((e = REGCOMP (rx, tmp->data, flags)) != 0)
+ {
+ regerror (e, rx, err->data, err->dsize);
+ regfree (rx);
+ FREE (&rx);
+ break;
+ }
+
+ /* get here only if everything went smootly */
+ if (ptr->pattern)
+ {
+ FREE (&ptr->pattern);
+ regfree ((regex_t *) ptr->rx);
+ FREE (&ptr->rx);
+ }
+
+ ptr->pattern = safe_strdup (tmp->data);
+ ptr->rx = rx;
+
+ /* $reply_regexp requires special treatment */
+ if (Context && Context->msgcount &&
+ strcmp (MuttVars[idx].option, "reply_regexp") == 0)
+ {
+ regmatch_t pmatch[1];
+ int i;
+
+#define CUR_ENV Context->hdrs[i]->env
+ for (i = 0; i < Context->msgcount; i++)
+ {
+ if (CUR_ENV && CUR_ENV->subject)
+ {
+ CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
+ CUR_ENV->subject, 1, pmatch, 0)) ?
+ CUR_ENV->subject :
+ CUR_ENV->subject + pmatch[0].rm_eo;
+ }
+ }
+#undef CUR_ENV
+ }
+ }
+ }
+ else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
+ {
+ if (query || *s->dptr != '=')
+ {
+ switch (DefaultMagic)
+ {
+ case M_MBOX:
+ p = "mbox";
+ break;
+ case M_MMDF:
+ p = "MMDF";
+ break;
+ case M_MH:
+ p = "MH";
+ break;
+ case M_MAILDIR:
+ p = "Maildir";
+ break;
+ default:
+ p = "unknown";
+ break;
+ }
+ snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
+ break;
+ }
+
+ s->dptr++;
+
+ /* copy the value of the string */
+ mutt_extract_token (tmp, s, 0);
+ if (mx_set_magic (tmp->data))
+ {
+ snprintf (err->data, err->dsize, "%s: invalid mailbox type", tmp->data);
+ r = -1;
+ break;
+ }
+ }
+ else if (DTYPE(MuttVars[idx].type) == DT_NUM)
+ {
+ short *ptr = (short *) MuttVars[idx].data;
+
+ if (query || *s->dptr != '=')
+ {
+ /* user requested the value of this variable */
+ snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
+ break;
+ }
+
+ s->dptr++;
+
+ mutt_extract_token (tmp, s, 0);
+ *ptr = (short) atoi (tmp->data);
+
+ /* these ones need a sanity check */
+ if (strcmp (MuttVars[idx].option, "history") == 0)
+ {
+ if (*ptr < 0)
+ *ptr = 0;
+ mutt_init_history ();
+ }
+ else if (strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
+ {
+ if (*ptr < 0)
+ *ptr = 0;
+ }
+ }
+ else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
+ {
+ if (query)
+ {
+ char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
+
+ snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
+ vals [ quadoption (MuttVars[idx].data) ]);
+ break;
+ }
+
+ if (*s->dptr == '=')
+ {
+ s->dptr++;
+ mutt_extract_token (tmp, s, 0);
+ if (strcasecmp ("yes", tmp->data) == 0)
+ set_quadoption (MuttVars[idx].data, M_YES);
+ else if (strcasecmp ("no", tmp->data) == 0)
+ set_quadoption (MuttVars[idx].data, M_NO);
+ else if (strcasecmp ("ask-yes", tmp->data) == 0)
+ set_quadoption (MuttVars[idx].data, M_ASKYES);
+ else if (strcasecmp ("ask-no", tmp->data) == 0)
+ set_quadoption (MuttVars[idx].data, M_ASKNO);
+ else
+ {
+ snprintf (err->data, err->dsize, "%s: invalid value.", tmp->data);
+ r = -1;
+ break;
+ }
+ }
+ else
+ {
+ if (inv)
+ toggle_quadoption (MuttVars[idx].data);
+ else if (unset)
+ set_quadoption (MuttVars[idx].data, M_NO);
+ else
+ set_quadoption (MuttVars[idx].data, M_YES);
+ }
+ }
+ else if (DTYPE (MuttVars[idx].type) == DT_SORT)
+ {
+ const struct mapping_t *map;
+
+ switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
+ {
+ case DT_SORT_ALIAS:
+ map = SortAliasMethods;
+ break;
+ case DT_SORT_BROWSER:
+ map = SortBrowserMethods;
+ break;
+ default:
+ map = SortMethods;
+ break;
+ }
+
+ if (query || *s->dptr != '=')
+ {
+ p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
+
+ snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
+ (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
+ (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
+ p);
+ return 0;
+ }
+ s->dptr++;
+ mutt_extract_token (tmp, s , 0);
+
+ if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
+ break;
+ }
+ else
+ {
+ snprintf (err->data, err->dsize, "%s: unknown type", MuttVars[idx].option);
+ r = -1;
+ break;
+ }
+
+ if (MuttVars[idx].flags & R_INDEX)
+ set_option (OPTFORCEREDRAWINDEX);
+ if (MuttVars[idx].flags & R_PAGER)
+ set_option (OPTFORCEREDRAWPAGER);
+ if (MuttVars[idx].flags & R_RESORT_SUB)
+ set_option (OPTSORTSUBTHREADS);
+ if (MuttVars[idx].flags & R_RESORT)
+ set_option (OPTNEEDRESORT);
+ }
+ return (r);
+}
+
+void mutt_nocurses_error (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fputc ('\n', stderr);
+}
+
+/* reads the specified initialization file. returns -1 if errors were found
+ so that we can pause to let the user know... */
+static int source_rc (const char *rcfile, BUFFER *err)
+{
+ FILE *f;
+ int line = 0, rc = 0;
+ BUFFER token;
+ char *linebuf = NULL;
+ size_t buflen;
+ pid_t pid;
+
+ if ((f = mutt_open_read (rcfile, &pid)) == NULL)
+ {
+ snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
+ return (-1);
+ }
+
+ memset (&token, 0, sizeof (token));
+ while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
+ {
+ if (mutt_parse_rc_line (linebuf, &token, err) == -1)
+ {
+ mutt_error ("Error in %s, line %d: %s", rcfile, line, err->data);
+ rc = -1;
+ }
+ }
+ FREE (&token.data);
+ safe_free ((void **) &linebuf);
+ fclose (f);
+ if (pid != -1)
+ mutt_wait_filter (pid);
+ if (rc)
+ snprintf (err->data, err->dsize, "source: errors in %s", rcfile);
+ return (rc);
+}
+
+static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ char path[_POSIX_PATH_MAX];
+
+ if (mutt_extract_token (tmp, s, 0) != 0)
+ {
+ snprintf (err->data, err->dsize, "source: error at %s", s->dptr);
+ return (-1);
+ }
+ if (MoreArgs (s))
+ {
+ strfcpy (err->data, "source: too many arguments", err->dsize);
+ return (-1);
+ }
+ strfcpy (path, tmp->data, sizeof (path));
+ mutt_expand_path (path, sizeof (path));
+ return (source_rc (path, err));
+}
+
+/* line command to execute
+
+ token scratch buffer to be used by parser. caller should free
+ token->data when finished. the reason for this variable is
+ to avoid having to allocate and deallocate a lot of memory
+ if we are parsing many lines. the caller can pass in the
+ memory to use, which avoids having to create new space for
+ every call to this function.
+
+ err where to write error messages */
+int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
+{
+ int i, r = -1;
+ BUFFER expn;
+
+ memset (&expn, 0, sizeof (expn));
+ expn.data = expn.dptr = line;
+ expn.dsize = strlen (line);
+
+ *err->data = 0;
+
+ SKIPWS (expn.dptr);
+ while (*expn.dptr)
+ {
+ if (*expn.dptr == '#')
+ break; /* rest of line is a comment */
+ if (*expn.dptr == ';')
+ {
+ expn.dptr++;
+ continue;
+ }
+ mutt_extract_token (token, &expn, 0);
+ for (i = 0; Commands[i].name; i++)
+ {
+ if (!strcmp (token->data, Commands[i].name))
+ {
+ if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
+ goto finish;
+ break;
+ }
+ }
+ if (!Commands[i].name)
+ {
+ snprintf (err->data, err->dsize, "%s: unknown command", token->data);
+ goto finish;
+ }
+ }
+ r = 0;
+finish:
+ if (expn.destroy)
+ FREE (&expn.data);
+ return (r);
+}
+
+char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
+{
+ int i;
+
+ for (i=0; map[i].name; i++)
+ if (map[i].value == val)
+ return (map[i].name);
+ return NULL;
+}
+
+int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
+{
+ int i;
+
+ for (i = 0; map[i].name; i++)
+ if (strcasecmp (map[i].name, name) == 0)
+ return (map[i].value);
+ return (-1);
+}
+
+#ifdef DEBUG
+static void start_debug (void)
+{
+ time_t t;
+ int i;
+ char buf[_POSIX_PATH_MAX];
+ char buf2[_POSIX_PATH_MAX];
+
+ /* rotate the old debug logs */
+ for (i=3; i>=0; i--)
+ {
+ snprintf (buf, sizeof(buf), "%s/.muttdebug%d", Homedir, i);
+ snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", Homedir, i+1);
+ rename (buf, buf2);
+ }
+ if ((debugfile = safe_fopen(buf, "w")) != NULL)
+ {
+ t = time (0);
+ fprintf (debugfile, "Mutt %s started at %s.\nDebugging at level %d.\n\n",
+ VERSION, asctime (localtime (&t)), debuglevel);
+ }
+}
+#endif
+
+static int mutt_execute_commands (LIST *p)
+{
+ BUFFER err, token;
+ char errstr[SHORT_STRING];
+
+ memset (&err, 0, sizeof (err));
+ err.data = errstr;
+ err.dsize = sizeof (errstr);
+ memset (&token, 0, sizeof (token));
+ for (; p; p = p->next)
+ {
+ if (mutt_parse_rc_line (p->data, &token, &err) != 0)
+ {
+ fprintf (stderr, "Error in command line: %s\n", err.data);
+ FREE (&token.data);
+ return (-1);
+ }
+ }
+ FREE (&token.data);
+ return 0;
+}
+
+void mutt_init (int skip_sys_rc, LIST *commands)
+{
+ struct passwd *pw;
+ struct utsname utsname;
+ char *p, buffer[STRING], error[STRING];
+ int i, default_rc = 0, need_pause = 0;
+ BUFFER err;
+
+ memset (&err, 0, sizeof (err));
+ err.data = error;
+ err.dsize = sizeof (error);
+
+ /* on one of the systems I use, getcwd() does not return the same prefix
+ as is listed in the passwd file */
+ if ((p = getenv ("HOME")))
+ Homedir = safe_strdup (p);
+
+ /* Get some information about the user */
+ if ((pw = getpwuid (getuid ())))
+ {
+ Username = safe_strdup (pw->pw_name);
+ if (!Homedir)
+ Homedir = safe_strdup (pw->pw_dir);
+ if ((p = strchr (pw->pw_gecos, ',')))
+ Realname = mutt_substrdup (pw->pw_gecos, p);
+ else
+ Realname = safe_strdup (pw->pw_gecos);
+ Shell = safe_strdup (pw->pw_shell);
+ }
+ else
+ {
+ if (!Homedir)
+ {
+ mutt_endwin (NULL);
+ fputs ("unable to determine home directory", stderr);
+ exit (1);
+ }
+ if ((p = getenv ("USER")))
+ Username = safe_strdup (p);
+ else
+ {
+ mutt_endwin (NULL);
+ fputs ("unable to determine user", stderr);
+ exit (1);
+ }
+ Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
+ }
+
+#ifdef DEBUG
+ /* Start up debugging mode if requested */
+ if (debuglevel > 0)
+ start_debug ();
+#endif
+
+ /* And about the host... */
+ uname (&utsname);
+ /* some systems report the FQDN instead of just the hostname */
+ if ((p = strchr (utsname.nodename, '.')))
+ {
+ Hostname = mutt_substrdup (utsname.nodename, p);
+ p++;
+ strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
+ }
+ else
+ Hostname = safe_strdup (utsname.nodename);
+
+#ifndef DOMAIN
+#define DOMAIN buffer
+ if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
+ Fqdn = safe_strdup ("@");
+ else
+#endif /* DOMAIN */
+ {
+# ifdef HIDDEN_HOST
+ Fqdn = safe_strdup (DOMAIN);
+# else
+ Fqdn = safe_malloc (strlen (DOMAIN) + strlen (Hostname) + 2);
+ sprintf (Fqdn, "%s.%s", Hostname, DOMAIN);
+# endif /* HIDDEN_HOST */
+ }
+
+ if ((p = getenv ("MAIL")))
+ Spoolfile = safe_strdup (p);
+ else
+ {
+#ifdef HOMESPOOL
+ snprintf (buffer, sizeof (buffer), "%s/%s", Homedir, MAILPATH);
+#else
+ snprintf (buffer, sizeof (buffer), "%s/%s", MAILPATH, Username);
+#endif
+ Spoolfile = safe_strdup (buffer);
+ }
+
+ if ((p = getenv ("MAILCAPS")))
+ MailcapPath = safe_strdup (p);
+ else
+ {
+ /* Default search path from RFC1524 */
+ MailcapPath = safe_strdup ("~/.mailcap:" SHAREDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
+ }
+
+ Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
+
+
+
+#ifdef _PGPPATH
+#ifdef _PGPV2PATH
+ PgpV2 = safe_strdup (_PGPV2PATH);
+ if ((p = getenv("PGPPATH")) != NULL)
+ {
+ snprintf (buffer, sizeof (buffer), "%s/pubring.pgp", p);
+ PgpV2Pubring = safe_strdup (buffer);
+ snprintf (buffer, sizeof (buffer), "%s/secring.pgp", p);
+ PgpV2Secring = safe_strdup (buffer);
+ }
+ else
+ {
+ snprintf (buffer, sizeof (buffer), "%s/.pgp/pubring.pgp", Homedir);
+ PgpV2Pubring = safe_strdup (buffer);
+ snprintf (buffer, sizeof (buffer), "%s/.pgp/secring.pgp", Homedir);
+ PgpV2Secring = safe_strdup (buffer);
+ }
+#endif
+
+#ifdef _PGPV3PATH
+ PgpV3 = safe_strdup (_PGPV3PATH);
+ if ((p = getenv("PGPPATH")) != NULL)
+ {
+ snprintf (buffer, sizeof (buffer), "%s/pubring.pkr", p);
+ PgpV3Pubring = safe_strdup (buffer);
+ snprintf (buffer, sizeof (buffer), "%s/secring.skr", p);
+ PgpV3Secring = safe_strdup (buffer);
+ }
+ else
+ {
+ snprintf (buffer, sizeof (buffer), "%s/.pgp/pubring.pkr", Homedir);
+ PgpV3Pubring = safe_strdup (buffer);
+ snprintf (buffer, sizeof (buffer), "%s/.pgp/secring.skr", Homedir);
+ PgpV3Secring = safe_strdup (buffer);
+ }
+#endif
+
+#endif /* _PGPPATH */
+
+
+
+#ifdef USE_POP
+ PopUser = safe_strdup (Username);
+#endif
+
+ Editor = safe_strdup ((p = getenv ("EDITOR")) ? p : "vi");
+ Visual = safe_strdup ((p = getenv ("VISUAL")) ? p : Editor);
+
+ if ((p = getenv ("REPLYTO")) != NULL)
+ {
+ BUFFER buf, token;
+
+ snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
+
+ memset (&buf, 0, sizeof (buf));
+ buf.data = buf.dptr = buffer;
+ buf.dsize = strlen (buffer);
+
+ memset (&token, 0, sizeof (token));
+ parse_my_hdr (&token, &buf, 0, &err);
+ FREE (&token.data);
+ }
+
+ /* Set standard defaults */
+ for (i = 0; MuttVars[i].option; i++)
+ mutt_restore_default (&MuttVars[i]);
+
+#ifndef LOCALES_HACK
+ /* Do we have a locale definition? */
+ if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
+ ((p = getenv ("LANG")) != NULL && p[0]) ||
+ ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
+ set_option (OPTLOCALES);
+#endif
+
+ mutt_init_history ();
+
+ if (!Muttrc)
+ {
+ snprintf (buffer, sizeof (buffer), "%s/.muttrc-%s", Homedir, VERSION);
+ if (access (buffer, F_OK) == -1)
+ snprintf (buffer, sizeof (buffer), "%s/.muttrc", Homedir);
+ default_rc = 1;
+ Muttrc = safe_strdup (buffer);
+ }
+ else
+ {
+ strfcpy (buffer, Muttrc, sizeof (buffer));
+ FREE (&Muttrc);
+ mutt_expand_path (buffer, sizeof (buffer));
+ Muttrc = safe_strdup (buffer);
+ }
+ FREE (&AliasFile);
+ AliasFile = safe_strdup (Muttrc);
+
+ /* Process the global rc file if it exists and the user hasn't explicity
+ requested not to via "-n". */
+ if (!skip_sys_rc)
+ {
+ snprintf (buffer, sizeof (buffer), "%s/Muttrc-%s", SHAREDIR, VERSION);
+ if (access (buffer, F_OK) == -1)
+ snprintf (buffer, sizeof (buffer), "%s/Muttrc", SHAREDIR);
+ if (access (buffer, F_OK) != -1)
+ {
+ if (source_rc (buffer, &err) != 0)
+ {
+ fputs (err.data, stderr);
+ fputc ('\n', stderr);
+ need_pause = 1;
+ }
+ }
+ }
+
+ /* Read the user's initialization file. */
+ if (access (Muttrc, F_OK) != -1)
+ {
+ if (!option (OPTNOCURSES))
+ endwin ();
+ if (source_rc (Muttrc, &err) != 0)
+ {
+ fputs (err.data, stderr);
+ fputc ('\n', stderr);
+ need_pause = 1;
+ }
+ }
+ else if (!default_rc)
+ {
+ /* file specified by -F does not exist */
+ snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
+ mutt_endwin (buffer);
+ exit (1);
+ }
+
+ if (mutt_execute_commands (commands) != 0)
+ need_pause = 1;
+
+ if (need_pause && !option (OPTNOCURSES))
+ {
+ if (mutt_any_key_to_continue (NULL) == -1)
+ mutt_exit(1);
+ }
+
+ set_option (OPTWEED); /* turn weeding on by default */
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sort.h"
+#include "buffy.h"
+
+#define DT_MASK 0x0f
+#define DT_BOOL 1 /* boolean option */
+#define DT_NUM 2 /* a number */
+#define DT_STR 3 /* a string */
+#define DT_PATH 4 /* a pathname */
+#define DT_QUAD 5 /* quad-option (yes/no/ask-yes/ask-no) */
+#define DT_SORT 6 /* sorting methods */
+#define DT_RX 7 /* regular expressions */
+#define DT_MAGIC 8 /* mailbox type */
+#define DT_SYN 9 /* synonym for another variable */
+
+#define DTYPE(x) ((x) & DT_MASK)
+
+/* subtypes */
+#define DT_SUBTYPE_MASK 0xf0
+#define DT_SORT_ALIAS 0x10
+#define DT_SORT_BROWSER 0x20
+
+/* flags to parse_set() */
+#define M_SET_INV (1<<0) /* default is to invert all vars */
+#define M_SET_UNSET (1<<1) /* default is to unset all vars */
+#define M_SET_RESET (1<<2) /* default is to reset all vars to default */
+
+/* forced redraw/resort types */
+#define R_NONE 0
+#define R_INDEX (1<<0)
+#define R_PAGER (1<<1)
+#define R_RESORT (1<<2) /* resort the mailbox */
+#define R_RESORT_SUB (1<<3) /* resort subthreads */
+#define R_BOTH (R_INDEX | R_PAGER)
+#define R_RESORT_BOTH (R_RESORT | R_RESORT_SUB)
+
+struct option_t
+{
+ char *option;
+ short type;
+ short flags;
+ unsigned long data;
+ unsigned long init; /* initial value */
+};
+
+#define UL (unsigned long)
+
+#ifndef ISPELL
+#define ISPELL "ispell"
+#endif
+
+struct option_t MuttVars[] = {
+ { "abort_nosubject", DT_QUAD, R_NONE, OPT_SUBJECT, M_ASKYES },
+ { "abort_unmodified", DT_QUAD, R_NONE, OPT_ABORT, M_YES },
+ { "alias_file", DT_PATH, R_NONE, UL &AliasFile, UL "~/.muttrc" },
+ { "alias_format", DT_STR, R_NONE, UL &AliasFmt, UL "%2n %t %-10a %r" },
+ { "allow_8bit", DT_BOOL, R_NONE, OPTALLOW8BIT, 1 },
+ { "alternates", DT_RX, R_BOTH, UL &Alternates, 0 },
+ { "arrow_cursor", DT_BOOL, R_BOTH, OPTARROWCURSOR, 0 },
+ { "ascii_chars", DT_BOOL, R_BOTH, OPTASCIICHARS, 0 },
+ { "askbcc", DT_BOOL, R_NONE, OPTASKBCC, 0 },
+ { "askcc", DT_BOOL, R_NONE, OPTASKCC, 0 },
+ { "attribution", DT_STR, R_NONE, UL &Attribution, UL "On %d, %n wrote:" },
+ { "autoedit", DT_BOOL, R_NONE, OPTAUTOEDIT, 0 },
+ { "auto_tag", DT_BOOL, R_NONE, OPTAUTOTAG, 0 },
+ { "beep", DT_BOOL, R_NONE, OPTBEEP, 1 },
+ { "beep_new", DT_BOOL, R_NONE, OPTBEEPNEW, 0 },
+ { "charset", DT_STR, R_NONE, UL &Charset, UL "iso-8859-1" },
+ { "check_new", DT_BOOL, R_NONE, OPTCHECKNEW, 1 },
+ { "confirmappend", DT_BOOL, R_NONE, OPTCONFIRMAPPEND, 1 },
+ { "confirmcreate", DT_BOOL, R_NONE, OPTCONFIRMCREATE, 1 },
+ { "copy", DT_QUAD, R_NONE, OPT_COPY, M_YES },
+ { "date_format", DT_STR, R_BOTH, UL &DateFmt, UL "!%a, %b %d, %Y at %I:%M:%S%p %Z" },
+ { "delete_format", DT_STR, R_NONE, UL &DeleteFmt, UL "[-- Attachment from %u deleted %<%b %d %T %Y> --]" },
+ { "default_hook", DT_STR, R_NONE, UL &DefaultHook, UL "~f %s !~P | (~P ~C %s)" },
+ { "delete", DT_QUAD, R_NONE, OPT_DELETE, M_ASKYES },
+ { "dsn_notify", DT_STR, R_NONE, UL &DsnNotify, UL "" },
+ { "dsn_return", DT_STR, R_NONE, UL &DsnReturn, UL "" },
+ { "edit_headers", DT_BOOL, R_NONE, OPTEDITHDRS, 0 },
+ { "edit_hdrs", DT_SYN, R_NONE, UL "edit_headers", 0 },
+ { "editor", DT_PATH, R_NONE, UL &Editor, 0 },
+ { "empty_to", DT_STR, R_NONE, UL &EmptyTo, UL "undisclosed-recipients" },
+ { "escape", DT_STR, R_NONE, UL &EscChar, UL "~" },
+ { "fast_reply", DT_BOOL, R_NONE, OPTFASTREPLY, 0 },
+ { "fcc_attach", DT_BOOL, R_NONE, OPTFCCATTACH, 1 },
+ { "folder", DT_PATH, R_NONE, UL &Maildir, UL "~/Mail" },
+ { "folder_format", DT_STR, R_NONE, UL &FolderFormat, UL "%N %F %2l %-8.8u %-8.8g %8s %d %f" },
+ { "followup_to", DT_BOOL, R_NONE, OPTFOLLOWUPTO, 1 },
+ { "force_name", DT_BOOL, R_NONE, OPTFORCENAME, 0 },
+ { "forward_decode", DT_BOOL, R_NONE, OPTFORWDECODE, 1 },
+ { "forw_decode", DT_SYN, R_NONE, UL "forward_decode", 0 },
+ { "forward_format", DT_STR, R_NONE, UL &ForwFmt, UL "[%a: %s]" },
+ { "forw_format", DT_SYN, R_NONE, UL "forward_format", 0 },
+ { "forward_quote", DT_BOOL, R_NONE, OPTFORWQUOTE, 0 },
+ { "forw_quote", DT_SYN, R_NONE, UL "forward_quote", 0 },
+ { "hdr_format", DT_SYN, R_NONE, UL "index_format", 0 },
+ { "hdrs", DT_BOOL, R_NONE, OPTHDRS, 1 },
+ { "header", DT_BOOL, R_NONE, OPTHEADER, 0 },
+ { "help", DT_BOOL, R_BOTH, OPTHELP, 1 },
+ { "history", DT_NUM, R_NONE, UL &HistSize, 10 },
+ { "hostname", DT_STR, R_NONE, UL &Fqdn, 0 },
+ { "in_reply_to", DT_STR, R_NONE, UL &InReplyTo, UL "%i; from %n on %{!%a, %b %d, %Y at %I:%M:%S%p %Z}" },
+ { "include", DT_QUAD, R_NONE, OPT_INCLUDE, M_ASKYES },
+ { "indent_string", DT_STR, R_NONE, UL &Prefix, UL "> " },
+ { "indent_str", DT_SYN, R_NONE, UL "indent_string", 0 },
+ { "index_format", DT_STR, R_BOTH, UL &HdrFmt, UL "%4C %Z %{%b %d} %-15.15L (%4l) %s" },
+ { "ignore_list_reply_to", DT_BOOL, R_NONE, OPTIGNORELISTREPLYTO, 0 },
+ { "ispell", DT_PATH, R_NONE, UL &Ispell, UL ISPELL },
+ { "locale", DT_STR, R_BOTH, UL &Locale, UL "C" },
+ { "mail_check", DT_NUM, R_NONE, UL &BuffyTimeout, 5 },
+ { "mailcap_path", DT_STR, R_NONE, UL &MailcapPath, 0 },
+ { "mark_old", DT_BOOL, R_BOTH, OPTMARKOLD, 1 },
+ { "markers", DT_BOOL, R_PAGER, OPTMARKERS, 1 },
+ { "mask", DT_RX, R_NONE, UL &Mask, UL "^(\\.\\.$|[^.])" },
+ { "mbox", DT_PATH, R_BOTH, UL &Inbox, UL "~/mbox" },
+ { "mbox_type", DT_MAGIC,R_NONE, UL &DefaultMagic, M_MBOX },
+ { "metoo", DT_BOOL, R_NONE, OPTMETOO, 0 },
+ { "menu_scroll", DT_BOOL, R_NONE, OPTMENUSCROLL, 0 },
+ { "meta_key", DT_BOOL, R_NONE, OPTMETAKEY, 0 },
+ { "mime_forward", DT_QUAD, R_NONE, OPT_MIMEFWD, 0 },
+ { "mime_forward_decode", DT_BOOL, R_NONE, OPTMIMEFORWDECODE, 0 },
+ { "mime_fwd", DT_SYN, R_NONE, UL "mime_forward", 0 },
+ { "move", DT_QUAD, R_NONE, OPT_MOVE, M_ASKNO },
+ { "message_format", DT_STR, R_NONE, UL &MsgFmt, UL "%s" },
+ { "msg_format", DT_SYN, R_NONE, UL "message_format", 0 },
+ { "pager", DT_PATH, R_NONE, UL &Pager, UL "builtin" },
+ { "pager_context", DT_NUM, R_NONE, UL &PagerContext, 0 },
+ { "pager_format", DT_STR, R_PAGER, UL &PagerFmt, UL "-%S- %C/%m: %-20.20n %s" },
+ { "pager_index_lines",DT_NUM, R_PAGER, UL &PagerIndexLines, 0 },
+ { "pager_stop", DT_BOOL, R_NONE, OPTPAGERSTOP, 0 },
+
+
+
+#ifdef _PGPPATH
+
+ { "pgp_autosign", DT_BOOL, R_NONE, OPTPGPAUTOSIGN, 0 },
+ { "pgp_autoencrypt", DT_BOOL, R_NONE, OPTPGPAUTOENCRYPT, 0 },
+ { "pgp_encryptself", DT_BOOL, R_NONE, OPTPGPENCRYPTSELF, 1 },
+ { "pgp_long_ids", DT_BOOL, R_NONE, OPTPGPLONGIDS, 0 },
+ { "pgp_replyencrypt", DT_BOOL, R_NONE, OPTPGPREPLYENCRYPT, 0 },
+ { "pgp_replysign", DT_BOOL, R_NONE, OPTPGPREPLYSIGN, 0 },
+ { "pgp_sign_as", DT_STR, R_NONE, UL &PgpSignAs, 0 },
+ { "pgp_sign_micalg", DT_STR, R_NONE, UL &PgpSignMicalg, UL "pgp-md5" },
+ { "pgp_strict_enc", DT_BOOL, R_NONE, OPTPGPSTRICTENC, 1 },
+ { "pgp_timeout", DT_NUM, R_NONE, UL &PgpTimeout, 300 },
+ { "pgp_verify_sig", DT_QUAD, R_NONE, OPT_VERIFYSIG, M_YES },
+
+ { "pgp_v2", DT_PATH, R_NONE, UL &PgpV2, 0 },
+ { "pgp_v2_language", DT_STR, R_NONE, UL &PgpV2Language, UL "en" },
+ { "pgp_v2_pubring", DT_PATH, R_NONE, UL &PgpV2Pubring, 0 },
+ { "pgp_v2_secring", DT_PATH, R_NONE, UL &PgpV2Secring, 0 },
+
+ { "pgp_v5", DT_PATH, R_NONE, UL &PgpV3, 0 },
+ { "pgp_v5_language", DT_STR, R_NONE, UL &PgpV3Language, 0 },
+ { "pgp_v5_pubring", DT_PATH, R_NONE, UL &PgpV3Pubring, 0 },
+ { "pgp_v5_secring", DT_PATH, R_NONE, UL &PgpV3Secring, 0 },
+
+# ifdef HAVE_PGP2
+ { "pgp_default_version", DT_STR, R_NONE, UL &PgpDefaultVersion, UL "pgp2" },
+# else
+# ifdef HAVE_PGP5
+ { "pgp_default_version", DT_STR, R_NONE, UL &PgpDefaultVersion, UL "pgp5" },
+# endif
+# endif
+ { "pgp_receive_version", DT_STR, R_NONE, UL &PgpReceiveVersion, UL "default" },
+ { "pgp_send_version", DT_STR, R_NONE, UL &PgpSendVersion, UL "default" },
+ { "pgp_key_version", DT_STR, R_NONE, UL &PgpKeyVersion, UL "default" },
+
+#endif /* _PGPPATH */
+
+
+
+ { "pipe_split", DT_BOOL, R_NONE, OPTPIPESPLIT, 0 },
+ { "pipe_decode", DT_BOOL, R_NONE, OPTPIPEDECODE, 0 },
+ { "pipe_sep", DT_STR, R_NONE, UL &PipeSep, UL "\n" },
+#ifdef USE_POP
+ { "pop_delete", DT_BOOL, R_NONE, OPTPOPDELETE, 0 },
+ { "pop_host", DT_STR, R_NONE, UL &PopHost, UL "" },
+ { "pop_port", DT_NUM, R_NONE, UL &PopPort, 110 },
+ { "pop_pass", DT_STR, R_NONE, UL &PopPass, UL "" },
+ { "pop_user", DT_STR, R_NONE, UL &PopUser, UL "" },
+#endif /* USE_POP */
+ { "post_indent_string",DT_STR, R_NONE, UL &PostIndentString, UL "" },
+ { "post_indent_str", DT_SYN, R_NONE, UL "post_indent_string", 0 },
+ { "postpone", DT_QUAD, R_NONE, OPT_POSTPONE, M_ASKYES },
+ { "postponed", DT_PATH, R_NONE, UL &Postponed, UL "~/postponed" },
+ { "print", DT_QUAD, R_NONE, OPT_PRINT, M_ASKNO },
+ { "print_command", DT_PATH, R_NONE, UL &PrintCmd, UL "lpr" },
+ { "print_cmd", DT_SYN, R_NONE, UL "print_command", 0 },
+ { "prompt_after", DT_BOOL, R_NONE, OPTPROMPTAFTER, 1 },
+ { "query_command", DT_PATH, R_NONE, UL &QueryCmd, UL "" },
+ { "quit", DT_QUAD, R_NONE, OPT_QUIT, M_YES },
+ { "quote_regexp", DT_RX, R_PAGER, UL &QuoteRegexp, UL "^([ \t]*[|>:}#])+" },
+ { "reply_regexp", DT_RX, R_INDEX|R_RESORT, UL &ReplyRegexp, UL "^(re([\\[0-9\\]+])*|aw):[ \t]*" },
+ { "read_inc", DT_NUM, R_NONE, UL &ReadInc, 10 },
+ { "read_only", DT_BOOL, R_NONE, OPTREADONLY, 0 },
+ { "realname", DT_STR, R_BOTH, UL &Realname, 0 },
+ { "recall", DT_QUAD, R_NONE, OPT_RECALL, M_ASKYES },
+ { "record", DT_PATH, R_NONE, UL &Outbox, UL "" },
+ { "reply_to", DT_QUAD, R_NONE, OPT_REPLYTO, M_ASKYES },
+ { "resolve", DT_BOOL, R_NONE, OPTRESOLVE, 1 },
+ { "reverse_alias", DT_BOOL, R_BOTH, OPTREVALIAS, 0 },
+ { "reverse_name", DT_BOOL, R_BOTH, OPTREVNAME, 0 },
+ { "save_address", DT_BOOL, R_NONE, OPTSAVEADDRESS, 0 },
+ { "save_empty", DT_BOOL, R_NONE, OPTSAVEEMPTY, 1 },
+ { "save_name", DT_BOOL, R_NONE, OPTSAVENAME, 0 },
+ { "sendmail", DT_PATH, R_NONE, UL &Sendmail, UL SENDMAIL " -oem -oi" },
+ { "sendmail_wait", DT_NUM, R_NONE, UL &SendmailWait, 0 },
+ { "shell", DT_PATH, R_NONE, UL &Shell, 0 },
+ { "sig_dashes", DT_BOOL, R_NONE, OPTSIGDASHES, 1 },
+ { "signature", DT_PATH, R_NONE, UL &Signature, UL "~/.signature" },
+ { "simple_search", DT_STR, R_NONE, UL &SimpleSearch, UL "~f %s | ~s %s" },
+ { "smart_wrap", DT_BOOL, R_PAGER, OPTWRAP, 1 },
+ { "sort", DT_SORT, R_INDEX|R_RESORT, UL &Sort, SORT_DATE },
+ { "sort_alias", DT_SORT|DT_SORT_ALIAS, R_NONE, UL &SortAlias, SORT_ALIAS },
+ { "sort_aux", DT_SORT, R_INDEX|R_RESORT_BOTH, UL &SortAux, SORT_DATE },
+ { "sort_browser", DT_SORT|DT_SORT_BROWSER, R_NONE, UL &BrowserSort, SORT_SUBJECT },
+ { "sort_re", DT_BOOL, R_INDEX|R_RESORT_BOTH, OPTSORTRE, 1 },
+ { "spoolfile", DT_PATH, R_NONE, UL &Spoolfile, 0 },
+ { "status_chars", DT_STR, R_BOTH, UL &StChars, UL "-*%" },
+ { "status_format", DT_STR, R_BOTH, UL &Status, UL "-%r-Mutt: %f [Msgs:%?M?%M/?%m%?n? New:%n?%?o? Old:%o?%?d? Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b?%?l? %l?]---(%s/%S)-%>-(%P)---" },
+ { "status_on_top", DT_BOOL, R_BOTH, OPTSTATUSONTOP, 0 },
+ { "strict_threads", DT_BOOL, R_RESORT|R_INDEX, OPTSTRICTTHREADS, 0 },
+ { "suspend", DT_BOOL, R_NONE, OPTSUSPEND, 1 },
+ { "thorough_search", DT_BOOL, R_NONE, OPTTHOROUGHSRC, 0 },
+ { "tilde", DT_BOOL, R_PAGER, OPTTILDE, 0 },
+ { "timeout", DT_NUM, R_NONE, UL &Timeout, 600 },
+ { "tmpdir", DT_PATH, R_NONE, UL &Tempdir, 0 },
+ { "to_chars", DT_STR, R_BOTH, UL &Tochars, UL " +TCF" },
+ { "use_8bitmime", DT_BOOL, R_NONE, OPTUSE8BITMIME, 0 },
+ { "use_domain", DT_BOOL, R_NONE, OPTUSEDOMAIN, 1 },
+ { "use_from", DT_BOOL, R_NONE, OPTUSEFROM, 1 },
+ { "use_mailcap", DT_QUAD, R_NONE, OPT_USEMAILCAP, 1 },
+ { "visual", DT_PATH, R_NONE, UL &Visual, 0 },
+ { "wait_key", DT_BOOL, R_NONE, OPTWAITKEY, 1 },
+ { "wrap_search", DT_BOOL, R_NONE, OPTWRAPSEARCH, 1 },
+ { "write_inc", DT_NUM, R_NONE, UL &WriteInc, 10 },
+ { NULL }
+};
+
+const struct mapping_t SortMethods[] = {
+ { "date", SORT_DATE },
+ { "date-sent", SORT_DATE },
+ { "date-received", SORT_RECEIVED },
+ { "mailbox-order", SORT_ORDER },
+ { "subject", SORT_SUBJECT },
+ { "from", SORT_FROM },
+ { "size", SORT_SIZE },
+ { "threads", SORT_THREADS },
+ { "to", SORT_TO },
+ { "score", SORT_SCORE },
+ { NULL, 0 }
+};
+
+const struct mapping_t SortBrowserMethods[] = {
+ { "alpha", SORT_SUBJECT },
+ { "date", SORT_DATE },
+ { "size", SORT_SIZE },
+ { "unsorted", SORT_ORDER },
+ { NULL }
+};
+
+const struct mapping_t SortAliasMethods[] = {
+ { "alias", SORT_ALIAS },
+ { "address", SORT_ADDRESS },
+ { "unsorted", SORT_ORDER },
+ { NULL }
+};
+
+/* functions used to parse commands in a rc file */
+
+static int parse_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_unlist (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_alias (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_unalias (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_ignore (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_unignore (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_source (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_set (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_my_hdr (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_unmy_hdr (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+
+struct command_t
+{
+ char *name;
+ int (*func) (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+ unsigned long data;
+};
+
+struct command_t Commands[] = {
+ { "alias", parse_alias, 0 },
+ { "auto_view", parse_list, UL &AutoViewList },
+ { "alternative_order", parse_list, UL &AlternativeOrderList},
+ { "bind", mutt_parse_bind, 0 },
+#ifdef HAVE_COLOR
+ { "color", mutt_parse_color, 0 },
+ { "uncolor", mutt_parse_uncolor, 0 },
+#endif
+ { "fcc-hook", mutt_parse_hook, M_FCCHOOK },
+ { "fcc-save-hook", mutt_parse_hook, M_FCCHOOK | M_SAVEHOOK },
+ { "folder-hook", mutt_parse_hook, M_FOLDERHOOK },
+ { "hdr_order", parse_list, UL &HeaderOrderList },
+ { "ignore", parse_ignore, 0 },
+ { "lists", parse_list, UL &MailLists },
+ { "macro", mutt_parse_macro, 0 },
+ { "mailboxes", mutt_parse_mailboxes, 0 },
+ { "mbox-hook", mutt_parse_hook, M_MBOXHOOK },
+ { "mono", mutt_parse_mono, 0 },
+ { "my_hdr", parse_my_hdr, 0 },
+ { "push", mutt_parse_push, 0 },
+ { "reset", parse_set, M_SET_RESET },
+ { "save-hook", mutt_parse_hook, M_SAVEHOOK },
+ { "score", mutt_parse_score, 0 },
+ { "send-hook", mutt_parse_hook, M_SENDHOOK },
+ { "set", parse_set, 0 },
+ { "source", parse_source, 0 },
+ { "toggle", parse_set, M_SET_INV },
+ { "unalias", parse_unalias, 0 },
+ { "unignore", parse_unignore, 0 },
+ { "unlists", parse_unlist, UL &MailLists },
+ { "unmy_hdr", parse_unmy_hdr, 0 },
+ { "unscore", mutt_parse_unscore, 0 },
+ { "unset", parse_set, M_SET_UNSET },
+ { NULL }
+};
--- /dev/null
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# 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.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# 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 $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_menu.h"
+#include "mutt_curses.h"
+#include "keymap.h"
+#include "mapping.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "functions.h"
+
+struct mapping_t Menus[] = {
+ { "alias", MENU_ALIAS },
+ { "attach", MENU_ATTACH },
+ { "browser", MENU_FOLDER },
+ { "compose", MENU_COMPOSE },
+ { "editor", MENU_EDITOR },
+ { "generic", MENU_GENERIC },
+ { "index", MENU_MAIN },
+ { "pager", MENU_PAGER },
+ { "postpone", MENU_POST },
+
+
+
+#ifdef _PGPPATH
+ { "pgp", MENU_PGP },
+#endif
+
+
+
+ { "query", MENU_QUERY },
+ { NULL, 0 }
+};
+
+#define mutt_check_menu(s) mutt_getvaluebyname(s, Menus)
+
+static struct mapping_t KeyNames[] = {
+ { "<PageUp>", KEY_PPAGE },
+ { "<PageDown>", KEY_NPAGE },
+ { "<Up>", KEY_UP },
+ { "<Down>", KEY_DOWN },
+ { "<Right>", KEY_RIGHT },
+ { "<Left>", KEY_LEFT },
+ { "<Delete>", KEY_DC },
+ { "<BackSpace>",KEY_BACKSPACE },
+ { "<Insert>", KEY_IC },
+ { "<Home>", KEY_HOME },
+ { "<End>", KEY_END },
+ { "<Enter>", KEY_ENTER },
+ { "<Return>", M_ENTER_C },
+ { NULL, 0 }
+};
+
+/* contains the last key the user pressed */
+int LastKey;
+
+struct keymap_t *Keymaps[MENU_MAX];
+
+static struct keymap_t *allocKeys (int len, keycode_t *keys)
+{
+ struct keymap_t *p;
+
+ p = safe_calloc (1, sizeof (struct keymap_t));
+ p->len = len;
+ p->keys = safe_malloc (len * sizeof (keycode_t));
+ memcpy (p->keys, keys, len * sizeof (keycode_t));
+ return (p);
+}
+
+static int parsekeys (char *s, keycode_t *d, int max)
+{
+ int n, len = max;
+
+ while (*s && len)
+ {
+ if ((n = mutt_getvaluebyname (s, KeyNames)) != -1)
+ {
+ s += strlen (s);
+ *d = n;
+ }
+ else if (tolower (*s) == 'f' && isdigit (s[1]))
+ {
+ n = 0;
+ for (s++; isdigit (*s) ; s++)
+ {
+ n *= 10;
+ n += *s - '0';
+ }
+ *d = KEY_F(n);
+ }
+ else
+ {
+ *d = *s;
+ s++;
+ }
+ d++;
+ len--;
+ }
+
+ return (max - len);
+}
+
+/* insert a key sequence into the specified map. the map is sorted by ASCII
+ * value (lowest to highest)
+ */
+void km_bindkey (char *s, int menu, int op, char *macro)
+{
+ struct keymap_t *map, *tmp, *last = NULL, *next;
+ keycode_t buf[MAX_SEQ];
+ int len, pos = 0, lastpos = 0;
+
+ len = parsekeys (s, buf, MAX_SEQ);
+
+ map = allocKeys (len, buf);
+ map->op = op;
+ map->macro = safe_strdup (macro);
+
+ tmp = Keymaps[menu];
+
+ while (tmp)
+ {
+ if (pos >= len || pos >= tmp->len)
+ {
+ /* map and tmp match, but have different lengths, so overwrite */
+ do
+ {
+ len = tmp->eq;
+ next = tmp->next;
+ if (tmp->macro)
+ free (tmp->macro);
+ free (tmp->keys);
+ free (tmp);
+ tmp = next;
+ }
+ while (tmp && len >= pos);
+ map->eq = len;
+ break;
+ }
+ else if (buf[pos] == tmp->keys[pos])
+ pos++;
+ else if (buf[pos] < tmp->keys[pos])
+ {
+ /* found location to insert between last and tmp */
+ map->eq = pos;
+ break;
+ }
+ else /* buf[pos] > tmp->keys[pos] */
+ {
+ last = tmp;
+ lastpos = pos;
+ if (pos > tmp->eq)
+ pos = tmp->eq;
+ tmp = tmp->next;
+ }
+ }
+
+ map->next = tmp;
+ if (last)
+ {
+ last->next = map;
+ last->eq = lastpos;
+ }
+ else
+ Keymaps[menu] = map;
+}
+
+static void push_string (char *s)
+{
+ char *pp, *p = s + strlen (s) - 1;
+ size_t l;
+ int i;
+
+ while (p >= s)
+ {
+ /* if we see something like "<PageUp>", look to see if it is a real
+ function name and return the corresponding value */
+ if (*p == '>')
+ {
+ for (pp = p - 1; pp >= s && *pp != '<'; pp--)
+ ;
+ if (pp >= s)
+ {
+ l = p - pp + 1;
+ for (i = 0; KeyNames[i].name; i++)
+ {
+ if (!strncasecmp (pp, KeyNames[i].name, l))
+ break;
+ }
+ if (KeyNames[i].name)
+ {
+ /* found a match */
+ mutt_ungetch (KeyNames[i].value);
+ p = pp - 1;
+ continue;
+ }
+ }
+ }
+ mutt_ungetch (*p--);
+ }
+}
+
+static int retry_generic (int menu, keycode_t *keys, int keyslen, int lastkey)
+{
+ if (menu != MENU_EDITOR && menu != MENU_GENERIC && menu != MENU_PAGER)
+ {
+ if (lastkey)
+ mutt_ungetch (lastkey);
+ for (; keyslen; keyslen--)
+ mutt_ungetch (keys[keyslen - 1]);
+ return (km_dokey (MENU_GENERIC));
+ }
+ if (menu != MENU_EDITOR)
+ {
+ /* probably a good idea to flush input here so we can abort macros */
+ mutt_flushinp ();
+ }
+ return OP_NULL;
+}
+
+/* return values:
+ * >0 function to execute
+ * OP_NULL no function bound to key sequence
+ * -1 error occured while reading input
+ */
+int km_dokey (int menu)
+{
+ struct keymap_t *map = Keymaps[menu];
+ int pos = 0;
+ int n = 0;
+
+ if (!map)
+ return (retry_generic (menu, NULL, 0, 0));
+
+ FOREVER
+ {
+ if ((LastKey = mutt_getch ()) == ERR)
+ return (-1);
+
+ while (LastKey > map->keys[pos])
+ {
+ if (pos > map->eq || !map->next)
+ return (retry_generic (menu, map->keys, pos, LastKey));
+ map = map->next;
+ }
+
+ if (LastKey != map->keys[pos])
+ return (retry_generic (menu, map->keys, pos, LastKey));
+
+ if (++pos == map->len)
+ {
+ if (map->op != OP_MACRO)
+ return (map->op);
+
+ if (n++ == 10)
+ {
+ mutt_flushinp ();
+ mutt_error ("Macro loop detected.");
+ return (-1);
+ }
+
+ push_string (map->macro);
+ map = Keymaps[menu];
+ pos = 0;
+ }
+ }
+
+ /* not reached */
+}
+
+static void create_bindings (struct binding_t *map, int menu)
+{
+ int i;
+
+ for (i = 0 ; map[i].name ; i++)
+ if (map[i].seq)
+ km_bindkey (map[i].seq, menu, map[i].op, NULL);
+}
+
+char *km_keyname (int c)
+{
+ static char buf[5];
+ char *p;
+
+ if ((p = mutt_getnamebyvalue (c, KeyNames)))
+ return p;
+
+ switch (c)
+ {
+ case '\033':
+ return "ESC";
+ case ' ':
+ return "SPC";
+ case '\n':
+ case '\r':
+ return "RET";
+ case '\t':
+ return "TAB";
+ }
+
+ if (c < 256 && c > -128 && iscntrl ((unsigned char) c))
+ {
+ if (c < 0)
+ c += 256;
+
+ if (c < 128)
+ {
+ buf[0] = '^';
+ buf[1] = (c + '@') & 0x7f;
+ buf[2] = 0;
+ }
+ else
+ snprintf (buf, sizeof (buf), "\\%d%d%d", c >> 6, (c >> 3) & 7, c & 7);
+ }
+ else if (c >= KEY_F0 && c < KEY_F(256)) /* this maximum is just a guess */
+ sprintf (buf, "F%d", c - KEY_F0);
+ else if (IsPrint (c))
+ snprintf (buf, sizeof (buf), "%c", (unsigned char) c);
+ else
+ snprintf (buf, sizeof (buf), "\\x%hx", (unsigned short) c);
+ return (buf);
+}
+
+int km_expand_key (char *s, size_t len, struct keymap_t *map)
+{
+ size_t l;
+ int p = 0;
+
+ if (!map)
+ return (0);
+
+ FOREVER
+ {
+ strfcpy (s, km_keyname (map->keys[p]), len);
+ len -= (1 + (l = strlen (s)));
+
+ if (++p >= map->len || !len)
+ return (1);
+
+ s += l;
+ *(s++) = ' ';
+ }
+
+ /* not reached */
+}
+
+struct keymap_t *km_find_func (int menu, int func)
+{
+ struct keymap_t *map = Keymaps[menu];
+
+ for (; map; map = map->next)
+ if (map->op == func)
+ break;
+ return (map);
+}
+
+void km_init (void)
+{
+ memset (Keymaps, 0, sizeof (struct keymap_t *) * MENU_MAX);
+
+ create_bindings (OpAttach, MENU_ATTACH);
+ create_bindings (OpBrowser, MENU_FOLDER);
+ create_bindings (OpCompose, MENU_COMPOSE);
+ create_bindings (OpMain, MENU_MAIN);
+ create_bindings (OpPager, MENU_PAGER);
+ create_bindings (OpPost, MENU_POST);
+ create_bindings (OpQuery, MENU_QUERY);
+
+
+
+#ifdef _PGPPATH
+ create_bindings (OpPgp, MENU_PGP);
+#endif
+
+
+
+ /* bindings for the line editor */
+ create_bindings (OpEditor, MENU_EDITOR);
+
+ km_bindkey ("<up>", MENU_EDITOR, OP_EDITOR_HISTORY_UP, NULL);
+ km_bindkey ("<down>", MENU_EDITOR, OP_EDITOR_HISTORY_DOWN, NULL);
+ km_bindkey ("<left>", MENU_EDITOR, OP_EDITOR_BACKWARD_CHAR, NULL);
+ km_bindkey ("<right>", MENU_EDITOR, OP_EDITOR_FORWARD_CHAR, NULL);
+ km_bindkey ("<home>", MENU_EDITOR, OP_EDITOR_BOL, NULL);
+ km_bindkey ("<end>", MENU_EDITOR, OP_EDITOR_EOL, NULL);
+ km_bindkey ("<backspace>", MENU_EDITOR, OP_EDITOR_BACKSPACE, NULL);
+ km_bindkey ("<delete>", MENU_EDITOR, OP_EDITOR_BACKSPACE, NULL);
+ km_bindkey ("\177", MENU_EDITOR, OP_EDITOR_BACKSPACE, NULL);
+
+ /* generic menu keymap */
+ create_bindings (OpGeneric, MENU_GENERIC);
+
+ km_bindkey ("<home>", MENU_GENERIC, OP_FIRST_ENTRY, NULL);
+ km_bindkey ("<end>", MENU_GENERIC, OP_LAST_ENTRY, NULL);
+ km_bindkey ("<pagedown>", MENU_GENERIC, OP_NEXT_PAGE, NULL);
+ km_bindkey ("<pageup>", MENU_GENERIC, OP_PREV_PAGE, NULL);
+ km_bindkey ("<right>", MENU_GENERIC, OP_NEXT_PAGE, NULL);
+ km_bindkey ("<left>", MENU_GENERIC, OP_PREV_PAGE, NULL);
+ km_bindkey ("<up>", MENU_GENERIC, OP_PREV_ENTRY, NULL);
+ km_bindkey ("<down>", MENU_GENERIC, OP_NEXT_ENTRY, NULL);
+ km_bindkey ("1", MENU_GENERIC, OP_JUMP, NULL);
+ km_bindkey ("2", MENU_GENERIC, OP_JUMP, NULL);
+ km_bindkey ("3", MENU_GENERIC, OP_JUMP, NULL);
+ km_bindkey ("4", MENU_GENERIC, OP_JUMP, NULL);
+ km_bindkey ("5", MENU_GENERIC, OP_JUMP, NULL);
+ km_bindkey ("6", MENU_GENERIC, OP_JUMP, NULL);
+ km_bindkey ("7", MENU_GENERIC, OP_JUMP, NULL);
+ km_bindkey ("8", MENU_GENERIC, OP_JUMP, NULL);
+ km_bindkey ("9", MENU_GENERIC, OP_JUMP, NULL);
+
+ /* Miscellaneous extra bindings */
+
+ km_bindkey (" ", MENU_MAIN, OP_DISPLAY_MESSAGE, NULL);
+ km_bindkey ("<up>", MENU_MAIN, OP_MAIN_PREV_UNDELETED, NULL);
+ km_bindkey ("<down>", MENU_MAIN, OP_MAIN_NEXT_UNDELETED, NULL);
+ km_bindkey ("J", MENU_MAIN, OP_NEXT_ENTRY, NULL);
+ km_bindkey ("K", MENU_MAIN, OP_PREV_ENTRY, NULL);
+ km_bindkey ("x", MENU_MAIN, OP_EXIT, NULL);
+
+ km_bindkey ("x", MENU_PAGER, OP_PAGER_EXIT, NULL);
+ km_bindkey ("q", MENU_PAGER, OP_PAGER_EXIT, NULL);
+ km_bindkey ("<backspace>", MENU_PAGER, OP_PREV_LINE, NULL);
+ km_bindkey ("<pagedown>", MENU_PAGER, OP_NEXT_PAGE, NULL);
+ km_bindkey ("<pageup>", MENU_PAGER, OP_PREV_PAGE, NULL);
+ km_bindkey ("<up>", MENU_PAGER, OP_MAIN_PREV_UNDELETED, NULL);
+ km_bindkey ("<right>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED, NULL);
+ km_bindkey ("<down>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED, NULL);
+ km_bindkey ("<left>", MENU_PAGER, OP_MAIN_PREV_UNDELETED, NULL);
+ km_bindkey ("<home>", MENU_PAGER, OP_PAGER_TOP, NULL);
+ km_bindkey ("<end>", MENU_PAGER, OP_PAGER_BOTTOM, NULL);
+ km_bindkey ("1", MENU_PAGER, OP_JUMP, NULL);
+ km_bindkey ("2", MENU_PAGER, OP_JUMP, NULL);
+ km_bindkey ("3", MENU_PAGER, OP_JUMP, NULL);
+ km_bindkey ("4", MENU_PAGER, OP_JUMP, NULL);
+ km_bindkey ("5", MENU_PAGER, OP_JUMP, NULL);
+ km_bindkey ("6", MENU_PAGER, OP_JUMP, NULL);
+ km_bindkey ("7", MENU_PAGER, OP_JUMP, NULL);
+ km_bindkey ("8", MENU_PAGER, OP_JUMP, NULL);
+ km_bindkey ("9", MENU_PAGER, OP_JUMP, NULL);
+
+ km_bindkey ("<return>", MENU_ALIAS, OP_TAG, NULL);
+}
+
+void km_error_key (int menu)
+{
+ char buf[SHORT_STRING];
+
+ if (km_expand_key (buf, sizeof (buf), km_find_func (menu, OP_HELP)))
+ mutt_error ("Key is not bound. Press '%s' for help.", buf);
+ else
+ mutt_error ("Key is not bound. See the manual.");
+}
+
+int mutt_parse_push (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ int r = 0;
+
+ mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
+ if (MoreArgs (s))
+ {
+ strfcpy (err->data, "push: too many arguments", err->dsize);
+ r = -1;
+ }
+ else
+ push_string (buf->data);
+ return (r);
+}
+
+/* expects to see: <menu-string> <key-string> */
+char *parse_keymap (int *menu, BUFFER *s, BUFFER *err)
+{
+ BUFFER buf;
+
+ memset (&buf, 0, sizeof (buf));
+
+ /* menu name */
+ mutt_extract_token (&buf, s, 0);
+ if (MoreArgs (s))
+ {
+ if ((*menu = mutt_check_menu (buf.data)) == -1)
+ {
+ snprintf (err->data, err->dsize, "%s: no such menu", buf.data);
+ }
+ else
+ {
+ /* key sequence */
+ mutt_extract_token (&buf, s, 0);
+
+ if (!*buf.data)
+ {
+ strfcpy (err->data, "null key sequence", err->dsize);
+ }
+ else if (MoreArgs (s))
+ return (buf.data);
+ }
+ }
+ else
+ {
+ strfcpy (err->data, "too few arguments", err->dsize);
+ }
+ FREE (&buf.data);
+ return (NULL);
+}
+
+static int
+try_bind (char *key, int menu, char *func, struct binding_t *bindings)
+{
+ int i;
+
+ for (i = 0; bindings[i].name; i++)
+ if (strcmp (func, bindings[i].name) == 0)
+ {
+ km_bindkey (key, menu, bindings[i].op, NULL);
+ return (0);
+ }
+ return (-1);
+}
+
+struct binding_t *km_get_table (int menu)
+{
+ switch (menu)
+ {
+ case MENU_MAIN:
+ return OpMain;
+ case MENU_GENERIC:
+ return OpGeneric;
+ case MENU_COMPOSE:
+ return OpCompose;
+ case MENU_PAGER:
+ return OpPager;
+ case MENU_POST:
+ return OpPost;
+ case MENU_FOLDER:
+ return OpBrowser;
+ case MENU_ATTACH:
+ return OpAttach;
+ case MENU_EDITOR:
+ return OpEditor;
+ case MENU_QUERY:
+ return OpQuery;
+
+
+
+#ifdef _PGPPATH
+ case MENU_PGP:
+ return OpPgp;
+#endif
+
+
+
+ }
+ return NULL;
+}
+
+/* bind menu-name '<key_sequence>' function-name */
+int mutt_parse_bind (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ struct binding_t *bindings = NULL;
+ char *key;
+ int menu, r = 0;
+
+ if ((key = parse_keymap (&menu, s, err)) == NULL)
+ return (-1);
+
+ /* function to execute */
+ mutt_extract_token (buf, s, 0);
+ if (MoreArgs (s))
+ {
+ strfcpy (err->data, "bind: too many arguments", err->dsize);
+ r = -1;
+ }
+ else if (strcasecmp ("noop", buf->data) == 0)
+ km_bindkey (key, menu, OP_NULL, NULL); /* the `unbind' command */
+ else
+ {
+ /* First check the "generic" list of commands */
+ if (menu == MENU_PAGER || menu == MENU_EDITOR || menu == MENU_GENERIC ||
+ try_bind (key, menu, buf->data, OpGeneric) != 0)
+ {
+ /* Now check the menu-specific list of commands (if they exist) */
+ bindings = km_get_table (menu);
+ if (bindings && try_bind (key, menu, buf->data, bindings) != 0)
+ {
+ snprintf (err->data, err->dsize, "%s: no such function in map", buf->data);
+ r = -1;
+ }
+ }
+ }
+ FREE (&key);
+ return (r);
+}
+
+/* macro <menu> <key> <macro> */
+int mutt_parse_macro (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ int menu, r = -1;
+ char *key;
+
+ if ((key = parse_keymap (&menu, s, err)) == NULL)
+ return (-1);
+
+ mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
+ /* make sure the macro sequence is not an empty string */
+ if (!*buf->data)
+ {
+ strfcpy (err->data, "macro: empty key sequence", err->dsize);
+ }
+ else if (MoreArgs (s))
+ {
+ strfcpy (err->data, "macro: too many arguments", err->dsize);
+ }
+ else
+ {
+ km_bindkey (key, menu, OP_MACRO, buf->data);
+ r = 0;
+ }
+ FREE (&key);
+ return (r);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef KEYMAP_H
+#define KEYMAP_H
+
+/* maximal length of a key binding sequence used for buffer in km_bindkey */
+#define MAX_SEQ 8
+
+/* type for key storage, the rest of mutt works fine with int type */
+typedef short keycode_t;
+
+void km_bindkey (char *, int, int, char *);
+int km_dokey (int);
+
+/* entry in the keymap tree */
+struct keymap_t
+{
+ char *macro; /* macro expansion (op == OP_MACRO) */
+ struct keymap_t *next; /* next key in map */
+ short op; /* operation to perform */
+ short eq; /* number of leading keys equal to next entry */
+ short len; /* length of key sequence (unit: sizeof (keycode_t)) */
+ keycode_t *keys; /* key sequence */
+};
+
+char *km_keyname (int);
+int km_expand_key (char *, size_t, struct keymap_t *);
+struct keymap_t *km_find_func (int, int);
+void km_init (void);
+void km_error_key (int);
+
+enum
+{
+ MENU_ALIAS,
+ MENU_ATTACH,
+ MENU_COMPOSE,
+ MENU_EDITOR,
+ MENU_FOLDER,
+ MENU_GENERIC,
+ MENU_MAIN,
+ MENU_PAGER,
+ MENU_POST,
+
+#ifdef _PGPPATH
+ MENU_PGP,
+#endif
+
+
+
+
+
+ MENU_QUERY,
+ MENU_MAX
+};
+
+/* the keymap trees (one for each menu) */
+extern struct keymap_t *Keymaps[];
+
+/* dokey() records the last real key pressed */
+extern int LastKey;
+
+extern struct mapping_t Menus[];
+
+struct binding_t
+{
+ char *name; /* name of the function */
+ int op; /* function id number */
+ char *seq; /* default key binding */
+};
+
+struct binding_t *km_get_table (int menu);
+
+extern struct binding_t OpGeneric[];
+extern struct binding_t OpPost[];
+extern struct binding_t OpMain[];
+extern struct binding_t OpAttach[];
+extern struct binding_t OpPager[];
+extern struct binding_t OpCompose[];
+extern struct binding_t OpBrowser[];
+extern struct binding_t OpEditor[];
+extern struct binding_t OpQuery[];
+
+#ifdef _PGPPATH
+extern struct binding_t OpPgp[];
+#endif /* _PGPPATH */
+
+#include "keymap_defs.h"
+
+#endif /* KEYMAP_H */
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mime.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+BODY *mutt_new_body (void)
+{
+ BODY *p = (BODY *) safe_calloc (1, sizeof (BODY));
+
+ p->disposition = DISPATTACH;
+ p->use_disp = 1;
+ return (p);
+}
+
+BODY *mutt_dup_body (BODY *b)
+{
+ BODY *bn;
+
+ bn = mutt_new_body();
+ memcpy(bn, b, sizeof (BODY));
+ return bn;
+}
+
+void mutt_free_body (BODY **p)
+{
+ BODY *a = *p, *b;
+
+ while (a)
+ {
+ b = a;
+ a = a->next;
+
+ if (b->parameter)
+ mutt_free_parameter (&b->parameter);
+ if (b->unlink && b->filename)
+ unlink (b->filename);
+ safe_free ((void **) &b->filename);
+ safe_free ((void **) &b->content);
+ safe_free ((void **) &b->subtype);
+ safe_free ((void **) &b->description);
+ safe_free ((void **) &b->form_name);
+
+ if (b->hdr)
+ {
+ /* Don't free twice (b->hdr->content = b->parts) */
+ b->hdr->content = NULL;
+ mutt_free_header(&b->hdr);
+ }
+
+ if (b->parts)
+ mutt_free_body (&b->parts);
+
+ safe_free ((void **) &b);
+ }
+
+ *p = 0;
+}
+
+void mutt_free_parameter (PARAMETER **p)
+{
+ PARAMETER *t = *p;
+ PARAMETER *o;
+
+ while (t)
+ {
+ safe_free ((void **) &t->attribute);
+ safe_free ((void **) &t->value);
+ o = t;
+ t = t->next;
+ safe_free ((void **) &o);
+ }
+ *p = 0;
+}
+
+LIST *mutt_add_list (LIST *head, const char *data)
+{
+ LIST *tmp;
+
+ for (tmp = head; tmp && tmp->next; tmp = tmp->next)
+ ;
+ if (tmp)
+ {
+ tmp->next = safe_malloc (sizeof (LIST));
+ tmp = tmp->next;
+ }
+ else
+ head = tmp = safe_malloc (sizeof (LIST));
+
+ tmp->data = safe_strdup (data);
+ tmp->next = NULL;
+ return head;
+}
+
+void mutt_free_list (LIST **list)
+{
+ LIST *p;
+
+ if (!list) return;
+ while (*list)
+ {
+ p = *list;
+ *list = (*list)->next;
+ safe_free ((void **) &p->data);
+ safe_free ((void **) &p);
+ }
+}
+
+HEADER *mutt_dup_header(HEADER *h)
+{
+ HEADER *hnew;
+
+ hnew = mutt_new_header();
+ memcpy(hnew, h, sizeof (HEADER));
+ return hnew;
+}
+
+void mutt_free_header (HEADER **h)
+{
+ mutt_free_envelope (&(*h)->env);
+ mutt_free_body (&(*h)->content);
+ safe_free ((void **) &(*h)->tree);
+ safe_free ((void **) &(*h)->path);
+ safe_free ((void **) h);
+}
+
+/* returns true if the header contained in "s" is in list "t" */
+int mutt_matches_ignore (const char *s, LIST *t)
+{
+ for (; t; t = t->next)
+ {
+ if (!strncasecmp (s, t->data, strlen (t->data)) || *t->data == '*')
+ return 1;
+ }
+ return 0;
+}
+
+/* prepend the path part of *path to *link */
+void mutt_expand_link (char *newpath, const char *path, const char *link)
+{
+ const char *lb = NULL;
+ size_t len;
+
+ /* link is full path */
+ if (*link == '/')
+ {
+ strfcpy (newpath, link, _POSIX_PATH_MAX);
+ return;
+ }
+
+ if ((lb = strrchr (path, '/')) == NULL)
+ {
+ /* no path in link */
+ strfcpy (newpath, link, _POSIX_PATH_MAX);
+ return;
+ }
+
+ len = lb - path + 1;
+ memcpy (newpath, path, len);
+ strfcpy (newpath + len, link, _POSIX_PATH_MAX - len);
+}
+
+char *mutt_expand_path (char *s, size_t slen)
+{
+ char p[_POSIX_PATH_MAX] = "";
+ char *q = NULL;
+
+ if (*s == '~')
+ {
+ if (*(s + 1) == '/' || *(s + 1) == 0)
+ snprintf (p, sizeof (p), "%s%s", Homedir, s + 1);
+ else
+ {
+ struct passwd *pw;
+
+ q = strchr (s + 1, '/');
+ if (q)
+ *q = 0;
+ if ((pw = getpwnam (s + 1)))
+ snprintf (p, sizeof (p), "%s/%s", pw->pw_dir, q ? q + 1 : "");
+ else
+ {
+ /* user not found! */
+ if (q)
+ *q = '/';
+ return (NULL);
+ }
+ }
+ }
+ else if (*s == '=' || *s == '+')
+ snprintf (p, sizeof (p), "%s/%s", NONULL (Maildir), s + 1);
+ else
+ {
+ if (*s == '>')
+ q = Inbox;
+ else if (*s == '<')
+ q = Outbox;
+ else if (*s == '!')
+ q = Spoolfile;
+ else if (*s == '-')
+ q = LastFolder;
+ else
+ return s;
+
+ if (!q)
+ return s;
+ snprintf (p, sizeof (p), "%s%s", q, s + 1);
+ }
+ if (*p)
+ strfcpy (s, p, slen); /* replace the string with the expanded version. */
+ return (s);
+}
+
+void *safe_calloc (size_t nmemb, size_t size)
+{
+ void *p;
+
+ if (!nmemb || !size)
+ return NULL;
+ if (!(p = calloc (nmemb, size)))
+ {
+ mutt_error ("Out of memory");
+ sleep (1);
+ mutt_exit (1);
+ }
+ return p;
+}
+
+void *safe_malloc (unsigned int siz)
+{
+ void *p;
+
+ if (siz == 0)
+ return 0;
+ if ((p = (void *) malloc (siz)) == 0)
+ {
+ mutt_error ("Out of memory!");
+ sleep (1);
+ mutt_exit (1);
+ }
+ return (p);
+}
+
+void safe_realloc (void **p, size_t siz)
+{
+ void *r;
+
+ if (siz == 0)
+ {
+ if (*p)
+ {
+ free (*p);
+ *p = NULL;
+ }
+ return;
+ }
+
+ if (*p)
+ r = (void *) realloc (*p, siz);
+ else
+ {
+ /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x */
+ r = (void *) malloc (siz);
+ }
+
+ if (!r)
+ {
+ mutt_error ("Out of memory!");
+ sleep (1);
+ mutt_exit (1);
+ }
+
+ *p = r;
+}
+
+void safe_free (void **p)
+{
+ if (*p)
+ {
+ free (*p);
+ *p = 0;
+ }
+}
+
+char *safe_strdup (const char *s)
+{
+ char *p;
+ size_t l;
+
+ if (!s || !*s) return 0;
+ l = strlen (s) + 1;
+ p = (char *)safe_malloc (l);
+ memcpy (p, s, l);
+ return (p);
+}
+
+char *mutt_skip_whitespace (char *p)
+{
+ SKIPWS (p);
+ return p;
+}
+
+int mutt_copy_bytes (FILE *in, FILE *out, size_t size)
+{
+ char buf[2048];
+ size_t chunk;
+
+ while (size > 0)
+ {
+ chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
+ if ((chunk = fread (buf, 1, chunk, in)) < 1)
+ break;
+ if (fwrite (buf, 1, chunk, out) != chunk)
+ {
+ dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n"));
+ return (-1);
+ }
+ size -= chunk;
+ }
+
+ return 0;
+}
+
+char *mutt_get_parameter (const char *s, PARAMETER *p)
+{
+ for (; p; p = p->next)
+ if (strcasecmp (s, p->attribute) == 0)
+ return (p->value);
+
+ return NULL;
+}
+
+/* returns 1 if Mutt can't display this type of data, 0 otherwise */
+int mutt_needs_mailcap (BODY *m)
+{
+ switch (m->type)
+ {
+ case TYPETEXT:
+
+ if (!strcasecmp ("plain", m->subtype) ||
+ !strcasecmp ("rfc822-headers", m->subtype) ||
+ !strcasecmp ("enriched", m->subtype))
+ return 0;
+ break;
+
+
+
+#ifdef _PGPPATH
+ case TYPEAPPLICATION:
+
+ if (!strcasecmp ("pgp", m->subtype) ||
+ !strcasecmp ("pgp-signed", m->subtype) ||
+ !strcasecmp ("x-pgp-message", m->subtype))
+ return 0;
+ break;
+#endif /* _PGPPATH */
+
+
+
+ case TYPEMULTIPART:
+ case TYPEMESSAGE:
+
+ return 0;
+ }
+
+ return 1;
+}
+
+int mutt_is_text_type (int t, char *s)
+{
+ if (t == TYPETEXT)
+ return 1;
+
+ if (t == TYPEMESSAGE)
+ {
+ if (!strcasecmp ("delivery-status", s))
+ return 1;
+ }
+
+
+
+#ifdef _PGPPATH
+ if (t == TYPEAPPLICATION)
+ {
+ if (!strcasecmp ("pgp-keys", s))
+ return 1;
+ }
+#endif /* _PGPPATH */
+
+
+
+ return 0;
+}
+
+void mutt_free_envelope (ENVELOPE **p)
+{
+ if (!*p) return;
+ rfc822_free_address (&(*p)->return_path);
+ rfc822_free_address (&(*p)->to);
+ rfc822_free_address (&(*p)->cc);
+ rfc822_free_address (&(*p)->bcc);
+ rfc822_free_address (&(*p)->sender);
+ rfc822_free_address (&(*p)->from);
+ rfc822_free_address (&(*p)->reply_to);
+ rfc822_free_address (&(*p)->mail_followup_to);
+ safe_free ((void **) &(*p)->subject);
+ safe_free ((void **) &(*p)->message_id);
+ mutt_free_list (&(*p)->references);
+ mutt_free_list (&(*p)->userhdrs);
+ safe_free ((void **) p);
+}
+
+void mutt_tabs_to_spaces (char *s)
+{
+ while (*s)
+ {
+ if (ISSPACE (*s))
+ *s = ' ';
+ s++;
+ }
+}
+
+void mutt_mktemp (char *s)
+{
+ snprintf (s, _POSIX_PATH_MAX, "%s/mutt-%s-%d-%d", NONULL (Tempdir), Hostname, (int) getpid (), Counter++);
+ unlink (s);
+}
+
+/* convert all characters in the string to lowercase */
+char *mutt_strlower (char *s)
+{
+ char *p = s;
+
+ while (*p)
+ {
+ *p = tolower (*p);
+ p++;
+ }
+
+ return (s);
+}
+
+/* strcmp() allowing NULL pointers */
+int mutt_strcmp (const char *s1, const char *s2)
+{
+ if (s1 != NULL)
+ {
+ if (s2 != NULL)
+ return strcmp (s1, s2);
+ else
+ return (1);
+ }
+ else
+ return ((s2 == NULL) ? 0 : -1);
+}
+
+void mutt_free_alias (ALIAS **p)
+{
+ ALIAS *t;
+
+ while (*p)
+ {
+ t = *p;
+ *p = (*p)->next;
+ safe_free ((void **) &t->name);
+ rfc822_free_address (&t->addr);
+ free (t);
+ }
+}
+
+/* collapse the pathname using ~ or = when possible */
+void mutt_pretty_mailbox (char *s)
+{
+ char *p = s, *q = s;
+ size_t len;
+
+ /* first attempt to collapse the pathname */
+ while (*p)
+ {
+ if (*p == '/' && p[1] == '/')
+ {
+ *q++ = '/';
+ p += 2;
+ }
+ else if (p[0] == '/' && p[1] == '.' && p[2] == '/')
+ {
+ *q++ = '/';
+ p += 3;
+ }
+ else
+ *q++ = *p++;
+ }
+ *q = 0;
+
+ if (strncmp (s, NONULL (Maildir), (len = strlen (NONULL (Maildir)))) == 0 && s[len] == '/')
+ {
+ *s++ = '=';
+ strcpy (s, s + len);
+ }
+ else if (strncmp (s, Homedir, (len = strlen (Homedir))) == 0 &&
+ s[len] == '/')
+ {
+ *s++ = '~';
+ strcpy (s, s + len - 1);
+ }
+}
+
+void mutt_unlink (const char *s)
+{
+ FILE *f;
+ struct stat sb;
+ char buf[2048];
+
+ if (stat (s, &sb) == 0)
+ {
+ if ((f = fopen (s, "r+")))
+ {
+ unlink (s);
+ memset (buf, 0, sizeof (buf));
+ while (sb.st_size > 0)
+ {
+ fwrite (buf, 1, sizeof (buf), f);
+ sb.st_size -= sizeof (buf);
+ }
+ fclose (f);
+ }
+ }
+}
+
+int mutt_copy_stream (FILE *fin, FILE *fout)
+{
+ size_t l;
+ char buf[LONG_STRING];
+
+ while ((l = fread (buf, 1, sizeof (buf), fin)) > 0)
+ {
+ if (fwrite (buf, 1, l, fout) != l)
+ return (-1);
+ }
+
+ return 0;
+}
+
+void mutt_expand_fmt (char *dest, size_t destlen, const char *fmt, const char *src)
+{
+ const char *p = fmt;
+ const char *last = p;
+ size_t len;
+ size_t slen = strlen (src);
+ int found = 0;
+
+ while ((p = strchr (p, '%')) != NULL)
+ {
+ if (p[1] == 's')
+ {
+ found++;
+
+ len = (size_t) (p - last);
+ if (len)
+ {
+ if (len > destlen - 1)
+ len = destlen - 1;
+
+ memcpy (dest, last, len);
+ dest += len;
+ destlen -= len;
+
+ if (destlen <= 0)
+ {
+ *dest = 0;
+ break; /* no more space */
+ }
+ }
+
+ strfcpy (dest, src, destlen);
+ if (slen > destlen)
+ {
+ /* no more room */
+ break;
+ }
+ dest += slen;
+ destlen -= slen;
+
+ p += 2;
+ last = p;
+ }
+ else if (p[1] == '%')
+ p++;
+
+ p++;
+ }
+
+ if (found)
+ strfcpy (dest, last, destlen);
+ else
+ snprintf (dest, destlen, "%s '%s'", fmt, src);
+}
+
+/* when opening files for writing, make sure the file doesn't already exist
+ * to avoid race conditions.
+ */
+FILE *safe_fopen (const char *path, const char *mode)
+{
+ struct stat osb, nsb;
+
+ if (mode[0] == 'w')
+ {
+ int fd;
+ int flags = O_CREAT | O_EXCL;
+
+ if (mode[1] == '+')
+ flags |= O_RDWR;
+ else
+ flags |= O_WRONLY;
+
+ if ((fd = open (path, flags, 0600)) < 0)
+ return NULL;
+
+ /* make sure the file is not symlink */
+ if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
+ osb.st_dev != nsb.st_dev || osb.st_ino != nsb.st_ino ||
+ osb.st_rdev != nsb.st_rdev)
+ {
+ dprint (1, (debugfile, "safe_fopen():%s is a symlink!\n", path));
+ close (fd);
+ return (NULL);
+ }
+
+ return (fdopen (fd, mode));
+ }
+ else
+ return (fopen (path, mode));
+}
+
+/* return 0 on success, -1 on error */
+int mutt_check_overwrite (const char *attname, const char *path,
+ char *fname, size_t flen, int flags)
+{
+ char tmp[_POSIX_PATH_MAX];
+ struct stat st;
+
+ strfcpy (fname, path, flen);
+ if (access (fname, F_OK) != 0)
+ return 0;
+ if (stat (fname, &st) != 0)
+ return -1;
+ if (S_ISDIR (st.st_mode))
+ {
+ if (mutt_yesorno ("File is a directory, save under it?", 1) != M_YES)
+ return (-1);
+ if (!attname || !attname[0])
+ {
+ tmp[0] = 0;
+ if (mutt_get_field ("File under directory: ", tmp, sizeof (tmp),
+ M_FILE | M_CLEAR) != 0 || !tmp[0])
+ return (-1);
+ snprintf (fname, flen, "%s/%s", path, tmp);
+ }
+ else
+ snprintf (fname, flen, "%s/%s", path, attname);
+ }
+
+ if (flags != M_SAVE_APPEND &&
+ access (fname, F_OK) == 0 &&
+ mutt_yesorno ("File exists, overwrite?", 0) != 1)
+ return (-1);
+
+ return 0;
+}
+
+void mutt_remove_trailing_ws (char *s)
+{
+ char *p;
+
+ for (p = s + strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
+ *p = 0;
+}
+
+void mutt_pretty_size (char *s, size_t len, long n)
+{
+ if (n == 0)
+ strfcpy (s, "0K", len);
+ else if (n < 103)
+ strfcpy (s, "0.1K", len);
+ else if (n < 10189) /* 0.1K - 9.9K */
+ snprintf (s, len, "%3.1fK", n / 1024.0);
+ else if (n < 1023949) /* 10K - 999K */
+ {
+ /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
+ snprintf (s, len, "%ldK", (n + 51) / 1024);
+ }
+ else if (n < 10433332) /* 1.0M - 9.9M */
+ snprintf (s, len, "%3.1fM", n / 1048576.0);
+ else /* 10M+ */
+ {
+ /* (10433332 + 52428) / 1048576 = 10 */
+ snprintf (s, len, "%ldM", (n + 52428) / 1048576);
+ }
+}
+
+void mutt_save_path (char *d, size_t dsize, ADDRESS *a)
+{
+ if (a && a->mailbox)
+ {
+ strfcpy (d, a->mailbox, dsize);
+ if (!option (OPTSAVEADDRESS))
+ {
+ char *p;
+
+ if ((p = strpbrk (d, "%@")))
+ *p = 0;
+ }
+ mutt_strlower (d);
+ }
+ else
+ *d = 0;
+}
+
+void mutt_safe_path (char *s, size_t l, ADDRESS *a)
+{
+ char *p;
+
+ mutt_save_path (s, l, a);
+ for (p = s; *p; p++)
+ if (*p == '/' || ISSPACE (*p) || !IsPrint ((unsigned char) *p))
+ *p = '_';
+}
+
+/* Read a line from ``fp'' into the dynamically allocated ``s'',
+ * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
+ * If a line ends with "\", this char and the linefeed is removed,
+ * and the next line is read too.
+ */
+char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line)
+{
+ size_t offset = 0;
+ char *ch;
+
+ if (!s)
+ {
+ s = safe_malloc (STRING);
+ *size = STRING;
+ }
+
+ FOREVER
+ {
+ if (fgets (s + offset, *size - offset, fp) == NULL)
+ {
+ free (s);
+ return NULL;
+ }
+ if ((ch = strchr (s + offset, '\n')) != NULL)
+ {
+ (*line)++;
+ *ch = 0;
+ if (ch > s && *(ch - 1) == '\r')
+ *--ch = 0;
+ if (ch == s || *(ch - 1) != '\\')
+ return s;
+ offset = ch - s - 1;
+ }
+ else
+ {
+ /* There wasn't room for the line -- increase ``s'' */
+ offset = *size - 1; /* overwrite the terminating 0 */
+ *size += STRING;
+ safe_realloc ((void **) &s, *size);
+ }
+ }
+}
+
+char *
+mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
+{
+ size_t len;
+
+ len = end - beg;
+ if (len > destlen - 1)
+ len = destlen - 1;
+ memcpy (dest, beg, len);
+ dest[len] = 0;
+ return dest;
+}
+
+char *mutt_substrdup (const char *begin, const char *end)
+{
+ size_t len;
+ char *p;
+
+ len = end - begin;
+ p = safe_malloc (len + 1);
+ memcpy (p, begin, len);
+ p[len] = 0;
+ return p;
+}
+
+void mutt_FormatString (char *dest, /* output buffer */
+ size_t destlen, /* output buffer len */
+ const char *src, /* template string */
+ format_t *callback, /* callback for processing */
+ unsigned long data, /* callback data */
+ format_flag flags) /* callback flags */
+{
+ char prefix[SHORT_STRING], buf[LONG_STRING], *cp, *wptr = dest, ch;
+ char ifstring[SHORT_STRING], elsestring[SHORT_STRING];
+ size_t wlen = 0, count, len;
+
+ destlen--; /* save room for the terminal \0 */
+ while (*src && wlen < destlen)
+ {
+ if (*src == '%')
+ {
+ if (*++src == '%')
+ {
+ *wptr++ = '%';
+ wlen++;
+ src++;
+ continue;
+ }
+
+ if (*src == '?')
+ {
+ flags |= M_FORMAT_OPTIONAL;
+ src++;
+ }
+ else
+ {
+ flags &= ~M_FORMAT_OPTIONAL;
+
+ /* eat the format string */
+ cp = prefix;
+ count = 0;
+ while (count < sizeof (prefix) &&
+ (isdigit (*src) || *src == '.' || *src == '-'))
+ {
+ *cp++ = *src++;
+ count++;
+ }
+ *cp = 0;
+ }
+
+ if (!*src)
+ break; /* bad format */
+
+ ch = *src++; /* save the character to switch on */
+
+ if (flags & M_FORMAT_OPTIONAL)
+ {
+ if (*src != '?')
+ break; /* bad format */
+ src++;
+
+ /* eat the `if' part of the string */
+ cp = ifstring;
+ count = 0;
+ while (count < sizeof (ifstring) && *src && *src != '?' && *src != '&')
+ {
+ *cp++ = *src++;
+ count++;
+ }
+ *cp = 0;
+
+ /* eat the `else' part of the string (optional) */
+ if (*src == '&')
+ src++; /* skip the & */
+ cp = elsestring;
+ count = 0;
+ while (count < sizeof (elsestring) && *src && *src != '?')
+ {
+ *cp++ = *src++;
+ count++;
+ }
+ *cp = 0;
+
+ if (!*src)
+ break; /* bad format */
+
+ src++; /* move past the trailing `?' */
+ }
+
+ /* handle generic cases first */
+ if (ch == '>')
+ {
+ /* right justify to EOL */
+ ch = *src++; /* pad char */
+ /* calculate space left on line. if we've already written more data
+ than will fit on the line, ignore the rest of the line */
+ count = (COLS < destlen ? COLS : destlen);
+ if (count > wlen)
+ {
+ count -= wlen; /* how many chars left on this line */
+ mutt_FormatString (buf, sizeof (buf), src, callback, data, flags);
+ len = strlen (buf);
+ if (count > len)
+ {
+ count -= len; /* how many chars to pad */
+ memset (wptr, ch, count);
+ wptr += count;
+ wlen += count;
+ }
+ if (len + wlen > destlen)
+ len = destlen - wlen;
+ memcpy (wptr, buf, len);
+ wptr += len;
+ wlen += len;
+ }
+ break; /* skip rest of input */
+ }
+ else if (ch == '|')
+ {
+ /* pad to EOL */
+ ch = *src++;
+ if (destlen > COLS)
+ destlen = COLS;
+ if (destlen > wlen)
+ {
+ count = destlen - wlen;
+ memset (wptr, ch, count);
+ wptr += count;
+ }
+ break; /* skip rest of input */
+ }
+ else
+ {
+ /* use callback function to handle this case */
+ src = callback (buf, sizeof (buf), ch, src, prefix, ifstring, elsestring, data, flags);
+
+ if ((len = strlen (buf)) + wlen > destlen)
+ {
+ if ((len = destlen - wlen) < 0)
+ len = 0;
+ }
+ memcpy (wptr, buf, len);
+ wptr += len;
+ wlen += len;
+ }
+ }
+ else if (*src == '\\')
+ {
+ if (!*++src)
+ break;
+ switch (*src)
+ {
+ case 'n':
+ *wptr = '\n';
+ break;
+ case 't':
+ *wptr = '\t';
+ break;
+ case 'r':
+ *wptr = '\r';
+ break;
+ case 'f':
+ *wptr = '\f';
+ break;
+ case 'v':
+ *wptr = '\v';
+ break;
+ default:
+ *wptr = *src;
+ break;
+ }
+ src++;
+ wptr++;
+ wlen++;
+ }
+ else
+ {
+ *wptr++ = *src++;
+ wlen++;
+ }
+ }
+ *wptr = 0;
+
+ if (flags & M_FORMAT_MAKEPRINT)
+ {
+ /* Make sure that the string is printable by changing all non-printable
+ chars to dots, or spaces for non-printable whitespace */
+ for (cp = dest ; *cp ; cp++)
+ if (!IsPrint (*cp) &&
+ !((flags & M_FORMAT_TREE) && (*cp <= M_TREE_MAX)))
+ *cp = isspace ((unsigned char) *cp) ? ' ' : '.';
+ }
+}
+
+/* This function allows the user to specify a command to read stdout from in
+ place of a normal file. If the last character in the string is a pipe (|),
+ then we assume it is a commmand to run instead of a normal file. */
+FILE *mutt_open_read (const char *path, pid_t *thepid)
+{
+ FILE *f;
+ int len = strlen (path);
+
+ if (path[len - 1] == '|')
+ {
+ /* read from a pipe */
+
+ char *s = safe_strdup (path);
+
+ s[len - 1] = 0;
+ endwin ();
+ *thepid = mutt_create_filter (s, NULL, &f, NULL);
+ free (s);
+ }
+ else
+ {
+ f = fopen (path, "r");
+ *thepid = -1;
+ }
+ return (f);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* flags for mutt_open_mailbox() */
+#define M_NOSORT (1<<0) /* do not sort the mailbox after opening it */
+#define M_APPEND (1<<1) /* open mailbox for appending messages */
+#define M_READONLY (1<<2) /* open in read-only mode */
+#define M_QUIET (1<<3) /* do not print any messages */
+
+/* mx_open_new_message() */
+#define M_ADD_FROM 1 /* add a From_ line */
+
+/* return values from mx_check_mailbox() */
+enum
+{
+ M_NEW_MAIL = 1, /* new mail received in mailbox */
+ M_LOCKED, /* couldn't lock the mailbox */
+ M_REOPENED /* mailbox was reopened */
+};
+
+typedef struct
+{
+ FILE *fp; /* pointer to the message data */
+#ifdef USE_IMAP
+ char *path; /* path to temp file */
+#endif /* USE_IMAP */
+ short magic; /* type of mailbox this message belongs to */
+ short write; /* nonzero if message is open for writing */
+} MESSAGE;
+
+CONTEXT *mx_open_mailbox (const char *, int, CONTEXT *);
+
+MESSAGE *mx_open_message (CONTEXT *, int);
+MESSAGE *mx_open_new_message (CONTEXT *, HEADER *, int);
+
+void mx_fastclose_mailbox (CONTEXT *);
+
+int mx_close_mailbox (CONTEXT *);
+int mx_sync_mailbox (CONTEXT *);
+int mx_close_message (MESSAGE **msg);
+int mx_get_magic (const char *);
+int mx_set_magic (const char *);
+int mx_check_mailbox (CONTEXT *, int *);
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define MAIN_C 1
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "keymap.h"
+#include "mailbox.h"
+#include "reldate.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+const char Notice[] = "\
+Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>\n\
+Mutt comes with ABSOLUTELY NO WARRANTY; for details type `mutt -vv'.\n\
+Mutt is free software, and you are welcome to redistribute it\n\
+under certain conditions; type `mutt -vv' for details.\n";
+
+const char Copyright[] = "\
+Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>\n\
+\n\
+ This program is free software; you can redistribute it and/or modify\n\
+ it under the terms of the GNU General Public License as published by\n\
+ the Free Software Foundation; either version 2 of the License, or\n\
+ (at your option) any later version.\n\
+\n\
+ This program is distributed in the hope that it will be useful,\n\
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
+ GNU General Public License for more details.\n\
+\n\
+ You should have received a copy of the GNU General Public License\n\
+ along with this program; if not, write to the Free Software\n\
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n";
+
+void mutt_exit (int code)
+{
+ mutt_endwin (NULL);
+ exit (code);
+}
+
+static void mutt_usage (void)
+{
+ printf ("Mutt %s (%s)\n", VERSION, ReleaseDate);
+ puts (
+"usage: mutt [ -nRzZ ] [ -e <cmd> ] [ -F <file> ] [ -m <type> ] [ -f <file> ]\n\
+ mutt [ -nx ] [ -e <cmd> ] [ -a <file> ] [ -F <file> ] [ -H <file> ] [ -i <file> ] [ -s <subj> ] [ -b <addr> ] [ -c <addr> ] <addr> [ ... ]\n\
+ mutt [ -n ] [ -e <cmd> ] [ -F <file> ] -p\n\
+ mutt -v[v]\n\
+\n\
+options:\n\
+ -a <file>\tattach a file to the message\n\
+ -b <address>\tspecify a blind carbon-copy (BCC) address\n\
+ -c <address>\tspecify a carbon-copy (CC) address\n\
+ -e <command>\tspecify a command to be executed after initialization\n\
+ -f <file>\tspecify which mailbox to read\n\
+ -F <file>\tspecify an alternate muttrc file\n\
+ -H <file>\tspecify a draft file to read header from\n\
+ -i <file>\tspecify a file which Mutt should include in the reply\n\
+ -m <type>\tspecify a default mailbox type\n\
+ -n\t\tcauses Mutt not to read the system Muttrc\n\
+ -p\t\trecall a postponed message\n\
+ -R\t\topen mailbox in read-only mode\n\
+ -s <subj>\tspecify a subject (must be in quotes if it has spaces)\n\
+ -v\t\tshow version and compile-time definitions\n\
+ -x\t\tsimulate the mailx send mode\n\
+ -y\t\tselect a mailbox specified in your `mailboxes' list\n\
+ -z\t\texit immediately if there are no messages in the mailbox\n\
+ -Z\t\topen the first folder with new message, exit immediately if none\n\
+ -h\t\tthis help message");
+
+ exit (0);
+}
+
+static void show_version (void)
+{
+ struct utsname uts;
+
+ printf ("Mutt %s (%s)\n", VERSION, ReleaseDate);
+ puts (Notice);
+
+ uname (&uts);
+
+#ifdef _AIX
+ printf ("System: %s %s.%s", uts.sysname, uts.version, uts.release);
+#elif defined (SCO)
+ printf ("System: SCO %s", uts.release);
+#else
+ printf ("System: %s %s", uts.sysname, uts.release);
+#endif
+
+#ifdef NCURSES_VERSION
+ printf (" [using ncurses %s]", NCURSES_VERSION);
+#elif defined(USE_SLANG_CURSES)
+ printf (" [using slang %d]", SLANG_VERSION);
+#endif
+
+ puts ("\nCompile options:");
+
+#ifdef DOMAIN
+ printf ("DOMAIN=\"%s\"\n", DOMAIN);
+#else
+ puts ("-DOMAIN");
+#endif
+
+ puts (
+#ifdef HIDDEN_HOST
+ "+HIDDEN_HOST "
+#else
+ "-HIDDEN_HOST "
+#endif
+
+#ifdef HOMESPOOL
+ "+HOMESPOOL "
+#else
+ "-HOMESPOOL "
+#endif
+
+#ifdef USE_SETGID
+ "+USE_SETGID "
+#else
+ "-USE_SETGID "
+#endif
+
+#ifdef USE_DOTLOCK
+ "+USE_DOTLOCK "
+#else
+ "-USE_DOTLOCK "
+#endif
+
+#ifdef USE_FCNTL
+ "+USE_FCNTL "
+#else
+ "-USE_FCNTL "
+#endif
+
+#ifdef USE_FLOCK
+ "+USE_FLOCK"
+#else
+ "-USE_FLOCK"
+#endif
+ );
+
+ puts (
+#ifdef USE_POP
+ "+USE_POP "
+#else
+ "-USE_POP "
+#endif
+
+#ifdef HAVE_REGCOMP
+ "+HAVE_REGCOMP "
+#else
+ "-HAVE_REGCOMP "
+#endif
+
+#ifdef USE_GNU_RX
+ "+USE_GNU_RX "
+#else
+ "-USE_GNU_RX "
+#endif
+
+#ifdef HAVE_COLOR
+ "+HAVE_COLOR "
+#else
+ "-HAVE_COLOR "
+#endif
+
+
+
+#ifdef _PGPPATH
+#ifdef HAVE_PGP5
+ "+HAVE_PGP5 "
+#endif
+#ifdef HAVE_PGP2
+ "+HAVE_PGP2 "
+#endif
+#endif
+
+
+
+#ifdef BUFFY_SIZE
+ "+BUFFY_SIZE "
+#else
+ "-BUFFY_SIZE "
+#endif
+
+#ifdef EXACT_ADDRESS
+ "+"
+#else
+ "-"
+#endif
+ "EXACT_ADDRESS"
+ );
+
+ printf ("SENDMAIL=\"%s\"\n", SENDMAIL);
+ printf ("MAILPATH=\"%s\"\n", MAILPATH);
+ printf ("SHAREDIR=\"%s\"\n", SHAREDIR);
+
+#ifdef ISPELL
+ printf ("ISPELL=\"%s\"\n", ISPELL);
+#else
+ puts ("-ISPELL");
+#endif
+
+
+
+#ifdef _PGPPATH
+ printf ("_PGPPATH=\"%s\"\n", _PGPPATH);
+# ifdef _PGPV2PATH
+ printf ("_PGPV2PATH=\"%s\"\n", _PGPV2PATH);
+# endif
+# ifdef _PGPV3PATH
+ printf ("_PGPV3PATH=\"%s\"\n", _PGPV3PATH);
+# endif
+#endif
+
+
+
+ puts ("\nMail bug reports along with this output to <mutt-users@cs.hmc.edu>.");
+
+ exit (0);
+}
+
+static void start_curses (void)
+{
+ km_init (); /* must come before mutt_init */
+
+#ifdef USE_SLANG_CURSES
+ SLtt_Ignore_Beep = 1; /* don't do that #*$@^! annoying visual beep! */
+ SLsmg_Display_Eight_Bit = 128; /* characters above this are printable */
+#else
+ /* should come before initscr() so that ncurses 4.2 doesn't try to install
+ its own SIGWINCH handler */
+ mutt_signal_init ();
+#endif
+ if (initscr () == NULL)
+ {
+ puts ("Error initializing terminal.");
+ exit (1);
+ }
+#ifdef USE_SLANG_CURSES
+ /* slang requires the signal handlers to be set after initializing */
+ mutt_signal_init ();
+#endif
+ ci_start_color ();
+ keypad (stdscr, TRUE);
+ cbreak ();
+ noecho ();
+#if HAVE_TYPEAHEAD
+ typeahead (-1); /* simulate smooth scrolling */
+#endif
+#if HAVE_META
+ meta (stdscr, TRUE);
+#endif
+}
+
+#define M_IGNORE (1<<0) /* -z */
+#define M_BUFFY (1<<1) /* -Z */
+#define M_NOSYSRC (1<<2) /* -n */
+#define M_RO (1<<3) /* -R */
+#define M_SELECT (1<<4) /* -y */
+
+int main (int argc, char **argv)
+{
+ char folder[_POSIX_PATH_MAX] = "";
+ char *subject = NULL;
+ char *includeFile = NULL;
+ char *draftFile = NULL;
+ char *newMagic = NULL;
+ HEADER *msg = NULL;
+ LIST *attach = NULL;
+ LIST *commands = NULL;
+ int sendflags = 0;
+ int flags = 0;
+ int version = 0;
+ int i;
+ extern char *optarg;
+ extern int optind;
+
+ mutt_error = mutt_nocurses_error;
+
+ SRAND (time (NULL));
+ setlocale (LC_CTYPE, "");
+ umask (077);
+
+#ifdef USE_SETGID
+ /* Determine the user's default gid and the gid to use for locking the spool
+ * mailbox on those systems which require setgid "mail" to write to the
+ * directory. This function also resets the gid to "normal" since the
+ * effective gid will be "mail" when we start (Mutt attempts to run
+ * non-setgid whenever possible to reduce the possibility of security holes).
+ */
+
+ /* Get the default gid for the user */
+ UserGid = getgid ();
+
+ /* it is assumed that we are setgid to the correct gid to begin with */
+ MailGid = getegid ();
+
+ /* reset the effective gid to the normal gid */
+ if (SETEGID (UserGid) != 0)
+ {
+ perror ("setegid");
+ exit (0);
+ }
+#endif
+
+ memset (Options, 0, sizeof (Options));
+
+ while ((i = getopt (argc, argv, "a:b:F:f:c:d:e:H:s:i:hm:npRvxyzZ")) != EOF)
+ switch (i)
+ {
+ case 'a':
+ attach = mutt_add_list (attach, optarg);
+ break;
+
+ case 'F':
+ FREE (&Muttrc);
+ Muttrc = safe_strdup (optarg);
+ break;
+
+ case 'f':
+ strfcpy (folder, optarg, sizeof (folder));
+ break;
+
+ case 'b':
+ case 'c':
+ if (!msg)
+ msg = mutt_new_header ();
+ if (!msg->env)
+ msg->env = mutt_new_envelope ();
+ if (i == 'b')
+ msg->env->bcc = rfc822_parse_adrlist (msg->env->bcc, optarg);
+ else
+ msg->env->cc = rfc822_parse_adrlist (msg->env->cc, optarg);
+ break;
+
+ case 'd':
+#ifdef DEBUG
+ debuglevel = atoi (optarg);
+ printf ("Debugging at level %d.\n", debuglevel);
+#else
+ printf ("DEBUG was not defined during compilation. Ignored.\n");
+#endif
+ break;
+
+ case 'e':
+ commands = mutt_add_list (commands, optarg);
+ break;
+
+ case 'H':
+ draftFile = optarg;
+ break;
+
+ case 'i':
+ includeFile = optarg;
+ break;
+
+ case 'm':
+ /* should take precedence over .muttrc setting, so save it for later */
+ newMagic = optarg;
+ break;
+
+ case 'n':
+ flags |= M_NOSYSRC;
+ break;
+
+ case 'p':
+ sendflags |= SENDPOSTPONED;
+ break;
+
+ case 'R':
+ flags |= M_RO; /* read-only mode */
+ break;
+
+ case 's':
+ subject = optarg;
+ break;
+
+ case 'v':
+ version++;
+ break;
+
+ case 'x': /* mailx compatible send mode */
+ sendflags |= SENDMAILX;
+ break;
+
+ case 'y': /* My special hack mode */
+ flags |= M_SELECT;
+ break;
+
+ case 'z':
+ flags |= M_IGNORE;
+ break;
+
+ case 'Z':
+ flags |= M_BUFFY | M_IGNORE;
+ break;
+
+ default:
+ mutt_usage ();
+ }
+
+ switch (version)
+ {
+ case 0:
+ break;
+ case 1:
+ show_version ();
+ break;
+ default:
+ printf ("Mutt %s (%s)\n", VERSION, ReleaseDate);
+ puts (Copyright);
+ exit (0);
+ }
+
+ /* Check for a batch send. */
+ if (!isatty (0))
+ {
+ set_option (OPTNOCURSES);
+ sendflags = SENDBATCH;
+ }
+
+ /* This must come before mutt_init() because curses needs to be started
+ before calling the init_pair() function to set the color scheme. */
+ if (!option (OPTNOCURSES))
+ start_curses ();
+
+ /* set defaults and read init files */
+ mutt_init (flags & M_NOSYSRC, commands);
+ mutt_free_list (&commands);
+
+ if (newMagic)
+ mx_set_magic (newMagic);
+
+ if (!option (OPTNOCURSES))
+ {
+ SETCOLOR (MT_COLOR_NORMAL);
+ clear ();
+ mutt_error = mutt_curses_error;
+ }
+
+ if (sendflags & SENDPOSTPONED)
+ {
+ if (!option (OPTNOCURSES))
+ mutt_flushinp ();
+ ci_send_message (SENDPOSTPONED, NULL, NULL, NULL, NULL);
+ mutt_endwin (NULL);
+ }
+ else if (subject || msg || sendflags || draftFile || includeFile || attach ||
+ optind < argc)
+ {
+ FILE *fin = NULL;
+ char buf[LONG_STRING];
+ char *tempfile = NULL, *infile = NULL;
+
+ if (!option (OPTNOCURSES))
+ mutt_flushinp ();
+
+ if (!msg)
+ msg = mutt_new_header ();
+
+ if (draftFile)
+ infile = draftFile;
+ else
+ {
+ if (!msg->env)
+ msg->env = mutt_new_envelope ();
+
+ for (i = optind; i < argc; i++)
+ msg->env->to = rfc822_parse_adrlist (msg->env->to, argv[i]);
+
+ if (!msg->env->to && !msg->env->cc)
+ {
+ if (!option (OPTNOCURSES))
+ mutt_endwin (NULL);
+ fputs ("No recipients specified.\n", stderr);
+ exit (1);
+ }
+
+ msg->env->subject = safe_strdup (subject);
+
+ if (includeFile)
+ infile = includeFile;
+ }
+
+ if (infile)
+ {
+ if (strcmp ("-", infile) == 0)
+ fin = stdin;
+ else
+ {
+ char path[_POSIX_PATH_MAX];
+
+ strfcpy (path, infile, sizeof (path));
+ mutt_expand_path (path, sizeof (path));
+ if ((fin = fopen (path, "r")) == NULL)
+ {
+ if (!option (OPTNOCURSES))
+ mutt_endwin (NULL);
+ perror (path);
+ exit (1);
+ }
+ }
+ mutt_mktemp (buf);
+ tempfile = safe_strdup (buf);
+
+ if (draftFile)
+ msg->env = mutt_read_rfc822_header (fin, NULL);
+
+ if (tempfile)
+ {
+ FILE *fout;
+
+ if ((fout = safe_fopen (tempfile, "w")) == NULL)
+ {
+ if (!option (OPTNOCURSES))
+ mutt_endwin (NULL);
+ perror (tempfile);
+ fclose (fin);
+ free (tempfile);
+ exit (1);
+ }
+
+ mutt_copy_stream (fin, fout);
+ fclose (fout);
+ if (fin != stdin)
+ fclose (fin);
+ }
+ }
+
+ if (attach)
+ {
+ LIST *t = attach;
+ BODY *a = NULL;
+
+ while (t)
+ {
+ if (a)
+ {
+ a->next = mutt_make_attach (t->data);
+ a = a->next;
+ }
+ else
+ msg->content = a = mutt_make_attach (t->data);
+ if (!a)
+ {
+ if (!option (OPTNOCURSES))
+ mutt_endwin (NULL);
+ fprintf (stderr, "%s: unable to attach file.\n", t->data);
+ mutt_free_list (&attach);
+ exit (1);
+ }
+ t = t->next;
+ }
+ mutt_free_list (&attach);
+ }
+
+ ci_send_message (sendflags, msg, tempfile, NULL, NULL);
+
+ if (!option (OPTNOCURSES))
+ mutt_endwin (NULL);
+ }
+ else
+ {
+ if (flags & M_BUFFY)
+ {
+ if (!mutt_buffy_check (0))
+ {
+ mutt_endwin ("No mailbox with new mail.");
+ exit (1);
+ }
+ folder[0] = 0;
+ mutt_buffy (folder);
+ }
+ else if (flags & M_SELECT)
+ {
+ folder[0] = 0;
+ mutt_select_file (folder, sizeof (folder), 1);
+ if (!folder[0])
+ {
+ mutt_endwin (NULL);
+ exit (0);
+ }
+ }
+
+ if (!folder[0])
+ strfcpy (folder, Spoolfile, sizeof (folder));
+ mutt_expand_path (folder, sizeof (folder));
+
+ if (flags & M_IGNORE)
+ {
+ struct stat st;
+
+ /* check to see if there are any messages in the folder */
+ if (stat (folder, &st) != 0)
+ {
+ mutt_endwin (strerror (errno));
+ exit (1);
+ }
+
+ if (st.st_size == 0)
+ {
+ mutt_endwin ("Mailbox is empty.");
+ exit (1);
+ }
+ }
+
+ mutt_folder_hook (folder);
+
+ if ((Context = mx_open_mailbox (folder, ((flags & M_RO) || option (OPTREADONLY)) ? M_READONLY : 0, NULL)) != NULL)
+ {
+ mutt_index_menu ();
+ mutt_endwin (NULL);
+ }
+ else
+ mutt_endwin (Errorbuf);
+ }
+
+ exit (0);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+struct mapping_t
+{
+ char *name;
+ int value;
+};
+
+char *mutt_getnamebyvalue (int, const struct mapping_t *);
+char *mutt_compile_help (char *, size_t, int, struct mapping_t *);
+
+int mutt_getvaluebyname (const char *, const struct mapping_t *);
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains code to parse ``mbox'' and ``mmdf'' style mailboxes */
+
+#include "mutt.h"
+#include "mailbox.h"
+#include "mx.h"
+#include "sort.h"
+#include "copy.h"
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <string.h>
+#include <utime.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/* struct used by mutt_sync_mailbox() to store new offsets */
+struct m_update_t
+{
+ long hdr;
+ long body;
+};
+
+#ifdef USE_DOTLOCK
+/*
+ * Determine whether or not to use a dotlock to lock the indicated file.
+ * On some systems, the spool directory is not world-writable. If it is
+ * group-writable, we might need to be setgid() to write the lock. If not
+ * group-writable, then we assume that fcntl() locking is enough and skip
+ * the dotlocking.
+ *
+ * return values:
+ * 2 need to be setgid to dotlock
+ * 1 can use a dotlock
+ * 0 don't use a dotlock
+ * -1 error
+ */
+static int can_dotlock (const char *path)
+{
+ char tmp[_POSIX_PATH_MAX];
+ char *p;
+#ifdef USE_SETGID
+ struct stat sb;
+#endif
+
+ strfcpy (tmp, path, sizeof (tmp));
+ if ((p = strrchr (tmp, '/')))
+ *p = 0;
+ else
+ strfcpy (tmp, ".", sizeof (tmp)); /* use current directory */
+
+ if (access (tmp, W_OK) == 0) return 1;
+
+#ifdef USE_SETGID
+ if (stat (tmp, &sb) == 0)
+ {
+ if ((sb.st_mode & S_IWGRP) == S_IWGRP)
+ {
+ /* can dotlock, but need to be setgid */
+ if (sb.st_gid == MailGid)
+ return (2);
+ else
+ {
+ mutt_error ("Need to be running setgid %d to lock mailbox!", sb.st_gid);
+ return (-1);
+ }
+ }
+ }
+#endif
+
+ if (mutt_yesorno ("Can't dotlock mailbox, continue anyway?", 0) == 1)
+ return 0;
+
+ return (-1);
+}
+#endif
+
+/* parameters:
+ * ctx - context to lock
+ * excl - exclusive lock?
+ * retry - should retry if unable to lock?
+ */
+int mbox_lock_mailbox (CONTEXT *ctx, int excl, int retry)
+{
+ int r = 0;
+
+#ifdef USE_DOTLOCK
+ r = can_dotlock (ctx->path);
+
+ if (r == -1)
+ return (-1);
+#ifdef USE_SETGID
+ else if (r == 2)
+ {
+ /* need to be setgid to lock the mailbox */
+ if (SETEGID (MailGid) != 0)
+ {
+ mutt_perror ("setegid");
+ return (-1);
+ }
+
+ ctx->setgid = 1;
+ }
+#endif /* USE_SETGID */
+#endif /* USE_DOTLOCK */
+
+ if ((r = mx_lock_file (ctx->path, fileno (ctx->fp), excl, r, retry)) == 0)
+ ctx->locked = 1;
+
+#ifdef USE_SETGID
+ if (ctx->setgid)
+ SETEGID (UserGid);
+#endif
+
+ return (r);
+}
+
+void mbox_unlock_mailbox (CONTEXT *ctx)
+{
+ if (ctx->locked)
+ {
+ fflush (ctx->fp);
+
+#ifdef USE_SETGID
+ if (ctx->setgid)
+ SETEGID (MailGid);
+#endif /* USE_SETGID */
+
+ mx_unlock_file (ctx->path, fileno (ctx->fp));
+ ctx->locked = 0;
+
+#ifdef USE_SETGID
+ if (ctx->setgid)
+ {
+ SETEGID (UserGid);
+ ctx->setgid = 0;
+ }
+#endif
+ }
+}
+
+int mmdf_parse_mailbox (CONTEXT *ctx)
+{
+ char buf[HUGE_STRING];
+ char return_path[LONG_STRING];
+ int lines;
+ time_t t, tz;
+ long loc, tmploc;
+ HEADER *hdr;
+ struct stat sb;
+#ifdef NFS_ATTRIBUTE_HACK
+ struct utimbuf newtime;
+#endif
+
+ if (stat (ctx->path, &sb) == -1)
+ {
+ mutt_perror (ctx->path);
+ return (-1);
+ }
+ ctx->mtime = sb.st_mtime;
+ ctx->size = sb.st_size;
+
+#ifdef NFS_ATTRIBUTE_HACK
+ if (sb.st_mtime > sb.st_atime)
+ {
+ newtime.modtime = sb.st_mtime;
+ newtime.actime = time (NULL);
+ utime (ctx->path, &newtime);
+ }
+#endif
+
+ /* precompute the local timezone to speed up calculation of the
+ received time */
+ tz = mutt_local_tz ();
+
+ buf[sizeof (buf) - 1] = 0;
+ FOREVER
+ {
+ if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
+ break;
+
+ if (strcmp (buf, MMDF_SEP) == 0)
+ {
+ loc = ftell (ctx->fp);
+
+ if (ctx->msgcount == ctx->hdrmax)
+ mx_alloc_memory (ctx);
+ ctx->hdrs[ctx->msgcount] = hdr = mutt_new_header ();
+ hdr->offset = loc;
+ hdr->index = ctx->msgcount;
+
+ if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
+ {
+ dprint (1, (debugfile, "mmdf_parse_mailbox: unexpected EOF\n"));
+ break;
+ }
+
+ return_path[0] = 0;
+ t = is_from (buf, return_path, sizeof (return_path));
+
+ if (!t)
+ fseek (ctx->fp, loc, 0);
+ else
+ hdr->received = t + tz;
+
+ hdr->env = mutt_read_rfc822_header (ctx->fp, hdr);
+
+ loc = ftell (ctx->fp);
+
+ if (hdr->content->length > 0 && hdr->lines > 0)
+ {
+ tmploc = loc + hdr->content->length;
+
+ if (tmploc < ctx->size)
+ {
+ fseek (ctx->fp, tmploc, 0);
+ if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL ||
+ strcmp (MMDF_SEP, buf) != 0)
+ {
+ fseek (ctx->fp, loc, 0);
+ hdr->content->length = -1;
+ }
+ }
+ else
+ hdr->content->length = -1;
+ }
+ else
+ hdr->content->length = -1;
+
+ if (hdr->content->length < 0)
+ {
+ lines = -1;
+ do {
+ loc = ftell (ctx->fp);
+ if (fgets (buf, sizeof (buf) - 1, ctx->fp) == NULL)
+ break;
+ lines++;
+ } while (strcmp (buf, MMDF_SEP) != 0);
+
+ hdr->lines = lines;
+ hdr->content->length = loc - hdr->content->offset;
+ }
+
+ if (!hdr->env->return_path && return_path[0])
+ hdr->env->return_path = rfc822_parse_adrlist (hdr->env->return_path, return_path);
+
+ if (!hdr->env->from)
+ hdr->env->from = rfc822_cpy_adr (hdr->env->return_path);
+
+ mx_update_context (ctx);
+ }
+ else
+ {
+ dprint (1, (debugfile, "mmdf_parse_mailbox: corrupt mailbox!\n"));
+ mutt_error ("Mailbox is corrupt!");
+ return (-1);
+ }
+ }
+
+ return 0;
+}
+
+/* Note that this function is also called when new mail is appended to the
+ * currently open folder, and NOT just when the mailbox is initially read.
+ *
+ * NOTE: it is assumed that the mailbox being read has been locked before
+ * this routine gets called. Strange things could happen if it's not!
+ */
+int mbox_parse_mailbox (CONTEXT *ctx)
+{
+ struct stat sb;
+ char buf[HUGE_STRING], return_path[STRING];
+ HEADER *curhdr;
+ time_t t, tz;
+ int count = 0, lines = 0;
+ long loc;
+#ifdef NFS_ATTRIBUTE_HACK
+ struct utimbuf newtime;
+#endif
+
+ /* Save information about the folder at the time we opened it. */
+ if (stat (ctx->path, &sb) == -1)
+ {
+ mutt_perror (ctx->path);
+ return (-1);
+ }
+
+ ctx->size = sb.st_size;
+ ctx->mtime = sb.st_mtime;
+
+#ifdef NFS_ATTRIBUTE_HACK
+ if (sb.st_mtime > sb.st_atime)
+ {
+ newtime.modtime = sb.st_mtime;
+ newtime.actime = time (NULL);
+ utime (ctx->path, &newtime);
+ }
+#endif
+
+ if (!ctx->readonly)
+ ctx->readonly = access (ctx->path, W_OK) ? 1 : 0;
+
+ /* precompute the local timezone to speed up calculation of the
+ date received */
+ tz = mutt_local_tz ();
+
+ loc = ftell (ctx->fp);
+ while (fgets (buf, sizeof (buf), ctx->fp) != NULL)
+ {
+ if ((t = is_from (buf, return_path, sizeof (return_path))))
+ {
+ /* Save the Content-Length of the previous message */
+ if (count > 0)
+ {
+#define PREV ctx->hdrs[ctx->msgcount-1]
+
+ if (PREV->content->length < 0)
+ {
+ PREV->content->length = loc - PREV->content->offset - 1;
+ if (PREV->content->length < 0)
+ PREV->content->length = 0;
+ }
+ if (!PREV->lines)
+ PREV->lines = lines ? lines - 1 : 0;
+ }
+
+ count++;
+
+ if (!ctx->quiet && ReadInc && ((count % ReadInc == 0) || count == 1))
+ mutt_message ("Reading %s... %d (%d%%)", ctx->path, count,
+ ftell (ctx->fp) / (ctx->size / 100 + 1));
+
+ if (ctx->msgcount == ctx->hdrmax)
+ mx_alloc_memory (ctx);
+
+ curhdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
+ curhdr->received = t + tz;
+ curhdr->offset = loc;
+ curhdr->index = ctx->msgcount;
+
+ curhdr->env = mutt_read_rfc822_header (ctx->fp, curhdr);
+
+ /* if we know how long this message is, either just skip over the body,
+ * or if we don't know how many lines there are, count them now (this will
+ * save time by not having to search for the next message marker).
+ */
+ if (curhdr->content->length > 0)
+ {
+ long tmploc;
+
+ loc = ftell (ctx->fp);
+ tmploc = loc + curhdr->content->length + 1;
+
+ if (tmploc < ctx->size)
+ {
+ /*
+ * check to see if the content-length looks valid. we expect to
+ * to see a valid message separator at this point in the stream
+ */
+ fseek (ctx->fp, tmploc, 0);
+ if (fgets (buf, sizeof (buf), ctx->fp) == NULL ||
+ strncmp ("From ", buf, 5) != 0)
+ {
+ dprint (1, (debugfile, "mbox_parse_mailbox: bad content-length in message %d (cl=%ld)\n", curhdr->index, curhdr->content->length));
+ dprint (1, (debugfile, "\tLINE: %s", buf));
+ fseek (ctx->fp, loc, 0); /* nope, return the previous position */
+ curhdr->content->length = -1;
+ }
+ }
+ else if (tmploc != ctx->size)
+ {
+ /* content-length would put us past the end of the file, so it
+ * must be wrong
+ */
+ curhdr->content->length = -1;
+ }
+
+ if (curhdr->content->length != -1)
+ {
+ /* good content-length. check to see if we know how many lines
+ * are in this message.
+ */
+ if (curhdr->lines == 0)
+ {
+ int cl = curhdr->content->length;
+
+ /* count the number of lines in this message */
+ fseek (ctx->fp, loc, 0);
+ while (cl-- > 0)
+ {
+ if (fgetc (ctx->fp) == '\n')
+ curhdr->lines++;
+ }
+ }
+
+ /* return to the offset of the next message separator */
+ fseek (ctx->fp, tmploc, 0);
+ }
+ }
+
+ mx_update_context (ctx);
+
+ if (!curhdr->env->return_path && return_path[0])
+ curhdr->env->return_path = rfc822_parse_adrlist (curhdr->env->return_path, return_path);
+
+ if (!curhdr->env->from)
+ curhdr->env->from = rfc822_cpy_adr (curhdr->env->return_path);
+
+ lines = 0;
+ }
+ else
+ lines++;
+
+ loc = ftell (ctx->fp);
+ }
+
+ /*
+ * Only set the content-length of the previous message if we have read more
+ * than one message during _this_ invocation. If this routine is called
+ * when new mail is received, we need to make sure not to clobber what
+ * previously was the last message since the headers may be sorted.
+ */
+ if (count > 0)
+ {
+ if (PREV->content->length < 0)
+ {
+ PREV->content->length = ftell (ctx->fp) - PREV->content->offset - 1;
+ if (PREV->content->length < 0)
+ PREV->content->length = 0;
+ }
+
+ if (!PREV->lines)
+ PREV->lines = lines ? lines - 1 : 0;
+ }
+
+ return (0);
+}
+
+#undef PREV
+
+/* open a mbox or mmdf style mailbox */
+int mbox_open_mailbox (CONTEXT *ctx)
+{
+ int rc;
+
+ if ((ctx->fp = fopen (ctx->path, "r")) == NULL)
+ {
+ mutt_perror (ctx->path);
+ return (-1);
+ }
+ mutt_block_signals ();
+ if (mbox_lock_mailbox (ctx, 0, 1) == -1)
+ {
+ mutt_unblock_signals ();
+ return (-1);
+ }
+
+ if (ctx->magic == M_MBOX)
+ rc = mbox_parse_mailbox (ctx);
+ else if (ctx->magic == M_MMDF)
+ rc = mmdf_parse_mailbox (ctx);
+ else
+ rc = -1;
+
+ mbox_unlock_mailbox (ctx);
+ mutt_unblock_signals ();
+ return (rc);
+}
+
+/* return 1 if address lists are strictly identical */
+static int strict_addrcmp (const ADDRESS *a, const ADDRESS *b)
+{
+ while (a && b)
+ {
+ if (mutt_strcmp (a->mailbox, b->mailbox) ||
+ mutt_strcmp (a->personal, b->personal))
+ return (0);
+
+ a = a->next;
+ b = b->next;
+ }
+ if (a || b)
+ return (0);
+
+ return (1);
+}
+
+static int strict_cmp_lists (const LIST *a, const LIST *b)
+{
+ while (a && b)
+ {
+ if (mutt_strcmp (a->data, b->data))
+ return (0);
+
+ a = a->next;
+ b = b->next;
+ }
+ if (a || b)
+ return (0);
+
+ return (1);
+}
+
+static int strict_cmp_envelopes (const ENVELOPE *e1, const ENVELOPE *e2)
+{
+ if (e1 && e2)
+ {
+ if (mutt_strcmp (e1->message_id, e2->message_id) ||
+ mutt_strcmp (e1->subject, e2->subject) ||
+ !strict_cmp_lists (e1->references, e2->references) ||
+ !strict_addrcmp (e1->from, e2->from) ||
+ !strict_addrcmp (e1->sender, e2->sender) ||
+ !strict_addrcmp (e1->reply_to, e2->reply_to) ||
+ !strict_addrcmp (e1->to, e2->to) ||
+ !strict_addrcmp (e1->cc, e2->cc) ||
+ !strict_addrcmp (e1->return_path, e2->return_path))
+ return (0);
+ else
+ return (1);
+ }
+ else
+ {
+ if (e1 == NULL && e2 == NULL)
+ return (1);
+ else
+ return (0);
+ }
+}
+
+static int strict_cmp_parameters (const PARAMETER *p1, const PARAMETER *p2)
+{
+ while (p1 && p2)
+ {
+ if (mutt_strcmp (p1->attribute, p2->attribute) ||
+ mutt_strcmp (p1->value, p2->value))
+ return (0);
+
+ p1 = p1->next;
+ p2 = p2->next;
+ }
+ if (p1 || p2)
+ return (0);
+
+ return (1);
+}
+
+static int strict_cmp_bodies (const BODY *b1, const BODY *b2)
+{
+ if (b1->type != b2->type ||
+ b1->encoding != b2->encoding ||
+ mutt_strcmp (b1->subtype, b2->subtype) ||
+ mutt_strcmp (b1->description, b2->description) ||
+ !strict_cmp_parameters (b1->parameter, b2->parameter) ||
+ b1->length != b2->length)
+ return (0);
+ return (1);
+}
+
+/* return 1 if headers are strictly identical */
+int mbox_strict_cmp_headers (const HEADER *h1, const HEADER *h2)
+{
+ if (h1 && h2)
+ {
+ if (h1->received != h2->received ||
+ h1->date_sent != h2->date_sent ||
+ h1->content->length != h2->content->length ||
+ h1->lines != h2->lines ||
+ h1->zhours != h2->zhours ||
+ h1->zminutes != h2->zminutes ||
+ h1->zoccident != h2->zoccident ||
+ h1->mime != h2->mime ||
+ !strict_cmp_envelopes (h1->env, h2->env) ||
+ !strict_cmp_bodies (h1->content, h2->content))
+ return (0);
+ else
+ return (1);
+ }
+ else
+ {
+ if (h1 == NULL && h2 == NULL)
+ return (1);
+ else
+ return (0);
+ }
+}
+
+/* check to see if the mailbox has changed on disk.
+ *
+ * return values:
+ * M_REOPENED mailbox has been reopened
+ * M_NEW_MAIL new mail has arrived!
+ * M_LOCKED couldn't lock the file
+ * 0 no change
+ * -1 error
+ */
+int mbox_check_mailbox (CONTEXT *ctx, int *index_hint)
+{
+ struct stat st;
+ char buffer[LONG_STRING];
+ int unlock = 0;
+ int modified = 0;
+
+ if (stat (ctx->path, &st) == 0)
+ {
+ if (st.st_mtime == ctx->mtime && st.st_size == ctx->size)
+ return (0);
+
+ if (st.st_size == ctx->size)
+ {
+ /* the file was touched, but it is still the same length, so just exit */
+ ctx->mtime = st.st_mtime;
+ return (0);
+ }
+
+ if (st.st_size > ctx->size)
+ {
+ /* lock the file if it isn't already */
+ if (!ctx->locked)
+ {
+ mutt_block_signals ();
+ if (mbox_lock_mailbox (ctx, 0, 0) == -1)
+ {
+ mutt_unblock_signals ();
+ /* we couldn't lock the mailbox, but nothing serious happened:
+ * probably the new mail arrived: no reason to wait till we can
+ * parse it: we'll get it on the next pass
+ * */
+ return (M_LOCKED);
+ }
+ unlock = 1;
+ }
+
+ /*
+ * Check to make sure that the only change to the mailbox is that
+ * message(s) were appended to this file. My heuristic is that we should
+ * see the message separator at *exactly* what used to be the end of the
+ * folder.
+ */
+ fseek (ctx->fp, ctx->size, 0);
+ if (fgets (buffer, sizeof (buffer), ctx->fp) != NULL)
+ {
+ if ((ctx->magic == M_MBOX && strncmp ("From ", buffer, 5) == 0) ||
+ (ctx->magic == M_MMDF && strcmp (MMDF_SEP, buffer) == 0))
+ {
+ fseek (ctx->fp, ctx->size, 0);
+ if (ctx->magic == M_MBOX)
+ mbox_parse_mailbox (ctx);
+ else
+ mmdf_parse_mailbox (ctx);
+
+ /* Only unlock the folder if it was locked inside of this routine.
+ * It may have been locked elsewhere, like in
+ * mutt_checkpoint_mailbox().
+ */
+
+ if (unlock)
+ {
+ mbox_unlock_mailbox (ctx);
+ mutt_unblock_signals ();
+ }
+
+ return (M_NEW_MAIL); /* signal that new mail arrived */
+ }
+ else
+ modified = 1;
+ }
+ else
+ {
+ dprint (1, (debugfile, "mbox_check_mailbox: fgets returned NULL.\n"));
+ modified = 1;
+ }
+ }
+ else
+ modified = 1;
+ }
+
+ if (modified)
+ {
+ if (mutt_reopen_mailbox (ctx, index_hint) != -1)
+ {
+ if (unlock)
+ {
+ mbox_unlock_mailbox (ctx);
+ mutt_unblock_signals ();
+ }
+ return (M_REOPENED);
+ }
+ }
+
+ /* fatal error */
+
+ mbox_unlock_mailbox (ctx);
+ mx_fastclose_mailbox (ctx);
+ mutt_unblock_signals ();
+ mutt_error ("Mailbox was corrupted!");
+ return (-1);
+}
+
+/* return values:
+ * 0 success
+ * -1 failure
+ */
+int mbox_sync_mailbox (CONTEXT *ctx)
+{
+ char tempfile[_POSIX_PATH_MAX];
+ char buf[16];
+ int i, j, save_sort = SORT_ORDER;
+ int need_sort = 0; /* flag to resort mailbox if new mail arrives */
+ int first; /* first message to be written */
+ long offset; /* location in mailbox to write changed messages */
+ struct stat statbuf;
+ struct utimbuf utimebuf;
+ struct m_update_t *newOffset = NULL;
+ FILE *fp;
+
+ /* sort message by their position in the mailbox on disk */
+ if (Sort != SORT_ORDER)
+ {
+ save_sort = Sort;
+ Sort = SORT_ORDER;
+ mutt_sort_headers (ctx, 0);
+ }
+
+ /* need to open the file for writing in such a way that it does not truncate
+ * the file, so use read-write mode.
+ */
+ if ((ctx->fp = freopen (ctx->path, "r+", ctx->fp)) == NULL)
+ {
+ mx_fastclose_mailbox (ctx);
+ mutt_error ("Fatal error! Could not reopen mailbox.");
+ return (-1);
+ }
+
+ mutt_block_signals ();
+
+ if (mbox_lock_mailbox (ctx, 1, 1) == -1)
+ {
+ mutt_unblock_signals ();
+ mutt_error ("Unable to lock mailbox!");
+ goto bail;
+ }
+
+ /* Check to make sure that the file hasn't changed on disk */
+ if ((i = mbox_check_mailbox (ctx, NULL)) == M_NEW_MAIL || i == M_REOPENED)
+ {
+ /* new mail arrived, or mailbox reopened */
+ need_sort = i;
+ goto bail;
+ }
+ else if (i < 0)
+ {
+ /* fatal error */
+ Sort = save_sort;
+ return (-1);
+ }
+
+ /* Create a temporary file to write the new version of the mailbox in. */
+ mutt_mktemp (tempfile);
+ if ((i = open (tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1 ||
+ (fp = fdopen (i, "w")) == NULL)
+ {
+ mutt_error ("Could not create temporary file!");
+ goto bail;
+ }
+
+ /* find the first deleted/changed message. we save a lot of time by only
+ * rewriting the mailbox from the point where it has actually changed.
+ */
+ for (i = 0 ; i < ctx->msgcount && !ctx->hdrs[i]->deleted &&
+ !ctx->hdrs[i]->changed && !ctx->hdrs[i]->attach_del; i++)
+ ;
+ if (i == ctx->msgcount)
+ {
+ /* this means ctx->changed or ctx->deleted was set, but no
+ * messages were found to be changed or deleted. This should
+ * never happen, is we presume it is a bug in mutt.
+ */
+ mutt_error ("sync: mbox modified, but no modified messages! (report this bug)");
+ sleep(5); /* the mutt_error /will/ get cleared! */
+ dprint(1, (debugfile, "mbox_sync_mailbox(): no modified messages.\n"));
+ goto bail;
+ }
+
+
+ first = i; /* save the index of the first changed/deleted message */
+ offset = ctx->hdrs[i]->offset; /* where to start overwriting */
+ /* the offset stored in the header does not include the MMDF_SEP, so make
+ * sure we seek to the correct location
+ */
+ if (ctx->magic == M_MMDF)
+ offset -= strlen (MMDF_SEP);
+
+ /* allocate space for the new offsets */
+ newOffset = safe_calloc (ctx->msgcount - first, sizeof (struct m_update_t));
+
+ for (i = first, j = 0; i < ctx->msgcount; i++)
+ {
+ if (! ctx->hdrs[i]->deleted)
+ {
+ j++;
+ if (!ctx->quiet && WriteInc && ((j % WriteInc) == 0 || j == 1))
+ mutt_message ("Writing messages... %d (%d%%)", j,
+ ftell (ctx->fp) / (ctx->size / 100 + 1));
+
+ if (ctx->magic == M_MMDF)
+ {
+ if (fputs (MMDF_SEP, fp) == EOF)
+ goto bail;
+ }
+
+ /* save the new offset for this message. we add `offset' because the
+ * temporary file only contains saved message which are located after
+ * `offset' in the real mailbox
+ */
+ newOffset[i - first].hdr = ftell (fp) + offset;
+
+ if (mutt_copy_message (fp, ctx, ctx->hdrs[i], M_CM_UPDATE, CH_FROM | CH_UPDATE | CH_UPDATE_LEN) == -1)
+ goto bail;
+
+ /* Since messages could have been deleted, the offsets stored in memory
+ * will be wrong, so update what we can, which is the offset of this
+ * message, and the offset of the body. If this is a multipart message,
+ * we just flush the in memory cache so that the message will be reparsed
+ * if the user accesses it later.
+ */
+ newOffset[i - first].body = ftell (fp) - ctx->hdrs[i]->content->length + offset;
+ mutt_free_body (&ctx->hdrs[i]->content->parts);
+
+ if (fputs (ctx->magic == M_MMDF ? MMDF_SEP : "\n", fp) == EOF)
+ goto bail;
+ }
+ }
+
+ if (fclose (fp) != 0)
+ {
+ dprint(1, (debugfile, "mutt_sync_mailbox(): fclose() returned non-zero.\n"));
+ unlink (tempfile);
+ goto bail;
+ }
+
+ /* Save the state of this folder. */
+ if (stat (ctx->path, &statbuf) == -1)
+ {
+ unlink (tempfile);
+ goto bail;
+ }
+
+ if ((fp = fopen (tempfile, "r")) == NULL)
+ {
+ mutt_unblock_signals ();
+ mx_fastclose_mailbox (ctx);
+ dprint (1, (debugfile, "mbox_sync_mailbox: unable to reopen temp copy of mailbox!\n"));
+ mutt_perror (tempfile);
+ return (-1);
+ }
+
+ fseek (ctx->fp, offset, 0); /* seek the append location */
+
+ /* do a sanity check to make sure the mailbox looks ok */
+ if (fgets (buf, sizeof (buf), ctx->fp) == NULL ||
+ (ctx->magic == M_MBOX && strncmp ("From ", buf, 5) != 0) ||
+ (ctx->magic == M_MMDF && strcmp (MMDF_SEP, buf) != 0))
+ {
+ dprint (1, (debugfile, "mbox_sync_mailbox(): message not in expected position."));
+ dprint (1, (debugfile, "\tLINE: %s\n", buf));
+ i = -1;
+ }
+ else
+ {
+ fseek (ctx->fp, offset, 0); /* return to proper offset */
+
+ /* copy the temp mailbox back into place starting at the first
+ * change/deleted message
+ */
+ i = mutt_copy_stream (fp, ctx->fp);
+
+ if (ferror (ctx->fp))
+ i = -1;
+
+ if (i == 0)
+ {
+ ctx->size = ftell (ctx->fp); /* update the size of the mailbox */
+ ftruncate (fileno (ctx->fp), ctx->size);
+ }
+ }
+
+ fclose (fp);
+ mbox_unlock_mailbox (ctx);
+
+ if (fclose (ctx->fp) != 0 || i == -1)
+ {
+ /* error occured while writing the mailbox back, so keep the temp copy
+ * around
+ */
+
+ char savefile[_POSIX_PATH_MAX];
+
+ snprintf (savefile, sizeof (savefile), "%s/mutt.%s-%s-%d",
+ NONULL (Tempdir), Username, Hostname, getpid ());
+ rename (tempfile, savefile);
+ mutt_unblock_signals ();
+ mx_fastclose_mailbox (ctx);
+ mutt_pretty_mailbox (savefile);
+ mutt_error ("Write failed! Saved partial mailbox to %s", savefile);
+ return (-1);
+ }
+
+ /* Restore the previous access/modification times */
+ utimebuf.actime = statbuf.st_atime;
+ utimebuf.modtime = statbuf.st_mtime;
+ utime (ctx->path, &utimebuf);
+
+ /* reopen the mailbox in read-only mode */
+ if ((ctx->fp = fopen (ctx->path, "r")) == NULL)
+ {
+ unlink (tempfile);
+ mutt_unblock_signals ();
+ mx_fastclose_mailbox (ctx);
+ mutt_error ("Fatal error! Could not reopen mailbox!");
+ Sort = save_sort;
+ return (-1);
+ }
+
+ /* update the offsets of the rewritten messages */
+ for (i = first, j = first; i < ctx->msgcount; i++)
+ {
+ if (!ctx->hdrs[i]->deleted)
+ {
+ ctx->hdrs[i]->offset = newOffset[i - first].hdr;
+ ctx->hdrs[i]->content->hdr_offset = newOffset[i - first].hdr;
+ ctx->hdrs[i]->content->offset = newOffset[i - first].body;
+ ctx->hdrs[i]->index = j++;
+ }
+ }
+ safe_free ((void **) &newOffset);
+ unlink (tempfile); /* remove partial copy of the mailbox */
+ mutt_unblock_signals ();
+ Sort = save_sort; /* Restore the default value. */
+
+ return (0); /* signal success */
+
+bail: /* Come here in case of disaster */
+
+ /* this is ok to call even if we haven't locked anything */
+ mbox_unlock_mailbox (ctx);
+
+ mutt_unblock_signals ();
+ safe_free ((void **) &newOffset);
+
+ if ((ctx->fp = freopen (ctx->path, "r", ctx->fp)) == NULL)
+ {
+ mutt_error ("Could not reopen mailbox!");
+ mx_fastclose_mailbox (ctx);
+ return (-1);
+ }
+
+ if (need_sort || save_sort != Sort)
+ {
+ Sort = save_sort;
+ /* if the mailbox was reopened, the thread tree will be invalid so make
+ * sure to start threading from scratch. */
+ mutt_sort_headers (ctx, (need_sort == M_REOPENED));
+ }
+
+ return (-1);
+}
+
+/* close a mailbox opened in write-mode */
+int mbox_close_mailbox (CONTEXT *ctx)
+{
+#ifdef USE_SETGID
+ if (ctx->setgid)
+ SETEGID (MailGid);
+#endif
+
+ mx_unlock_file (ctx->path, fileno (ctx->fp));
+
+#ifdef USE_SETGID
+ if (ctx->setgid)
+ {
+ SETEGID (UserGid);
+ ctx->setgid = 0;
+ }
+#endif
+
+ mutt_unblock_signals ();
+ mx_fastclose_mailbox (ctx);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mutt_menu.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#define M_MODEFMT "-- Mutt: %s"
+
+static void print_enriched_string (int attr, unsigned char *s, int do_color)
+{
+ while (*s)
+ {
+ if (*s < M_TREE_MAX)
+ {
+ if (do_color)
+ SETCOLOR (MT_COLOR_TREE);
+ while (*s && *s < M_TREE_MAX)
+ {
+ switch (*s)
+ {
+ case M_TREE_LLCORNER:
+ addch (option (OPTASCIICHARS) ? '`' : ACS_LLCORNER);
+ break;
+ case M_TREE_ULCORNER:
+ addch (option (OPTASCIICHARS) ? ',' : ACS_ULCORNER);
+ break;
+ case M_TREE_LTEE:
+ addch (option (OPTASCIICHARS) ? '|' : ACS_LTEE);
+ break;
+ case M_TREE_HLINE:
+ addch (option (OPTASCIICHARS) ? '-' : ACS_HLINE);
+ break;
+ case M_TREE_VLINE:
+ addch (option (OPTASCIICHARS) ? '|' : ACS_VLINE);
+ break;
+ case M_TREE_SPACE:
+ addch (' ');
+ break;
+ case M_TREE_RARROW:
+ addch ('>');
+ break;
+ case M_TREE_STAR:
+ addch ('*'); /* fake thread indicator */
+ break;
+ case M_TREE_HIDDEN:
+ addch ('&');
+ break;
+ }
+ s++;
+ }
+ if (do_color) attrset(attr);
+ }
+ else
+ {
+ addch (*s);
+ s++;
+ }
+ }
+}
+
+void menu_pad_string (char *s, size_t l)
+{
+#if !defined(HAVE_BKGDSET) && !defined (USE_SLANG_CURSES)
+ int n = strlen (s);
+#endif
+ int shift = option (OPTARROWCURSOR) ? 3 : 0;
+
+ l--; /* save room for the terminal \0 */
+ if (l > COLS - shift)
+ l = COLS - shift;
+#if !defined (HAVE_BKGDSET) && !defined (USE_SLANG_CURSES)
+ /* we have to pad the string with blanks to the end of line */
+ if (n < l)
+ {
+ while (n < l)
+ s[n++] = ' ';
+ s[n] = 0;
+ }
+ else
+#endif
+ s[l] = 0;
+}
+
+void menu_redraw_full (MUTTMENU *menu)
+{
+ SETCOLOR (MT_COLOR_NORMAL);
+ /* clear() doesn't optimize screen redraws */
+ move (0, 0);
+ clrtobot ();
+
+ if (option (OPTHELP))
+ {
+ SETCOLOR (MT_COLOR_STATUS);
+ mvprintw (option (OPTSTATUSONTOP) ? LINES-2 : 0, 0, "%-*.*s", COLS, COLS, menu->help);
+ SETCOLOR (MT_COLOR_NORMAL);
+ menu->offset = 1;
+ menu->pagelen = LINES - 3;
+ }
+ else
+ {
+ menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
+ menu->pagelen = LINES - 2;
+ }
+
+ mutt_show_error ();
+
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+}
+
+void menu_redraw_index (MUTTMENU *menu)
+{
+ char buf[STRING];
+ int i;
+
+ for (i = menu->top; i < menu->top + menu->pagelen; i++)
+ {
+ if (i < menu->max)
+ {
+ menu->make_entry (buf, sizeof (buf), menu, i);
+ menu_pad_string (buf, sizeof (buf));
+
+ if (option (OPTARROWCURSOR))
+ {
+ attrset (menu->color (i));
+ CLEARLINE (i - menu->top + menu->offset);
+
+ if (i == menu->current)
+ {
+ SETCOLOR (MT_COLOR_INDICATOR);
+ addstr ("->");
+ attrset (menu->color (i));
+ addch (' ');
+ }
+ else
+ move (i - menu->top + menu->offset, 3);
+
+ print_enriched_string (menu->color(i), (unsigned char *) buf, 1);
+ SETCOLOR (MT_COLOR_NORMAL);
+ }
+ else
+ {
+ if (i == menu->current)
+ {
+ SETCOLOR (MT_COLOR_INDICATOR);
+ BKGDSET (MT_COLOR_INDICATOR);
+ }
+ else
+ attrset (menu->color (i));
+
+ CLEARLINE (i - menu->top + menu->offset);
+ print_enriched_string (menu->color(i), (unsigned char *) buf, i != menu->current);
+ SETCOLOR (MT_COLOR_NORMAL);
+ BKGDSET (MT_COLOR_NORMAL);
+ }
+ }
+ else
+ CLEARLINE (i - menu->top + menu->offset);
+ }
+ menu->redraw = 0;
+}
+
+void menu_redraw_motion (MUTTMENU *menu)
+{
+ char buf[STRING];
+
+ move (menu->oldcurrent + menu->offset - menu->top, 0);
+ SETCOLOR (MT_COLOR_NORMAL);
+ BKGDSET (MT_COLOR_NORMAL);
+
+ if (option (OPTARROWCURSOR))
+ {
+ /* clear the pointer */
+ attrset (menu->color (menu->oldcurrent));
+ addstr (" ");
+
+ if (menu->redraw & REDRAW_MOTION_RESYNCH)
+ {
+ clrtoeol ();
+ menu->make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
+ menu_pad_string (buf, sizeof (buf));
+ move (menu->oldcurrent + menu->offset - menu->top, 3);
+ print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
+ SETCOLOR (MT_COLOR_NORMAL);
+ }
+
+ /* now draw it in the new location */
+ move (menu->current + menu->offset - menu->top, 0);
+ SETCOLOR (MT_COLOR_INDICATOR);
+ addstr ("->");
+ SETCOLOR (MT_COLOR_NORMAL);
+ }
+ else
+ {
+ /* erase the current indicator */
+ attrset (menu->color (menu->oldcurrent));
+ clrtoeol ();
+ menu->make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
+ menu_pad_string (buf, sizeof (buf));
+ print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
+
+ /* now draw the new one to reflect the change */
+ menu->make_entry (buf, sizeof (buf), menu, menu->current);
+ menu_pad_string (buf, sizeof (buf));
+ SETCOLOR (MT_COLOR_INDICATOR);
+ BKGDSET (MT_COLOR_INDICATOR);
+ CLEARLINE (menu->current - menu->top + menu->offset);
+ print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
+ SETCOLOR (MT_COLOR_NORMAL);
+ BKGDSET (MT_COLOR_NORMAL);
+ }
+ menu->redraw &= REDRAW_STATUS;
+}
+
+void menu_redraw_current (MUTTMENU *menu)
+{
+ char buf[STRING];
+
+ move (menu->current + menu->offset - menu->top, 0);
+ menu->make_entry (buf, sizeof (buf), menu, menu->current);
+ menu_pad_string (buf, sizeof (buf));
+
+ if (option (OPTARROWCURSOR))
+ {
+ int attr = menu->color (menu->current);
+ attrset (attr);
+ clrtoeol ();
+ SETCOLOR (MT_COLOR_INDICATOR);
+ addstr ("->");
+ attrset (attr);
+ addch (' ');
+ menu_pad_string (buf, sizeof (buf));
+ print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 1);
+ SETCOLOR (MT_COLOR_NORMAL);
+ }
+ else
+ {
+ SETCOLOR (MT_COLOR_INDICATOR);
+ BKGDSET (MT_COLOR_INDICATOR);
+ clrtoeol ();
+ print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
+ SETCOLOR (MT_COLOR_NORMAL);
+ BKGDSET (MT_COLOR_NORMAL);
+ }
+ menu->redraw &= REDRAW_STATUS;
+}
+
+void menu_check_recenter (MUTTMENU *menu)
+{
+ if (menu->current >= menu->top + menu->pagelen)
+ {
+ if (option (OPTMENUSCROLL))
+ menu->top = menu->current - menu->pagelen + 1;
+ else
+ menu->top += menu->pagelen * ((menu->current - menu->top) / menu->pagelen);
+ menu->redraw = REDRAW_INDEX;
+ }
+ else if (menu->current < menu->top)
+ {
+ if (option (OPTMENUSCROLL))
+ menu->top = menu->current;
+ else
+ {
+ menu->top -= menu->pagelen * ((menu->top + menu->pagelen - 1 - menu->current) / menu->pagelen);
+ if (menu->top < 0)
+ menu->top = 0;
+ }
+ menu->redraw = REDRAW_INDEX;
+ }
+}
+
+void menu_jump (MUTTMENU *menu)
+{
+ int n;
+ char buf[SHORT_STRING];
+
+ if (menu->max)
+ {
+ mutt_ungetch (LastKey);
+ buf[0] = 0;
+ if (mutt_get_field ("Jump to: ", buf, sizeof (buf), 0) == 0 && buf[0])
+ {
+ n = atoi (buf) - 1;
+ if (n >= 0 && n < menu->max)
+ {
+ menu->current = n;
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("Invalid index number.");
+ }
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_next_line (MUTTMENU *menu)
+{
+ if (menu->max)
+ {
+ if (menu->top < menu->max - 1)
+ {
+ menu->top++;
+ if (menu->current < menu->top)
+ menu->current++;
+ menu->redraw = REDRAW_INDEX;
+ }
+ else
+ mutt_error ("You cannot scroll down farther.");
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_prev_line (MUTTMENU *menu)
+{
+ if (menu->top > 0)
+ {
+ menu->top--;
+ if (menu->current >= menu->top + menu->pagelen)
+ menu->current--;
+ menu->redraw = REDRAW_INDEX;
+ }
+ else
+ mutt_error ("You cannot scroll up farther.");
+}
+
+void menu_next_page (MUTTMENU *menu)
+{
+ if (menu->max)
+ {
+ if (menu->top + menu->pagelen < menu->max)
+ {
+ menu->top += menu->pagelen;
+ if (menu->current < menu->top)
+ menu->current = menu->top;
+ menu->redraw = REDRAW_INDEX;
+ }
+ else if (menu->current != menu->max - 1)
+ {
+ menu->current = menu->max - 1;
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("You are on the last page.");
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_prev_page (MUTTMENU *menu)
+{
+ if (menu->top > 0)
+ {
+ if ((menu->top -= menu->pagelen) < 0)
+ menu->top = 0;
+ if (menu->current >= menu->top + menu->pagelen)
+ menu->current = menu->top + menu->pagelen - 1;
+ menu->redraw = REDRAW_INDEX;
+ }
+ else if (menu->current)
+ {
+ menu->current = 0;
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("You are on the first page.");
+}
+
+void menu_top_page (MUTTMENU *menu)
+{
+ if (menu->current != menu->top)
+ {
+ menu->current = menu->top;
+ menu->redraw = REDRAW_MOTION;
+ }
+}
+
+void menu_bottom_page (MUTTMENU *menu)
+{
+ if (menu->max)
+ {
+ menu->current = menu->top + menu->pagelen - 1;
+ if (menu->current > menu->max - 1)
+ menu->current = menu->max - 1;
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_middle_page (MUTTMENU *menu)
+{
+ int i;
+
+ if (menu->max)
+ {
+ i = menu->top + menu->pagelen;
+ if (i > menu->max - 1)
+ i = menu->max - 1;
+ menu->current = menu->top + (i - menu->top) / 2;
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_first_entry (MUTTMENU *menu)
+{
+ if (menu->max)
+ {
+ menu->current = 0;
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_last_entry (MUTTMENU *menu)
+{
+ if (menu->max)
+ {
+ menu->current = menu->max - 1;
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_half_up (MUTTMENU *menu)
+{
+ if (menu->top > 0)
+ {
+ if ((menu->top -= menu->pagelen / 2) < 0)
+ menu->top = 0;
+ if (menu->current >= menu->top + menu->pagelen)
+ menu->current = menu->top + menu->pagelen - 1;
+ menu->redraw = REDRAW_INDEX;
+ }
+ else if (menu->current)
+ {
+ menu->current = 0;
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("First entry is shown.");
+}
+
+void menu_half_down (MUTTMENU *menu)
+{
+ if (menu->max)
+ {
+ if (menu->top + menu->pagelen < menu->max)
+ {
+ menu->top += menu->pagelen / 2;
+ if (menu->current < menu->top)
+ menu->current = menu->top;
+ menu->redraw = REDRAW_INDEX;
+ }
+ else if (menu->current != menu->max - 1)
+ {
+ menu->current = menu->max - 1;
+ menu->redraw = REDRAW_INDEX;
+ }
+ else
+ mutt_error ("Last entry is shown.");
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_current_top (MUTTMENU *menu)
+{
+ if (menu->max)
+ {
+ menu->top = menu->current;
+ menu->redraw = REDRAW_INDEX;
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_current_middle (MUTTMENU *menu)
+{
+ if (menu->max)
+ {
+ menu->top = menu->current - menu->pagelen / 2;
+ if (menu->top < 0)
+ menu->top = 0;
+ menu->redraw = REDRAW_INDEX;
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_current_bottom (MUTTMENU *menu)
+{
+ if (menu->max)
+ {
+ menu->top = menu->current - menu->pagelen + 1;
+ if (menu->top < 0)
+ menu->top = 0;
+ menu->redraw = REDRAW_INDEX;
+ }
+ else
+ mutt_error ("No entries.");
+}
+
+void menu_next_entry (MUTTMENU *menu)
+{
+ if (menu->current < menu->max - 1)
+ {
+ menu->current++;
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("You are on the last entry.");
+}
+
+void menu_prev_entry (MUTTMENU *menu)
+{
+ if (menu->current)
+ {
+ menu->current--;
+ menu->redraw = REDRAW_MOTION;
+ }
+ else
+ mutt_error ("You are on the first entry.");
+}
+
+static int default_color (int i)
+{
+ return ColorDefs[MT_COLOR_NORMAL];
+}
+
+MUTTMENU *mutt_new_menu (void)
+{
+ MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
+
+ p->offset = 1;
+ p->redraw = REDRAW_FULL;
+ p->pagelen = PAGELEN;
+ p->color = default_color;
+ return (p);
+}
+
+void mutt_menuDestroy (MUTTMENU **p)
+{
+ safe_free ((void **) &(*p)->searchBuf);
+ safe_free ((void **) p);
+}
+
+#define M_SEARCH_UP 1
+#define M_SEARCH_DOWN 2
+
+static int menu_search (MUTTMENU *menu, int op)
+{
+ int r;
+ int searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
+ regex_t re;
+ char buf[SHORT_STRING];
+
+ if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE)
+ {
+ strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
+ if (mutt_get_field ("Search for: ", buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
+ return (-1);
+ safe_free ((void **) &menu->searchBuf);
+ menu->searchBuf = safe_strdup (buf);
+ menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
+ }
+ else
+ {
+ if (!menu->searchBuf)
+ {
+ mutt_error ("No search pattern.");
+ return (-1);
+ }
+
+ if (op == OP_SEARCH_OPPOSITE)
+ searchDir = -searchDir;
+ }
+
+ if ((r = REGCOMP (&re, menu->searchBuf, REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0)
+ {
+ regerror (r, &re, buf, sizeof (buf));
+ regfree (&re);
+ mutt_error ("%s", buf);
+ return (-1);
+ }
+
+ r = menu->current + searchDir;
+ while (r >= 0 && r < menu->max)
+ {
+ if (menu->search (menu, &re, r) == 0)
+ {
+ regfree (&re);
+ return r;
+ }
+
+ r += searchDir;
+ }
+
+ regfree (&re);
+ mutt_error ("Not found.");
+ return (-1);
+}
+
+int mutt_menuLoop (MUTTMENU *menu)
+{
+ int i = OP_NULL;
+ char buf[STRING];
+
+ FOREVER
+ {
+ mutt_curs_set (0);
+
+ /* See if all or part of the screen needs to be updated. */
+ if (menu->redraw & REDRAW_FULL)
+ {
+ menu_redraw_full (menu);
+ /* allow the caller to do any local configuration */
+ return (OP_REDRAW);
+ }
+
+ menu_check_recenter (menu);
+
+ if (menu->redraw & REDRAW_STATUS)
+ {
+ snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
+ SETCOLOR (MT_COLOR_STATUS);
+ mvprintw (option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0, "%-*.*s", COLS, COLS, buf);
+ SETCOLOR (MT_COLOR_NORMAL);
+ menu->redraw &= ~REDRAW_STATUS;
+ }
+
+ if (menu->redraw & REDRAW_INDEX)
+ menu_redraw_index (menu);
+ else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
+ menu_redraw_motion (menu);
+ else if (menu->redraw == REDRAW_CURRENT)
+ menu_redraw_current (menu);
+
+ menu->oldcurrent = menu->current;
+
+ /* move the cursor out of the way */
+ move (menu->current - menu->top + menu->offset,
+ (option (OPTARROWCURSOR) ? 2 : COLS-1));
+
+ mutt_refresh ();
+ i = km_dokey (menu->menu);
+ if (i == OP_TAG_PREFIX)
+ {
+ mvaddstr (LINES - 1, 0, "Tag-");
+ i = km_dokey (menu->menu);
+ menu->tagprefix = 1;
+ CLEARLINE (LINES - 1);
+ }
+ else if (menu->tagged && option (OPTAUTOTAG))
+ menu->tagprefix = 1;
+ else
+ menu->tagprefix = 0;
+
+ mutt_curs_set (1);
+
+#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
+ if (Signals & S_SIGWINCH)
+ {
+ mutt_resize_screen ();
+ menu->redraw = REDRAW_FULL;
+ Signals &= ~S_SIGWINCH;
+ }
+#endif
+
+ if (i == -1)
+ continue;
+
+ mutt_clear_error ();
+
+ switch (i)
+ {
+ case OP_NEXT_ENTRY:
+ menu_next_entry (menu);
+ break;
+ case OP_PREV_ENTRY:
+ menu_prev_entry (menu);
+ break;
+ case OP_HALF_DOWN:
+ menu_half_down (menu);
+ break;
+ case OP_HALF_UP:
+ menu_half_up (menu);
+ break;
+ case OP_NEXT_PAGE:
+ menu_next_page (menu);
+ break;
+ case OP_PREV_PAGE:
+ menu_prev_page (menu);
+ break;
+ case OP_NEXT_LINE:
+ menu_next_line (menu);
+ break;
+ case OP_PREV_LINE:
+ menu_prev_line (menu);
+ break;
+ case OP_FIRST_ENTRY:
+ menu_first_entry (menu);
+ break;
+ case OP_LAST_ENTRY:
+ menu_last_entry (menu);
+ break;
+ case OP_TOP_PAGE:
+ menu_top_page (menu);
+ break;
+ case OP_MIDDLE_PAGE:
+ menu_middle_page (menu);
+ break;
+ case OP_BOTTOM_PAGE:
+ menu_bottom_page (menu);
+ break;
+ case OP_CURRENT_TOP:
+ menu_current_top (menu);
+ break;
+ case OP_CURRENT_MIDDLE:
+ menu_current_middle (menu);
+ break;
+ case OP_CURRENT_BOTTOM:
+ menu_current_bottom (menu);
+ break;
+ case OP_SEARCH:
+ case OP_SEARCH_REVERSE:
+ case OP_SEARCH_NEXT:
+ case OP_SEARCH_OPPOSITE:
+ if (menu->search)
+ {
+ menu->oldcurrent = menu->current;
+ if ((menu->current = menu_search (menu, i)) != -1)
+ menu->redraw = REDRAW_MOTION;
+ else
+ menu->current = menu->oldcurrent;
+ }
+ else
+ mutt_error ("Search is not implemented for this menu.");
+ break;
+
+ case OP_JUMP:
+ menu_jump (menu);
+ break;
+
+ case OP_ENTER_COMMAND:
+ mutt_enter_command ();
+ if (option (OPTFORCEREDRAWINDEX))
+ {
+ menu->redraw = REDRAW_FULL;
+ unset_option (OPTFORCEREDRAWINDEX);
+ unset_option (OPTFORCEREDRAWPAGER);
+ }
+ break;
+
+ case OP_TAG:
+ if (menu->tag)
+ {
+ if (menu->max)
+ {
+ if (menu->tag (menu, menu->current))
+ menu->tagged++;
+ else
+ menu->tagged--;
+ if (option (OPTRESOLVE) && menu->current < menu->max - 1)
+ {
+ menu->current++;
+ menu->redraw = REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw = REDRAW_CURRENT;
+ }
+ else
+ mutt_error ("No entries.");
+ }
+ else
+ mutt_error ("Tagging is not supported.");
+ break;
+
+ case OP_SHELL_ESCAPE:
+ mutt_shell_escape ();
+ MAYBE_REDRAW (menu->redraw);
+ break;
+
+ case OP_REDRAW:
+ clearok (stdscr, TRUE);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_HELP:
+ mutt_help (menu->menu);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_NULL:
+ km_error_key (menu->menu);
+ break;
+
+ default:
+ return (i);
+ }
+ }
+ /* not reached */
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file contains routines specific to MH and ``maildir'' style mailboxes
+ */
+
+#include "mutt.h"
+#include "mx.h"
+#include "mailbox.h"
+#include "copy.h"
+#include "buffy.h"
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <limits.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+
+void mh_parse_message (CONTEXT *ctx,
+ const char *subdir,
+ const char *fname,
+ int *count,
+ int isOld)
+{
+ char path[_POSIX_PATH_MAX];
+ char *p;
+ FILE *f;
+ HEADER *h;
+ struct stat st;
+
+ if (subdir)
+ snprintf (path, sizeof (path), "%s/%s/%s", ctx->path, subdir, fname);
+ else
+ snprintf (path, sizeof (path), "%s/%s", ctx->path, fname);
+
+ if ((f = fopen (path, "r")) != NULL)
+ {
+ (*count)++;
+
+ if (!ctx->quiet && ReadInc && ((*count % ReadInc) == 0 || *count == 1))
+ mutt_message ("Reading %s... %d", ctx->path, *count);
+
+ if (ctx->msgcount == ctx->hdrmax)
+ mx_alloc_memory (ctx);
+
+ h = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
+
+ if (subdir)
+ {
+ snprintf (path, sizeof (path), "%s/%s", subdir, fname);
+ h->path = safe_strdup (path);
+ }
+ else
+ h->path = safe_strdup (fname);
+
+ h->env = mutt_read_rfc822_header (f, h);
+
+ fstat (fileno (f), &st);
+ fclose (f);
+
+ if (!h->received)
+ h->received = h->date_sent;
+
+ if (h->content->length <= 0)
+ h->content->length = st.st_size - h->content->offset;
+
+ /* index doesn't have a whole lot of meaning for MH and maildir mailboxes,
+ * but this is used to find the current message after a resort in
+ * the `index' event loop.
+ */
+ h->index = ctx->msgcount;
+
+ if (ctx->magic == M_MAILDIR)
+ {
+ /* maildir stores its flags in the filename, so ignore the flags in
+ * the header of the message
+ */
+
+ h->old = isOld;
+
+ if ((p = strchr (h->path, ':')) != NULL && strncmp (p + 1, "2,", 2) == 0)
+ {
+ p += 3;
+ while (*p)
+ {
+ switch (*p)
+ {
+ case 'F':
+
+ h->flagged = 1;
+ break;
+
+ case 'S': /* seen */
+
+ h->read = 1;
+ break;
+
+ case 'R': /* replied */
+
+ h->replied = 1;
+ break;
+ }
+ p++;
+ }
+ }
+ }
+
+ /* set flags and update context info */
+ mx_update_context (ctx);
+ }
+}
+
+/*
+ * Mark all the mails in ctx read.
+ */
+void tag_all_read (CONTEXT * ctx)
+{
+ int i;
+
+ ctx->new = 0;
+ if (ctx->hdrs == NULL)
+ return;
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ ctx->hdrs[i]->read = 1;
+ ctx->hdrs[i]->old = 1;
+ }
+}
+
+/*
+ * Mark one mail as unread
+ */
+int tag_unread (CONTEXT * ctx, char *name)
+{
+ int i;
+
+ if (ctx->hdrs == NULL)
+ return (1);
+ for (i = ctx->msgcount - 1; i >= 0; i--)
+ if (!strcmp (name, ctx->hdrs[i]->path))
+ {
+ ctx->hdrs[i]->read = 0;
+ ctx->hdrs[i]->old = 0;
+ return (1);
+ }
+ return (0);
+}
+
+/* Parse a .mh_sequences file. Only supports "unseen:" field.
+ */
+
+#define SKIP_BLANK(p) \
+ while ((*(p) == ' ') || (*(p) == '\t')) p++;
+
+#define SKIP_EOL(p) \
+{ \
+ while ((*p != '\0') && (*(p) != '\n') && (*(p) != '\r')) p++; \
+ while ((*(p) == '\n') || (*(p) == '\r')) p++; \
+}
+
+/* Parses and returns the next unsigned number in the string <cur>,
+ advancing the pointer to the character after the number ended.
+ */
+static unsigned parse_number (const char **cur)
+{
+ unsigned i = 0;
+ for (; (**cur >= '0') && (**cur <= '9'); (*cur)++)
+ {
+ i *= 10;
+ i += **cur - '0';
+ }
+ return i;
+}
+
+/* if context is NULL, it uses <folder> as the mailbox path, otherwise,
+ uses ctx->path. If context is not null, tags all the unread messages
+ and sets the position correctly.
+ returns the number of unread messages in the mailbox.
+ */
+int mh_parse_sequences (CONTEXT * ctx, const char *folder)
+{
+ FILE *mh_sequences;
+ struct stat sb;
+ char *content;
+ const char *cur;
+ int len;
+ int base, last;
+ char buf[_POSIX_PATH_MAX];
+ char filename[100];
+ int unread = 0;
+ static HASH *cache = 0;
+ int *h;
+
+ if (!cache) {
+ cache = hash_create (100); /* If this ain't enough, read less mail! */
+ if (!cache)
+ {
+ mutt_error ("mh_parse_sequences: Unable to allocate hash table!\n");
+ return -1;
+ }
+ }
+
+ if (ctx)
+ folder = ctx->path;
+
+ snprintf (buf, sizeof (buf), "%s/.mh_sequences", folder);
+ if (stat (buf, &sb) != 0)
+ return (-1);
+
+ if (ctx)
+ tag_all_read (ctx);
+
+ if (!ctx && sb.st_mtime < BuffyDoneTime && (h = hash_find (cache, folder))) /* woo! we win! */
+ return *h;
+
+ /*
+ * Parse the .mh_sequence to find the unread ones.
+ */
+
+ content = safe_malloc (sb.st_size + 100);
+ if (content == NULL)
+ {
+ mutt_perror ("malloc");
+ return (-1);
+ }
+ mh_sequences = fopen (buf, "r");
+ if (mh_sequences == NULL)
+ {
+ mutt_message ("Cannot open %s", buf);
+ safe_free ((void **) &content);
+ return (-1);
+ }
+
+ len = fread (content, 1, sb.st_size, mh_sequences);
+ content[len] = '\0';
+
+ fclose (mh_sequences);
+
+ cur = content;
+ SKIP_BLANK (cur);
+ while (*cur != '\0')
+ {
+ if (!strncmp (cur, "unseen", 6))
+ {
+ cur += 6;
+ SKIP_BLANK (cur);
+ if (*cur == ':')
+ {
+ cur++;
+ SKIP_BLANK (cur);
+ }
+ while ((*cur >= '0') && (*cur <= '9'))
+ {
+ base = parse_number (&cur);
+ SKIP_BLANK (cur);
+ if (*cur == '-')
+ {
+ cur++;
+ SKIP_BLANK (cur);
+ last = parse_number (&cur);
+ }
+ else
+ last = base;
+ if (ctx)
+ for (; base <= last; base++)
+ {
+ sprintf (filename, "%d", base);
+ if (tag_unread (ctx, filename))
+ unread++;
+ }
+ else
+ unread += last - base + 1;
+ SKIP_BLANK (cur);
+ }
+ }
+ SKIP_EOL (cur);
+ SKIP_BLANK (cur);
+ }
+ if (unread != 0)
+ {
+ mutt_message ("Folder %s : %d unread", folder, unread);
+ }
+ if (ctx)
+ ctx->new = unread;
+
+ safe_free ((void **) &content);
+
+ /* Cache the data so we only have to do 'stat's in the future */
+ if (!(h = hash_find (cache, folder)))
+ {
+ h = safe_malloc (sizeof (int));
+ if (!h) /* dear god, I hope not... */
+ return -1;
+ hash_insert (cache, folder, h, 1); /* every other time we just adjust the pointer */
+ }
+ *h = unread;
+
+ return unread;
+}
+
+/* Ignore the garbage files. A valid MH message consists of only
+ * digits. Deleted message get moved to a filename with a comma before
+ * it.
+ */
+int mh_valid_message (const char *s)
+{
+ for (; *s; s++)
+ {
+ if (!isdigit (*s))
+ return 0;
+ }
+ return 1;
+}
+
+/* Read a MH/maildir style mailbox.
+ *
+ * args:
+ * ctx [IN/OUT] context for this mailbox
+ * subdir [IN] NULL for MH mailboxes, otherwise the subdir of the
+ * maildir mailbox to read from
+ */
+int mh_read_dir (CONTEXT *ctx, const char *subdir)
+{
+ DIR *dirp;
+ struct dirent *de;
+ char buf[_POSIX_PATH_MAX];
+ int isOld = 0;
+ int count = 0;
+ struct stat st;
+ int has_mh_sequences = 0;
+
+ if (subdir)
+ {
+ snprintf (buf, sizeof (buf), "%s/%s", ctx->path, subdir);
+ isOld = (strcmp ("cur", subdir) == 0) && option (OPTMARKOLD);
+ }
+ else
+ strfcpy (buf, ctx->path, sizeof (buf));
+
+ if (stat (buf, &st) == -1)
+ return (-1);
+
+ if ((dirp = opendir (buf)) == NULL)
+ return (-1);
+
+ if (!subdir || (subdir && strcmp (subdir, "new") == 0))
+ ctx->mtime = st.st_mtime;
+
+ while ((de = readdir (dirp)) != NULL)
+ {
+ if (ctx->magic == M_MH)
+ {
+ if (!mh_valid_message (de->d_name))
+ {
+ if (!strcmp (de->d_name, ".mh_sequences"))
+ has_mh_sequences++;
+ continue;
+ }
+ }
+ else if (*de->d_name == '.')
+ {
+ /* Skip files that begin with a dot. This currently isn't documented
+ * anywhere, but it was a suggestion from the author of QMail on the
+ * mailing list.
+ */
+ continue;
+ }
+
+ mh_parse_message (ctx, subdir, de->d_name, &count, isOld);
+ }
+ {
+ int i;
+#define this_body ctx->hdrs[i]->content
+ ctx->size = 0;
+ for (i = 0; i < ctx->msgcount; i++)
+ ctx->size += this_body->length + this_body->offset -
+ this_body->hdr_offset;
+#undef this_body
+ }
+
+ /*
+ * MH style .
+ */
+ if (has_mh_sequences)
+ {
+ mh_parse_sequences (ctx, NULL);
+ }
+
+ closedir (dirp);
+ return 0;
+}
+
+/* read a maildir style mailbox */
+int maildir_read_dir (CONTEXT * ctx)
+{
+ /* maildir looks sort of like MH, except that there are two subdirectories
+ * of the main folder path from which to read messages
+ */
+ if (mh_read_dir (ctx, "new") == -1 || mh_read_dir (ctx, "cur") == -1)
+ return (-1);
+
+ return 0;
+}
+
+/* Open a new (unique) message in a maildir mailbox. In order to avoid the
+ * need for locks, the filename is generated in such a way that it is unique,
+ * even over NFS: <time>.<pid>_<count>.<hostname>. The _<count> part is
+ * optional, but required for programs like Mutt which do not change PID for
+ * each message that is created in the mailbox (otherwise you could end up
+ * creating only a single file per second).
+ */
+void maildir_create_filename (const char *path, HEADER *hdr, char *msg, char *full)
+{
+ char subdir[_POSIX_PATH_MAX];
+ char suffix[16];
+ struct stat sb;
+
+ /* the maildir format stores the status flags in the filename */
+ suffix[0] = 0;
+
+ if (hdr && (hdr->flagged || hdr->replied || hdr->read))
+ {
+ sprintf (suffix, ":2,%s%s%s",
+ hdr->flagged ? "F" : "",
+ hdr->replied ? "R" : "",
+ hdr->read ? "S" : "");
+ }
+
+ if (hdr && (hdr->read || hdr->old))
+ strfcpy (subdir, "cur", sizeof (subdir));
+ else
+ strfcpy (subdir, "new", sizeof (subdir));
+
+ FOREVER
+ {
+ snprintf (msg, _POSIX_PATH_MAX, "%s/%ld.%d_%d.%s%s",
+ subdir, time (NULL), getpid (), Counter++, Hostname, suffix);
+ snprintf (full, _POSIX_PATH_MAX, "%s/%s", path, msg);
+ if (stat (full, &sb) == -1 && errno == ENOENT) return;
+ }
+}
+
+/* save changes to a message to disk */
+static int mh_sync_message (CONTEXT *ctx, int msgno)
+{
+ HEADER *h = ctx->hdrs[msgno];
+ FILE *f;
+ FILE *d;
+ int rc = -1;
+ char oldpath[_POSIX_PATH_MAX];
+ char newpath[_POSIX_PATH_MAX];
+
+ snprintf (oldpath, sizeof (oldpath), "%s/%s", ctx->path, h->path);
+
+ mutt_mktemp (newpath);
+ if ((f = safe_fopen (newpath, "w")) == NULL)
+ {
+ dprint (1, (debugfile, "mh_sync_message: %s: %s (errno %d).\n",
+ newpath, strerror (errno), errno));
+ return (-1);
+ }
+
+ rc = mutt_copy_message (f, ctx, h, M_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN);
+
+ if (rc == 0)
+ {
+ /* replace the original version of the message with the new one */
+ if ((f = freopen (newpath, "r", f)) != NULL)
+ {
+ unlink (newpath);
+ if ((d = mx_open_file_lock (oldpath, "w")) != NULL)
+ {
+ mutt_copy_stream (f, d);
+ mx_unlock_file (oldpath, fileno (d));
+ fclose (d);
+ }
+ else
+ {
+ fclose (f);
+ return (-1);
+ }
+ fclose (f);
+ }
+ else
+ {
+ mutt_perror (newpath);
+ return (-1);
+ }
+
+ /*
+ * if the status of this message changed, the offsets for the body parts
+ * will be wrong, so free up the memory. This is ok since it will get
+ * parsed again the next time the user tries to view it.
+ */
+ mutt_free_body (&h->content->parts);
+ }
+ else
+ {
+ fclose (f);
+ unlink (newpath);
+ }
+
+ return (rc);
+}
+
+static int maildir_sync_message (CONTEXT *ctx, int msgno)
+{
+ HEADER *h = ctx->hdrs[msgno];
+ char newpath[_POSIX_PATH_MAX];
+ char partpath[_POSIX_PATH_MAX];
+ char fullpath[_POSIX_PATH_MAX];
+ char oldpath[_POSIX_PATH_MAX];
+ char *p;
+
+ if ((p = strchr (h->path, '/')) == NULL)
+ {
+ dprint (1, (debugfile, "maildir_sync_message: %s: unable to find subdir!\n",
+ h->path));
+ return (-1);
+ }
+ p++;
+ strfcpy (newpath, p, sizeof (newpath));
+
+ /* kill the previous flags */
+ if ((p = strchr (newpath, ':')) != NULL) *p = 0;
+
+ if (h->replied || h->read || h->flagged)
+ {
+ strcat (newpath, ":2,");
+ if (h->flagged) strcat (newpath, "F");
+ if (h->replied) strcat (newpath, "R");
+ if (h->read) strcat (newpath, "S");
+ }
+
+ /* decide which subdir this message belongs in */
+ strfcpy (partpath, (h->read || h->old) ? "cur" : "new", sizeof (partpath));
+ strcat (partpath, "/");
+ strcat (partpath, newpath);
+ snprintf (fullpath, sizeof (fullpath), "%s/%s", ctx->path, partpath);
+ snprintf (oldpath, sizeof (oldpath), "%s/%s", ctx->path, h->path);
+
+ if (strcmp (fullpath, oldpath) == 0 && !h->attach_del)
+ {
+ /* message hasn't really changed */
+ return 0;
+ }
+
+ /*
+ * At this point, this should work to actually delete the attachment
+ * without breaking anything else (mh_sync_message doesn't appear to
+ * make any mailbox assumptions except the one file per message, which
+ * is compatible with maildir
+ */
+ if (h->attach_del)
+ {
+ char tmppath[_POSIX_PATH_MAX];
+ char ftpath[_POSIX_PATH_MAX];
+
+ strfcpy (tmppath, "tmp/", sizeof (tmppath));
+ strcat (tmppath, newpath);
+
+ snprintf (ftpath, sizeof (ftpath), "%s/%s", ctx->path, tmppath);
+ dprint (1, (debugfile, "maildir_sync_message: deleting attachment!\n"));
+
+ if (rename (oldpath, ftpath) != 0)
+ {
+ mutt_perror ("rename");
+ return (-1);
+ }
+ safe_free ((void **)&h->path);
+ h->path = safe_strdup (tmppath);
+ dprint (1, (debugfile, "maildir_sync_message: deleting attachment2!\n"));
+ mh_sync_message (ctx, msgno);
+ dprint (1, (debugfile, "maildir_sync_message: deleting attachment3!\n"));
+ if (rename (ftpath, fullpath) != 0)
+ {
+ mutt_perror ("rename");
+ return (-1);
+ }
+ }
+ else
+ {
+ if (rename (oldpath, fullpath) != 0)
+ {
+ mutt_perror ("rename");
+ return (-1);
+ }
+ }
+ safe_free ((void **) &h->path);
+ h->path = safe_strdup (partpath);
+ return (0);
+}
+
+int mh_sync_mailbox (CONTEXT * ctx)
+{
+ char path[_POSIX_PATH_MAX], tmp[_POSIX_PATH_MAX];
+ int i, rc = 0;
+ FILE *mh_sequences;
+
+
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ if (ctx->hdrs[i]->deleted)
+ {
+ snprintf (path, sizeof (path), "%s/%s", ctx->path, ctx->hdrs[i]->path);
+ if (ctx->magic == M_MAILDIR)
+ unlink (path);
+ else
+ {
+ /* MH just moves files out of the way when you delete them */
+ snprintf (tmp, sizeof (tmp), "%s/,%s", ctx->path, ctx->hdrs[i]->path);
+ unlink (tmp);
+ rename (path, tmp);
+ }
+ }
+ else if (ctx->hdrs[i]->changed || ctx->hdrs[i]->attach_del)
+ {
+ if (ctx->magic == M_MAILDIR)
+ maildir_sync_message (ctx, i);
+ else
+ {
+ /* FOO - seems ok to ignore errors, but might want to warn... */
+ mh_sync_message (ctx, i);
+ }
+ }
+ }
+
+ snprintf (path, sizeof (path), "%s/%s", ctx->path, ".mh_sequences");
+ mh_sequences = fopen (path, "w");
+ if (mh_sequences == NULL)
+ {
+ mutt_message ("fopen %s failed", path);
+ }
+ else
+ {
+ fprintf (mh_sequences, "unseen: ");
+ for (i = 0; i < ctx->msgcount; i++)
+ if ((ctx->hdrs[i]->read == 0) && !(ctx->hdrs[i]->deleted))
+ fprintf (mh_sequences, "%s ", ctx->hdrs[i]->path);
+ fprintf (mh_sequences, "\n");
+ fclose (mh_sequences);
+ }
+
+ return (rc);
+}
+
+/* check for new mail */
+int mh_check_mailbox (CONTEXT * ctx, int *index_hint)
+{
+ DIR *dirp;
+ struct dirent *de;
+ char buf[_POSIX_PATH_MAX];
+ struct stat st;
+ LIST *lst = NULL, *tmp = NULL, *prev;
+ int i, lng = 0;
+ int count = 0;
+
+ /* MH users might not like the behavior of this function because it could
+ * take awhile if there are many messages in the mailbox.
+ */
+ if (!option (OPTCHECKNEW))
+ return (0); /* disabled */
+
+ if (ctx->magic == M_MH)
+ strfcpy (buf, ctx->path, sizeof (buf));
+ else
+ snprintf (buf, sizeof (buf), "%s/new", ctx->path);
+
+ if (stat (buf, &st) == -1)
+ return (-1);
+
+ if (st.st_mtime == ctx->mtime)
+ return (0); /* unchanged */
+
+ if ((dirp = opendir (buf)) == NULL)
+ return (-1);
+
+ while ((de = readdir (dirp)) != NULL)
+ {
+ if (ctx->magic == M_MH)
+ {
+ if (!mh_valid_message (de->d_name))
+ continue;
+ }
+ else /* maildir */
+ {
+ if (*de->d_name == '.')
+ continue;
+ }
+
+ if (tmp)
+ {
+ tmp->next = mutt_new_list ();
+ tmp = tmp->next;
+ }
+ else
+ lst = tmp = mutt_new_list ();
+ tmp->data = safe_strdup (de->d_name);
+ }
+ closedir (dirp);
+
+ ctx->mtime = st.st_mtime; /* save the time we scanned at */
+
+ if (!lst)
+ return 0;
+
+ /* if maildir, skip the leading "new/" */
+ lng = (ctx->magic == M_MAILDIR) ? 4 : 0;
+
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ if (ctx->magic == M_MAILDIR && ctx->hdrs[i]->old)
+ continue; /* only look at NEW messages */
+
+ prev = NULL;
+ tmp = lst;
+ while (tmp)
+ {
+ if (strcmp (tmp->data, ctx->hdrs[i]->path + lng) == 0)
+ {
+ if (prev)
+ prev->next = tmp->next;
+ else
+ lst = lst->next;
+ safe_free ((void **) &tmp->data);
+ safe_free ((void **) &tmp);
+ break;
+ }
+ else
+ {
+ prev = tmp;
+ tmp = tmp->next;
+ }
+ }
+ }
+
+ for (tmp = lst; tmp; tmp = lst)
+ {
+ mh_parse_message (ctx, (ctx->magic == M_MH ? NULL : "new"), tmp->data, &count, 0);
+
+ lst = tmp->next;
+ safe_free ((void **) &tmp->data);
+ safe_free ((void **) &tmp);
+ }
+
+ return (1);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Content-Type */
+enum
+{
+ TYPEOTHER,
+ TYPEAUDIO,
+ TYPEAPPLICATION,
+ TYPEIMAGE,
+ TYPEMESSAGE,
+ TYPEMULTIPART,
+ TYPETEXT,
+ TYPEVIDEO
+};
+
+/* Content-Transfer-Encoding */
+enum
+{
+ ENCOTHER,
+ ENC7BIT,
+ ENC8BIT,
+ ENCQUOTEDPRINTABLE,
+ ENCBASE64,
+ ENCBINARY
+};
+
+/* Content-Disposition values */
+enum
+{
+ DISPINLINE,
+ DISPATTACH,
+ DISPFORMDATA
+};
+
+/* MIME encoding/decoding global vars */
+extern int Index_hex[];
+extern int Index_64[];
+extern char Base64_chars[];
+
+#define hexval(c) Index_hex[(int)(c)]
+#define base64val(c) Index_64[(int)(c)]
+
+#define is_multipart(x) \
+ ((x)->type == TYPEMULTIPART \
+ || ((x)->type == TYPEMESSAGE && (!strcasecmp((x)->subtype, "rfc822") \
+ || !strcasecmp((x)->subtype, "news"))))
+
+extern const char *BodyTypes[];
+extern const char *BodyEncodings[];
+
+#define TYPE(X) BodyTypes[(X)]
+#define ENCODING(X) BodyEncodings[(X)]
--- /dev/null
+#
+# sample mime.types
+#
+application/andrew-inset ez
+application/excel xls
+application/octet-stream bin
+application/oda oda
+application/pdf pdf
+application/pgp pgp
+application/postscript ps PS eps
+application/rtf rtf
+application/x-arj-compressed arj
+application/x-bcpio bcpio
+application/x-chess-pgn pgn
+application/x-cpio cpio
+application/x-csh csh
+application/x-debian-package deb
+application/x-msdos-program com exe bat
+application/x-dvi dvi
+application/x-gtar gtar
+application/x-gunzip gz
+application/x-hdf hdf
+application/x-latex latex
+application/x-mif mif
+application/x-netcdf cdf nc
+application/x-perl pl pm
+application/x-rar-compressed rar
+application/x-sh sh
+application/x-shar shar
+application/x-sv4cpio sv4cpio
+application/x-sv4crc sv4crc
+application/x-tar tar
+application/x-tar-gz tgz tar.gz
+application/x-tcl tcl
+application/x-tex tex
+application/x-texinfo texi texinfo
+application/x-troff t tr roff
+application/x-troff-man man
+application/x-troff-me me
+application/x-troff-ms ms
+application/x-ustar ustar
+application/x-wais-source src
+application/x-zip-compressed zip
+
+audio/basic snd
+audio/midi mid midi
+audio/ulaw au
+audio/x-aiff aif aifc aiff
+audio/x-wav wav
+
+image/gif gif
+image/ief ief
+image/jpeg jpe jpeg jpg
+image/png png
+image/tiff tif tiff
+image/x-cmu-raster ras
+image/x-portable-anymap pnm
+image/x-portable-bitmap pbm
+image/x-portable-graymap pgm
+image/x-portable-pixmap ppm
+image/x-rgb rgb
+image/x-xbitmap xbm
+image/x-xpixmap xpm
+image/x-xwindowdump xwd
+
+text/html html htm
+text/plain asc txt
+text/richtext rtx
+text/tab-separated-values tsv
+text/x-setext etx
+
+video/dl dl
+video/fli fli
+video/gl gl
+video/mpeg mp2 mpe mpeg mpg
+video/quicktime mov qt
+video/x-msvideo avi
+video/x-sgi-movie movie
+
+x-world/x-vrml vrm vrml wrl
--- /dev/null
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+# $Id$
+
+errstatus=0
+
+for file
+do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d
+ do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp" 1>&2
+
+ mkdir "$pathcomp" || lasterr=$?
+
+ if test ! -d "$pathcomp"; then
+ errstatus=$lasterr
+ fi
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <limits.h>
+#include <stdarg.h>
+
+#include "rfc822.h"
+#include "hash.h"
+
+/* nifty trick I stole from ELM 2.5alpha. */
+#ifdef MAIN_C
+#define WHERE
+#define INITVAL(x) = x
+#else
+#define WHERE extern
+#define INITVAL(x)
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+#define HUGE_STRING 5120
+#define LONG_STRING 1024
+#define STRING 256
+#define SHORT_STRING 128
+
+/* flags for mutt_copy_header() */
+#define CH_UPDATE 1 /* update the status and x-status fields? */
+#define CH_WEED (1<<1) /* weed the headers? */
+#define CH_DECODE (1<<2) /* do RFC1522 decoding? */
+#define CH_XMIT (1<<3) /* transmitting this message? */
+#define CH_FROM (1<<4) /* retain the "From " message separator? */
+#define CH_PREFIX (1<<5) /* use Prefix string? */
+#define CH_NOSTATUS (1<<6) /* supress the status and x-status fields */
+#define CH_REORDER (1<<7) /* Re-order output of headers */
+#define CH_NONEWLINE (1<<8) /* don't output terminating newline */
+#define CH_MIME (1<<9) /* ignore MIME fields */
+#define CH_UPDATE_LEN (1<<10) /* update Lines: and Content-Length: */
+#define CH_TXTPLAIN (1<<11) /* generate text/plain MIME headers */
+
+/* flags for mutt_enter_string() */
+#define M_ALIAS 1 /* do alias "completion" by calling up the alias-menu */
+#define M_FILE (1<<1) /* do file completion */
+#define M_EFILE (1<<2) /* do file completion, plus incoming folders */
+#define M_CMD (1<<3) /* do completion on previous word */
+#define M_PASS (1<<4) /* password mode (no echo) */
+#define M_CLEAR (1<<5) /* clear input if printable character is pressed */
+
+/* flags for mutt_get_token() */
+#define M_TOKEN_EQUAL 1 /* treat '=' as a special */
+#define M_TOKEN_CONDENSE (1<<1) /* ^(char) to control chars (macros) */
+#define M_TOKEN_SPACE (1<<2) /* don't treat whitespace as a term */
+#define M_TOKEN_QUOTE (1<<3) /* don't interpret quotes */
+#define M_TOKEN_PATTERN (1<<4) /* !)|~ are terms (for patterns) */
+#define M_TOKEN_COMMENT (1<<5) /* don't reap comments */
+#define M_TOKEN_SEMICOLON (1<<6) /* don't treat ; as special */
+
+typedef struct
+{
+ char *data; /* pointer to data */
+ char *dptr; /* current read/write position */
+ size_t dsize; /* length of data */
+ int destroy; /* destroy `data' when done? */
+} BUFFER;
+
+/* flags for _mutt_system() */
+#define M_DETACH_PROCESS 1 /* detach subprocess from group */
+
+/* flags for mutt_FormatString() */
+typedef enum
+{
+ M_FORMAT_FORCESUBJ = (1<<0), /* print the subject even if unchanged */
+ M_FORMAT_TREE = (1<<1), /* draw the thread tree */
+ M_FORMAT_MAKEPRINT = (1<<2), /* make sure that all chars are printable */
+ M_FORMAT_OPTIONAL = (1<<3)
+} format_flag;
+
+/* types for mutt_add_hook() */
+#define M_FOLDERHOOK 1
+#define M_MBOXHOOK (1<<1)
+#define M_SENDHOOK (1<<2)
+#define M_FCCHOOK (1<<3)
+#define M_SAVEHOOK (1<<4)
+
+/* tree characters for linearize_tree and print_enriched_string */
+#define M_TREE_LLCORNER 1
+#define M_TREE_ULCORNER 2
+#define M_TREE_LTEE 3
+#define M_TREE_HLINE 4
+#define M_TREE_VLINE 5
+#define M_TREE_SPACE 6
+#define M_TREE_RARROW 7
+#define M_TREE_STAR 8
+#define M_TREE_HIDDEN 9
+#define M_TREE_MAX 10
+
+enum
+{
+ /* modes for mutt_view_attachment() */
+ M_REGULAR = 1,
+ M_MAILCAP,
+ M_AS_TEXT,
+
+ /* action codes used by mutt_set_flag() and mutt_pattern_function() */
+ M_ALL,
+ M_NONE,
+ M_NEW,
+ M_OLD,
+ M_REPLIED,
+ M_READ,
+ M_UNREAD,
+ M_DELETE,
+ M_UNDELETE,
+ M_DELETED,
+ M_FLAG,
+ M_TAG,
+ M_UNTAG,
+ M_LIMIT,
+ M_EXPIRED,
+ M_SUPERSEDED,
+
+ /* actions for mutt_pattern_comp/mutt_pattern_exec */
+ M_AND,
+ M_OR,
+ M_TO,
+ M_CC,
+ M_SUBJECT,
+ M_FROM,
+ M_DATE,
+ M_DATE_RECEIVED,
+ M_ID,
+ M_BODY,
+ M_HEADER,
+ M_WHOLE_MSG,
+ M_SENDER,
+ M_MESSAGE,
+ M_SCORE,
+ M_SIZE,
+ M_REFERENCE,
+ M_RECIPIENT,
+ M_LIST,
+ M_PERSONAL_RECIP,
+ M_PERSONAL_FROM,
+ M_ADDRESS,
+
+ /* Options for Mailcap lookup */
+ M_EDIT,
+ M_COMPOSE,
+ M_PRINT,
+ M_AUTOVIEW,
+
+ /* Options for mutt_save_attachment */
+ M_SAVE_APPEND
+};
+
+/* possible arguments to set_quadoption() */
+enum
+{
+ M_NO,
+ M_YES,
+ M_ASKNO,
+ M_ASKYES
+};
+
+/* quad-option vars */
+enum
+{
+
+#ifdef _PGPPATH
+ OPT_VERIFYSIG, /* verify PGP signatures */
+#endif
+
+ OPT_USEMAILCAP,
+ OPT_PRINT,
+ OPT_INCLUDE,
+ OPT_DELETE,
+ OPT_MIMEFWD,
+ OPT_MOVE,
+ OPT_COPY,
+ OPT_POSTPONE,
+ OPT_QUIT,
+ OPT_REPLYTO,
+ OPT_ABORT,
+ OPT_RECALL,
+ OPT_SUBJECT
+};
+
+/* flags to ci_send_message() */
+#define SENDREPLY (1<<0)
+#define SENDGROUPREPLY (1<<1)
+#define SENDLISTREPLY (1<<2)
+#define SENDFORWARD (1<<3)
+#define SENDPOSTPONED (1<<4)
+#define SENDBATCH (1<<5)
+#define SENDMAILX (1<<6)
+#define SENDKEY (1<<7)
+
+/* boolean vars */
+enum
+{
+ OPTPROMPTAFTER,
+ OPTSTATUSONTOP,
+ OPTALLOW8BIT,
+ OPTASCIICHARS,
+ OPTMETOO,
+ OPTEDITHDRS,
+ OPTARROWCURSOR,
+ OPTASKCC,
+ OPTHEADER,
+ OPTREVALIAS,
+ OPTREVNAME,
+ OPTFORCENAME,
+ OPTSAVEEMPTY,
+ OPTPAGERSTOP,
+ OPTSIGDASHES,
+ OPTASKBCC,
+ OPTAUTOEDIT,
+#ifdef _PGPPATH
+ OPTPGPAUTOSIGN,
+ OPTPGPAUTOENCRYPT,
+ OPTPGPLONGIDS,
+ OPTPGPREPLYENCRYPT,
+ OPTPGPREPLYSIGN,
+ OPTPGPENCRYPTSELF,
+ OPTPGPSTRICTENC,
+#endif
+ OPTMARKOLD,
+ OPTCONFIRMCREATE,
+ OPTCONFIRMAPPEND,
+ OPTPOPDELETE,
+ OPTSAVENAME,
+ OPTTHOROUGHSRC,
+ OPTTILDE,
+ OPTMARKERS,
+ OPTFCCATTACH,
+ OPTPIPESPLIT,
+ OPTPIPEDECODE,
+ OPTREADONLY,
+ OPTRESOLVE,
+ OPTSTRICTTHREADS,
+ OPTAUTOTAG,
+ OPTBEEP,
+ OPTHELP,
+ OPTHDRS,
+ OPTWEED,
+ OPTWRAP,
+ OPTCHECKNEW,
+ OPTFASTREPLY,
+ OPTWAITKEY,
+ OPTWRAPSEARCH,
+ OPTIGNORELISTREPLYTO,
+ OPTSAVEADDRESS,
+ OPTSUSPEND,
+ OPTSORTRE,
+ OPTUSEDOMAIN,
+ OPTUSEFROM,
+ OPTUSE8BITMIME,
+ OPTFORWDECODE,
+ OPTMIMEFORWDECODE,
+ OPTFORWQUOTE,
+ OPTBEEPNEW,
+ OPTFOLLOWUPTO,
+ OPTMENUSCROLL, /* scroll menu instead of implicit next-page */
+ OPTMETAKEY, /* interpret ALT-x as ESC-x */
+ OPTAUXSORT, /* (pseudo) using auxillary sort function */
+ OPTFORCEREFRESH, /* (pseudo) refresh even during macros */
+ OPTLOCALES, /* (pseudo) set if user has valid locale definition */
+ OPTNOCURSES, /* (pseudo) when sending in batch mode */
+ OPTNEEDREDRAW, /* (pseudo) to notify caller of a submenu */
+ OPTSEARCHREVERSE, /* (pseudo) used by ci_search_command */
+ OPTMSGERR, /* (pseudo) used by mutt_error/mutt_message */
+ OPTSEARCHINVALID, /* (pseudo) used to invalidate the search pat */
+ OPTSIGNALSBLOCKED, /* (pseudo) using by mutt_block_signals () */
+ OPTNEEDRESORT, /* (pseudo) used to force a re-sort */
+ OPTVIEWATTACH, /* (pseudo) signals that we are viewing attachments */
+ OPTFORCEREDRAWINDEX, /* (pseudo) used to force a redraw in the main index */
+ OPTFORCEREDRAWPAGER, /* (pseudo) used to force a redraw in the pager */
+ OPTSORTSUBTHREADS, /* (pseudo) used when $sort_aux changes */
+ OPTNEEDRESCORE, /* (pseudo) set when the `score' command is used */
+
+#ifdef _PGPPATH
+ OPTPGPCHECKTRUST, /* (pseudo) used by pgp_select_key () */
+ OPTDONTHANDLEPGPKEYS, /* (pseudo) used to extract PGP keys */
+#endif
+
+
+
+
+
+ OPTMAX
+};
+
+#define mutt_bit_alloc(n) calloc ((n + 7) / 8, sizeof (char))
+#define mutt_bit_set(v,n) v[n/8] |= (1 << (n % 8))
+#define mutt_bit_unset(v,n) v[n/8] &= ~(1 << (n % 8))
+#define mutt_bit_toggle(v,n) v[n/8] ^= (1 << (n % 8))
+#define mutt_bit_isset(v,n) (v[n/8] & (1 << (n % 8)))
+
+#define set_option(x) mutt_bit_set(Options,x)
+#define unset_option(x) mutt_bit_unset(Options,x)
+#define toggle_option(x) mutt_bit_toggle(Options,x)
+#define option(x) mutt_bit_isset(Options,x)
+
+/* Bit fields for ``Signals'' */
+#define S_INTERRUPT (1<<1)
+#define S_SIGWINCH (1<<2)
+#define S_ALARM (1<<3)
+
+typedef struct list_t
+{
+ char *data;
+ struct list_t *next;
+} LIST;
+
+#define mutt_new_list() safe_calloc (1, sizeof (LIST))
+void mutt_add_to_list (LIST **, BUFFER *);
+void mutt_free_list (LIST **);
+int mutt_matches_ignore (const char *, LIST *);
+
+/* add an element to a list */
+LIST *mutt_add_list (LIST *, const char *);
+
+void mutt_init (int, LIST *);
+
+typedef struct alias
+{
+ char *name;
+ ADDRESS *addr;
+ struct alias *next;
+ short tagged;
+ short num;
+} ALIAS;
+
+typedef struct envelope
+{
+ ADDRESS *return_path;
+ ADDRESS *from;
+ ADDRESS *to;
+ ADDRESS *cc;
+ ADDRESS *bcc;
+ ADDRESS *sender;
+ ADDRESS *reply_to;
+ ADDRESS *mail_followup_to;
+ char *subject;
+ char *real_subj; /* offset of the real subject */
+ char *message_id;
+ char *supersedes;
+ LIST *references; /* message references (in reverse order) */
+ LIST *userhdrs; /* user defined headers */
+} ENVELOPE;
+
+typedef struct parameter
+{
+ char *attribute;
+ char *value;
+ struct parameter *next;
+} PARAMETER;
+
+/* Information that helps in determing the Content-* of an attachment */
+typedef struct content
+{
+ long hibin; /* 8-bit characters */
+ long lobin; /* unprintable 7-bit chars (eg., control chars) */
+ long ascii; /* number of ascii chars */
+ long linemax; /* length of the longest line in the file */
+ unsigned int space : 1; /* whitespace at the end of lines? */
+ unsigned int binary : 1; /* long lines, or CR not in CRLF pair */
+ unsigned int from : 1; /* has a line beginning with "From "? */
+ unsigned int dot : 1; /* has a line consisting of a single dot? */
+} CONTENT;
+
+typedef struct body
+{
+ char *subtype; /* content-type subtype */
+ PARAMETER *parameter; /* parameters of the content-type */
+ char *description; /* content-description */
+ char *form_name; /* Content-Disposition form-data name param */
+ long hdr_offset; /* offset in stream where the headers begin.
+ * this info is used when invoking metamail,
+ * where we need to send the headers of the
+ * attachment
+ */
+ long offset; /* offset where the actual data begins */
+ long length; /* length (in bytes) of attachment */
+ char *filename; /* when sending a message, this is the file
+ * to which this structure refers
+ */
+ char *d_filename; /* filename to be used for the
+ * content-disposition header.
+ * If NULL, filename is used
+ * instead.
+ */
+ CONTENT *content; /* structure used to store detailed info about
+ * the content of the attachment. this is used
+ * to determine what content-transfer-encoding
+ * is required when sending mail.
+ */
+ struct body *next; /* next attachment in the list */
+ struct body *parts; /* parts of a multipart or message/rfc822 */
+ struct header *hdr; /* header information for message/rfc822 */
+
+ unsigned int type : 3; /* content-type primary type */
+ unsigned int encoding : 3; /* content-transfer-encoding */
+ unsigned int disposition : 2; /* content-disposition */
+ unsigned int use_disp : 1; /* Content-Disposition field printed? */
+ unsigned int unlink : 1; /* flag to indicate the the file named by
+ * "filename" should be unlink()ed before
+ * free()ing this structure
+ */
+ unsigned int tagged : 1;
+ unsigned int deleted : 1; /* attachment marked for deletion */
+
+} BODY;
+
+typedef struct header
+{
+ unsigned int mime : 1; /* has a Mime-Version header? */
+ unsigned int mailcap : 1; /* requires mailcap to display? */
+ unsigned int flagged : 1; /* marked important? */
+ unsigned int tagged : 1;
+ unsigned int deleted : 1;
+ unsigned int changed : 1;
+ unsigned int attach_del : 1; /* has an attachment marked for deletion */
+ unsigned int old : 1;
+ unsigned int read : 1;
+ unsigned int expired : 1; /* already expired? */
+ unsigned int superseded : 1; /* got superseded? */
+
+
+
+
+#ifdef _PGPPATH
+ unsigned int pgp : 3;
+#endif
+
+
+
+
+
+
+
+
+
+ unsigned int replied : 1;
+ unsigned int subject_changed : 1; /* used for threading */
+ unsigned int display_subject : 1; /* used for threading */
+ unsigned int fake_thread : 1; /* no ref matched, but subject did */
+ unsigned int threaded : 1; /* message has been threaded */
+
+ /* timezone of the sender of this message */
+ unsigned int zhours : 5;
+ unsigned int zminutes : 6;
+ unsigned int zoccident : 1;
+
+ /* bits used for caching when searching */
+ unsigned int searched : 1;
+ unsigned int matched : 1;
+
+ int pair; /* color-pair to use when displaying in the index */
+
+ time_t date_sent; /* time when the message was sent (UTC) */
+ time_t received; /* time when the message was placed in the mailbox */
+ long offset; /* where in the stream does this message begin? */
+ int lines; /* how many lines in the body of this message? */
+ int index; /* the absolute (unsorted) message number */
+ int msgno; /* number displayed to the user */
+ int virtual; /* virtual message number */
+ int score;
+ ENVELOPE *env; /* envelope information */
+ BODY *content; /* list of MIME parts */
+ char *path;
+
+ /* the following are used for threading support */
+ struct header *parent;
+ struct header *child; /* decendants of this message */
+ struct header *next; /* next message in this thread */
+ struct header *prev; /* previous message in thread */
+ struct header *last_sort; /* last message in subthread, for secondary SORT_LAST */
+ char *tree; /* character string to print thread tree */
+
+} HEADER;
+
+#include "mutt_regex.h"
+
+/* flag to mutt_pattern_comp() */
+#define M_FULL_MSG 1 /* enable body and header matching */
+
+typedef enum {
+ M_MATCH_FULL_ADDRESS = 1
+} pattern_exec_flag;
+
+typedef struct pattern_t
+{
+ short op;
+ short not;
+ int min;
+ int max;
+ struct pattern_t *next;
+ struct pattern_t *child; /* arguments to logical op */
+ regex_t *rx;
+} pattern_t;
+
+typedef struct
+{
+ char *path;
+ FILE *fp;
+ time_t mtime;
+ off_t size;
+ off_t vsize;
+ char *pattern; /* limit pattern string */
+ pattern_t *limit_pattern; /* compiled limit pattern */
+ HEADER **hdrs;
+ HEADER *tree; /* top of thread tree */
+ HASH *id_hash; /* hash table by msg id */
+ HASH *subj_hash; /* hash table by subject */
+ int *v2r; /* mapping from virtual to real msgno */
+ int hdrmax; /* number of pointers in hdrs */
+ int msgcount; /* number of messages in the mailbox */
+ int vcount; /* the number of virtual messages */
+ int tagged; /* how many messages are tagged? */
+ int new; /* how many new messages? */
+ int unread; /* how many unread messages? */
+ int deleted; /* how many deleted messages */
+ int flagged; /* how many flagged messages */
+ int msgnotreadyet; /* which msg "new" in pager, -1 if none */
+#ifdef USE_IMAP
+ void *data; /* driver specific data */
+ int fd;
+#endif /* USE_IMAP */
+
+ short magic; /* mailbox type */
+
+ unsigned int locked : 1; /* is the mailbox locked? */
+ unsigned int changed : 1; /* mailbox has been modified */
+ unsigned int readonly : 1; /* don't allow changes to the mailbox */
+ unsigned int dontwrite : 1; /* dont write the mailbox on close */
+ unsigned int append : 1; /* mailbox is opened in append mode */
+ unsigned int setgid : 1;
+ unsigned int quiet : 1; /* inhibit status messages? */
+ unsigned int revsort : 1; /* mailbox sorted in reverse? */
+} CONTEXT;
+
+typedef struct attachptr
+{
+ BODY *content;
+ char *tree;
+ int level;
+} ATTACHPTR;
+
+typedef struct
+{
+ FILE *fpin;
+ FILE *fpout;
+ char *prefix;
+ int flags;
+} STATE;
+
+/* flags for the STATE struct */
+#define M_DISPLAY (1<<0) /* output is displayed to the user */
+
+
+
+#ifdef _PGPPATH
+#define M_VERIFY (1<<1) /* perform signature verification */
+#endif
+
+
+
+#define state_puts(x,y) fputs(x,(y)->fpout)
+#define state_putc(x,y) fputc(x,(y)->fpout)
+
+#include "protos.h"
+#include "globals.h"
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef USE_SLANG_CURSES
+
+#ifndef unix /* this symbol is not defined by the hp-ux compiler (sigh) */
+#define unix
+#endif /* unix */
+
+#include "slcurses.h"
+
+#define KEY_DC SL_KEY_DELETE
+#define KEY_IC SL_KEY_IC
+
+/*
+ * ncurses and SLang seem to send different characters when the Enter key is
+ * pressed, so define some macros to properly detect the Enter key.
+ */
+#define M_ENTER_C '\r'
+#define M_ENTER_S "\r"
+
+#else
+
+#ifdef HAVE_NCURSES_H
+#include <ncurses.h>
+#else
+#include <curses.h>
+#endif
+
+#define M_ENTER_C '\n'
+#define M_ENTER_S "\n"
+
+#endif /* USE_SLANG_CURSES */
+
+/* AIX defines ``lines'' in <term.h>, but it's used as a var name in
+ * various places in Mutt
+ */
+#ifdef lines
+#undef lines
+#endif /* lines */
+
+#define CLEARLINE(x) move(x,0), clrtoeol()
+#define CENTERLINE(x,y) move(y, (COLS-strlen(x))/2), addstr(x)
+#define BEEP if (option (OPTBEEP)) beep
+
+#if ! (defined(USE_SLANG_CURSES) || defined(HAVE_CURS_SET))
+#define curs_set(x)
+#endif
+
+#if !defined(USE_SLANG_CURSES) && defined(HAVE_BKGDSET)
+#define BKGDSET(x) bkgdset (ColorDefs[x] | ' ')
+#else
+#define BKGDSET(x)
+#endif
+
+#if (defined(USE_SLANG_CURSES) || defined(HAVE_CURS_SET))
+void mutt_curs_set (int);
+#else
+#define mutt_curs_set(x)
+#endif
+#define PAGELEN (LINES-3)
+
+#define ctrl(c) ((c)-'@')
+
+#define CI_is_return(c) ((c) == '\r' || (c) == '\n' || (c) == KEY_ENTER)
+
+int mutt_getch (void);
+
+void mutt_endwin (const char *);
+void mutt_flushinp (void);
+void mutt_refresh (void);
+void mutt_resize_screen (void);
+void mutt_ungetch (int);
+
+/* ----------------------------------------------------------------------------
+ * Support for color
+ */
+
+enum
+{
+ MT_COLOR_HDEFAULT = 0,
+ MT_COLOR_QUOTED,
+ MT_COLOR_SIGNATURE,
+ MT_COLOR_INDICATOR,
+ MT_COLOR_STATUS,
+ MT_COLOR_TREE,
+ MT_COLOR_NORMAL,
+ MT_COLOR_ERROR,
+ MT_COLOR_TILDE,
+ MT_COLOR_MARKERS,
+ MT_COLOR_BODY,
+ MT_COLOR_HEADER,
+ MT_COLOR_MESSAGE,
+ MT_COLOR_ATTACHMENT,
+ MT_COLOR_SEARCH,
+ MT_COLOR_BOLD,
+ MT_COLOR_UNDERLINE,
+ MT_COLOR_INDEX,
+ MT_COLOR_MAX
+};
+
+typedef struct color_line
+{
+ regex_t rx;
+ char *pattern;
+ pattern_t *color_pattern; /* compiled pattern to speed up index color
+ calculation */
+ short fg;
+ short bg;
+ int pair;
+ struct color_line *next;
+} COLOR_LINE;
+
+extern int *ColorQuote;
+extern int ColorQuoteUsed;
+extern int ColorDefs[];
+extern COLOR_LINE *ColorHdrList;
+extern COLOR_LINE *ColorBodyList;
+extern COLOR_LINE *ColorIndexList;
+
+void ci_init_color (void);
+void ci_start_color (void);
+
+#define SETCOLOR(X) attrset(ColorDefs[X])
+
+#define MAYBE_REDRAW(x) if (option (OPTNEEDREDRAW)) { unset_option (OPTNEEDREDRAW); x = REDRAW_FULL; }
+
+/* ----------------------------------------------------------------------------
+ * These are here to avoid compiler warnings with -Wall under SunOS 4.1.x
+ */
+
+#if !defined(STDC_HEADERS) && !defined(NCURSES_VERSION) && !defined(USE_SLANG_CURSES)
+extern int endwin();
+extern int printw();
+extern int beep();
+extern int isendwin();
+extern int w32addch();
+extern int keypad();
+extern int wclrtobot();
+extern int mvprintw();
+extern int getcurx();
+extern int getcury();
+extern int noecho();
+extern int wdelch();
+extern int wrefresh();
+extern int wmove();
+extern int wclear();
+extern int waddstr();
+extern int wclrtoeol();
+#endif
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file is named mutt_menu.h so it doesn't collide with ncurses menu.h
+ */
+
+#include "keymap.h"
+#include "mutt_regex.h"
+
+#define REDRAW_INDEX (1)
+#define REDRAW_MOTION (1<<1)
+#define REDRAW_MOTION_RESYNCH (1<<2)
+#define REDRAW_CURRENT (1<<3)
+#define REDRAW_STATUS (1<<4)
+#define REDRAW_FULL (1<<5)
+#define REDRAW_BODY (1<<6)
+
+typedef struct menu_t
+{
+ char *title; /* the title of this menu */
+ char *help; /* quickref for the current menu */
+ void *data; /* extra data for the current menu */
+ int current; /* current entry */
+ int max; /* the number of entries in the menu */
+ int redraw; /* when to redraw the screen */
+ int menu; /* menu definition for keymap entries. */
+ int offset; /* which screen row to start the index */
+ int pagelen; /* number of entries per screen */
+ int tagprefix;
+
+ /* callback to generate an index line for the requested element */
+ void (*make_entry) (char *, size_t, struct menu_t *, int);
+
+ /* how to search the menu */
+ int (*search) (struct menu_t *, regex_t *re, int n);
+
+ int (*tag) (struct menu_t *, int i);
+
+ /* color pair to be used for the requested element
+ * (default function returns ColorDefs[MT_COLOR_NORMAL])
+ */
+ int (*color) (int i);
+
+ /* the following are used only by mutt_menuLoop() */
+ int top; /* entry that is the top of the current page */
+ int oldcurrent; /* for driver use only. */
+ char *searchBuf; /* last search pattern */
+ int searchDir; /* direction of search */
+ int tagged; /* number of tagged entries */
+} MUTTMENU;
+
+void menu_jump (MUTTMENU *);
+void menu_redraw_full (MUTTMENU *);
+void menu_redraw_index (MUTTMENU *);
+void menu_redraw_motion (MUTTMENU *);
+void menu_redraw_current (MUTTMENU *);
+void menu_first_entry (MUTTMENU *);
+void menu_last_entry (MUTTMENU *);
+void menu_top_page (MUTTMENU *);
+void menu_bottom_page (MUTTMENU *);
+void menu_middle_page (MUTTMENU *);
+void menu_next_page (MUTTMENU *);
+void menu_prev_page (MUTTMENU *);
+void menu_next_line (MUTTMENU *);
+void menu_prev_line (MUTTMENU *);
+void menu_half_up (MUTTMENU *);
+void menu_half_down (MUTTMENU *);
+void menu_current_top (MUTTMENU *);
+void menu_current_middle (MUTTMENU *);
+void menu_current_bottom (MUTTMENU *);
+void menu_check_recenter (MUTTMENU *);
+void menu_status_line (char *, size_t, MUTTMENU *, const char *);
+
+MUTTMENU *mutt_new_menu (void);
+void mutt_menuDestroy (MUTTMENU **);
+int mutt_menuLoop (MUTTMENU *);
+
+/* used in both the index and pager index to make an entry. */
+void index_make_entry (char *, size_t, struct menu_t *, int);
+int index_color (int);
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * A (more) generic interface to regular expression matching
+ */
+
+#ifndef MUTT_REGEX_H
+#define MUTT_REGEX_H
+
+#ifdef HAVE_REGCOMP
+#include <regex.h>
+#else
+#include "rxposix.h"
+#endif
+
+/* this is a non-standard option supported by Solaris 2.5.x which allows
+ * patterns of the form \<...\>
+ */
+#ifndef REG_WORDS
+#define REG_WORDS 0
+#endif
+
+#define REGCOMP(X,Y,Z) regcomp(X, Y, REG_WORDS|REG_EXTENDED|(Z))
+#define REGEXEC(X,Y) regexec(&X, Y, (size_t)0, (regmatch_t *)0, (int)0)
+
+typedef struct
+{
+ char *pattern; /* printable version */
+ regex_t *rx; /* compiled expression */
+ int not; /* do not match */
+} REGEXP;
+
+WHERE REGEXP Alternates;
+WHERE REGEXP Mask;
+WHERE REGEXP QuoteRegexp;
+WHERE REGEXP ReplyRegexp;
+
+#endif /* MUTT_REGEX_H */
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mx.h"
+#include "rfc2047.h"
+#include "sort.h"
+#include "mailbox.h"
+#include "copy.h"
+#include "keymap.h"
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+
+#ifdef BUFFY_SIZE
+#include "buffy.h"
+#endif
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifndef BUFFY_SIZE
+#include <utime.h>
+#endif
+
+/* HP-UX and ConvexOS don't have this macro */
+#ifndef S_ISLNK
+#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK ? 1 : 0)
+#endif
+
+#define mutt_is_spool(s) (strcmp (Spoolfile, s) == 0)
+
+#define MAXLOCKATTEMPT 5
+
+#ifdef USE_DOTLOCK
+/* parameters:
+ * path - file to lock
+ * retry - should retry if unable to lock?
+ */
+static int dotlock_file (const char *path, int retry)
+{
+ const char *pathptr = path;
+ char lockfile[_POSIX_PATH_MAX];
+ char nfslockfile[_POSIX_PATH_MAX];
+ char realpath[_POSIX_PATH_MAX];
+ struct stat sb;
+ size_t prev_size = 0;
+ int count = 0;
+ int attempt = 0;
+ int fd;
+
+ /* if the file is a symlink, find the real file to which it refers */
+ FOREVER
+ {
+ dprint(2,(debugfile,"dotlock_file(): locking %s\n", pathptr));
+
+ if (lstat (pathptr, &sb) != 0)
+ {
+ mutt_perror (pathptr);
+ return (-1);
+ }
+
+ if (S_ISLNK (sb.st_mode))
+ {
+ char linkfile[_POSIX_PATH_MAX];
+ char linkpath[_POSIX_PATH_MAX];
+
+ if ((count = readlink (pathptr, linkfile, sizeof (linkfile))) == -1)
+ {
+ mutt_perror (path);
+ return (-1);
+ }
+ linkfile[count] = 0; /* readlink() does not NUL terminate the string! */
+ mutt_expand_link (linkpath, pathptr, linkfile);
+ strfcpy (realpath, linkpath, sizeof (realpath));
+ pathptr = realpath;
+ }
+ else
+ break;
+ }
+
+ snprintf (nfslockfile, sizeof (nfslockfile), "%s.%s.%d", pathptr, Hostname, (int) getpid ());
+ snprintf (lockfile, sizeof (lockfile), "%s.lock", pathptr);
+ unlink (nfslockfile);
+
+ while ((fd = open (nfslockfile, O_WRONLY | O_EXCL | O_CREAT, 0)) < 0)
+ if (errno != EAGAIN)
+ {
+ mutt_perror ("cannot open NFS lock file!");
+ return (-1);
+ }
+
+ close (fd);
+
+ count = 0;
+ FOREVER
+ {
+ link (nfslockfile, lockfile);
+ if (stat (nfslockfile, &sb) != 0)
+ {
+ mutt_perror ("stat");
+ return (-1);
+ }
+
+ if (sb.st_nlink == 2)
+ break;
+
+ if (stat (path, &sb) != 0)
+ sb.st_size = 0;
+
+ if (count == 0)
+ prev_size = sb.st_size;
+
+ /* only try to remove the lock if the file is not changing */
+ if (prev_size == sb.st_size && ++count >= retry ? MAXLOCKATTEMPT : 0)
+ {
+ if (retry && mutt_yesorno ("Lock count exceeded, remove lock?", 1) == 1)
+ {
+ unlink (lockfile);
+ count = 0;
+ attempt = 0;
+ continue;
+ }
+ else
+ return (-1);
+ }
+
+ prev_size = sb.st_size;
+
+ mutt_message ("Waiting for lock attempt #%d...", ++attempt);
+ sleep (1);
+ }
+
+ unlink (nfslockfile);
+
+ return 0;
+}
+
+static int undotlock_file (const char *path)
+{
+ const char *pathptr = path;
+ char lockfile[_POSIX_PATH_MAX];
+ char realpath[_POSIX_PATH_MAX];
+ struct stat sb;
+ int n;
+
+ FOREVER
+ {
+ dprint (2,(debugfile,"undotlock: unlocking %s\n",path));
+
+ if (lstat (pathptr, &sb) != 0)
+ {
+ mutt_perror (pathptr);
+ return (-1);
+ }
+
+ if (S_ISLNK (sb.st_mode))
+ {
+ char linkfile[_POSIX_PATH_MAX];
+ char linkpath[_POSIX_PATH_MAX];
+
+ if ((n = readlink (pathptr, linkfile, sizeof (linkfile))) == -1)
+ {
+ mutt_perror (pathptr);
+ return (-1);
+ }
+ linkfile[n] = 0; /* readlink() does not NUL terminate the string! */
+ mutt_expand_link (linkpath, pathptr, linkfile);
+ strfcpy (realpath, linkpath, sizeof (realpath));
+ pathptr = realpath;
+ continue;
+ }
+ else
+ break;
+ }
+
+ snprintf (lockfile, sizeof (lockfile), "%s.lock", pathptr);
+ unlink (lockfile);
+ return 0;
+}
+#endif /* USE_DOTLOCK */
+
+/* Args:
+ * excl if excl != 0, request an exclusive lock
+ * dot if dot != 0, try to dotlock the file
+ * timeout should retry locking?
+ */
+int mx_lock_file (const char *path, int fd, int excl, int dot, int timeout)
+{
+#if defined (USE_FCNTL) || defined (USE_FLOCK)
+ int count;
+ int attempt;
+ struct stat prev_sb;
+#endif
+ int r = 0;
+
+#ifdef USE_FCNTL
+ struct flock lck;
+
+
+ memset (&lck, 0, sizeof (struct flock));
+ lck.l_type = excl ? F_WRLCK : F_RDLCK;
+ lck.l_whence = SEEK_SET;
+
+ count = 0;
+ attempt = 0;
+ while (fcntl (fd, F_SETLK, &lck) == -1)
+ {
+ struct stat sb;
+ dprint(1,(debugfile, "mx_lock_file(): fcntl errno %d.\n", errno));
+ if (errno != EAGAIN && errno != EACCES)
+ {
+ mutt_perror ("fcntl");
+ return (-1);
+ }
+
+ if (fstat (fd, &sb) != 0)
+ sb.st_size = 0;
+
+ if (count == 0)
+ prev_sb = sb;
+
+ /* only unlock file if it is unchanged */
+ if (prev_sb.st_size == sb.st_size && ++count >= timeout?MAXLOCKATTEMPT:0)
+ {
+ if (timeout)
+ mutt_error ("Timeout exceeded while attempting fcntl lock!");
+ return (-1);
+ }
+
+ prev_sb = sb;
+
+ mutt_message ("Waiting for fcntl lock... %d", ++attempt);
+ sleep (1);
+ }
+#endif /* USE_FCNTL */
+
+#ifdef USE_FLOCK
+ count = 0;
+ attempt = 0;
+ while (flock (fd, (excl ? LOCK_EX : LOCK_SH) | LOCK_NB) == -1)
+ {
+ struct stat sb;
+ if (errno != EWOULDBLOCK)
+ {
+ mutt_perror ("flock");
+ r = -1;
+ break;
+ }
+
+ if (fstat(fd,&sb) != 0 )
+ sb.st_size=0;
+
+ if (count == 0)
+ prev_sb=sb;
+
+ /* only unlock file if it is unchanged */
+ if (prev_sb.st_size == sb.st_size && ++count >= timeout?MAXLOCKATTEMPT:0)
+ {
+ if (timeout)
+ mutt_error ("Timeout exceeded while attempting flock lock!");
+ r = -1;
+ break;
+ }
+
+ prev_sb = sb;
+
+ mutt_message ("Waiting for flock attempt... %d", ++attempt);
+ sleep (1);
+ }
+#endif /* USE_FLOCK */
+
+#ifdef USE_DOTLOCK
+ if (r == 0 && dot)
+ r = dotlock_file (path,timeout);
+#endif /* USE_DOTLOCK */
+
+ if (r == -1)
+ {
+ /* release any other locks obtained in this routine */
+
+#ifdef USE_FCNTL
+ lck.l_type = F_UNLCK;
+ fcntl (fd, F_SETLK, &lck);
+#endif /* USE_FCNTL */
+
+#ifdef USE_FLOCK
+ flock (fd, LOCK_UN);
+#endif /* USE_FLOCK */
+
+ return (-1);
+ }
+
+ return 0;
+}
+
+int mx_unlock_file (const char *path, int fd)
+{
+#ifdef USE_FCNTL
+ struct flock unlockit = { F_UNLCK, 0, 0, 0 };
+
+ memset (&unlockit, 0, sizeof (struct flock));
+ unlockit.l_type = F_UNLCK;
+ unlockit.l_whence = SEEK_SET;
+ fcntl (fd, F_SETLK, &unlockit);
+#endif
+
+#ifdef USE_FLOCK
+ flock (fd, LOCK_UN);
+#endif
+
+#ifdef USE_DOTLOCK
+ undotlock_file (path);
+#endif
+
+ return 0;
+}
+
+/* open a file and lock it */
+FILE *mx_open_file_lock (const char *path, const char *mode)
+{
+ FILE *f;
+
+ if ((f = safe_fopen (path, mode)) != NULL)
+ {
+ if (mx_lock_file (path, fileno (f), *mode != 'r', 1, 1) != 0)
+ {
+ fclose (f);
+ f = NULL;
+ }
+ }
+
+ return (f);
+}
+
+/* try to figure out what type of mailbox ``path'' is
+ *
+ * return values:
+ * M_* mailbox type
+ * 0 not a mailbox
+ * -1 error
+ */
+int mx_get_magic (const char *path)
+{
+ struct stat st;
+ int magic = 0;
+ char tmp[_POSIX_PATH_MAX];
+ FILE *f;
+
+#ifdef USE_IMAP
+ if (*path == '{')
+ return M_IMAP;
+#endif /* USE_IMAP */
+
+ if (stat (path, &st) == -1)
+ {
+ dprint (1, (debugfile, "mx_get_magic(): unable to stat %s: %s (errno %d).\n",
+ path, strerror (errno), errno));
+ mutt_perror (path);
+ return (-1);
+ }
+
+ if (S_ISDIR (st.st_mode))
+ {
+ /* check for maildir-style mailbox */
+
+ snprintf (tmp, sizeof (tmp), "%s/cur", path);
+ if (stat (tmp, &st) == 0 && S_ISDIR (st.st_mode))
+ return (M_MAILDIR);
+
+ /* check for mh-style mailbox */
+
+ snprintf (tmp, sizeof (tmp), "%s/.mh_sequences", path);
+ if (access (tmp, F_OK) == 0)
+ return (M_MH);
+
+ snprintf (tmp, sizeof (tmp), "%s/.xmhcache", path);
+ if (access (tmp, F_OK) == 0)
+ return (M_MH);
+ }
+ else if (st.st_size == 0)
+ {
+ /* hard to tell what zero-length files are, so assume the default magic */
+ if (DefaultMagic == M_MBOX || DefaultMagic == M_MMDF)
+ return (DefaultMagic);
+ else
+ return (M_MBOX);
+ }
+ else if ((f = fopen (path, "r")) != NULL)
+ {
+#ifndef BUFFY_SIZE
+ struct utimbuf times;
+#endif
+
+ fgets (tmp, sizeof (tmp), f);
+ if (strncmp ("From ", tmp, 5) == 0)
+ magic = M_MBOX;
+ else if (strcmp (MMDF_SEP, tmp) == 0)
+ magic = M_MMDF;
+ fclose (f);
+#ifndef BUFFY_SIZE
+ /* need to restore the times here, the file was not really accessed,
+ * only the type was accessed. This is important, because detection
+ * of "new mail" depends on those times set correctly.
+ */
+ times.actime = st.st_atime;
+ times.modtime = st.st_mtime;
+ utime (path, ×);
+#endif
+ }
+ else
+ {
+ dprint (1, (debugfile, "mx_get_magic(): unable to open file %s for reading.\n",
+ path));
+ mutt_perror (path);
+ return (-1);
+ }
+
+ return (magic);
+}
+
+/*
+ * set DefaultMagic to the given value
+ */
+int mx_set_magic (const char *s)
+{
+ if (strcasecmp (s, "mbox") == 0)
+ DefaultMagic = M_MBOX;
+ else if (strcasecmp (s, "mmdf") == 0)
+ DefaultMagic = M_MMDF;
+ else if (strcasecmp (s, "mh") == 0)
+ DefaultMagic = M_MH;
+ else if (strcasecmp (s, "maildir") == 0)
+ DefaultMagic = M_MAILDIR;
+ else
+ return (-1);
+
+ return 0;
+}
+
+static int mx_open_mailbox_append (CONTEXT *ctx)
+{
+ ctx->append = 1;
+ if (access (ctx->path, W_OK) == 0)
+ {
+ switch (ctx->magic = mx_get_magic (ctx->path))
+ {
+ case 0:
+ mutt_error ("%s is not a mailbox.", ctx->path);
+ /* fall through */
+ case -1:
+ return (-1);
+ }
+ }
+ else if (errno == ENOENT)
+ {
+ ctx->magic = DefaultMagic;
+
+ if (ctx->magic == M_MH || ctx->magic == M_MAILDIR)
+ {
+ char tmp[_POSIX_PATH_MAX];
+
+ if (mkdir (ctx->path, S_IRWXU))
+ {
+ mutt_perror (tmp);
+ return (-1);
+ }
+
+ if (ctx->magic == M_MAILDIR)
+ {
+ snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
+ if (mkdir (tmp, S_IRWXU))
+ {
+ mutt_perror (tmp);
+ rmdir (ctx->path);
+ return (-1);
+ }
+
+ snprintf (tmp, sizeof (tmp), "%s/new", ctx->path);
+ if (mkdir (tmp, S_IRWXU))
+ {
+ mutt_perror (tmp);
+ snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
+ rmdir (tmp);
+ rmdir (ctx->path);
+ return (-1);
+ }
+ snprintf (tmp, sizeof (tmp), "%s/tmp", ctx->path);
+ if (mkdir (tmp, S_IRWXU))
+ {
+ mutt_perror (tmp);
+ snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
+ rmdir (tmp);
+ snprintf (tmp, sizeof (tmp), "%s/new", ctx->path);
+ rmdir (tmp);
+ rmdir (ctx->path);
+ return (-1);
+ }
+ }
+ else
+ {
+ int i;
+
+ snprintf (tmp, sizeof (tmp), "%s/.mh_sequences", ctx->path);
+ if ((i = creat (tmp, S_IRWXU)) == -1)
+ {
+ mutt_perror (tmp);
+ rmdir (ctx->path);
+ return (-1);
+ }
+ close (i);
+ }
+ }
+ }
+ else
+ {
+ mutt_perror (ctx->path);
+ return (-1);
+ }
+
+ switch (ctx->magic)
+ {
+ case M_MBOX:
+ case M_MMDF:
+ if ((ctx->fp = fopen (ctx->path, "a")) == NULL ||
+ mbox_lock_mailbox (ctx, 1, 1) != 0)
+ {
+ if (!ctx->fp)
+ mutt_perror (ctx->path);
+ return (-1);
+ }
+ fseek (ctx->fp, 0, 2);
+ break;
+
+ case M_MH:
+ case M_MAILDIR:
+ /* nothing to do */
+ break;
+
+ default:
+ return (-1);
+ }
+
+ return 0;
+}
+
+/*
+ * open a mailbox and parse it
+ *
+ * Args:
+ * flags M_NOSORT do not sort mailbox
+ * M_APPEND open mailbox for appending
+ * M_READONLY open mailbox in read-only mode
+ * M_QUIET only print error messages
+ * ctx if non-null, context struct to use
+ */
+CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx)
+{
+ CONTEXT *ctx = pctx;
+ int rc;
+
+ if (!ctx)
+ ctx = safe_malloc (sizeof (CONTEXT));
+ memset (ctx, 0, sizeof (CONTEXT));
+ ctx->path = safe_strdup (path);
+
+ ctx->msgnotreadyet = -1;
+
+ if (flags & M_QUIET)
+ ctx->quiet = 1;
+ if (flags & M_READONLY)
+ ctx->readonly = 1;
+
+ if (flags & M_APPEND)
+ {
+ if (mx_open_mailbox_append (ctx) != 0)
+ {
+ mx_fastclose_mailbox (ctx);
+ if (!pctx)
+ safe_free ((void **) &ctx);
+ return NULL;
+ }
+ return ctx;
+ }
+
+ switch (ctx->magic = mx_get_magic (path))
+ {
+ case 0:
+ mutt_error ("%s is not a mailbox.", path);
+ /* fall through */
+
+ case -1:
+ mx_fastclose_mailbox (ctx);
+ if (!pctx)
+ free (ctx);
+ return (NULL);
+ }
+
+ /* if the user has a `push' command in their .muttrc, or in a folder-hook,
+ * it will cause the progress messages not to be displayed because
+ * mutt_refresh() will think we are in the middle of a macro. so set a
+ * flag to indicate that we should really refresh the screen.
+ */
+ set_option (OPTFORCEREFRESH);
+
+ /* create hash tables */
+ ctx->id_hash = hash_create (257);
+ ctx->subj_hash = hash_create (257);
+
+ if (!ctx->quiet)
+ mutt_message ("Reading %s...", ctx->path);
+
+ switch (ctx->magic)
+ {
+ case M_MH:
+ rc = mh_read_dir (ctx, NULL);
+ break;
+
+ case M_MAILDIR:
+ rc = maildir_read_dir (ctx);
+ break;
+
+ case M_MMDF:
+ case M_MBOX:
+ rc = mbox_open_mailbox (ctx);
+ break;
+
+#ifdef USE_IMAP
+ case M_IMAP:
+ rc = imap_open_mailbox (ctx);
+ break;
+#endif /* USE_IMAP */
+
+ default:
+ rc = -1;
+ break;
+ }
+
+ if (rc == 0)
+ {
+ if ((flags & M_NOSORT) == 0)
+ {
+ /* avoid unnecessary work since the mailbox is completely unthreaded
+ to begin with */
+ unset_option (OPTSORTSUBTHREADS);
+ unset_option (OPTNEEDRESCORE);
+ mutt_sort_headers (ctx, 1);
+ }
+ if (!ctx->quiet)
+ mutt_clear_error ();
+ }
+ else
+ {
+ mx_fastclose_mailbox (ctx);
+ if (!pctx)
+ safe_free ((void **) &ctx);
+ }
+
+ unset_option (OPTFORCEREFRESH);
+ return (ctx);
+}
+
+/* free up memory associated with the mailbox context */
+void mx_fastclose_mailbox (CONTEXT *ctx)
+{
+ int i;
+
+#ifdef USE_IMAP
+ if (ctx->magic == M_IMAP)
+ imap_fastclose_mailbox (ctx);
+#endif /* USE_IMAP */
+ if (ctx->subj_hash)
+ hash_destroy (&ctx->subj_hash, NULL);
+ if (ctx->id_hash)
+ hash_destroy (&ctx->id_hash, NULL);
+ for (i = 0; i < ctx->msgcount; i++)
+ mutt_free_header (&ctx->hdrs[i]);
+ safe_free ((void **) &ctx->hdrs);
+ safe_free ((void **) &ctx->v2r);
+ safe_free ((void **) &ctx->path);
+ safe_free ((void **) &ctx->pattern);
+ if (ctx->limit_pattern)
+ mutt_pattern_free (&ctx->limit_pattern);
+ if (ctx->fp)
+ fclose (ctx->fp);
+ memset (ctx, 0, sizeof (CONTEXT));
+}
+
+/* save changes to disk */
+static int sync_mailbox (CONTEXT *ctx)
+{
+#ifdef BUFFY_SIZE
+ BUFFY *tmp = NULL;
+#endif
+ int rc = -1;
+
+ if (!ctx->quiet)
+ mutt_message ("Writing %s...", ctx->path);
+ switch (ctx->magic)
+ {
+ case M_MBOX:
+ case M_MMDF:
+ rc = mbox_sync_mailbox (ctx);
+#ifdef BUFFY_SIZE
+ tmp = mutt_find_mailbox (ctx->path);
+#endif
+ break;
+
+ case M_MH:
+ case M_MAILDIR:
+ rc = mh_sync_mailbox (ctx);
+ break;
+
+#ifdef USE_IMAP
+ case M_IMAP:
+ rc = imap_sync_mailbox (ctx);
+ break;
+#endif /* USE_IMAP */
+ }
+
+#ifdef BUFFY_SIZE
+ if (tmp && tmp->new == 0)
+ mutt_update_mailbox (tmp);
+#endif
+ return rc;
+}
+
+/* save changes and close mailbox */
+int mx_close_mailbox (CONTEXT *ctx)
+{
+ int i, move_messages = 0, purge = 1, read_msgs = 0;
+ int isSpool = 0;
+ CONTEXT f;
+ char mbox[_POSIX_PATH_MAX];
+ char buf[SHORT_STRING];
+
+ if (ctx->readonly || ctx->dontwrite)
+ {
+ /* mailbox is readonly or we don't want to write */
+ mx_fastclose_mailbox (ctx);
+ return 0;
+ }
+
+ if (ctx->append)
+ {
+ /* mailbox was opened in write-mode */
+ if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
+ mbox_close_mailbox (ctx);
+ else
+ mx_fastclose_mailbox (ctx);
+ return 0;
+ }
+
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read)
+ read_msgs++;
+ }
+
+ if (read_msgs && quadoption (OPT_MOVE) != M_NO)
+ {
+ char *p;
+
+ if ((p = mutt_find_hook (M_MBOXHOOK, ctx->path)))
+ {
+ isSpool = 1;
+ strfcpy (mbox, p, sizeof (mbox));
+ }
+ else
+ {
+ strfcpy (mbox, Inbox, sizeof (mbox));
+ isSpool = mutt_is_spool (ctx->path) && !mutt_is_spool (mbox);
+ }
+ mutt_expand_path (mbox, sizeof (mbox));
+
+ if (isSpool)
+ {
+ snprintf (buf, sizeof (buf), "Move read messages to %s?", mbox);
+ if ((move_messages = query_quadoption (OPT_MOVE, buf)) == -1)
+ return (-1);
+ }
+ }
+
+ if (ctx->deleted)
+ {
+ snprintf (buf, sizeof (buf), "Purge %d deleted message%s?",
+ ctx->deleted, ctx->deleted == 1 ? "" : "s");
+ if ((purge = query_quadoption (OPT_DELETE, buf)) < 0)
+ return (-1);
+ }
+
+ if (option (OPTMARKOLD))
+ {
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ if (!ctx->hdrs[i]->deleted && !ctx->hdrs[i]->old)
+ mutt_set_flag (ctx, ctx->hdrs[i], M_OLD, 1);
+ }
+ }
+
+ if (move_messages)
+ {
+ if (mx_open_mailbox (mbox, M_APPEND, &f) == NULL)
+ return (-1);
+
+ mutt_message ("Moving read messages to %s...", mbox);
+
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted)
+ {
+ mutt_append_message (&f, ctx, ctx->hdrs[i], 0, CH_UPDATE_LEN);
+ ctx->hdrs[i]->deleted = 1;
+ ctx->deleted++;
+ }
+ }
+
+ mx_close_mailbox (&f);
+ }
+ else if (!ctx->changed && ctx->deleted == 0)
+ {
+ mutt_message ("Mailbox is unchanged.");
+ mx_fastclose_mailbox (ctx);
+ return 0;
+ }
+
+ if (!purge)
+ {
+ for (i = 0; i < ctx->msgcount; i++)
+ ctx->hdrs[i]->deleted = 0;
+ ctx->deleted = 0;
+ }
+
+ if (ctx->changed || ctx->deleted)
+ {
+ if (sync_mailbox (ctx) == -1)
+ return (-1);
+ }
+
+ if (move_messages)
+ mutt_message ("%d kept, %d moved, %d deleted.",
+ ctx->msgcount - ctx->deleted, read_msgs, ctx->deleted);
+ else
+ mutt_message ("%d kept, %d deleted.",
+ ctx->msgcount - ctx->deleted, ctx->deleted);
+
+ if (ctx->msgcount == ctx->deleted &&
+ (ctx->magic == M_MMDF || ctx->magic == M_MBOX) &&
+ strcmp (ctx->path, Spoolfile) != 0 && !option (OPTSAVEEMPTY))
+ unlink (ctx->path);
+
+ mx_fastclose_mailbox (ctx);
+
+ return 0;
+}
+
+/* save changes to mailbox
+ *
+ * return values:
+ * 0 success
+ * -1 error
+ */
+int mx_sync_mailbox (CONTEXT *ctx)
+{
+ int rc, i, j;
+
+ if (ctx->dontwrite)
+ {
+ char buf[STRING], tmp[STRING];
+ if (km_expand_key (buf, sizeof(buf),
+ km_find_func (MENU_MAIN, OP_TOGGLE_WRITE)))
+ snprintf (tmp, sizeof(tmp), " Press '%s' to toggle write", buf);
+ else
+ strfcpy (tmp, "Use 'toggle-write' to re-enable write!", sizeof(tmp));
+
+ mutt_error ("Mailbox is marked unwritable. %s", tmp);
+ return -1;
+ }
+ else if (ctx->readonly)
+ {
+ mutt_error ("Mailbox is read-only.");
+ return -1;
+ }
+
+ if (!ctx->changed && !ctx->deleted)
+ {
+ mutt_message ("Mailbox is unchanged.");
+ return (0);
+ }
+
+ if (ctx->deleted)
+ {
+ char buf[SHORT_STRING];
+
+ snprintf (buf, sizeof (buf), "Purge %d deleted message%s?",
+ ctx->deleted, ctx->deleted == 1 ? "" : "s");
+ if ((rc = query_quadoption (OPT_DELETE, buf)) < 0)
+ return (-1);
+ else if (rc == M_NO)
+ {
+ if (!ctx->changed)
+ return 0; /* nothing to do! */
+ for (i = 0 ; i < ctx->msgcount ; i++)
+ ctx->hdrs[i]->deleted = 0;
+ ctx->deleted = 0;
+ }
+ }
+
+ if ((rc = sync_mailbox (ctx)) == 0)
+ {
+ mutt_message ("%d kept, %d deleted.", ctx->msgcount - ctx->deleted,
+ ctx->deleted);
+ sleep (1); /* allow the user time to read the message */
+
+ if (ctx->msgcount == ctx->deleted &&
+ (ctx->magic == M_MBOX || ctx->magic == M_MMDF) &&
+ strcmp (ctx->path, Spoolfile) != 0 && !option (OPTSAVEEMPTY))
+ {
+ unlink (ctx->path);
+ mx_fastclose_mailbox (ctx);
+ return 0;
+ }
+
+ /* update memory to reflect the new state of the mailbox */
+ ctx->vcount = 0;
+ ctx->vsize = 0;
+ ctx->tagged = 0;
+ ctx->deleted = 0;
+ ctx->new = 0;
+ ctx->unread = 0;
+ ctx->changed = 0;
+ ctx->flagged = 0;
+#define this_body ctx->hdrs[j]->content
+ for (i = 0, j = 0; i < ctx->msgcount; i++)
+ {
+ if (!ctx->hdrs[i]->deleted)
+ {
+ if (i != j)
+ {
+ ctx->hdrs[j] = ctx->hdrs[i];
+ ctx->hdrs[i] = NULL;
+ }
+ ctx->hdrs[j]->msgno = j;
+ if (ctx->hdrs[j]->virtual != -1)
+ {
+ ctx->v2r[ctx->vcount] = j;
+ ctx->hdrs[j]->virtual = ctx->vcount++;
+ ctx->vsize += this_body->length + this_body->offset -
+ this_body->hdr_offset;
+ }
+ ctx->hdrs[j]->changed = 0;
+ if (ctx->hdrs[j]->tagged)
+ ctx->tagged++;
+ if (ctx->hdrs[j]->flagged)
+ ctx->flagged++;
+ if (!ctx->hdrs[j]->read)
+ {
+ ctx->unread++;
+ if (!ctx->hdrs[j]->old)
+ ctx->new++;
+ }
+ j++;
+ }
+ else
+ {
+ if (ctx->magic == M_MH || ctx->magic == M_MAILDIR)
+ ctx->size -= (ctx->hdrs[i]->content->length +
+ ctx->hdrs[i]->content->offset -
+ ctx->hdrs[i]->content->hdr_offset);
+ /* remove message from the hash tables */
+ if (ctx->hdrs[i]->env->real_subj)
+ hash_delete (ctx->subj_hash, ctx->hdrs[i]->env->real_subj, ctx->hdrs[i], NULL);
+ if (ctx->hdrs[i]->env->message_id)
+ hash_delete (ctx->id_hash, ctx->hdrs[i]->env->message_id, ctx->hdrs[i], NULL);
+ mutt_free_header (&ctx->hdrs[i]);
+ }
+ }
+#undef this_body
+ ctx->msgcount = j;
+
+ mutt_sort_headers (ctx, 1); /* rethread from scratch */
+ }
+
+ return (rc);
+}
+
+int mh_open_new_message (MESSAGE *msg, CONTEXT *dest, HEADER *hdr)
+{
+ int hi = 1;
+ int fd, n;
+ char *cp;
+ char path[_POSIX_PATH_MAX];
+ DIR *dirp;
+ struct dirent *de;
+
+ do
+ {
+ if ((dirp = opendir (dest->path)) == NULL)
+ {
+ mutt_perror (dest->path);
+ return (-1);
+ }
+
+ /* figure out what the next message number is */
+ while ((de = readdir (dirp)) != NULL)
+ {
+ cp = de->d_name;
+ while (*cp)
+ {
+ if (!isdigit (*cp))
+ break;
+ cp++;
+ }
+ if (!*cp)
+ {
+ n = atoi (de->d_name);
+ if (n > hi)
+ hi = n;
+ }
+ }
+ closedir (dirp);
+ hi++;
+ snprintf (path, sizeof (path), "%s/%d", dest->path, hi);
+ if ((fd = open (path, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1)
+ {
+ if (errno != EEXIST)
+ {
+ mutt_perror (path);
+ return (-1);
+ }
+ }
+ }
+ while (fd < 0);
+
+ if ((msg->fp = fdopen (fd, "w")) == NULL)
+ return (-1);
+
+ return 0;
+}
+
+int maildir_open_new_message (MESSAGE *msg, CONTEXT *dest, HEADER *hdr)
+{
+ char tmp[_POSIX_PATH_MAX];
+ char path[_POSIX_PATH_MAX];
+
+ maildir_create_filename (dest->path, hdr, path, tmp);
+ if ((msg->fp = safe_fopen (tmp, "w")) == NULL)
+ return (-1);
+ return 0;
+}
+
+int mbox_open_new_message (MESSAGE *msg, CONTEXT *dest, HEADER *hdr)
+{
+ msg->fp = dest->fp;
+ return 0;
+}
+
+/* args:
+ * dest destintation mailbox
+ * hdr message being copied (required for maildir support, because
+ * the filename depends on the message flags)
+ */
+MESSAGE *mx_open_new_message (CONTEXT *dest, HEADER *hdr, int flags)
+{
+ MESSAGE *msg;
+ int (*func) (MESSAGE *, CONTEXT *, HEADER *);
+ ADDRESS *p = NULL;
+ time_t t;
+
+ switch (dest->magic)
+ {
+ case M_MMDF:
+ case M_MBOX:
+ func = mbox_open_new_message;
+ break;
+ case M_MAILDIR:
+ func = maildir_open_new_message;
+ break;
+ case M_MH:
+ func = mh_open_new_message;
+ break;
+ default:
+ dprint (1, (debugfile, "mx_open_new_message(): function unimplemented for mailbox type %d.\n",
+ dest->magic));
+ return (NULL);
+ }
+
+ msg = safe_calloc (1, sizeof (MESSAGE));
+ msg->magic = dest->magic;
+ msg->write = 1;
+
+ if (func (msg, dest, hdr) == 0)
+ {
+ if (dest->magic == M_MMDF)
+ fputs (MMDF_SEP, msg->fp);
+
+ if (msg->magic != M_MAILDIR && (flags & M_ADD_FROM))
+ {
+ if (hdr)
+ {
+ if (hdr->env->return_path)
+ p = hdr->env->return_path;
+ else if (hdr->env->sender)
+ p = hdr->env->sender;
+ else
+ p = hdr->env->from;
+
+ if (!hdr->received)
+ hdr->received = time (NULL);
+ t = hdr->received;
+ }
+ else
+ t = time (NULL);
+
+ fprintf (msg->fp, "From %s %s", p ? p->mailbox : Username, ctime (&t));
+ }
+ }
+ else
+ safe_free ((void **) &msg);
+
+ return msg;
+}
+
+int mutt_reopen_mailbox (CONTEXT *ctx, int *index_hint)
+{
+ int (*cmp_headers) (const HEADER *, const HEADER *) = NULL;
+ HEADER **old_hdrs;
+ int old_msgcount;
+ int msg_mod = 0;
+ int index_hint_set;
+ int i, j;
+ int rc = -1;
+
+ /* silent operations */
+ ctx->quiet = 1;
+
+ mutt_message ("Reopening mailbox...");
+
+ /* our heuristics require the old mailbox to be unsorted */
+ if (Sort != SORT_ORDER)
+ {
+ short old_sort;
+
+ old_sort = Sort;
+ Sort = SORT_ORDER;
+ mutt_sort_headers (ctx, 1);
+ Sort = old_sort;
+ }
+
+ /* save the old headers */
+ old_msgcount = ctx->msgcount;
+ old_hdrs = ctx->hdrs;
+
+ /* simulate a close */
+ hash_destroy (&ctx->id_hash, NULL);
+ hash_destroy (&ctx->subj_hash, NULL);
+ safe_free ((void **) &ctx->v2r);
+ if (ctx->readonly)
+ {
+ for (i = 0; i < ctx->msgcount; i++)
+ mutt_free_header (&(ctx->hdrs[i])); /* nothing to do! */
+ safe_free ((void **) &ctx->hdrs);
+ }
+ else
+ ctx->hdrs = NULL;
+
+ ctx->hdrmax = 0; /* force allocation of new headers */
+ ctx->msgcount = 0;
+ ctx->vcount = 0;
+ ctx->tagged = 0;
+ ctx->deleted = 0;
+ ctx->new = 0;
+ ctx->unread = 0;
+ ctx->flagged = 0;
+ ctx->changed = 0;
+ ctx->id_hash = hash_create (257);
+ ctx->subj_hash = hash_create (257);
+
+ switch (ctx->magic)
+ {
+ case M_MBOX:
+ fseek (ctx->fp, 0, 0);
+ cmp_headers = mbox_strict_cmp_headers;
+ rc = mbox_parse_mailbox (ctx);
+ break;
+
+ case M_MMDF:
+ fseek (ctx->fp, 0, 0);
+ cmp_headers = mbox_strict_cmp_headers;
+ rc = mmdf_parse_mailbox (ctx);
+ break;
+
+ case M_MH:
+ /* cmp_headers = mh_strict_cmp_headers; */
+ rc = mh_read_dir (ctx, NULL);
+ break;
+
+ case M_MAILDIR:
+ /* cmp_headers = maildir_strict_cmp_headers; */
+ rc = maildir_read_dir (ctx);
+ break;
+
+ default:
+ rc = -1;
+ break;
+ }
+
+ if (rc == -1)
+ {
+ /* free the old headers */
+ for (j = 0; j < old_msgcount; j++)
+ mutt_free_header (&(old_hdrs[j]));
+ safe_free ((void **) &old_hdrs);
+
+ ctx->quiet = 0;
+ return (-1);
+ }
+
+ /* now try to recover the old flags */
+
+ index_hint_set = (index_hint == NULL);
+
+ if (!ctx->readonly)
+ {
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ int found = 0;
+
+ /* some messages have been deleted, and new messages have been
+ * appended at the end; the heuristic is that old messages have then
+ * "advanced" towards the beginning of the folder, so we begin the
+ * search at index "i"
+ */
+ for (j = i; j < old_msgcount; j++)
+ {
+ if (old_hdrs[j] == NULL)
+ continue;
+ if (cmp_headers (ctx->hdrs[i], old_hdrs[j]))
+ {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ {
+ for (j = 0; j < i; j++)
+ {
+ if (old_hdrs[j] == NULL)
+ continue;
+ if (cmp_headers (ctx->hdrs[i], old_hdrs[j]))
+ {
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ if (found)
+ {
+ /* this is best done here */
+ if (!index_hint_set && *index_hint == j)
+ *index_hint = i;
+
+ if (old_hdrs[j]->changed)
+ {
+ /* Only update the flags if the old header was changed;
+ * otherwise, the header may have been modified externally,
+ * and we don't want to lose _those_ changes
+ */
+ mutt_set_flag (ctx, ctx->hdrs[i], M_FLAG, old_hdrs[j]->flagged);
+ mutt_set_flag (ctx, ctx->hdrs[i], M_REPLIED, old_hdrs[j]->replied);
+ mutt_set_flag (ctx, ctx->hdrs[i], M_OLD, old_hdrs[j]->old);
+ mutt_set_flag (ctx, ctx->hdrs[i], M_READ, old_hdrs[j]->read);
+ }
+ mutt_set_flag (ctx, ctx->hdrs[i], M_DELETE, old_hdrs[j]->deleted);
+ mutt_set_flag (ctx, ctx->hdrs[i], M_TAG, old_hdrs[j]->tagged);
+
+ /* we don't need this header any more */
+ mutt_free_header (&(old_hdrs[j]));
+ }
+ }
+
+ /* free the remaining old headers */
+ for (j = 0; j < old_msgcount; j++)
+ {
+ if (old_hdrs[j])
+ {
+ mutt_free_header (&(old_hdrs[j]));
+ msg_mod = 1;
+ }
+ }
+ safe_free ((void **) &old_hdrs);
+ }
+
+ ctx->quiet = 0;
+
+ return ((ctx->changed || msg_mod) ? M_REOPENED : M_NEW_MAIL);
+}
+
+/* check for new mail */
+int mx_check_mailbox (CONTEXT *ctx, int *index_hint)
+{
+ if (ctx)
+ {
+ switch (ctx->magic)
+ {
+ case M_MBOX:
+ case M_MMDF:
+ return (mbox_check_mailbox (ctx, index_hint));
+
+ case M_MH:
+ case M_MAILDIR:
+ return (mh_check_mailbox (ctx, index_hint));
+
+#ifdef USE_IMAP
+ case M_IMAP:
+ return (imap_check_mailbox (ctx, index_hint));
+#endif /* USE_IMAP */
+ }
+ }
+
+ dprint (1, (debugfile, "mx_check_mailbox: null or invalid context.\n"));
+ return (-1);
+}
+
+/* return a stream pointer for a message */
+MESSAGE *mx_open_message (CONTEXT *ctx, int msgno)
+{
+ MESSAGE *msg;
+
+ msg = safe_calloc (1, sizeof (MESSAGE));
+ switch (msg->magic = ctx->magic)
+ {
+ case M_MBOX:
+ case M_MMDF:
+ msg->fp = ctx->fp;
+ break;
+
+ case M_MH:
+ case M_MAILDIR:
+ {
+ HEADER *cur = ctx->hdrs[msgno];
+ char path[_POSIX_PATH_MAX];
+
+ snprintf (path, sizeof (path), "%s/%s", ctx->path, cur->path);
+ if ((msg->fp = fopen (path, "r")) == NULL)
+ {
+ mutt_perror (path);
+ dprint (1, (debugfile, "mx_open_message: fopen: %s: %s (errno %d).\n",
+ path, strerror (errno), errno));
+ free (msg);
+ msg = NULL;
+ }
+ }
+ break;
+
+#ifdef USE_IMAP
+ case M_IMAP:
+ if (imap_fetch_message (msg, ctx, msgno) != 0)
+ {
+ free (msg);
+ msg = NULL;
+ }
+ break;
+#endif /* USE_IMAP */
+
+ default:
+
+ dprint (1, (debugfile, "mx_open_message(): function not implemented for mailbox type %d.\n", ctx->magic));
+ safe_free ((void **) &msg);
+ break;
+ }
+ return (msg);
+}
+
+/* close a pointer to a message */
+int mx_close_message (MESSAGE **msg)
+{
+ int r = 0;
+
+ if ((*msg)->write)
+ {
+ /* add the message terminator */
+ switch ((*msg)->magic)
+ {
+ case M_MMDF:
+ fputs (MMDF_SEP, (*msg)->fp);
+ break;
+
+ case M_MBOX:
+ fputc ('\n', (*msg)->fp);
+ break;
+ }
+ }
+
+ if ((*msg)->magic == M_MH || (*msg)->magic == M_MAILDIR || (*msg)->magic == M_IMAP)
+ r = fclose ((*msg)->fp);
+
+ free (*msg);
+ *msg = NULL;
+ return (r);
+}
+
+void mx_alloc_memory (CONTEXT *ctx)
+{
+ int i;
+
+ if (ctx->hdrs)
+ {
+ safe_realloc ((void **) &ctx->hdrs, sizeof (HEADER *) * (ctx->hdrmax += 25));
+ safe_realloc ((void **) &ctx->v2r, sizeof (int) * ctx->hdrmax);
+ }
+ else
+ {
+ ctx->hdrs = safe_malloc (sizeof (HEADER *) * (ctx->hdrmax += 25));
+ ctx->v2r = safe_malloc (sizeof (int) * ctx->hdrmax);
+ }
+ for (i = ctx->msgcount ; i < ctx->hdrmax ; i++)
+ {
+ ctx->hdrs[i] = NULL;
+ ctx->v2r[i] = -1;
+ }
+}
+
+/* this routine is called to update the counts in the context structure for
+ * the last message header parsed.
+ */
+void mx_update_context (CONTEXT *ctx)
+{
+ HEADER *h = ctx->hdrs[ctx->msgcount];
+
+
+
+
+
+
+
+
+
+
+
+
+
+#ifdef _PGPPATH
+ /* NOTE: this _must_ be done before the check for mailcap! */
+ h->pgp = pgp_query (h->content);
+ if (!h->pgp)
+#endif /* _PGPPATH */
+
+
+
+ if (mutt_needs_mailcap (h->content))
+ h->mailcap = 1;
+ if (h->flagged)
+ ctx->flagged++;
+ if (h->deleted)
+ ctx->deleted++;
+ if (!h->read)
+ {
+ ctx->unread++;
+ if (!h->old)
+ ctx->new++;
+ }
+ if (!ctx->pattern)
+ {
+ ctx->v2r[ctx->vcount] = ctx->msgcount;
+ h->virtual = ctx->vcount++;
+ }
+ else
+ h->virtual = -1;
+ h->msgno = ctx->msgcount;
+ ctx->msgcount++;
+
+ if (h->env->supersedes)
+ {
+ HEADER *h2 = hash_find (ctx->id_hash, h->env->supersedes);
+
+ /* safe_free (&h->env->supersedes); should I ? */
+ if (h2)
+ {
+ h2->superseded = 1;
+ mutt_score_message (h2);
+ }
+ }
+
+ /* add this message to the hash tables */
+ if (h->env->message_id)
+ hash_insert (ctx->id_hash, h->env->message_id, h, 0);
+ if (h->env->real_subj)
+ hash_insert (ctx->subj_hash, h->env->real_subj, h, 1);
+
+ mutt_score_message (h);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This header file contains prototypes for internal functions used by the
+ * generic mailbox api. None of these functions should be called directly.
+ */
+
+/* supported mailbox formats */
+enum
+{
+ M_MBOX = 1,
+ M_MMDF,
+ M_MH,
+ M_MAILDIR,
+ M_IMAP
+};
+
+WHERE short DefaultMagic INITVAL (M_MBOX);
+
+#define MMDF_SEP "\001\001\001\001\n"
+
+int mbox_sync_mailbox (CONTEXT *);
+int mbox_open_mailbox (CONTEXT *);
+int mbox_check_mailbox (CONTEXT *, int *);
+int mbox_close_mailbox (CONTEXT *);
+int mbox_lock_mailbox (CONTEXT *, int, int);
+int mbox_parse_mailbox (CONTEXT *);
+int mmdf_parse_mailbox (CONTEXT *);
+
+int mh_read_dir (CONTEXT *, const char *);
+int mh_sync_mailbox (CONTEXT *);
+int mh_check_mailbox (CONTEXT *, int *);
+int mh_parse_sequences (CONTEXT *, const char *);
+
+int maildir_read_dir (CONTEXT *);
+void maildir_create_filename (const char *, HEADER *, char *, char *);
+
+int mbox_strict_cmp_headers (const HEADER *, const HEADER *);
+int mutt_reopen_mailbox (CONTEXT *, int *);
+
+void mx_alloc_memory (CONTEXT *);
+void mx_update_context (CONTEXT *);
+
+FILE *mx_open_file_lock (const char *, const char *);
+
+int mx_lock_file (const char *, int, int, int, int);
+int mx_unlock_file (const char *path, int fd);
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mutt_regex.h"
+#include "keymap.h"
+#include "mutt_menu.h"
+#include "sort.h"
+#include "pager.h"
+#include "attach.h"
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+
+
+
+
+
+
+#include <sys/stat.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define M_NOSHOW 0
+#define M_SHOWFLAT (1 << 0)
+#define M_SHOWCOLOR (1 << 1)
+#define M_HIDE (1 << 2)
+#define M_SEARCH (1 << 3)
+#define M_TYPES (1 << 4)
+#define M_SHOW (M_SHOWCOLOR | M_SHOWFLAT)
+
+#define ISHEADER(x) ((x) == MT_COLOR_HEADER || (x) == MT_COLOR_HDEFAULT)
+
+#define IsAttach(x) (x && (x)->bdy)
+#define IsHeader(x) (x && (x)->hdr)
+
+#define CHECK_MODE(x) if (!(x)) \
+ { \
+ mutt_flushinp (); \
+ mutt_error ("Not available in this menu."); \
+ break; \
+ }
+
+#define CHECK_READONLY if (Context->readonly) \
+ { \
+ mutt_flushinp (); \
+ mutt_error ("Mailbox is read-only."); \
+ break; \
+ }
+
+struct q_class_t
+{
+ int length;
+ int color;
+ char *prefix;
+ struct q_class_t *next, *prev;
+ struct q_class_t *down, *up;
+};
+
+struct syntax_t
+{
+ int color;
+ short first;
+ short last;
+};
+
+struct line_t
+{
+ long offset;
+ short type;
+ short continuation;
+ short chunks;
+ short search_cnt;
+ struct syntax_t *syntax;
+ struct syntax_t *search;
+ struct q_class_t *quote;
+};
+
+#define ANSI_OFF (1<<0)
+#define ANSI_BLINK (1<<1)
+#define ANSI_BOLD (1<<2)
+#define ANSI_UNDERLINE (1<<3)
+#define ANSI_REVERSE (1<<4)
+#define ANSI_COLOR (1<<5)
+
+typedef struct _ansi_attr {
+ int attr;
+ int fg;
+ int bg;
+ int pair;
+} ansi_attr;
+
+static short InHelp = 0;
+
+#define NumSigLines 4
+
+static int check_sig (const char *s, struct line_t *info, int n)
+{
+ int count = 0;
+
+ while (n > 0 && count <= NumSigLines)
+ {
+ if (info[n].type != MT_COLOR_SIGNATURE)
+ break;
+ count++;
+ n--;
+ }
+
+ if (count == 0)
+ return (-1);
+
+ if (count > NumSigLines)
+ {
+ /* check for a blank line */
+ while (*s)
+ {
+ if (!ISSPACE (*s))
+ return 0;
+ s++;
+ }
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+resolve_color (struct line_t *lineInfo, int n, int cnt, int flags, int special,
+ ansi_attr *a)
+{
+ int def_color; /* color without syntax hilight */
+ int color; /* final color */
+ static int last_color; /* last color set */
+ int search = 0, i, m;
+
+ if (!cnt)
+ last_color = -1; /* force attrset() */
+
+ if (lineInfo[n].continuation)
+ {
+ if (!cnt && option (OPTMARKERS))
+ {
+ SETCOLOR (MT_COLOR_MARKERS);
+ addch ('+');
+ last_color = ColorDefs[MT_COLOR_MARKERS];
+ }
+ m = (lineInfo[n].syntax)[0].first;
+ cnt += (lineInfo[n].syntax)[0].last;
+ }
+ else
+ m = n;
+ if (!(flags & M_SHOWCOLOR))
+ def_color = ColorDefs[MT_COLOR_NORMAL];
+ else if (lineInfo[m].type == MT_COLOR_HEADER)
+ def_color = (lineInfo[m].syntax)[0].color;
+ else
+ def_color = ColorDefs[lineInfo[m].type];
+
+ if ((flags & M_SHOWCOLOR) && lineInfo[m].type == MT_COLOR_QUOTED)
+ {
+ struct q_class_t *class = lineInfo[m].quote;
+
+ if (class)
+ {
+ def_color = class->color;
+
+ while (class && class->length > cnt)
+ {
+ def_color = class->color;
+ class = class->up;
+ }
+ }
+ }
+
+ color = def_color;
+ if (flags & M_SHOWCOLOR)
+ {
+ for (i = 0; i < lineInfo[m].chunks; i++)
+ {
+ /* we assume the chunks are sorted */
+ if (cnt > (lineInfo[m].syntax)[i].last)
+ continue;
+ if (cnt < (lineInfo[m].syntax)[i].first)
+ break;
+ if (cnt != (lineInfo[m].syntax)[i].last)
+ {
+ color = (lineInfo[m].syntax)[i].color;
+ break;
+ }
+ /* don't break here, as cnt might be
+ * in the next chunk as well */
+ }
+ }
+
+ if (flags & M_SEARCH)
+ {
+ for (i = 0; i < lineInfo[m].search_cnt; i++)
+ {
+ if (cnt > (lineInfo[m].search)[i].last)
+ continue;
+ if (cnt < (lineInfo[m].search)[i].first)
+ break;
+ if (cnt != (lineInfo[m].search)[i].last)
+ {
+ color = ColorDefs[MT_COLOR_SEARCH];
+ search = 1;
+ break;
+ }
+ }
+ }
+
+ /* handle "special" bold & underlined characters */
+ if (special || a->attr)
+ {
+ if (special == A_BOLD || (a->attr & ANSI_BOLD))
+ {
+ if (ColorDefs[MT_COLOR_BOLD] && !search)
+ color = ColorDefs[MT_COLOR_BOLD];
+ else
+ color ^= A_BOLD;
+ }
+ else if (special == A_UNDERLINE || (a->attr & ANSI_UNDERLINE))
+ {
+ if (ColorDefs[MT_COLOR_UNDERLINE] && !search)
+ color = ColorDefs[MT_COLOR_UNDERLINE];
+ else
+ color ^= A_UNDERLINE;
+ }
+ else if (a->attr & ANSI_REVERSE)
+ {
+ color ^= A_REVERSE;
+ }
+ else if (a->attr & ANSI_BLINK)
+ {
+ color ^= A_BLINK;
+ }
+#ifdef HAVE_COLOR
+ else if (a->attr & ANSI_COLOR)
+ {
+ if (a->pair == -1)
+ a->pair = mutt_alloc_color (a->fg,a->bg);
+ color = a->pair;
+ }
+#endif
+ else if (a->attr & ANSI_OFF)
+ {
+ a->attr = 0;
+ }
+ }
+
+ if (color != last_color)
+ {
+ attrset (color);
+ last_color = color;
+ }
+}
+
+static void
+append_line (struct line_t *lineInfo, int n, int cnt)
+{
+ int m;
+
+ lineInfo[n+1].type = lineInfo[n].type;
+ (lineInfo[n+1].syntax)[0].color = (lineInfo[n].syntax)[0].color;
+ lineInfo[n+1].continuation = 1;
+
+ /* find the real start of the line */
+ m = n;
+ while (m >= 0)
+ {
+ if (lineInfo[m].continuation == 0) break;
+ m--;
+ }
+ (lineInfo[n+1].syntax)[0].first = m;
+ (lineInfo[n+1].syntax)[0].last = (lineInfo[n].continuation) ?
+ cnt + (lineInfo[n].syntax)[0].last : cnt;
+}
+
+static void
+cleanup_quote (struct q_class_t **QuoteList)
+{
+ struct q_class_t *ptr;
+
+ while (*QuoteList)
+ {
+ if ((*QuoteList)->down)
+ cleanup_quote (&((*QuoteList)->down));
+ ptr = (*QuoteList)->next;
+ safe_free ((void **) &(*QuoteList)->prefix);
+ safe_free ((void **) QuoteList);
+ *QuoteList = ptr;
+ }
+
+ return;
+}
+
+static struct q_class_t *
+classify_quote (struct q_class_t **QuoteList, const char *qptr,
+ int length, int *force_redraw, int *q_level)
+{
+ struct q_class_t *q_list = *QuoteList;
+ struct q_class_t *class = NULL, *tmp = NULL, *ptr;
+ char *tail_qptr;
+ int offset, tail_lng;
+
+ /* Did I mention how much I like emulating Lisp in C? */
+
+ /* classify quoting prefix */
+ while (q_list)
+ {
+ if (length <= q_list->length)
+ {
+ if (strncmp (qptr, q_list->prefix, length) == 0)
+ {
+ /* same prefix: return the current class */
+ if (length == q_list->length)
+ return q_list;
+
+ /* found shorter common prefix */
+ if (tmp == NULL)
+ {
+ /* add a node above q_list */
+ tmp = (struct q_class_t *) safe_calloc (1, sizeof (struct q_class_t));
+ tmp->prefix = (char *) safe_calloc (1, length + 1);
+ strncpy (tmp->prefix, qptr, length);
+ tmp->length = length;
+ if (*q_level >= ColorQuoteUsed)
+ *q_level = 1;
+ else
+ (*q_level)++;
+ tmp->color = ColorQuote[(*q_level) - 1];
+
+ /* replace q_list by tmp in the top level list */
+ if (q_list->next)
+ {
+ tmp->next = q_list->next;
+ q_list->next->prev = tmp;
+ }
+ if (q_list->prev)
+ {
+ tmp->prev = q_list->prev;
+ q_list->prev->next = tmp;
+ }
+
+ /* make q_list a child of tmp */
+ tmp->down = q_list;
+ q_list->up = tmp;
+
+ /* q_list has no siblings */
+ q_list->next = NULL;
+ q_list->prev = NULL;
+
+ /* update the root if necessary */
+ if (q_list == *QuoteList)
+ *QuoteList = tmp;
+
+ /* tmp should be the return class too */
+ class = tmp;
+
+ /* next class to test */
+ q_list = tmp->next;
+ }
+ else
+ {
+ /* save the next sibling for later */
+ ptr = q_list->next;
+
+ /* unlink q_list from the top level list */
+ if (q_list->next)
+ q_list->next->prev = q_list->prev;
+ if (q_list->prev)
+ q_list->prev->next = q_list->next;
+
+ /* at this point, we have a tmp->down; link q_list to it */
+ q_list->next = tmp->down;
+ tmp->down->prev = q_list;
+ q_list->prev = NULL;
+ tmp->down = q_list;
+ q_list->up = tmp;
+
+ /* next class to test */
+ q_list = ptr;
+ }
+
+ /* in both cases q_list points now to the next top-level node */
+ *force_redraw = 1;
+ continue;
+ }
+ else
+ {
+ /* shorter, but not a substring of the current class: try next */
+ q_list = q_list->next;
+ continue;
+ }
+ }
+ else
+ {
+ /* longer than the top level prefix: try subclassing it */
+ if (tmp == NULL && strncmp (qptr, q_list->prefix, q_list->length) == 0)
+ {
+ /* ok, we may link it as a subclass */
+ ptr = q_list;
+ offset = q_list->length;
+
+ q_list = q_list->down;
+ tail_lng = length - offset;
+ tail_qptr = (char *) qptr + offset;
+
+ while (q_list)
+ {
+ if (length <= q_list->length)
+ {
+ if (strncmp (tail_qptr, (q_list->prefix) + offset, tail_lng) == 0)
+ {
+ /* same prefix: return the current class */
+ if (length == q_list->length)
+ return q_list;
+
+ /* found shorter common prefix */
+ if (tmp == NULL)
+ {
+ /* add a node above q_list */
+ tmp = (struct q_class_t *) safe_calloc (1,
+ sizeof (struct q_class_t));
+ tmp->prefix = (char *) safe_calloc (1, length + 1);
+ strncpy (tmp->prefix, qptr, length);
+ tmp->length = length;
+ if (*q_level >= ColorQuoteUsed)
+ *q_level = 1;
+ else
+ (*q_level)++;
+ tmp->color = ColorQuote[(*q_level) - 1];
+
+ /* replace q_list by tmp */
+ if (q_list->next)
+ {
+ tmp->next = q_list->next;
+ q_list->next->prev = tmp;
+ }
+ if (q_list->prev)
+ {
+ tmp->prev = q_list->prev;
+ q_list->prev->next = tmp;
+ }
+
+ /* make q_list a child of tmp */
+ tmp->down = q_list;
+ tmp->up = q_list->up;
+ q_list->up = tmp;
+ if (tmp->up->down == q_list)
+ tmp->up->down = tmp;
+
+ /* q_list has no siblings */
+ q_list->next = NULL;
+ q_list->prev = NULL;
+
+ /* tmp should be the return class too */
+ class = tmp;
+
+ /* next class to test */
+ q_list = tmp->next;
+ }
+ else
+ {
+ /* save the next sibling for later */
+ ptr = q_list->next;
+
+ /* unlink q_list from the top level list */
+ if (q_list->next)
+ q_list->next->prev = q_list->prev;
+ if (q_list->prev)
+ q_list->prev->next = q_list->next;
+
+ /* at this point, we have a tmp->down; link q_list to it */
+ q_list->next = tmp->down;
+ tmp->down->prev = q_list;
+ q_list->prev = NULL;
+ tmp->down = q_list;
+ q_list->up = tmp;
+
+ /* next class to test */
+ q_list = ptr;
+ }
+
+ *force_redraw = 1;
+ continue;
+ }
+ else
+ {
+ q_list = q_list->next;
+ continue;
+ }
+ }
+ else
+ {
+ /* longer than the current prefix: try subclassing it */
+ if (tmp == NULL && strncmp (tail_qptr, (q_list->prefix) + offset,
+ q_list->length - offset) == 0)
+ {
+ /* still a subclass: go down one level */
+ ptr = q_list;
+ offset = q_list->length;
+
+ q_list = q_list->down;
+ tail_lng = length - offset;
+ tail_qptr = (char *) qptr + offset;
+
+ continue;
+ }
+ else
+ {
+ /* nope, try the next prefix */
+ q_list = q_list->next;
+ continue;
+ }
+ }
+ }
+
+ /* if it's still not found so far we mai add it as a sibling */
+ if (class == NULL)
+ {
+ tmp = (struct q_class_t *) safe_calloc (1, sizeof (struct q_class_t));
+ tmp->prefix = (char *) safe_calloc (1, length + 1);
+ strncpy (tmp->prefix, qptr, length);
+ tmp->length = length;
+ if (*q_level >= ColorQuoteUsed)
+ *q_level = 1;
+ else
+ (*q_level)++;
+ tmp->color = ColorQuote[(*q_level) - 1];
+
+ if (ptr->down)
+ {
+ tmp->next = ptr->down;
+ ptr->down->prev = tmp;
+ }
+ ptr->down = tmp;
+ tmp->up = ptr;
+
+ return tmp;
+ }
+ else
+ return class;
+ }
+ else
+ {
+ /* nope, try the next prefix */
+ q_list = q_list->next;
+ continue;
+ }
+ }
+ }
+
+ if (class == NULL)
+ {
+ /* not found so far: add it as a top level class */
+ class = (struct q_class_t *) safe_calloc (1, sizeof (struct q_class_t));
+ class->prefix = (char *) safe_calloc (1, length + 1);
+ strncpy (class->prefix, qptr, length);
+ class->length = length;
+ if (*q_level >= ColorQuoteUsed)
+ *q_level = 1;
+ else
+ (*q_level)++;
+ class->color = ColorQuote[(*q_level) - 1];
+
+ if (*QuoteList)
+ {
+ class->next = *QuoteList;
+ (*QuoteList)->prev = class;
+ }
+ *QuoteList = class;
+ }
+
+ return class;
+}
+
+static void
+resolve_types (const char *buf, struct line_t *lineInfo, int n, int last,
+ struct q_class_t **QuoteList, int *q_level, int *force_redraw,
+ int q_classify)
+{
+ COLOR_LINE *color_line;
+ regmatch_t pmatch[1];
+ int found, offset, null_rx, i;
+
+ if (n == 0 || ISHEADER (lineInfo[n-1].type))
+ {
+ if (buf[0] == '\n')
+ lineInfo[n].type = MT_COLOR_NORMAL;
+ else if (n > 0 && (buf[0] == ' ' || buf[0] == '\t'))
+ {
+ lineInfo[n].type = lineInfo[n-1].type; /* wrapped line */
+ (lineInfo[n].syntax)[0].color = (lineInfo[n-1].syntax)[0].color;
+ }
+ else
+ {
+ lineInfo[n].type = MT_COLOR_HDEFAULT;
+ color_line = ColorHdrList;
+ while (color_line)
+ {
+ if (REGEXEC (color_line->rx, buf) == 0)
+ {
+ lineInfo[n].type = MT_COLOR_HEADER;
+ lineInfo[n].syntax[0].color = color_line->pair;
+ break;
+ }
+ color_line = color_line->next;
+ }
+ }
+ }
+ else if (strncmp ("[-- ", buf, 4) == 0)
+ lineInfo[n].type = MT_COLOR_ATTACHMENT;
+ else if (strcmp ("-- \n", buf) == 0 || strcmp ("-- \r\n", buf) == 0)
+ {
+ i = n + 1;
+
+ lineInfo[n].type = MT_COLOR_SIGNATURE;
+ while (i < last && check_sig (buf, lineInfo, i - 1) == 0 &&
+ (lineInfo[i].type == MT_COLOR_NORMAL ||
+ lineInfo[i].type == MT_COLOR_QUOTED ||
+ lineInfo[i].type == MT_COLOR_HEADER))
+ {
+ /* oops... */
+ if (lineInfo[i].chunks)
+ {
+ lineInfo[i].chunks = 0;
+ safe_realloc ((void **) &(lineInfo[n].syntax),
+ sizeof (struct syntax_t));
+ }
+ lineInfo[i++].type = MT_COLOR_SIGNATURE;
+ }
+ }
+ else if (check_sig (buf, lineInfo, n - 1) == 0)
+ lineInfo[n].type = MT_COLOR_SIGNATURE;
+ else if (regexec ((regex_t *) QuoteRegexp.rx, buf, 1, pmatch, 0) == 0 &&
+ strncmp (buf, ">From ", 6))
+ {
+ if (q_classify && lineInfo[n].quote == NULL)
+ lineInfo[n].quote = classify_quote (QuoteList, buf + pmatch[0].rm_so,
+ pmatch[0].rm_eo - pmatch[0].rm_so,
+ force_redraw, q_level);
+ lineInfo[n].type = MT_COLOR_QUOTED;
+ }
+ else
+ lineInfo[n].type = MT_COLOR_NORMAL;
+
+ /* body patterns */
+ if (lineInfo[n].type == MT_COLOR_NORMAL ||
+ lineInfo[n].type == MT_COLOR_QUOTED)
+ {
+ i = 0;
+
+ offset = 0;
+ lineInfo[n].chunks = 0;
+ do
+ {
+ if (!buf[offset])
+ break;
+
+ found = 0;
+ null_rx = 0;
+ color_line = ColorBodyList;
+ while (color_line)
+ {
+ if (regexec (&color_line->rx, buf + offset, 1, pmatch,
+ (offset ? REG_NOTBOL : 0)) == 0)
+ {
+ if (pmatch[0].rm_eo != pmatch[0].rm_so)
+ {
+ if (!found)
+ {
+ if (++(lineInfo[n].chunks) > 1)
+ safe_realloc ((void **)&(lineInfo[n].syntax),
+ (lineInfo[n].chunks) * sizeof (struct syntax_t));
+ }
+ i = lineInfo[n].chunks - 1;
+ pmatch[0].rm_so += offset;
+ pmatch[0].rm_eo += offset;
+ if (!found ||
+ pmatch[0].rm_so < (lineInfo[n].syntax)[i].first ||
+ (pmatch[0].rm_so == (lineInfo[n].syntax)[i].first &&
+ pmatch[0].rm_eo > (lineInfo[n].syntax)[i].last))
+ {
+ (lineInfo[n].syntax)[i].color = color_line->pair;
+ (lineInfo[n].syntax)[i].first = pmatch[0].rm_so;
+ (lineInfo[n].syntax)[i].last = pmatch[0].rm_eo;
+ }
+ found = 1;
+ null_rx = 0;
+ }
+ else
+ null_rx = 1; /* empty regexp; don't add it, but keep looking */
+ }
+ color_line = color_line->next;
+ }
+
+ if (null_rx)
+ offset++; /* avoid degenerate cases */
+ else
+ offset = (lineInfo[n].syntax)[i].last;
+ } while (found || null_rx);
+ }
+}
+
+static int
+fill_buffer (FILE *f, long *last_pos, long offset, unsigned char *buf,
+ unsigned char *fmt, size_t blen, int *buf_ready)
+{
+ unsigned char *p;
+ static int b_read;
+
+ if (*buf_ready == 0)
+ {
+ buf[blen - 1] = 0;
+ if (offset != *last_pos)
+ fseek (f, offset, 0);
+ if (fgets ((char *) buf, blen - 1, f) == NULL)
+ {
+ fmt[0] = 0;
+ return (-1);
+ }
+ *last_pos = ftell (f);
+ b_read = (int) (*last_pos - offset);
+ *buf_ready = 1;
+
+ /* copy "buf" to "fmt", but without bold and underline controls */
+ p = buf;
+ while (*p)
+ {
+ if (*p == '\010' && (p > buf))
+ {
+ if (*(p+1) == '_') /* underline */
+ p += 2;
+ else if (*(p+1)) /* bold or overstrike */
+ {
+ *(fmt-1) = *(p+1);
+ p += 2;
+ }
+ else /* ^H */
+ *fmt++ = *p++;
+ }
+ else
+ *fmt++ = *p++;
+ }
+ *fmt = 0;
+ }
+ return b_read;
+}
+
+static int grok_ansi(unsigned char *buf, int pos, ansi_attr *a)
+{
+ int x = pos;
+
+ while (isdigit(buf[x]) || buf[x] == ';')
+ x++;
+
+ /* Character Attributes */
+ if (a != NULL && buf[x] == 'm')
+ {
+ while (pos < x)
+ {
+ if (buf[pos] == '1' && (pos+1 == x || buf[pos+1] == ';'))
+ {
+ a->attr |= ANSI_BOLD;
+ pos += 2;
+ }
+ else if (buf[pos] == '4' && (pos+1 == x || buf[pos+1] == ';'))
+ {
+ a->attr |= ANSI_UNDERLINE;
+ pos += 2;
+ }
+ else if (buf[pos] == '5' && (pos+1 == x || buf[pos+1] == ';'))
+ {
+ a->attr |= ANSI_BLINK;
+ pos += 2;
+ }
+ else if (buf[pos] == '7' && (pos+1 == x || buf[pos+1] == ';'))
+ {
+ a->attr |= ANSI_REVERSE;
+ pos += 2;
+ }
+ else if (buf[pos] == '0' && (pos+1 == x || buf[pos+1] == ';'))
+ {
+#ifdef HAVE_COLOR
+ if (a->pair != -1)
+ mutt_free_color(a->fg,a->bg);
+#endif
+ a->attr = ANSI_OFF;
+ a->pair = -1;
+ pos += 2;
+ }
+ else if (buf[pos] == '3' && isdigit(buf[pos+1]))
+ {
+ a->attr |= ANSI_COLOR;
+ a->fg = buf[pos+1] - '0';
+ pos += 3;
+ }
+ else if (buf[pos] == '4' && isdigit(buf[pos+1]))
+ {
+ a->attr |= ANSI_COLOR;
+ a->bg = buf[pos+1] - '0';
+ pos += 3;
+ }
+ else
+ {
+ while (pos < x && buf[pos] != ';') pos++;
+ pos++;
+ }
+ }
+ }
+ pos = x;
+ return pos;
+}
+
+/*
+ * Args:
+ * flags M_NOSHOW, don't show characters
+ * M_SHOWFLAT, show characters (used for displaying help)
+ * M_SHOWCOLOR, show characters in color
+ * M_HIDE, don't show quoted text
+ * M_SEARCH, resolve search patterns
+ * M_TYPES, compute line's type
+ *
+ * Return values:
+ * -1 EOF was reached
+ * 0 normal exit, line was not displayed
+ * >0 normal exit, line was displayed
+ */
+
+static int
+display_line (FILE *f, long *last_pos, struct line_t **lineInfo, int n,
+ int *last, int *max, int flags, struct q_class_t **QuoteList,
+ int *q_level, int *force_redraw, regex_t *SearchRE)
+{
+ unsigned char buf[LONG_STRING], fmt[LONG_STRING];
+ unsigned char *buf_ptr = buf, c;
+ int ch, vch, t, col, cnt, b_read;
+ int buf_ready = 0, change_last = 0;
+ int special = 0, last_special = 0;
+ int offset;
+ int def_color;
+ int m;
+ ansi_attr a = {0,0,0,-1};
+ regmatch_t pmatch[1];
+
+ if (n == *last)
+ {
+ (*last)++;
+ change_last = 1;
+ }
+
+ if (*last == *max)
+ {
+ safe_realloc ((void **)lineInfo, sizeof (struct line_t) * (*max += LINES));
+ for (ch = *last; ch < *max ; ch++)
+ {
+ memset (&((*lineInfo)[ch]), 0, sizeof (struct line_t));
+ (*lineInfo)[ch].type = -1;
+ (*lineInfo)[ch].search_cnt = -1;
+ (*lineInfo)[ch].syntax = safe_malloc (sizeof (struct syntax_t));
+ ((*lineInfo)[ch].syntax)[0].first = ((*lineInfo)[ch].syntax)[0].last = -1;
+ }
+ }
+
+ /* only do color hiliting if we are viewing a message */
+ if (flags & (M_SHOWCOLOR | M_TYPES))
+ {
+ if ((*lineInfo)[n].type == -1)
+ {
+ /* determine the line class */
+ if (fill_buffer (f, last_pos, (*lineInfo)[n].offset, buf, fmt, sizeof (buf), &buf_ready) < 0)
+ {
+ if (change_last)
+ (*last)--;
+ return (-1);
+ }
+
+ resolve_types ((char *) fmt, *lineInfo, n, *last,
+ QuoteList, q_level, force_redraw, flags & M_SHOWCOLOR);
+ }
+
+ /* this also prevents searching through the hidden lines */
+ if ((flags & M_HIDE) && (*lineInfo)[n].type == MT_COLOR_QUOTED)
+ flags = M_NOSHOW;
+ }
+
+ /* At this point, (*lineInfo[n]).quote may still be undefined. We
+ * don't wont to compute it every time M_TYPES is set, since this
+ * would slow down the "bottom" function unacceptably. A compromise
+ * solution is hence to call regexec() again, just to find out the
+ * length of the quote prefix.
+ */
+ if ((flags & M_SHOWCOLOR) && !(*lineInfo)[n].continuation &&
+ (*lineInfo)[n].type == MT_COLOR_QUOTED && (*lineInfo)[n].quote == NULL)
+ {
+ if (fill_buffer (f, last_pos, (*lineInfo)[n].offset, buf, fmt, sizeof (buf), &buf_ready) < 0)
+ {
+ if (change_last)
+ (*last)--;
+ return (-1);
+ }
+ regexec ((regex_t *) QuoteRegexp.rx, (char *) fmt, 1, pmatch, 0);
+ (*lineInfo)[n].quote = classify_quote (QuoteList,
+ (char *) fmt + pmatch[0].rm_so,
+ pmatch[0].rm_eo - pmatch[0].rm_so,
+ force_redraw, q_level);
+ }
+
+ if ((flags & M_SEARCH) && !(*lineInfo)[n].continuation && (*lineInfo)[n].search_cnt == -1)
+ {
+ if (fill_buffer (f, last_pos, (*lineInfo)[n].offset, buf, fmt, sizeof (buf), &buf_ready) < 0)
+ {
+ if (change_last)
+ (*last)--;
+ return (-1);
+ }
+
+ offset = 0;
+ (*lineInfo)[n].search_cnt = 0;
+ while (regexec (SearchRE, (char *) fmt + offset, 1, pmatch, (offset ? REG_NOTBOL : 0)) == 0)
+ {
+ if (++((*lineInfo)[n].search_cnt) > 1)
+ safe_realloc ((void **) &((*lineInfo)[n].search),
+ ((*lineInfo)[n].search_cnt) * sizeof (struct syntax_t));
+ else
+ (*lineInfo)[n].search = safe_malloc (sizeof (struct syntax_t));
+ pmatch[0].rm_so += offset;
+ pmatch[0].rm_eo += offset;
+ ((*lineInfo)[n].search)[(*lineInfo)[n].search_cnt - 1].first = pmatch[0].rm_so;
+ ((*lineInfo)[n].search)[(*lineInfo)[n].search_cnt - 1].last = pmatch[0].rm_eo;
+
+ if (pmatch[0].rm_eo == pmatch[0].rm_so)
+ offset++; /* avoid degenerate cases */
+ else
+ offset = pmatch[0].rm_eo;
+ if (!fmt[offset])
+ break;
+ }
+ }
+
+ if (!(flags & M_SHOW) && (*lineInfo)[n+1].offset > 0)
+ {
+ /* we've already scanned this line, so just exit */
+ return 0;
+ }
+ if ((flags & M_SHOWCOLOR) && *force_redraw && (*lineInfo)[n+1].offset > 0)
+ {
+ /* no need to try to display this line... */
+ return 1; /* fake display */
+ }
+
+ if ((b_read = fill_buffer (f, last_pos, (*lineInfo)[n].offset, buf, fmt,
+ sizeof (buf), &buf_ready)) < 0)
+ {
+ if (change_last)
+ (*last)--;
+ return (-1);
+ }
+
+ /* now chose a good place to break the line */
+
+ ch = -1; /* index of the last space or TAB */
+ cnt = 0;
+ col = option (OPTMARKERS) ? (*lineInfo)[n].continuation : 0;
+ while (col < COLS && cnt < b_read)
+ {
+ c = *buf_ptr++;
+ if (c == '\n')
+ break;
+
+ while (*buf_ptr == '\010' && cnt + 2 < b_read)
+ {
+ cnt += 2;
+ buf_ptr += 2;
+ c = buf[cnt];
+ }
+
+ if (*buf_ptr == '\033' && *(buf_ptr + 1) && *(buf_ptr + 1) == '[')
+ {
+ cnt = grok_ansi(buf, cnt+3, NULL);
+ cnt++;
+ buf_ptr = buf + cnt;
+ continue;
+ }
+
+ if (c == '\t')
+ {
+ ch = cnt;
+ /* expand TABs */
+ if ((t = (col & ~7) + 8) < COLS)
+ {
+ col = t;
+ cnt++;
+ }
+ else
+ break;
+ }
+ else if (IsPrint (c))
+ {
+ if (c == ' ')
+ ch = cnt;
+ col++;
+ cnt++;
+ }
+ else if (iscntrl (c) && c < '@')
+ {
+ if (c == '\r' && *buf_ptr == '\n')
+ cnt++;
+ else if (col < COLS - 1)
+ {
+ col += 2;
+ cnt++;
+ }
+ else
+ break;
+ }
+ else
+ {
+ col++;
+ cnt++;
+ }
+ }
+
+ /* move the break point only if smart_wrap is set */
+ if (option (OPTWRAP))
+ {
+ if (col == COLS)
+ {
+ if (ch != -1 && buf[cnt] != ' ' && buf[cnt] != '\t' && buf[cnt] != '\n' && buf[cnt] != '\r')
+ {
+ buf_ptr = buf + ch;
+ /* skip trailing blanks */
+ while (ch && (buf[ch] == ' ' || buf[ch] == '\t' || buf[ch] == '\r'))
+ ch--;
+ cnt = ch + 1;
+ }
+ else
+ buf_ptr = buf + cnt; /* a very long word... */
+ }
+ /* skip leading blanks on the next line too */
+ while (*buf_ptr == ' ' || *buf_ptr == '\t')
+ buf_ptr++;
+ }
+
+ if (*buf_ptr == '\r')
+ buf_ptr++;
+ if (*buf_ptr == '\n')
+ buf_ptr++;
+
+ if ((int) (buf_ptr - buf) < b_read && !(*lineInfo)[n+1].continuation)
+ append_line (*lineInfo, n, (int) (buf_ptr - buf));
+ (*lineInfo)[n+1].offset = (*lineInfo)[n].offset + (long) (buf_ptr - buf);
+
+ /* if we don't need to display the line we are done */
+ if (!(flags & M_SHOW))
+ return 0;
+
+ if (flags & M_SHOWCOLOR)
+ {
+ m = ((*lineInfo)[n].continuation) ? ((*lineInfo)[n].syntax)[0].first : n;
+ if ((*lineInfo)[m].type == MT_COLOR_HEADER)
+ def_color = ((*lineInfo)[m].syntax)[0].color;
+ else
+ def_color = (*lineInfo)[m].type;
+
+ attrset (def_color);
+#ifdef HAVE_BKGDSET
+ bkgdset (def_color | ' ');
+#endif
+ clrtoeol ();
+ SETCOLOR (MT_COLOR_NORMAL);
+ BKGDSET (MT_COLOR_NORMAL);
+ }
+ else
+ clrtoeol ();
+
+ /* display the line */
+ col = option (OPTMARKERS) ? (*lineInfo)[n].continuation : 0;
+ for (ch = 0, vch = 0; ch < cnt; ch++, vch++)
+ {
+ special = 0;
+
+ c = buf[ch];
+ while (buf[ch+1] == '\010' && ch+2 < b_read)
+ {
+ if (buf[ch+2] == c)
+ {
+ special = A_BOLD;
+ last_special = 1;
+ ch += 2;
+ }
+ else if (buf[ch] == '_' || buf[ch+2] == '_')
+ {
+ special = A_UNDERLINE;
+ last_special = 1;
+ ch += 2;
+ c = (buf[ch] == '_') ? buf[ch-2] : buf[ch];
+ }
+ else
+ {
+ special = 0; /* overstrike: nothing to do! */
+ last_special = 0;
+ ch += 2;
+ c = buf[ch];
+ }
+ }
+
+ /* Handle ANSI sequences */
+ if (c == '\033' && buf[ch+1] == '[')
+ {
+ ch = grok_ansi(buf, ch+2, &a);
+ c = buf[ch];
+ continue;
+ }
+
+ if (c == '\t')
+ {
+ if ((flags & (M_SHOWCOLOR | M_SEARCH)) || last_special || a.attr)
+ {
+ resolve_color (*lineInfo, n, vch, flags, special, &a);
+ if (!special)
+ last_special = 0;
+ }
+
+ t = (col & ~7) + 8;
+ while (col < t)
+ {
+ addch (' ');
+ col++;
+ }
+ }
+ else if (IsPrint (c))
+ {
+ if ((flags & (M_SHOWCOLOR | M_SEARCH)) || special || last_special
+ || a.attr)
+ resolve_color (*lineInfo, n, vch, flags, special, &a);
+ if (!special)
+ last_special = 0;
+
+ addch (c);
+ col++;
+ }
+ else if (iscntrl (c) && c < '@')
+ {
+ if ((c != '\r' && c !='\n') || (buf[ch+1] != '\n' && buf[ch+1] != '\0'))
+ {
+ if ((flags & (M_SHOWCOLOR | M_SEARCH)) || last_special || a.attr)
+ {
+ resolve_color (*lineInfo, n, vch, flags, special, &a);
+ if (!special)
+ last_special = 0;
+ }
+
+ addch ('^');
+ addch (c + '@');
+ col += 2;
+ }
+ }
+ else
+ {
+ if ((flags & (M_SHOWCOLOR | M_SEARCH)) || last_special || a.attr)
+ {
+ resolve_color (*lineInfo, n, vch, flags, special, &a);
+ if (!special)
+ last_special = 0;
+ }
+
+ if (ISSPACE (c))
+ addch (c); /* unbreakable space */
+ else
+ addch ('.');
+ col++;
+ }
+ }
+
+ /* avoid a bug in ncurses... */
+#ifndef USE_SLANG_CURSES
+ if (col == 0)
+ {
+ SETCOLOR (MT_COLOR_NORMAL);
+ addch (' ');
+ }
+#endif
+
+ /* end the last color pattern (needed by S-Lang) */
+ if (last_special || (col != COLS && (flags & (M_SHOWCOLOR | M_SEARCH))))
+ resolve_color (*lineInfo, n, vch, flags, 0, &a);
+
+ /* ncurses always wraps lines when you get to the right side of the
+ * screen, but S-Lang seems to only wrap if the next character is *not*
+ * a newline (grr!).
+ */
+#ifndef USE_SLANG_CURSES
+ if (col < COLS)
+#endif
+ addch ('\n');
+
+ /* build a return code */
+ if (!(flags & M_SHOW))
+ flags = 0;
+
+ return (flags);
+}
+
+static int
+upNLines (int nlines, struct line_t *info, int cur, int hiding)
+{
+ while (cur > 0 && nlines > 0)
+ {
+ cur--;
+ if (!hiding || info[cur].type != MT_COLOR_QUOTED)
+ nlines--;
+ }
+
+ return cur;
+}
+
+/* This pager is actually not so simple as it once was. It now operates in
+ two modes: one for viewing messages and the other for viewing help. These
+ can be distinguished by whether or not ``hdr'' is NULL. The ``hdr'' arg
+ is there so that we can do operations on the current message without the
+ need to pop back out to the main-menu. */
+int
+mutt_pager (const char *banner, const char *fname, int do_color, pager_t *extra)
+{
+ static char searchbuf[STRING];
+ char buffer[LONG_STRING];
+ char helpstr[SHORT_STRING];
+ int maxLine, lastLine = 0;
+ struct line_t *lineInfo;
+ struct q_class_t *QuoteList = NULL;
+ int i, j, ch = 0, rc = -1, hideQuoted = 0, q_level = 0, force_redraw = 0;
+ int lines = 0, curline = 0, topline = 0, oldtopline = 0, err, first = 1;
+ int r = -1;
+ int redraw = REDRAW_FULL;
+ FILE *fp = NULL;
+ long last_pos = 0, last_offset = 0;
+ int old_smart_wrap, old_markers;
+ struct stat sb;
+ regex_t SearchRE;
+ int SearchCompiled = 0, SearchFlag = 0, SearchBack = 0;
+ int has_types = (IsHeader(extra) || do_color); /* main message or rfc822 attachment */
+
+ int bodyoffset = 1; /* offset of first line of real text */
+ int statusoffset = 0; /* offset for the status bar */
+ int helpoffset = LINES - 2; /* offset for the help bar. */
+ int bodylen = LINES - 2 - bodyoffset; /* length of displayable area */
+
+ MUTTMENU *index = NULL; /* the Pager Index (PI) */
+ int indexoffset = 0; /* offset for the PI */
+ int indexlen = PagerIndexLines; /* indexlen not always == PIL */
+ int indicator = indexlen / 3; /* the indicator line of the PI */
+ int old_PagerIndexLines; /* some people want to resize it
+ * while inside the pager... */
+
+ static struct mapping_t PagerHelp[] = {
+ { "Exit", OP_PAGER_EXIT },
+ { "PrevPg", OP_PREV_PAGE },
+ { "NextPg ", OP_NEXT_PAGE },
+ { NULL, 0 }
+ };
+ static struct mapping_t PagerHelpExtra[] = {
+ { "Attach", OP_VIEW_ATTACHMENTS },
+ { "Del", OP_DELETE },
+ { "Reply", OP_REPLY },
+ { "Next ", OP_MAIN_NEXT_UNDELETED },
+ { NULL, 0 }
+ };
+
+ do_color = do_color ? M_SHOWCOLOR : M_SHOWFLAT;
+
+ if ((fp = fopen (fname, "r")) == NULL)
+ {
+ mutt_perror (fname);
+ return (-1);
+ }
+
+ if (stat (fname, &sb) != 0)
+ {
+ mutt_perror (fname);
+ fclose (fp);
+ return (-1);
+ }
+ unlink (fname);
+
+ /* Initialize variables */
+
+ if (IsHeader (extra) && !extra->hdr->read)
+ {
+ Context->msgnotreadyet = extra->hdr->msgno;
+ mutt_set_flag (Context, extra->hdr, M_READ, 1);
+ }
+
+ lineInfo = safe_malloc (sizeof (struct line_t) * (maxLine = LINES));
+ for (i = 0 ; i < maxLine ; i++)
+ {
+ memset (&lineInfo[i], 0, sizeof (struct line_t));
+ lineInfo[i].type = -1;
+ lineInfo[i].search_cnt = -1;
+ lineInfo[i].syntax = safe_malloc (sizeof (struct syntax_t));
+ (lineInfo[i].syntax)[0].first = (lineInfo[i].syntax)[0].last = -1;
+ }
+
+ mutt_compile_help (helpstr, sizeof (helpstr), MENU_PAGER, PagerHelp);
+ if (IsHeader (extra))
+ {
+ mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER, PagerHelpExtra);
+ strcat (helpstr, buffer);
+ }
+ if (!InHelp)
+ {
+ mutt_make_help (buffer, sizeof (buffer), "Help", MENU_PAGER, OP_HELP);
+ strcat (helpstr, buffer);
+ }
+
+ while (ch != -1)
+ {
+ mutt_curs_set (0);
+
+ if (redraw & REDRAW_FULL)
+ {
+ SETCOLOR (MT_COLOR_NORMAL);
+ /* clear() doesn't optimize screen redraws */
+ move (0, 0);
+ clrtobot ();
+
+ if (IsHeader (extra) && Context->vcount + 1 < PagerIndexLines)
+ indexlen = Context->vcount + 1;
+ else
+ indexlen = PagerIndexLines;
+
+ indicator = indexlen / 3;
+
+ if (option (OPTSTATUSONTOP))
+ {
+ indexoffset = 0;
+ statusoffset = IsHeader (extra) ? indexlen : 0;
+ bodyoffset = statusoffset + 1;
+ helpoffset = LINES - 2;
+ bodylen = helpoffset - bodyoffset;
+ if (!option (OPTHELP))
+ bodylen++;
+ }
+ else
+ {
+ helpoffset = 0;
+ indexoffset = 1;
+ statusoffset = LINES - 2;
+ if (!option (OPTHELP))
+ indexoffset = 0;
+ bodyoffset = indexoffset + (IsHeader (extra) ? indexlen : 0);
+ bodylen = statusoffset - bodyoffset;
+ }
+
+ if (option (OPTHELP))
+ {
+ SETCOLOR (MT_COLOR_STATUS);
+ mvprintw (helpoffset, 0, "%-*.*s", COLS, COLS, helpstr);
+ SETCOLOR (MT_COLOR_NORMAL);
+ }
+
+ if (IsHeader (extra) && PagerIndexLines)
+ {
+ if (index == NULL)
+ {
+ /* only allocate the space if/when we need the index.
+ Initialise the menu as per the main index */
+ index = mutt_new_menu();
+ index->menu = MENU_MAIN;
+ index->make_entry = index_make_entry;
+ index->color = index_color;
+ index->max = Context->vcount;
+ index->current = extra->hdr->virtual;
+ }
+
+ SETCOLOR (MT_COLOR_NORMAL);
+ index->offset = indexoffset + (option (OPTSTATUSONTOP) ? 1 : 0);
+
+ index->pagelen = indexlen - 1;
+
+ /* some fudge to work out where abouts the indicator should go */
+ if (index->current - indicator < 0)
+ index->top = 0;
+ else if (index->max - index->current < index->pagelen - indicator)
+ index->top = index->max - index->pagelen;
+ else
+ index->top = index->current - indicator;
+
+ menu_redraw_index(index);
+ }
+
+ redraw |= REDRAW_BODY | REDRAW_INDEX | REDRAW_STATUS;
+ mutt_show_error ();
+ }
+
+ if ((redraw & REDRAW_BODY) || topline != oldtopline)
+ {
+ do {
+ move (bodyoffset, 0);
+ curline = oldtopline = topline;
+ lines = 0;
+ force_redraw = 0;
+
+ while (lines < bodylen && lineInfo[curline].offset <= sb.st_size - 1)
+ {
+ if (display_line (fp, &last_pos, &lineInfo, curline, &lastLine,
+ &maxLine, do_color | hideQuoted | SearchFlag,
+ &QuoteList, &q_level, &force_redraw, &SearchRE) > 0)
+ lines++;
+ curline++;
+ }
+ last_offset = lineInfo[curline].offset;
+ } while (force_redraw);
+
+ SETCOLOR (MT_COLOR_TILDE);
+ BKGDSET (MT_COLOR_TILDE);
+ while (lines < bodylen)
+ {
+ clrtoeol ();
+ if (option (OPTTILDE))
+ addch ('~');
+ addch ('\n');
+ lines++;
+ }
+ /* We are going to update the pager status bar, so it isn't
+ * necessary to reset to normal color now. */
+
+ redraw |= REDRAW_STATUS; /* need to update the % seen */
+ }
+
+ if (redraw & REDRAW_STATUS)
+ {
+ /* print out the pager status bar */
+ SETCOLOR (MT_COLOR_STATUS);
+ BKGDSET (MT_COLOR_STATUS);
+ CLEARLINE (statusoffset);
+ if (IsHeader (extra))
+ {
+ _mutt_make_string (buffer,
+ COLS-9 < sizeof (buffer) ? COLS-9 : sizeof (buffer),
+ NONULL (PagerFmt), extra->hdr, M_FORMAT_MAKEPRINT);
+ }
+ printw ("%-*.*s -- (", COLS-10, COLS-10, IsHeader (extra) ? buffer : banner);
+ if (last_pos < sb.st_size - 1)
+ printw ("%d%%)", (int) (100 * last_offset / sb.st_size));
+ else
+ addstr (topline == 0 ? "all)" : "end)");
+ BKGDSET (MT_COLOR_NORMAL);
+ SETCOLOR (MT_COLOR_NORMAL);
+ }
+
+ if ((redraw & REDRAW_INDEX) && index)
+ {
+ /* redraw the pager_index indicator, because the
+ * flags for this message might have changed. */
+ menu_redraw_current (index);
+
+ /* print out the index status bar */
+ menu_status_line (buffer, sizeof (buffer), index, Status);
+ move (indexoffset + (option (OPTSTATUSONTOP) ? 0 : (indexlen - 1)), 0);
+ SETCOLOR (MT_COLOR_STATUS);
+ printw ("%-*.*s", COLS, COLS, buffer);
+ SETCOLOR (MT_COLOR_NORMAL);
+ }
+
+ redraw = 0;
+
+ move (statusoffset, COLS-1);
+ mutt_refresh ();
+ ch = km_dokey (MENU_PAGER);
+ mutt_clear_error ();
+ mutt_curs_set (1);
+
+ if (Signals & S_INTERRUPT)
+ {
+ mutt_query_exit ();
+ continue;
+ }
+#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
+ else if (Signals & S_SIGWINCH)
+ {
+ mutt_resize_screen ();
+
+ for (i = 0; i < maxLine; i++)
+ {
+ lineInfo[i].offset = 0;
+ lineInfo[i].type = -1;
+ lineInfo[i].continuation = 0;
+ lineInfo[i].chunks = 0;
+ lineInfo[i].search_cnt = -1;
+ lineInfo[i].quote = NULL;
+
+ safe_realloc ((void **)&(lineInfo[i].syntax), sizeof (struct syntax_t));
+ if (SearchCompiled && lineInfo[i].search)
+ safe_free ((void **) &(lineInfo[i].search));
+ }
+
+ if (SearchCompiled)
+ {
+ regfree (&SearchRE);
+ SearchCompiled = 0;
+ SearchFlag = 0;
+ }
+
+ lastLine = 0;
+ topline = 0;
+
+ redraw = REDRAW_FULL;
+ Signals &= ~S_SIGWINCH;
+ ch = 0;
+ continue;
+ }
+#endif
+ else if (ch == -1)
+ {
+ ch = 0;
+ continue;
+ }
+
+ rc = ch;
+
+ switch (ch)
+ {
+ case OP_PAGER_EXIT:
+ rc = -1;
+ ch = -1;
+ break;
+
+ case OP_NEXT_PAGE:
+ if (lineInfo[curline].offset < sb.st_size-1)
+ {
+ topline = upNLines (PagerContext, lineInfo, curline, hideQuoted);
+ }
+ else if (option (OPTPAGERSTOP))
+ {
+ /* emulate "less -q" and don't go on to the next message. */
+ mutt_error ("Bottom of message is shown.");
+ }
+ else
+ {
+ /* end of the current message, so display the next message. */
+ rc = OP_MAIN_NEXT_UNDELETED;
+ ch = -1;
+ }
+ break;
+
+ case OP_PREV_PAGE:
+ if (topline != 0)
+ {
+ topline = upNLines (bodylen-PagerContext, lineInfo, topline, hideQuoted);
+ }
+ else
+ mutt_error ("Top of message is shown.");
+ break;
+
+ case OP_NEXT_LINE:
+ if (lineInfo[curline].offset < sb.st_size-1)
+ {
+ topline++;
+ if (hideQuoted)
+ {
+ while (lineInfo[topline].type == MT_COLOR_QUOTED &&
+ topline < lastLine)
+ topline++;
+ }
+ }
+ else
+ mutt_error ("Bottom of message is shown.");
+ break;
+
+ case OP_PREV_LINE:
+ if (topline)
+ topline = upNLines (1, lineInfo, topline, hideQuoted);
+ else
+ mutt_error ("Top of message is shown.");
+ break;
+
+ case OP_PAGER_TOP:
+ topline = 0;
+ break;
+
+ case OP_HALF_UP:
+ if (topline)
+ topline = upNLines (bodylen/2, lineInfo, topline, hideQuoted);
+ else
+ mutt_error ("Top of message is shown.");
+ break;
+
+ case OP_HALF_DOWN:
+ if (lineInfo[curline].offset < sb.st_size-1)
+ {
+ topline = upNLines (bodylen/2, lineInfo, curline, hideQuoted);
+ }
+ else if (option (OPTPAGERSTOP))
+ {
+ /* emulate "less -q" and don't go on to the next message. */
+ mutt_error ("Bottom of message is shown.");
+ }
+ else
+ {
+ /* end of the current message, so display the next message. */
+ rc = OP_MAIN_NEXT_UNDELETED;
+ ch = -1;
+ }
+ break;
+
+ case OP_SEARCH_NEXT:
+ case OP_SEARCH_OPPOSITE:
+ if (SearchCompiled)
+ {
+ if ((!SearchBack && ch==OP_SEARCH_NEXT) ||
+ (SearchBack &&ch==OP_SEARCH_OPPOSITE))
+ {
+ /* searching forward */
+ for (i = topline + 1; i < lastLine; i++)
+ {
+ if ((!hideQuoted || lineInfo[i].type != MT_COLOR_QUOTED) &&
+ !lineInfo[i].continuation && lineInfo[i].search_cnt > 0)
+ break;
+ }
+
+ if (i < lastLine)
+ topline = i;
+ else
+ mutt_error ("Not found.");
+ }
+ else
+ {
+ /* searching backward */
+ for (i = topline - 1; i >= 0; i--)
+ {
+ if ((!hideQuoted || (has_types &&
+ lineInfo[i].type != MT_COLOR_QUOTED)) &&
+ !lineInfo[i].continuation && lineInfo[i].search_cnt > 0)
+ break;
+ }
+
+ if (i >= 0)
+ topline = i;
+ else
+ mutt_error ("Not found.");
+ }
+
+ if (lineInfo[topline].search_cnt > 0)
+ SearchFlag = M_SEARCH;
+
+ break;
+ }
+ /* no previous search pattern, so fall through to search */
+
+ case OP_SEARCH:
+ case OP_SEARCH_REVERSE:
+ /* leave SearchBack alone if ch == OP_SEARCH_NEXT */
+ if (ch == OP_SEARCH)
+ SearchBack = 0;
+ else if (ch == OP_SEARCH_REVERSE)
+ SearchBack = 1;
+
+ if (mutt_get_field ((SearchBack ? "Reverse search: " : "Search: "),
+ searchbuf, sizeof (searchbuf), M_CLEAR) != 0 ||
+ !searchbuf[0])
+ break;
+
+ if (SearchCompiled)
+ {
+ regfree (&SearchRE);
+ for (i = 0; i < lastLine; i++)
+ {
+ if (lineInfo[i].search)
+ safe_free ((void **) &(lineInfo[i].search));
+ lineInfo[i].search_cnt = -1;
+ }
+ }
+
+ if ((err = REGCOMP (&SearchRE, searchbuf, REG_NEWLINE | mutt_which_case (searchbuf))) != 0)
+ {
+ regerror (err, &SearchRE, buffer, sizeof (buffer));
+ mutt_error ("%s", buffer);
+ regfree (&SearchRE);
+ for (i = 0; i < maxLine ; i++)
+ {
+ /* cleanup */
+ if (lineInfo[i].search)
+ safe_free ((void **) &(lineInfo[i].search));
+ lineInfo[i].search_cnt = -1;
+ }
+ SearchFlag = 0;
+ SearchCompiled = 0;
+ }
+ else
+ {
+ SearchCompiled = 1;
+ /* update the search pointers */
+ i = 0;
+ while (display_line (fp, &last_pos, &lineInfo, i, &lastLine,
+ &maxLine, M_SEARCH, &QuoteList, &q_level,
+ &force_redraw, &SearchRE) == 0)
+ i++;
+
+ if (!SearchBack)
+ {
+ /* searching forward */
+ for (i = topline; i < lastLine; i++)
+ {
+ if ((!hideQuoted || lineInfo[i].type != MT_COLOR_QUOTED) &&
+ !lineInfo[i].continuation && lineInfo[i].search_cnt > 0)
+ break;
+ }
+
+ if (i < lastLine) topline = i;
+ }
+ else
+ {
+ /* searching backward */
+ for (i = topline; i >= 0; i--)
+ {
+ if ((!hideQuoted || lineInfo[i].type != MT_COLOR_QUOTED) &&
+ !lineInfo[i].continuation && lineInfo[i].search_cnt > 0)
+ break;
+ }
+
+ if (i >= 0) topline = i;
+ }
+
+ if (lineInfo[topline].search_cnt == 0)
+ {
+ SearchFlag = 0;
+ mutt_error ("Not found.");
+ }
+ else
+ SearchFlag = M_SEARCH;
+ }
+ redraw = REDRAW_BODY;
+ break;
+
+ case OP_SEARCH_TOGGLE:
+ if (SearchCompiled)
+ {
+ SearchFlag ^= M_SEARCH;
+ redraw = REDRAW_BODY;
+ }
+ break;
+
+ case OP_HELP:
+ /* don't let the user enter the help-menu from the help screen! */
+ if (! InHelp)
+ {
+ InHelp = 1;
+ mutt_help (MENU_PAGER);
+ redraw = REDRAW_FULL;
+ InHelp = 0;
+ }
+ else
+ mutt_error ("Help is currently being shown.");
+ break;
+
+ case OP_PAGER_HIDE_QUOTED:
+ if (has_types)
+ {
+ hideQuoted = hideQuoted ? 0 : M_HIDE;
+ if (hideQuoted && lineInfo[topline].type == MT_COLOR_QUOTED)
+ topline = upNLines (1, lineInfo, topline, hideQuoted);
+ else
+ redraw = REDRAW_BODY;
+ }
+ break;
+
+ case OP_PAGER_SKIP_QUOTED:
+ if (has_types)
+ {
+ int dretval = 0;
+ int new_topline = topline;
+
+ while ((new_topline < lastLine ||
+ (0 == (dretval = display_line (fp, &last_pos, &lineInfo,
+ new_topline, &lastLine, &maxLine, M_TYPES,
+ &QuoteList, &q_level, &force_redraw, &SearchRE))))
+ && lineInfo[new_topline].type != MT_COLOR_QUOTED)
+ new_topline++;
+
+ if (dretval < 0)
+ {
+ mutt_error ("No more quoted text.");
+ break;
+ }
+
+ while ((new_topline < lastLine ||
+ (0 == (dretval = display_line (fp, &last_pos, &lineInfo,
+ new_topline, &lastLine, &maxLine, M_TYPES,
+ &QuoteList, &q_level, &force_redraw, &SearchRE))))
+ && lineInfo[new_topline].type == MT_COLOR_QUOTED)
+ new_topline++;
+
+ if (dretval < 0)
+ {
+ mutt_error ("No more unquoted text after quoted text.");
+ break;
+ }
+ topline = new_topline;
+ }
+ break;
+
+ case OP_PAGER_BOTTOM: /* move to the end of the file */
+ if (lineInfo[curline].offset < sb.st_size - 1)
+ {
+ i = curline;
+ /* make sure the types are defined to the end of file */
+ while (display_line (fp, &last_pos, &lineInfo, i, &lastLine,
+ &maxLine, (has_types ? M_TYPES : M_NOSHOW),
+ &QuoteList, &q_level, &force_redraw,
+ &SearchRE) == 0)
+ i++;
+ topline = upNLines (bodylen, lineInfo, lastLine, hideQuoted);
+ }
+ else
+ mutt_error ("Bottom of message is shown.");
+ break;
+
+ case OP_REDRAW:
+ clearok (stdscr, TRUE);
+ redraw = REDRAW_FULL;
+ break;
+
+ case OP_NULL:
+ km_error_key (MENU_PAGER);
+ break;
+
+ /* --------------------------------------------------------------------
+ * The following are operations on the current message rather than
+ * adjusting the view of the message.
+ */
+
+ case OP_BOUNCE_MESSAGE:
+ CHECK_MODE(IsHeader (extra));
+ ci_bounce_message (extra->hdr, &redraw);
+ break;
+
+ case OP_CREATE_ALIAS:
+ CHECK_MODE(IsHeader (extra));
+ mutt_create_alias (extra->hdr->env, NULL);
+ MAYBE_REDRAW (redraw);
+ break;
+
+ case OP_DELETE:
+ CHECK_MODE(IsHeader (extra));
+ CHECK_READONLY;
+ mutt_set_flag (Context, extra->hdr, M_DELETE, 1);
+ redraw = REDRAW_STATUS | REDRAW_INDEX;
+ if (option (OPTRESOLVE))
+ {
+ ch = -1;
+ rc = OP_MAIN_NEXT_UNDELETED;
+ };
+ break;
+
+ case OP_DELETE_THREAD:
+ case OP_DELETE_SUBTHREAD:
+ CHECK_MODE(IsHeader (extra));
+ CHECK_READONLY;
+
+ r = mutt_thread_set_flag (extra->hdr, M_DELETE, 1,
+ ch == OP_DELETE_THREAD ? 0 : 1);
+
+ if (r != -1)
+ {
+ if (option (OPTRESOLVE))
+ {
+ rc = (ch == OP_DELETE_THREAD) ?
+ OP_MAIN_NEXT_THREAD : OP_MAIN_NEXT_SUBTHREAD;
+ ch = -1;
+ }
+
+ if (!option (OPTRESOLVE) && PagerIndexLines)
+ redraw = REDRAW_FULL;
+ else
+ redraw = REDRAW_STATUS | REDRAW_INDEX;
+ }
+ break;
+
+ case OP_DISPLAY_ADDRESS:
+ CHECK_MODE(IsHeader (extra));
+ mutt_display_address (extra->hdr->env->from);
+ break;
+
+ case OP_ENTER_COMMAND:
+ old_smart_wrap = option (OPTWRAP);
+ old_markers = option (OPTMARKERS);
+ old_PagerIndexLines = PagerIndexLines;
+
+ mutt_enter_command ();
+
+ if (option (OPTNEEDRESORT))
+ {
+ unset_option (OPTNEEDRESORT);
+ CHECK_MODE(IsHeader (extra));
+ set_option (OPTNEEDRESORT);
+ }
+
+ if (old_PagerIndexLines != PagerIndexLines)
+ {
+ if (index)
+ mutt_menuDestroy (&index);
+ index = NULL;
+ }
+
+ if (option (OPTWRAP) != old_smart_wrap ||
+ option (OPTMARKERS) != old_markers)
+ {
+ /* count the real lines above */
+ j = 0;
+ for (i = 0; i <= topline; i++)
+ {
+ if (!lineInfo[i].continuation)
+ j++;
+ }
+
+ /* we need to restart the whole thing */
+ for (i = 0; i < maxLine; i++)
+ {
+ lineInfo[i].offset = 0;
+ lineInfo[i].type = -1;
+ lineInfo[i].continuation = 0;
+ lineInfo[i].chunks = 0;
+ lineInfo[i].search_cnt = -1;
+ lineInfo[i].quote = NULL;
+
+ safe_realloc ((void **)&(lineInfo[i].syntax), sizeof (struct syntax_t));
+ if (SearchCompiled && lineInfo[i].search)
+ safe_free ((void **) &(lineInfo[i].search));
+ }
+
+ if (SearchCompiled)
+ {
+ regfree (&SearchRE);
+ SearchCompiled = 0;
+ }
+ SearchFlag = 0;
+
+ /* try to keep the old position */
+ topline = 0;
+ lastLine = 0;
+ while (j > 0 && display_line (fp, &last_pos, &lineInfo, topline,
+ &lastLine, &maxLine,
+ (has_types ? M_TYPES : 0),
+ &QuoteList, &q_level, &force_redraw,
+ &SearchRE) == 0)
+ {
+ if (! lineInfo[topline].continuation)
+ j--;
+ if (j > 0)
+ topline++;
+ }
+
+ ch = 0;
+ }
+
+ if (option (OPTFORCEREDRAWPAGER))
+ redraw = REDRAW_FULL;
+ unset_option (OPTFORCEREDRAWINDEX);
+ unset_option (OPTFORCEREDRAWPAGER);
+ break;
+
+ case OP_FLAG_MESSAGE:
+ CHECK_MODE(IsHeader (extra));
+ CHECK_READONLY;
+ mutt_set_flag (Context, extra->hdr, M_FLAG, !extra->hdr->flagged);
+ redraw = REDRAW_STATUS | REDRAW_INDEX;
+ if (option (OPTRESOLVE))
+ {
+ ch = -1;
+ rc = OP_MAIN_NEXT_UNDELETED;
+ }
+ break;
+
+ case OP_PIPE:
+ CHECK_MODE(IsHeader (extra) || IsAttach (extra));
+ if (IsAttach (extra))
+ mutt_pipe_attachment_list (extra->fp, 0, extra->bdy, 0);
+ else
+ mutt_pipe_message (extra->hdr);
+ break;
+
+ case OP_PRINT:
+ CHECK_MODE(IsHeader (extra));
+ mutt_print_message (extra->hdr);
+ break;
+
+ case OP_MAIL:
+ CHECK_MODE(IsHeader (extra));
+ ci_send_message (0, NULL, NULL, NULL, NULL);
+ redraw = REDRAW_FULL;
+ break;
+
+ case OP_REPLY:
+ CHECK_MODE(IsHeader (extra));
+ ci_send_message (SENDREPLY, NULL, NULL, extra->ctx, extra->hdr);
+ redraw = REDRAW_FULL;
+ break;
+
+ case OP_RECALL_MESSAGE:
+ CHECK_MODE(IsHeader (extra));
+ ci_send_message (SENDPOSTPONED, NULL, NULL, extra->ctx, extra->hdr);
+ redraw = REDRAW_FULL;
+ break;
+
+ case OP_GROUP_REPLY:
+ CHECK_MODE(IsHeader (extra));
+ ci_send_message (SENDREPLY | SENDGROUPREPLY, NULL, NULL, extra->ctx, extra->hdr);
+ redraw = REDRAW_FULL;
+ break;
+
+ case OP_LIST_REPLY:
+ CHECK_MODE(IsHeader (extra));
+ ci_send_message (SENDREPLY | SENDLISTREPLY, NULL, NULL, extra->ctx, extra->hdr);
+ redraw = REDRAW_FULL;
+ break;
+
+ case OP_FORWARD_MESSAGE:
+ CHECK_MODE(IsHeader (extra));
+ ci_send_message (SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr);
+ redraw = REDRAW_FULL;
+ break;
+
+ case OP_SAVE:
+ if (IsAttach (extra))
+ {
+ mutt_save_attachment_list (extra->fp, 0, extra->bdy);
+ break;
+ }
+ /* fall through */
+ case OP_COPY_MESSAGE:
+ case OP_DECODE_SAVE:
+ case OP_DECODE_COPY:
+ CHECK_MODE(IsHeader (extra));
+ if (mutt_save_message (extra->hdr,
+ (ch == OP_SAVE || ch == OP_DECODE_SAVE),
+ (ch == OP_DECODE_SAVE || ch == OP_DECODE_COPY),
+ &redraw) == 0 && (ch == OP_SAVE || ch == OP_DECODE_SAVE))
+ {
+ if (option (OPTRESOLVE))
+ {
+ ch = -1;
+ rc = OP_MAIN_NEXT_UNDELETED;
+ }
+ else
+ redraw |= REDRAW_STATUS | REDRAW_INDEX;
+ }
+ MAYBE_REDRAW (redraw);
+ break;
+
+ case OP_SHELL_ESCAPE:
+ mutt_shell_escape ();
+ MAYBE_REDRAW (redraw);
+ break;
+
+ case OP_TAG:
+ CHECK_MODE(IsHeader (extra));
+ mutt_set_flag (Context, extra->hdr, M_TAG, !extra->hdr->tagged);
+ redraw = REDRAW_STATUS | REDRAW_INDEX;
+ if (option (OPTRESOLVE))
+ {
+ ch = -1;
+ rc = OP_MAIN_NEXT_UNDELETED;
+ }
+ break;
+
+ case OP_TOGGLE_NEW:
+ CHECK_MODE(IsHeader (extra));
+ CHECK_READONLY;
+ if (extra->hdr->read || extra->hdr->old)
+ mutt_set_flag (Context, extra->hdr, M_NEW, 1);
+ else if (!first)
+ mutt_set_flag (Context, extra->hdr, M_READ, 1);
+ first = 0;
+ Context->msgnotreadyet = -1;
+ redraw = REDRAW_STATUS | REDRAW_INDEX;
+ if (option (OPTRESOLVE))
+ {
+ ch = -1;
+ rc = OP_NEXT_ENTRY;
+ }
+ break;
+
+ case OP_UNDELETE:
+ CHECK_MODE(IsHeader (extra));
+ CHECK_READONLY;
+ mutt_set_flag (Context, extra->hdr, M_DELETE, 0);
+ redraw = REDRAW_STATUS | REDRAW_INDEX;
+ if (option (OPTRESOLVE))
+ {
+ ch = -1;
+ rc = OP_NEXT_ENTRY;
+ }
+ break;
+
+ case OP_UNDELETE_THREAD:
+ case OP_UNDELETE_SUBTHREAD:
+ CHECK_MODE(IsHeader (extra));
+ CHECK_READONLY;
+
+ r = mutt_thread_set_flag (extra->hdr, M_DELETE, 0,
+ ch == OP_UNDELETE_THREAD ? 0 : 1);
+
+ if (r != -1)
+ {
+ if (option (OPTRESOLVE))
+ {
+ rc = (ch == OP_DELETE_THREAD) ?
+ OP_MAIN_NEXT_THREAD : OP_MAIN_NEXT_SUBTHREAD;
+ ch = -1;
+ }
+
+ if (!option (OPTRESOLVE) && PagerIndexLines)
+ redraw = REDRAW_FULL;
+ else
+ redraw = REDRAW_STATUS | REDRAW_INDEX;
+ }
+ break;
+
+ case OP_VERSION:
+ mutt_version ();
+ break;
+
+ case OP_VIEW_ATTACHMENTS:
+ CHECK_MODE(IsHeader (extra));
+ mutt_view_attachments (extra->hdr);
+ if (extra->hdr->attach_del)
+ Context->changed = 1;
+ redraw = REDRAW_FULL;
+ break;
+
+
+
+#ifdef _PGPPATH
+ case OP_FORGET_PASSPHRASE:
+ mutt_forget_passphrase ();
+ break;
+
+ case OP_MAIL_KEY:
+ CHECK_MODE(IsHeader(extra));
+ ci_send_message (SENDKEY, NULL, NULL, extra->ctx, extra->hdr);
+ redraw = REDRAW_FULL;
+ break;
+
+ case OP_EXTRACT_KEYS:
+ CHECK_MODE(IsHeader(extra));
+ pgp_extract_keys_from_messages(extra->hdr);
+ redraw = REDRAW_FULL;
+ break;
+#endif /* _PGPPATH */
+
+
+
+ default:
+ ch = -1;
+ break;
+ }
+ }
+
+ fclose (fp);
+ if (IsHeader (extra))
+ Context->msgnotreadyet = -1;
+
+ cleanup_quote (&QuoteList);
+
+ for (i = 0; i < maxLine ; i++)
+ {
+ safe_free ((void **) &(lineInfo[i].syntax));
+ if (SearchCompiled && lineInfo[i].search)
+ safe_free ((void **) &(lineInfo[i].search));
+ }
+ if (SearchCompiled)
+ {
+ regfree (&SearchRE);
+ SearchCompiled = 0;
+ }
+ safe_free ((void **) &lineInfo);
+ if (index)
+ mutt_menuDestroy(&index);
+ return (rc != -1 ? rc : 0);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+typedef struct
+{
+ CONTEXT *ctx; /* current mailbox */
+ HEADER *hdr; /* current message */
+ BODY *bdy; /* current attachment */
+ FILE *fp; /* source stream */
+} pager_t;
+
+int mutt_do_pager (const char *, const char *, int, pager_t *);
+int mutt_pager (const char *, const char *, int, pager_t *);
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_regex.h"
+#include "mailbox.h"
+#include "mime.h"
+#include "rfc2047.h"
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif /* _PGPPATH */
+
+
+
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+/* Reads an arbitrarily long header field, and looks ahead for continuation
+ * lines. ``line'' must point to a dynamically allocated string; it is
+ * increased if more space is required to fit the whole line.
+ */
+static char *read_rfc822_line (FILE *f, char *line, size_t *linelen)
+{
+ char *buf = line;
+ char ch;
+ size_t offset = 0;
+
+ FOREVER
+ {
+ if (fgets (buf, *linelen - offset, f) == NULL || /* end of file or */
+ (ISSPACE (*line) && !offset)) /* end of headers */
+ {
+ *line = 0;
+ return (line);
+ }
+
+ buf += strlen (buf) - 1;
+ if (*buf == '\n')
+ {
+ /* we did get a full line. remove trailing space */
+ while (ISSPACE (*buf))
+ *buf-- = 0; /* we cannot come beyond line's beginning because
+ * it begins with a non-space */
+
+ /* check to see if the next line is a continuation line */
+ if ((ch = fgetc (f)) != ' ' && ch != '\t')
+ {
+ ungetc (ch, f);
+ return (line); /* next line is a separate header field or EOH */
+ }
+
+ /* eat tabs and spaces from the beginning of the continuation line */
+ while ((ch = fgetc (f)) == ' ' || ch == '\t')
+ ;
+ ungetc (ch, f);
+ *++buf = ' '; /* string is still terminated because we removed
+ at least one whitespace char above */
+ }
+
+ buf++;
+ offset = buf - line;
+ if (*linelen < offset + STRING)
+ {
+ /* grow the buffer */
+ *linelen += STRING;
+ safe_realloc ((void **) &line, *linelen);
+ buf = line + offset;
+ }
+ }
+ /* not reached */
+}
+
+static LIST *mutt_parse_references (char *s)
+{
+ LIST *t, *lst = NULL;
+
+ while ((s = strtok (s, " \t")) != NULL)
+ {
+ /*
+ * some mail clients add other garbage besides message-ids, so do a quick
+ * check to make sure this looks like a valid message-id
+ */
+ if (*s == '<')
+ {
+ t = (LIST *)safe_malloc (sizeof (LIST));
+ t->data = safe_strdup (s);
+ t->next = lst;
+ lst = t;
+ }
+ s = NULL;
+ }
+
+ return (lst);
+}
+
+int mutt_check_encoding (const char *c)
+{
+ if (strncasecmp ("7bit", c, sizeof ("7bit")-1) == 0)
+ return (ENC7BIT);
+ else if (strncasecmp ("8bit", c, sizeof ("8bit")-1) == 0)
+ return (ENC8BIT);
+ else if (strncasecmp ("binary", c, sizeof ("binary")-1) == 0)
+ return (ENCBINARY);
+ else if (strncasecmp ("quoted-printable", c, sizeof ("quoted-printable")-1) == 0)
+ return (ENCQUOTEDPRINTABLE);
+ else if (strncasecmp ("base64", c, sizeof("base64")-1) == 0)
+ return (ENCBASE64);
+ else
+ return (ENCOTHER);
+}
+
+static PARAMETER *parse_parameters (const char *s)
+{
+ PARAMETER *head = 0, *cur = 0, *new;
+ char buffer[LONG_STRING];
+ const char *p;
+ size_t i;
+
+ while (*s)
+ {
+ if ((p = strpbrk (s, "=;")) == NULL)
+ {
+ dprint(1, (debugfile, "parse_parameters: malformed parameter: %s\n", s));
+ return (head); /* just bail out now */
+ }
+
+ /* if we hit a ; now the parameter has no value, just skip it */
+ if (*p != ';')
+ {
+ i = p - s;
+
+ new = mutt_new_parameter ();
+
+ new->attribute = safe_malloc (i + 1);
+ memcpy (new->attribute, s, i);
+ new->attribute[i] = 0;
+
+ /* remove whitespace from the end of the attribute name */
+ while (ISSPACE (new->attribute[--i]))
+ new->attribute[i] = 0;
+
+ s = p + 1; /* skip over the = */
+ SKIPWS (s);
+
+ if (*s == '"')
+ {
+ s++;
+ for (i=0; *s && *s != '"' && i < sizeof (buffer) - 1; i++, s++)
+ {
+ if (*s == '\\')
+ {
+ /* Quote the next character */
+ buffer[i] = s[1];
+ if (!*++s)
+ break;
+ }
+ else
+ buffer[i] = *s;
+ }
+ buffer[i] = 0;
+ if (*s)
+ s++; /* skip over the " */
+ }
+ else
+ {
+ for (i=0; *s && *s != ' ' && *s != ';' && i < sizeof (buffer) - 1; i++, s++)
+ buffer[i] = *s;
+ buffer[i] = 0;
+ }
+
+ new->value = safe_strdup (buffer);
+
+ /* Add this parameter to the list */
+ if (head)
+ {
+ cur->next = new;
+ cur = cur->next;
+ }
+ else
+ head = cur = new;
+ }
+ else
+ {
+ dprint (1, (debugfile, "parse_parameters(): parameter with no value: %s\n", s));
+ s = p;
+ }
+
+ /* Find the next parameter */
+ if (*s != ';' && (s = strchr (s, ';')) == NULL)
+ break; /* no more parameters */
+
+ do
+ {
+ s++;
+
+ /* Move past any leading whitespace */
+ SKIPWS (s);
+ }
+ while (*s == ';'); /* skip empty parameters */
+ }
+
+ return (head);
+}
+
+int mutt_check_mime_type (const char *s)
+{
+ if (strcasecmp ("text", s) == 0)
+ return TYPETEXT;
+ else if (strcasecmp ("multipart", s) == 0)
+ return TYPEMULTIPART;
+ else if (strcasecmp ("application", s) == 0)
+ return TYPEAPPLICATION;
+ else if (strcasecmp ("message", s) == 0)
+ return TYPEMESSAGE;
+ else if (strcasecmp ("image", s) == 0)
+ return TYPEIMAGE;
+ else if (strcasecmp ("audio", s) == 0)
+ return TYPEAUDIO;
+ else if (strcasecmp ("video", s) == 0)
+ return TYPEVIDEO;
+ else
+ return TYPEOTHER;
+}
+
+static void parse_content_type (char *s, BODY *ct)
+{
+ char *pc;
+ char buffer[SHORT_STRING];
+ short i = 0;
+
+ safe_free((void **)&ct->subtype);
+ mutt_free_parameter(&ct->parameter);
+
+ /* First extract any existing parameters */
+ if ((pc = strchr(s, ';')) != NULL)
+ {
+ *pc++ = 0;
+ while (*pc && ISSPACE (*pc))
+ pc++;
+ ct->parameter = parse_parameters(pc);
+
+ /* Some pre-RFC1521 gateways still use the "name=filename" convention */
+ if ((pc = mutt_get_parameter("name", ct->parameter)) != 0)
+ ct->filename = safe_strdup(pc);
+ }
+
+ /* Now get the subtype */
+ if ((pc = strchr(s, '/')))
+ {
+ *pc++ = 0;
+ while (*pc && !ISSPACE (*pc) && *pc != ';')
+ {
+ buffer[i++] = *pc;
+ pc++;
+ }
+ buffer[i] = 0;
+ ct->subtype = safe_strdup (buffer);
+ }
+
+ /* Finally, get the major type */
+ ct->type = mutt_check_mime_type (s);
+
+ if (ct->subtype == NULL)
+ {
+ /* Some older non-MIME mailers (i.e., mailtool, elm) have a content-type
+ * field, so we can attempt to convert the type to BODY here.
+ */
+ if (ct->type == TYPETEXT)
+ ct->subtype = safe_strdup ("plain");
+ else if (ct->type == TYPEAUDIO)
+ ct->subtype = safe_strdup ("basic");
+ else if (ct->type == TYPEMESSAGE)
+ ct->subtype = safe_strdup ("rfc822");
+ else if (ct->type == TYPEOTHER)
+ {
+ ct->type = TYPEAPPLICATION;
+ snprintf (buffer, sizeof (buffer), "x-%s", s);
+ ct->subtype = safe_strdup (buffer);
+ }
+ else
+ ct->subtype = safe_strdup ("x-unknown");
+ }
+}
+
+static void parse_content_disposition (char *s, BODY *ct)
+{
+ PARAMETER *parms;
+
+ if (!strncasecmp ("inline", s, 6))
+ ct->disposition = DISPINLINE;
+ else if (!strncasecmp ("form-data", s, 9))
+ ct->disposition = DISPFORMDATA;
+ else
+ ct->disposition = DISPATTACH;
+
+ /* Check to see if a default filename was given */
+ if ((s = strchr (s, ';')) != NULL)
+ {
+ s++;
+ SKIPWS (s);
+ if ((s = mutt_get_parameter ("filename", (parms = parse_parameters (s)))) != 0)
+ {
+ /* free() here because the content-type parsing routine might
+ * have allocated space if a "name=filename" parameter was
+ * specified.
+ */
+ safe_free ((void **) &ct->filename);
+ ct->filename = safe_strdup (s);
+ }
+ if ((s = mutt_get_parameter ("name", parms)) != 0)
+ ct->form_name = safe_strdup (s);
+ mutt_free_parameter (&parms);
+ }
+}
+
+/* args:
+ * fp stream to read from
+ *
+ * digest 1 if reading subparts of a multipart/digest, 0
+ * otherwise
+ */
+
+BODY *mutt_read_mime_header (FILE *fp, int digest)
+{
+ BODY *p = mutt_new_body();
+ char *c;
+ char *line = safe_malloc (LONG_STRING);
+ size_t linelen = LONG_STRING;
+
+ p->hdr_offset = ftell(fp);
+
+ p->encoding = ENC7BIT; /* default from RFC1521 */
+ p->type = digest ? TYPEMESSAGE : TYPETEXT;
+
+ while (*(line = read_rfc822_line (fp, line, &linelen)) != 0)
+ {
+ /* Find the value of the current header */
+ if ((c = strchr (line, ':')))
+ {
+ *c = 0;
+ c++;
+ SKIPWS (c);
+ if (!*c)
+ {
+ dprint (1, (debugfile, "mutt_read_mime_header(): skipping empty header field: %s\n", line));
+ continue;
+ }
+ }
+ else
+ {
+ dprint (1, (debugfile, "read_mime_header: bogus MIME header: %s\n", line));
+ break;
+ }
+
+ if (!strncasecmp ("content-", line, 8))
+ {
+ if (!strcasecmp ("type", line + 8))
+ parse_content_type (c, p);
+ else if (!strcasecmp ("transfer-encoding", line + 8))
+ p->encoding = mutt_check_encoding (c);
+ else if (!strcasecmp ("disposition", line + 8))
+ parse_content_disposition (c, p);
+ else if (!strcasecmp ("description", line + 8))
+ {
+ safe_free ((void **) &p->description);
+ p->description = safe_strdup (c);
+ rfc2047_decode (p->description, p->description, strlen (p->description) + 1);
+ }
+ }
+ }
+ p->offset = ftell (fp); /* Mark the start of the real data */
+ if (p->type == TYPETEXT && !p->subtype)
+ p->subtype = safe_strdup ("plain");
+ else if (p->type == TYPEMESSAGE && !p->subtype)
+ p->subtype = safe_strdup ("rfc822");
+
+ free (line);
+
+ return (p);
+}
+
+/* parse a MESSAGE/RFC822 body
+ *
+ * args:
+ * fp stream to read from
+ *
+ * parent structure which contains info about the message/rfc822
+ * body part
+ *
+ * NOTE: this assumes that `parent->length' has been set!
+ */
+
+BODY *mutt_parse_messageRFC822 (FILE *fp, BODY *parent)
+{
+ BODY *msg;
+
+ parent->hdr = mutt_new_header ();
+ parent->hdr->offset = ftell (fp);
+ parent->hdr->env = mutt_read_rfc822_header (fp, parent->hdr);
+ msg = parent->hdr->content;
+
+ /* ignore the length given in the content-length since it could be wrong
+ and we already have the info to calculate the correct length */
+ /* if (msg->length == -1) */
+ msg->length = parent->length - (msg->offset - parent->offset);
+
+ /* if body of this message is empty, we can end up with a negative length */
+ if (msg->length < 0)
+ msg->length = 0;
+
+ if (msg->type == TYPEMULTIPART)
+ msg->parts = mutt_parse_multipart (fp, mutt_get_parameter ("boundary", msg->parameter), msg->offset + msg->length, strcasecmp ("digest", msg->subtype) == 0);
+ else if (msg->type == TYPEMESSAGE)
+ msg->parts = mutt_parse_messageRFC822 (fp, msg);
+ else
+ return (msg);
+
+ /* try to recover from parsing error */
+ if (!msg->parts)
+ {
+ msg->type = TYPETEXT;
+ safe_free ((void **) &msg->subtype);
+ msg->subtype = safe_strdup ("plain");
+ }
+
+ return (msg);
+}
+
+/* parse a multipart structure
+ *
+ * args:
+ * fp stream to read from
+ *
+ * boundary body separator
+ *
+ * end_off length of the multipart body (used when the final
+ * boundary is missing to avoid reading too far)
+ *
+ * digest 1 if reading a multipart/digest, 0 otherwise
+ */
+
+BODY *mutt_parse_multipart (FILE *fp, const char *boundary, long end_off, int digest)
+{
+ int blen, len, crlf = 0;
+ char buffer[LONG_STRING];
+ BODY *head = 0, *last = 0, *new = 0;
+ int i;
+ int final = 0; /* did we see the ending boundary? */
+
+ if (!boundary)
+ {
+ mutt_error ("multipart message has no boundary parameter!");
+ return (NULL);
+ }
+
+ blen = strlen (boundary);
+ while (ftell (fp) < end_off && fgets (buffer, LONG_STRING, fp) != NULL)
+ {
+ len = strlen (buffer);
+
+ /* take note of the line ending. I'm assuming that either all endings
+ * will use <CR><LF> or none will.
+ */
+ if (len > 1 && buffer[len - 2] == '\r')
+ crlf = 1;
+
+ if (buffer[0] == '-' && buffer[1] == '-' &&
+ strncmp (buffer + 2, boundary, blen) == 0)
+ {
+ if (last)
+ {
+ last->length = ftell (fp) - last->offset - len - 1 - crlf;
+ if (last->parts && last->parts->length == 0)
+ last->parts->length = ftell (fp) - last->parts->offset - len - 1 - crlf;
+ /* if the body is empty, we can end up with a -1 length */
+ if (last->length < 0)
+ last->length = 0;
+ }
+
+ /* Remove any trailing whitespace, up to the length of the boundary */
+ for (i = len - 1; ISSPACE (buffer[i]) && i >= blen + 2; i--)
+ buffer[i] = 0;
+
+ /* Check for the end boundary */
+ if (strcmp (buffer + blen + 2, "--") == 0)
+ {
+ final = 1;
+ break; /* done parsing */
+ }
+ else if (buffer[2 + blen] == 0)
+ {
+ new = mutt_read_mime_header (fp, digest);
+ if (head)
+ {
+ last->next = new;
+ last = new;
+ }
+ else
+ last = head = new;
+ }
+ }
+ }
+
+ /* in case of missing end boundary, set the length to something reasonable */
+ if (last && last->length == 0 && !final)
+ last->length = end_off - last->offset;
+
+ /* parse recursive MIME parts */
+ for (last = head; last; last = last->next)
+ {
+ switch (last->type)
+ {
+ case TYPEMULTIPART:
+ fseek (fp, last->offset, 0);
+ last->parts = mutt_parse_multipart (fp, mutt_get_parameter ("boundary", last->parameter), last->offset + last->length, strcasecmp ("digest", last->subtype) == 0);
+ break;
+
+ case TYPEMESSAGE:
+ if (last->subtype &&
+ (strcasecmp (last->subtype, "rfc822") == 0 ||
+ strcasecmp (last->subtype, "news") == 0))
+ {
+ fseek (fp, last->offset, 0);
+ last->parts = mutt_parse_messageRFC822 (fp, last);
+ }
+ break;
+ }
+ }
+
+ return (head);
+}
+
+static const char *uncomment_timezone (char *buf, size_t buflen, const char *tz)
+{
+ char *p;
+ size_t len;
+
+ if (*tz != '(')
+ return tz; /* no need to do anything */
+ tz++;
+ SKIPWS (tz);
+ if ((p = strpbrk (tz, " )")) == NULL)
+ return tz;
+ len = p - tz;
+ if (len > buflen - 1)
+ len = buflen - 1;
+ memcpy (buf, tz, len);
+ buf[len] = 0;
+ return buf;
+}
+
+static struct tz_t
+{
+ char *tzname;
+ unsigned char zhours;
+ unsigned char zminutes;
+ unsigned char zoccident; /* west of UTC? */
+ unsigned char xxx; /* unused */
+}
+TimeZones[] =
+{
+ { "sst", 11, 0, 1, 0 }, /* Samoa */
+ { "pst", 8, 0, 1, 0 },
+ { "mst", 7, 0, 1, 0 },
+ { "pdt", 7, 0, 1, 0 },
+ { "cst", 6, 0, 1, 0 },
+ { "mdt", 6, 0, 1, 0 },
+ { "cdt", 5, 0, 1, 0 },
+ { "est", 5, 0, 1, 0 },
+ { "ast", 4, 0, 1, 0 }, /* Atlantic */
+ { "edt", 4, 0, 1, 0 },
+ { "wgt", 3, 0, 1, 0 }, /* Western Greenland */
+ { "wgst", 2, 0, 1, 0 }, /* Western Greenland DST */
+ { "aat", 1, 0, 1, 0 }, /* Atlantic Africa Time */
+ { "egt", 1, 0, 1, 0 }, /* Eastern Greenland */
+ { "egst", 0, 0, 0, 0 }, /* Eastern Greenland DST */
+ { "gmt", 0, 0, 0, 0 },
+ { "utc", 0, 0, 0, 0 },
+ { "wat", 0, 0, 0, 0 }, /* West Africa */
+ { "wet", 0, 0, 0, 0 }, /* Western Europe */
+ { "bst", 1, 0, 0, 0 }, /* British DST */
+ { "cat", 1, 0, 0, 0 }, /* Central Africa */
+ { "cet", 1, 0, 0, 0 }, /* Central Europe */
+ { "met", 1, 0, 0, 0 }, /* this is now officially CET */
+ { "west", 1, 0, 0, 0 }, /* Western Europe DST */
+ { "cest", 2, 0, 0, 0 }, /* Central Europe DST */
+ { "eet", 2, 0, 0, 0 }, /* Eastern Europe */
+ { "ist", 2, 0, 0, 0 }, /* Israel */
+ { "sat", 2, 0, 0, 0 }, /* South Africa */
+ { "ast", 3, 0, 0, 0 }, /* Arabia */
+ { "eat", 3, 0, 0, 0 }, /* East Africa */
+ { "eest", 3, 0, 0, 0 }, /* Eastern Europe DST */
+ { "idt", 3, 0, 0, 0 }, /* Israel DST */
+ { "msk", 3, 0, 0, 0 }, /* Moscow */
+ { "adt", 4, 0, 0, 0 }, /* Arabia DST */
+ { "msd", 4, 0, 0, 0 }, /* Moscow DST */
+ { "gst", 4, 0, 0, 0 }, /* Presian Gulf */
+ { "smt", 4, 0, 0, 0 }, /* Seychelles */
+ { "ist", 5, 30, 0, 0 }, /* India */
+ { "ict", 7, 0, 0, 0 }, /* Indochina */
+/*{ "cst", 8, 0, 0, 0 },*/ /* China */
+ { "hkt", 8, 0, 0, 0 }, /* Hong Kong */
+/*{ "sst", 8, 0, 0, 0 },*/ /* Singapore */
+ { "wst", 8, 0, 0, 0 }, /* Western Australia */
+ { "jst", 9, 0, 0, 0 }, /* Japan */
+/*{ "cst", 9, 30, 0, 0 },*/ /* Australian Central Standard Time */
+ { "kst", 10, 0, 0, 0 }, /* Korea */
+ { "nzst", 12, 0, 0, 0 }, /* New Zealand */
+ { "nzdt", 13, 0, 0, 0 }, /* New Zealand DST */
+ { NULL, 0, 0, 0, 0 }
+};
+
+/* parses a date string in RFC822 format:
+ *
+ * Date: [ weekday , ] day-of-month month year hour:minute:second timezone
+ *
+ * This routine assumes that `h' has been initialized to 0. the `timezone'
+ * field is optional, defaulting to +0000 if missing.
+ */
+static time_t parse_date (char *s, HEADER *h)
+{
+ int count = 0;
+ char *t;
+ int hour, min, sec;
+ struct tm tm;
+ int i;
+ int tz_offset = 0;
+ int zhours = 0;
+ int zminutes = 0;
+ int zoccident = 0;
+ const char *ptz;
+ char tzstr[SHORT_STRING];
+
+ /* kill the day of the week, if it exists. */
+ if ((t = strchr (s, ',')))
+ t++;
+ else
+ t = s;
+ SKIPWS (t);
+
+ memset (&tm, 0, sizeof (tm));
+
+ while ((t = strtok (t, " \t")) != NULL)
+ {
+ switch (count)
+ {
+ case 0: /* day of the month */
+ if (!isdigit (*t))
+ return (-1);
+ tm.tm_mday = atoi (t);
+ if (tm.tm_mday > 31)
+ return (-1);
+ break;
+
+ case 1: /* month of the year */
+ if ((i = mutt_check_month (t)) < 0)
+ return (-1);
+ tm.tm_mon = i;
+ break;
+
+ case 2: /* year */
+ tm.tm_year = atoi (t);
+ if (tm.tm_year >= 1900)
+ tm.tm_year -= 1900;
+ break;
+
+ case 3: /* time of day */
+ if (sscanf (t, "%d:%d:%d", &hour, &min, &sec) == 3)
+ ;
+ else if (sscanf (t, "%d:%d", &hour, &min) == 2)
+ sec = 0;
+ else
+ {
+ dprint(1, (debugfile, "parse_date: could not process time format: %s\n", t));
+ return(-1);
+ }
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec;
+ break;
+
+ case 4: /* timezone */
+ /* sometimes we see things like (MST) or (-0700) so attempt to
+ * compensate by uncommenting the string if non-RFC822 compliant
+ */
+ ptz = uncomment_timezone (tzstr, sizeof (tzstr), t);
+
+ if (*ptz == '+' || *ptz == '-')
+ {
+ if (ptz[1] && ptz[2] && ptz[3] && ptz[4]
+ && isdigit (ptz[1]) && isdigit (ptz[2])
+ && isdigit (ptz[3]) && isdigit (ptz[4]))
+ {
+ zhours = (ptz[1] - '0') * 10 + (ptz[2] - '0');
+ zminutes = (ptz[3] - '0') * 10 + (ptz[4] - '0');
+
+ if (ptz[0] == '-')
+ zoccident = 1;
+ }
+ }
+ else
+ {
+ for (i = 0; TimeZones[i].tzname; i++)
+ if (!strcasecmp (TimeZones[i].tzname, ptz))
+ {
+ zhours = TimeZones[i].zhours;
+ zminutes = TimeZones[i].zminutes;
+ zoccident = TimeZones[i].zoccident;
+ break;
+ }
+
+ /* ad hoc support for the European MET (now officially CET) TZ */
+ if (strcasecmp (t, "MET") == 0)
+ {
+ if ((t = strtok (NULL, " \t")) != NULL)
+ {
+ if (!strcasecmp (t, "DST"))
+ zhours++;
+ }
+ }
+ }
+ tz_offset = zhours * 3600 + zminutes * 60;
+ if (!zoccident)
+ tz_offset = -tz_offset;
+ break;
+ }
+ count++;
+ t = 0;
+ }
+
+ if (count < 4) /* don't check for missing timezone */
+ {
+ dprint(1,(debugfile, "parse_date(): error parsing date format, using received time\n"));
+ return (-1);
+ }
+
+ if (h)
+ {
+ h->zhours = zhours;
+ h->zminutes = zminutes;
+ h->zoccident = zoccident;
+ }
+
+ return (mutt_mktime (&tm, 0) + tz_offset);
+}
+
+/* extract the first substring that looks like a message-id */
+static char *extract_message_id (const char *s)
+{
+ const char *p;
+ char *r;
+ size_t l;
+
+ if ((s = strchr (s, '<')) == NULL || (p = strchr (s, '>')) == NULL)
+ return (NULL);
+ l = (size_t)(p - s) + 1;
+ r = safe_malloc (l + 1);
+ memcpy (r, s, l);
+ r[l] = 0;
+ return (r);
+}
+
+void mutt_parse_mime_message (CONTEXT *ctx, HEADER *cur)
+{
+ MESSAGE *msg;
+
+ if (cur->content->type != TYPEMESSAGE && cur->content->type != TYPEMULTIPART)
+ return; /* nothing to do */
+
+ if ((msg = mx_open_message (ctx, cur->msgno)))
+ {
+ fseek (msg->fp, cur->content->offset, 0);
+
+ if (cur->content->type == TYPEMULTIPART)
+ {
+ if (!cur->content->parts)
+ cur->content->parts = mutt_parse_multipart (msg->fp, mutt_get_parameter ("boundary", cur->content->parameter), cur->content->offset + cur->content->length, strcasecmp ("digest", cur->content->subtype) == 0);
+ }
+ else
+ {
+ if (!cur->content->parts)
+ cur->content->parts = mutt_parse_messageRFC822 (msg->fp, cur->content);
+ }
+
+ /* try to recover from parsing error */
+ if (!cur->content->parts)
+ {
+ cur->content->type = TYPETEXT;
+ safe_free ((void **) &cur->content->subtype);
+ cur->content->subtype = safe_strdup ("plain");
+ }
+
+
+
+#ifdef _PGPPATH
+ cur->pgp = pgp_query (cur->content);
+#endif /* _PGPPATH */
+
+
+
+ mx_close_message (&msg);
+ }
+}
+
+/* mutt_read_rfc822_header() -- parses a RFC822 header
+ *
+ * Args:
+ *
+ * f stream to read from
+ *
+ * hdr header structure of current message (optional). If hdr is
+ * NULL, then we are reading a postponed message, or called
+ * from mutt_edit_headers() so we should keep a list of the
+ * user-defined headers.
+ */
+ENVELOPE *mutt_read_rfc822_header (FILE *f, HEADER *hdr)
+{
+ ENVELOPE *e = mutt_new_envelope();
+ LIST *last = NULL;
+ char *line = safe_malloc (LONG_STRING);
+ char *p;
+ char in_reply_to[STRING];
+ long loc;
+ int matched;
+ size_t linelen = LONG_STRING;
+
+ in_reply_to[0] = 0;
+
+ if (hdr)
+ {
+ hdr->content = mutt_new_body ();
+
+ /* set the defaults from RFC1521 */
+ hdr->content->type = TYPETEXT;
+ hdr->content->subtype = safe_strdup ("plain");
+ hdr->content->encoding = ENC7BIT;
+ hdr->content->length = -1;
+ }
+
+ loc = ftell (f);
+ while (*(line = read_rfc822_line (f, line, &linelen)) != 0)
+ {
+ matched = 0;
+
+ if ((p = strpbrk (line, ": \t")) == NULL || *p != ':')
+ {
+ char return_path[LONG_STRING];
+ time_t t;
+
+ /* some bogus MTAs will quote the original "From " line */
+ if (strncmp (">From ", line, 6) == 0)
+ {
+ loc = ftell (f);
+ continue; /* just ignore */
+ }
+ else if ((t = is_from (line, return_path, sizeof (return_path))))
+ {
+ /* MH somtimes has the From_ line in the middle of the header! */
+ if (hdr && !hdr->received)
+ hdr->received = t + mutt_local_tz ();
+ loc = ftell (f);
+ continue;
+ }
+
+ fseek (f, loc, 0);
+ break; /* end of header */
+ }
+
+ *p = 0;
+ p++;
+ SKIPWS (p);
+ if (!*p)
+ continue; /* skip empty header fields */
+
+ switch (tolower (line[0]))
+ {
+ case 'a':
+ if (strcasecmp (line+1, "pparently-to") == 0)
+ {
+ e->to = rfc822_parse_adrlist (e->to, p);
+ matched = 1;
+ }
+ else if (strcasecmp (line+1, "pparently-from") == 0)
+ {
+ e->from = rfc822_parse_adrlist (e->from, p);
+ matched = 1;
+ }
+ break;
+
+ case 'b':
+ if (strcasecmp (line+1, "cc") == 0)
+ {
+ e->bcc = rfc822_parse_adrlist (e->bcc, p);
+ matched = 1;
+ }
+ break;
+
+ case 'c':
+ if (strcasecmp (line+1, "c") == 0)
+ {
+ e->cc = rfc822_parse_adrlist (e->cc, p);
+ matched = 1;
+ }
+ else if (strncasecmp (line + 1, "ontent-", 7) == 0)
+ {
+ if (strcasecmp (line+8, "type") == 0)
+ {
+ if (hdr)
+ parse_content_type (p, hdr->content);
+ matched = 1;
+ }
+ else if (strcasecmp (line+8, "transfer-encoding") == 0)
+ {
+ if (hdr)
+ hdr->content->encoding = mutt_check_encoding (p);
+ matched = 1;
+ }
+ else if (strcasecmp (line+8, "length") == 0)
+ {
+ if (hdr)
+ hdr->content->length = atoi (p);
+ matched = 1;
+ }
+ else if (strcasecmp (line+8, "description") == 0)
+ {
+ if (hdr)
+ {
+ safe_free ((void **) &hdr->content->description);
+ hdr->content->description = safe_strdup (p);
+ rfc2047_decode (hdr->content->description,
+ hdr->content->description,
+ strlen (hdr->content->description) + 1);
+ }
+ matched = 1;
+ }
+ else if (strcasecmp (line+8, "disposition") == 0)
+ {
+ if (hdr)
+ parse_content_disposition (p, hdr->content);
+ matched = 1;
+ }
+ }
+ break;
+
+ case 'd':
+ if (!strcasecmp ("ate", line + 1))
+ {
+ if (hdr)
+ hdr->date_sent = parse_date (p, hdr);
+ matched = 1;
+ }
+ break;
+
+ case 'e':
+ if (!strcasecmp ("xpires", line + 1) &&
+ hdr && parse_date (p, hdr) < time (NULL))
+ hdr->expired = 1;
+ break;
+
+ case 'f':
+ if (!strcasecmp ("rom", line + 1))
+ {
+ e->from = rfc822_parse_adrlist (e->from, p);
+ matched = 1;
+ }
+ break;
+
+ case 'i':
+ if (!strcasecmp (line+1, "n-reply-to"))
+ {
+ if (hdr)
+ {
+ strfcpy (in_reply_to, p, sizeof (in_reply_to));
+ rfc2047_decode (in_reply_to, in_reply_to,
+ sizeof (in_reply_to));
+ }
+ }
+ break;
+
+ case 'l':
+ if (!strcasecmp (line + 1, "ines"))
+ {
+ if (hdr)
+ hdr->lines = atoi (p);
+ matched = 1;
+ }
+ break;
+
+ case 'm':
+ if (!strcasecmp (line + 1, "ime-version"))
+ {
+ if (hdr)
+ hdr->mime = 1;
+ matched = 1;
+ }
+ else if (!strcasecmp (line + 1, "essage-id"))
+ {
+ /* We add a new "Message-Id:" when building a message */
+ safe_free ((void **) &e->message_id);
+ e->message_id = extract_message_id (p);
+ matched = 1;
+ }
+ else if (!strncasecmp (line + 1, "ail-", 4))
+ {
+ if (!strcasecmp (line + 5, "reply-to"))
+ {
+ /* override the Reply-To: field */
+ rfc822_free_address (&e->reply_to);
+ e->reply_to = rfc822_parse_adrlist (e->reply_to, p);
+ matched = 1;
+ }
+ else if (!strcasecmp (line + 5, "followup-to"))
+ {
+ e->mail_followup_to = rfc822_parse_adrlist (e->mail_followup_to, p);
+ matched = 1;
+ }
+ }
+ break;
+
+ case 'r':
+ if (!strcasecmp (line + 1, "eferences"))
+ {
+ mutt_free_list (&e->references);
+ e->references = mutt_parse_references (p);
+ matched = 1;
+ }
+ else if (!strcasecmp (line + 1, "eply-to"))
+ {
+ e->reply_to = rfc822_parse_adrlist (e->reply_to, p);
+ matched = 1;
+ }
+ else if (!strcasecmp (line + 1, "eturn-path"))
+ {
+ e->return_path = rfc822_parse_adrlist (e->return_path, p);
+ matched = 1;
+ }
+ else if (!strcasecmp (line + 1, "eceived"))
+ {
+ if (hdr && !hdr->received)
+ {
+ char *d = strchr (p, ';');
+
+ if (d)
+ hdr->received = parse_date (d + 1, NULL);
+ }
+ matched = 1;
+ }
+ break;
+
+ case 's':
+ if (!strcasecmp (line + 1, "ubject"))
+ {
+ if (!e->subject)
+ e->subject = safe_strdup (p);
+ matched = 1;
+ }
+ else if (!strcasecmp (line + 1, "ender"))
+ {
+ e->sender = rfc822_parse_adrlist (e->sender, p);
+ matched = 1;
+ }
+ else if (!strcasecmp (line + 1, "tatus"))
+ {
+ if (hdr)
+ {
+ while (*p)
+ {
+ switch(*p)
+ {
+ case 'r':
+ hdr->replied = 1;
+ break;
+ case 'O':
+ if (option (OPTMARKOLD))
+ hdr->old = 1;
+ break;
+ case 'R':
+ hdr->read = 1;
+ break;
+ }
+ p++;
+ }
+ }
+ matched = 1;
+ }
+ else if ((!strcasecmp ("upersedes", line + 1) ||
+ !strcasecmp ("upercedes", line + 1)) && hdr)
+ e->supersedes = safe_strdup (p);
+ break;
+
+ case 't':
+ if (strcasecmp (line+1, "o") == 0)
+ {
+ e->to = rfc822_parse_adrlist (e->to, p);
+ matched = 1;
+ }
+ break;
+
+ case 'x':
+ if (strcasecmp (line+1, "-status") == 0)
+ {
+ if (hdr)
+ {
+ while (*p)
+ {
+ switch (*p)
+ {
+ case 'A':
+ hdr->replied = 1;
+ break;
+ case 'D':
+ hdr->deleted = 1;
+ break;
+ case 'F':
+ hdr->flagged = 1;
+ break;
+ default:
+ break;
+ }
+ p++;
+ }
+ }
+ matched = 1;
+ }
+
+ default:
+ break;
+ }
+
+ /* if hdr==NULL, then we are using this to parse either a postponed
+ * message, or a outgoing message (edit_hdrs), so we want to keep
+ * track of the user-defined headers
+ */
+ if (!matched && !hdr)
+ {
+ if (last)
+ {
+ last->next = mutt_new_list ();
+ last = last->next;
+ }
+ else
+ last = e->userhdrs = mutt_new_list ();
+ line[strlen (line)] = ':';
+ last->data = safe_strdup (line);
+ }
+
+ loc = ftell (f);
+ }
+
+ free (line);
+
+ if (hdr)
+ {
+ hdr->content->hdr_offset = hdr->offset;
+ hdr->content->offset = ftell (f);
+
+ /* if an in-reply-to was given, check to see if it is in the references
+ * list already. if not, add it so we can do a better job of threading.
+ */
+ if (in_reply_to[0] && (p = extract_message_id (in_reply_to)) != NULL)
+ {
+ if (!e->references ||
+ (e->references && strcmp (e->references->data, p) != 0))
+ {
+ LIST *tmp = mutt_new_list ();
+
+ tmp->data = p;
+ tmp->next = e->references;
+ e->references = tmp;
+ }
+ else
+ safe_free ((void **) &p);
+ }
+
+ /* do RFC2047 decoding */
+ rfc2047_decode_adrlist (e->from);
+ rfc2047_decode_adrlist (e->to);
+ rfc2047_decode_adrlist (e->cc);
+ rfc2047_decode_adrlist (e->reply_to);
+ rfc2047_decode_adrlist (e->mail_followup_to);
+ rfc2047_decode_adrlist (e->return_path);
+ rfc2047_decode_adrlist (e->sender);
+
+ if (e->subject)
+ {
+ regmatch_t pmatch[1];
+
+ rfc2047_decode (e->subject, e->subject, strlen (e->subject) + 1);
+
+ if (regexec (ReplyRegexp.rx, e->subject, 1, pmatch, 0) == 0)
+ e->real_subj = e->subject + pmatch[0].rm_eo;
+ else
+ e->real_subj = e->subject;
+ }
+
+ /* check for missing or invalid date */
+ if (hdr->date_sent <= 0)
+ {
+ dprint(1,(debugfile,"read_rfc822_header(): no date found, using received time from msg separator\n"));
+ hdr->date_sent = hdr->received;
+ }
+ }
+
+ return (e);
+}
+
+ADDRESS *mutt_parse_adrlist (ADDRESS *p, const char *s)
+{
+ const char *q;
+
+ /* check for a simple whitespace separated list of addresses */
+ if ((q = strpbrk (s, "\"<>():;,\\")) == NULL)
+ {
+ char tmp[HUGE_STRING];
+ char *r;
+
+ strfcpy (tmp, s, sizeof (tmp));
+ r = tmp;
+ while ((r = strtok (r, " \t")) != NULL)
+ {
+ p = rfc822_parse_adrlist (p, r);
+ r = NULL;
+ }
+ }
+ else
+ p = rfc822_parse_adrlist (p, s);
+
+ return p;
+}
--- /dev/null
+BODY *mutt_parse_multipart (FILE *, const char *, long, int);
+BODY *mutt_parse_messageRFC822 (FILE *, BODY *);
+BODY *mutt_read_mime_header (FILE *, int);
+
+ENVELOPE *mutt_read_rfc822_header (FILE *, HEADER *);
+
+time_t is_from (const char *, char *, size_t);
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mapping.h"
+#include "keymap.h"
+#include "mailbox.h"
+#include "copy.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+static int eat_regexp (pattern_t *pat, BUFFER *, BUFFER *);
+static int eat_date (pattern_t *pat, BUFFER *, BUFFER *);
+static int eat_range (pattern_t *pat, BUFFER *, BUFFER *);
+
+struct pattern_flags
+{
+ int tag; /* character used to represent this op */
+ int op; /* operation to perform */
+ int class;
+ int (*eat_arg) (pattern_t *, BUFFER *, BUFFER *);
+}
+Flags[] =
+{
+ { 'A', M_ALL, 0, NULL },
+ { 'b', M_BODY, M_FULL_MSG, eat_regexp },
+ { 'B', M_WHOLE_MSG, M_FULL_MSG, eat_regexp },
+ { 'c', M_CC, 0, eat_regexp },
+ { 'C', M_RECIPIENT, 0, eat_regexp },
+ { 'd', M_DATE, 0, eat_date },
+ { 'D', M_DELETED, 0, NULL },
+ { 'e', M_SENDER, 0, eat_regexp },
+ { 'E', M_EXPIRED, 0, NULL },
+ { 'f', M_FROM, 0, eat_regexp },
+ { 'F', M_FLAG, 0, NULL },
+ { 'h', M_HEADER, M_FULL_MSG, eat_regexp },
+ { 'i', M_ID, 0, eat_regexp },
+ { 'L', M_ADDRESS, 0, eat_regexp },
+ { 'l', M_LIST, 0, NULL },
+ { 'm', M_MESSAGE, 0, eat_range },
+ { 'n', M_SCORE, 0, eat_range },
+ { 'N', M_NEW, 0, NULL },
+ { 'O', M_OLD, 0, NULL },
+ { 'p', M_PERSONAL_RECIP, 0, NULL },
+ { 'P', M_PERSONAL_FROM, 0, NULL },
+ { 'Q', M_REPLIED, 0, NULL },
+ { 'R', M_READ, 0, NULL },
+ { 'r', M_DATE_RECEIVED, 0, eat_date },
+ { 's', M_SUBJECT, 0, eat_regexp },
+ { 'S', M_SUPERSEDED, 0, NULL },
+ { 'T', M_TAG, 0, NULL },
+ { 't', M_TO, 0, eat_regexp },
+ { 'U', M_UNREAD, 0, NULL },
+ { 'x', M_REFERENCE, 0, eat_regexp },
+ { 'z', M_SIZE, 0, eat_range },
+ { 0 }
+};
+
+static pattern_t *SearchPattern = NULL; /* current search pattern */
+static char LastSearch[STRING] = { 0 }; /* last pattern searched for */
+static char LastSearchExpn[LONG_STRING] = { 0 }; /* expanded version of
+ LastSearch */
+
+#define M_MAXRANGE -1
+
+int mutt_getvaluebychar (char ch, struct mapping_t *table)
+{
+ int i;
+
+ for (i = 0; table[i].name; i++)
+ {
+ if (ch == table[i].name[0])
+ return table[i].value;
+ }
+
+ return (-1);
+}
+
+/* if no uppercase letters are given, do a case-insensitive search */
+int mutt_which_case (const char *s)
+{
+ while (*s)
+ {
+ if (isalpha (*s) && isupper (*s))
+ return 0; /* case-sensitive */
+ s++;
+ }
+ return REG_ICASE; /* case-insensitive */
+}
+
+static int
+msg_search (CONTEXT *ctx, regex_t *rx, char *buf, size_t blen, int op, int msgno)
+{
+ char tempfile[_POSIX_PATH_MAX];
+ MESSAGE *msg = NULL;
+ STATE s;
+ struct stat st;
+ FILE *fp = NULL;
+ long lng = 0;
+ int match = 0;
+ HEADER *h = ctx->hdrs[msgno];
+
+ if ((msg = mx_open_message (ctx, msgno)) != NULL)
+ {
+ if (option (OPTTHOROUGHSRC))
+ {
+ /* decode the header / body */
+ memset (&s, 0, sizeof (s));
+ s.fpin = msg->fp;
+ mutt_mktemp (tempfile);
+ if ((s.fpout = safe_fopen (tempfile, "w+")) == NULL)
+ {
+ mutt_perror (tempfile);
+ return (0);
+ }
+
+ if (op != M_BODY)
+ mutt_copy_header (msg->fp, h, s.fpout, CH_FROM | CH_DECODE, NULL);
+
+ if (op != M_HEADER)
+ {
+ mutt_parse_mime_message (ctx, h);
+
+
+
+#ifdef _PGPPATH
+ if (h->pgp & PGPENCRYPT && !pgp_valid_passphrase())
+ {
+ mx_close_message (&msg);
+ fclose (fp);
+ unlink (tempfile);
+ return (0);
+ }
+#endif
+
+
+
+ fseek (msg->fp, h->offset, 0);
+ mutt_body_handler (h->content, &s);
+ }
+
+ fp = s.fpout;
+ fflush (fp);
+ fseek (fp, 0, 0);
+ fstat (fileno (fp), &st);
+ lng = (long) st.st_size;
+ }
+ else
+ {
+ /* raw header / body */
+ fp = msg->fp;
+ if (op != M_BODY)
+ {
+ fseek (fp, h->offset, 0);
+ lng = h->content->offset - h->offset;
+ }
+ if (op != M_HEADER)
+ {
+ if (op == M_BODY)
+ fseek (fp, h->content->offset, 0);
+ lng += h->content->length;
+ }
+ }
+
+ /* search the file "fp" */
+ while (lng > 0)
+ {
+ if (fgets (buf, blen - 1, fp) == NULL)
+ break; /* don't loop forever */
+ if (regexec (rx, buf, 0, NULL, 0) == 0)
+ {
+ match = 1;
+ break;
+ }
+ lng -= strlen (buf);
+ }
+
+ mx_close_message (&msg);
+
+ if (option (OPTTHOROUGHSRC))
+ {
+ fclose (fp);
+ unlink (tempfile);
+ }
+ }
+
+ return match;
+}
+
+int eat_regexp (pattern_t *pat, BUFFER *s, BUFFER *err)
+{
+ BUFFER buf;
+ int r;
+
+ memset (&buf, 0, sizeof (buf));
+ if (mutt_extract_token (&buf, s, M_TOKEN_PATTERN | M_TOKEN_COMMENT) != 0 ||
+ !buf.data)
+ {
+ snprintf (err->data, err->dsize, "Error in expression: %s", s->dptr);
+ return (-1);
+ }
+ pat->rx = safe_malloc (sizeof (regex_t));
+ r = REGCOMP (pat->rx, buf.data, REG_NEWLINE | REG_NOSUB | mutt_which_case (buf.data));
+ FREE (&buf.data);
+ if (r)
+ {
+ regerror (r, pat->rx, err->data, err->dsize);
+ regfree (pat->rx);
+ safe_free ((void **) &pat->rx);
+ return (-1);
+ }
+ return 0;
+}
+
+int eat_range (pattern_t *pat, BUFFER *s, BUFFER *err)
+{
+ char *tmp;
+
+ if ((*s->dptr != '-') && (*s->dptr != '<'))
+ {
+ /* range minimum */
+ if (*s->dptr == '>')
+ {
+ pat->max = M_MAXRANGE;
+ pat->min = strtol (s->dptr + 1, &tmp, 0);
+ }
+ else
+ pat->min = strtol (s->dptr, &tmp, 0);
+ if (toupper (*tmp) == 'K') /* is there a prefix? */
+ {
+ pat->min *= 1024;
+ tmp++;
+ }
+ else if (toupper (*tmp) == 'M')
+ {
+ pat->min *= 1048576;
+ tmp++;
+ }
+ if (*s->dptr == '>')
+ {
+ s->dptr = tmp;
+ return 0;
+ }
+ if (*tmp != '-')
+ {
+ /* exact value */
+ pat->max = pat->min;
+ s->dptr = tmp;
+ return 0;
+ }
+ tmp++;
+ }
+ else
+ {
+ s->dptr++;
+ tmp = s->dptr;
+ }
+
+ if (isdigit (*tmp))
+ {
+ /* range maximum */
+ pat->max = strtol (tmp, &tmp, 0);
+ if (toupper (*tmp) == 'K')
+ {
+ pat->max *= 1024;
+ tmp++;
+ }
+ else if (toupper (*tmp) == 'M')
+ {
+ pat->max *= 1048576;
+ tmp++;
+ }
+ }
+ else
+ pat->max = M_MAXRANGE;
+
+ s->dptr = tmp;
+ return 0;
+}
+
+static const char *getDate (const char *s, struct tm *t, BUFFER *err)
+{
+ char *p;
+ time_t now = time (NULL);
+ struct tm *tm = localtime (&now);
+
+ t->tm_mday = strtol (s, &p, 0);
+ if (t->tm_mday < 1 || t->tm_mday > 31)
+ {
+ snprintf (err->data, err->dsize, "Invalid day of month: %s", s);
+ return NULL;
+ }
+ if (*p != '/')
+ {
+ /* fill in today's month and year */
+ t->tm_mon = tm->tm_mon;
+ t->tm_year = tm->tm_year;
+ return p;
+ }
+ p++;
+ t->tm_mon = strtol (p, &p, 0) - 1;
+ if (t->tm_mon < 0 || t->tm_mon > 11)
+ {
+ snprintf (err->data, err->dsize, "Invalid month: %s", p);
+ return NULL;
+ }
+ if (*p != '/')
+ {
+ t->tm_year = tm->tm_year;
+ return p;
+ }
+ p++;
+ t->tm_year = strtol (p, &p, 0);
+ if (t->tm_year < 70) /* year 2000+ */
+ t->tm_year += 100;
+ else if (t->tm_year > 1900)
+ t->tm_year -= 1900;
+ return p;
+}
+
+/* Ny years
+ Nm months
+ Nw weeks
+ Nd days */
+static const char *get_offset (struct tm *tm, const char *s)
+{
+ char *ps;
+ int offset = strtol (s, &ps, 0);
+
+ switch (*ps)
+ {
+ case 'y':
+ tm->tm_year -= offset;
+ break;
+ case 'm':
+ tm->tm_mon -= offset;
+ break;
+ case 'w':
+ tm->tm_mday -= 7 * offset;
+ break;
+ case 'd':
+ tm->tm_mday -= offset;
+ break;
+ }
+ mutt_normalize_time (tm);
+ return (ps + 1);
+}
+
+static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
+{
+ BUFFER buffer;
+ struct tm min, max;
+
+ memset (&buffer, 0, sizeof (buffer));
+ if (mutt_extract_token (&buffer, s, M_TOKEN_COMMENT | M_TOKEN_PATTERN) != 0
+ || !buffer.data)
+ {
+ strfcpy (err->data, "error in expression", err->dsize);
+ return (-1);
+ }
+
+ memset (&min, 0, sizeof (min));
+ /* the `0' time is Jan 1, 1970 UTC, so in order to prevent a negative time
+ when doing timezone conversion, we use Jan 2, 1970 UTC as the base
+ here */
+ min.tm_mday = 2;
+ min.tm_year = 70;
+
+ memset (&max, 0, sizeof (max));
+
+ /* Arbitrary year in the future. Don't set this too high
+ or mutt_mktime() returns something larger than will
+ fit in a time_t on some systems */
+ max.tm_year = 130;
+ max.tm_mon = 11;
+ max.tm_mday = 31;
+ max.tm_hour = 23;
+ max.tm_min = 59;
+ max.tm_sec = 59;
+
+ if (strchr ("<>=", buffer.data[0]))
+ {
+ /* offset from current time
+ <3d less than three days ago
+ >3d more than three days ago
+ =3d exactly three days ago */
+ time_t now = time (NULL);
+ struct tm *tm = localtime (&now);
+ int exact = 0;
+
+ if (buffer.data[0] == '<')
+ {
+ memcpy (&min, tm, sizeof (min));
+ tm = &min;
+ }
+ else
+ {
+ memcpy (&max, tm, sizeof (max));
+ tm = &max;
+
+ if (buffer.data[0] == '=')
+ exact++;
+ }
+ tm->tm_hour = 23;
+ tm->tm_min = max.tm_sec = 59;
+
+ get_offset (tm, buffer.data + 1);
+
+ if (exact)
+ {
+ /* start at the beginning of the day in question */
+ memcpy (&min, &max, sizeof (max));
+ min.tm_hour = min.tm_sec = min.tm_min = 0;
+ }
+ }
+ else
+ {
+ const char *pc = buffer.data;
+
+ if (*pc != '-')
+ {
+ /* mininum date specified */
+ if ((pc = getDate (pc, &min, err)) == NULL)
+ {
+ FREE (&buffer.data);
+ return (-1);
+ }
+ }
+
+ if (*pc && *pc == '-')
+ {
+ /* max date */
+ pc++; /* skip the `-' */
+ SKIPWS (pc);
+ if (*pc)
+ if (!getDate (pc, &max, err))
+ {
+ FREE (&buffer.data);
+ return (-1);
+ }
+ }
+ else
+ {
+ /* search for messages on a specific day */
+ max.tm_year = min.tm_year;
+ max.tm_mon = min.tm_mon;
+ max.tm_mday = min.tm_mday;
+ }
+ }
+
+ pat->min = mutt_mktime (&min, 1);
+ pat->max = mutt_mktime (&max, 1);
+
+ FREE (&buffer.data);
+
+ return 0;
+}
+
+static struct pattern_flags *lookup_tag (char tag)
+{
+ int i;
+
+ for (i = 0; Flags[i].tag; i++)
+ if (Flags[i].tag == tag)
+ return (&Flags[i]);
+ return NULL;
+}
+
+static /* const */ char *find_matching_paren (/* const */ char *s)
+{
+ int level = 1;
+
+ for (; *s; s++)
+ {
+ if (*s == '(')
+ level++;
+ else if (*s == ')')
+ {
+ level--;
+ if (!level)
+ break;
+ }
+ }
+ return s;
+}
+
+void mutt_pattern_free (pattern_t **pat)
+{
+ pattern_t *tmp;
+
+ while (*pat)
+ {
+ tmp = *pat;
+ *pat = (*pat)->next;
+
+ if (tmp->rx)
+ {
+ regfree (tmp->rx);
+ safe_free ((void **) &tmp->rx);
+ }
+ if (tmp->child)
+ mutt_pattern_free (&tmp->child);
+ safe_free ((void **) &tmp);
+ }
+}
+
+pattern_t *mutt_pattern_comp (/* const */ char *s, int flags, BUFFER *err)
+{
+ pattern_t *curlist = NULL;
+ pattern_t *tmp;
+ pattern_t *last = NULL;
+ int not = 0;
+ int or = 0;
+ int implicit = 1; /* used to detect logical AND operator */
+ struct pattern_flags *entry;
+ char *p;
+ char *buf;
+ BUFFER ps;
+
+ memset (&ps, 0, sizeof (ps));
+ ps.dptr = s;
+ ps.dsize = strlen (s);
+
+ while (*ps.dptr)
+ {
+ SKIPWS (ps.dptr);
+ switch (*ps.dptr)
+ {
+ case '!':
+ ps.dptr++;
+ not = !not;
+ break;
+ case '|':
+ if (!or)
+ {
+ if (!curlist)
+ {
+ snprintf (err->data, err->dsize, "error in pattern at: %s", ps.dptr);
+ return NULL;
+ }
+ if (curlist->next)
+ {
+ /* A & B | C == (A & B) | C */
+ tmp = new_pattern ();
+ tmp->op = M_AND;
+ tmp->child = curlist;
+
+ curlist = tmp;
+ last = curlist;
+ }
+
+ or = 1;
+ }
+ ps.dptr++;
+ implicit = 0;
+ not = 0;
+ break;
+ case '~':
+ if (implicit && or)
+ {
+ /* A | B & C == (A | B) & C */
+ tmp = new_pattern ();
+ tmp->op = M_OR;
+ tmp->child = curlist;
+ curlist = tmp;
+ last = tmp;
+ or = 0;
+ }
+
+ tmp = new_pattern ();
+ tmp->not = not;
+ not = 0;
+
+ if (last)
+ last->next = tmp;
+ else
+ curlist = tmp;
+ last = tmp;
+
+ ps.dptr++; /* move past the ~ */
+ if ((entry = lookup_tag (*ps.dptr)) == NULL)
+ {
+ snprintf (err->data, err->dsize, "%c: invalid command", *ps.dptr);
+ mutt_pattern_free (&curlist);
+ return NULL;
+ }
+ if (entry->class && (flags & entry->class) == 0)
+ {
+ snprintf (err->data, err->dsize, "%c: not supported in this mode", *ps.dptr);
+ mutt_pattern_free (&curlist);
+ return NULL;
+ }
+ tmp->op = entry->op;
+
+ ps.dptr++; /* eat the operator and any optional whitespace */
+ SKIPWS (ps.dptr);
+
+ if (entry->eat_arg)
+ {
+ if (!*ps.dptr)
+ {
+ snprintf (err->data, err->dsize, "missing parameter");
+ mutt_pattern_free (&curlist);
+ return NULL;
+ }
+ if (entry->eat_arg (tmp, &ps, err) == -1)
+ {
+ mutt_pattern_free (&curlist);
+ return NULL;
+ }
+ }
+ implicit = 1;
+ break;
+ case '(':
+ p = find_matching_paren (ps.dptr + 1);
+ if (*p != ')')
+ {
+ snprintf (err->data, err->dsize, "mismatched parenthesis: %s", ps.dptr);
+ mutt_pattern_free (&curlist);
+ return NULL;
+ }
+ /* compile the sub-expression */
+ buf = mutt_substrdup (ps.dptr + 1, p);
+ if ((tmp = mutt_pattern_comp (buf, flags, err)) == NULL)
+ {
+ FREE (&buf);
+ mutt_pattern_free (&curlist);
+ return NULL;
+ }
+ FREE (&buf);
+ if (last)
+ last->next = tmp;
+ else
+ curlist = tmp;
+ last = tmp;
+ tmp->not = not;
+ not = 0;
+ ps.dptr = p + 1; /* restore location */
+ break;
+ default:
+ snprintf (err->data, err->dsize, "error in pattern at: %s", ps.dptr);
+ mutt_pattern_free (&curlist);
+ return NULL;
+ }
+ }
+ if (!curlist)
+ {
+ strfcpy (err->data, "empty pattern", err->dsize);
+ return NULL;
+ }
+ if (curlist->next)
+ {
+ tmp = new_pattern ();
+ tmp->op = or ? M_OR : M_AND;
+ tmp->child = curlist;
+ curlist = tmp;
+ }
+ return (curlist);
+}
+
+static int
+perform_and (pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr)
+{
+ for (; pat; pat = pat->next)
+ if (mutt_pattern_exec (pat, flags, ctx, hdr) <= 0)
+ return 0;
+ return 1;
+}
+
+static int
+perform_or (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr)
+{
+ for (; pat; pat = pat->next)
+ if (mutt_pattern_exec (pat, flags, ctx, hdr) > 0)
+ return 1;
+ return 0;
+}
+
+static int match_adrlist (regex_t *rx, int match_personal, ADDRESS *a)
+{
+ for (; a; a = a->next)
+ {
+ if ((a->mailbox && regexec (rx, a->mailbox, 0, NULL, 0) == 0) ||
+ (match_personal && a->personal && regexec (rx, a->personal, 0, NULL, 0) == 0))
+ return 1;
+ }
+ return 0;
+}
+
+static int match_reference (regex_t *rx, LIST *refs)
+{
+ for (; refs; refs = refs->next)
+ if (regexec (rx, refs->data, 0, NULL, 0) == 0)
+ return 1;
+ return 0;
+}
+
+static int match_user (ADDRESS *p)
+{
+ for (; p; p = p->next)
+ if (mutt_addr_is_user (p))
+ return 1;
+ return 0;
+}
+
+/* flags
+ M_MATCH_FULL_ADDRESS match both personal and machine address */
+int
+mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h)
+{
+ char buf[STRING];
+
+ switch (pat->op)
+ {
+ case M_AND:
+ return (pat->not ^ (perform_and (pat->child, flags, ctx, h) > 0));
+ case M_OR:
+ return (pat->not ^ (perform_or (pat->child, flags, ctx, h) > 0));
+ case M_ALL:
+ return (!pat->not);
+ case M_EXPIRED:
+ return (pat->not ^ h->expired);
+ case M_SUPERSEDED:
+ return (pat->not ^ h->superseded);
+ case M_FLAG:
+ return (pat->not ^ h->flagged);
+ case M_TAG:
+ return (pat->not ^ h->tagged);
+ case M_NEW:
+ return (pat->not ? h->old || h->read : !(h->old || h->read));
+ case M_UNREAD:
+ return (pat->not ? h->read : !h->read);
+ case M_REPLIED:
+ return (pat->not ^ h->replied);
+ case M_OLD:
+ return (pat->not ? (!h->old || h->read) : (h->old && !h->read));
+ case M_READ:
+ return (pat->not ^ h->read);
+ case M_DELETED:
+ return (pat->not ^ h->deleted);
+ case M_MESSAGE:
+ return (pat->not ^ (h->msgno >= pat->min - 1 && (pat->max == M_MAXRANGE ||
+ h->msgno <= pat->max - 1)));
+ case M_DATE:
+ return (pat->not ^ (h->date_sent >= pat->min && h->date_sent <= pat->max));
+ case M_DATE_RECEIVED:
+ return (pat->not ^ (h->received >= pat->min && h->received <= pat->max));
+ case M_BODY:
+ case M_HEADER:
+ case M_WHOLE_MSG:
+ return (pat->not ^ msg_search (ctx, pat->rx, buf, sizeof (buf), pat->op, h->msgno));
+ case M_SENDER:
+ return (pat->not ^ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS, h->env->sender));
+ case M_FROM:
+ return (pat->not ^ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS, h->env->from));
+ case M_TO:
+ return (pat->not ^ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS, h->env->to));
+ case M_CC:
+ return (pat->not ^ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS, h->env->cc));
+ case M_SUBJECT:
+ return (pat->not ^ (h->env->subject && regexec (pat->rx, h->env->subject, 0, NULL, 0) == 0));
+ case M_ID:
+ return (pat->not ^ (h->env->message_id && regexec (pat->rx, h->env->message_id, 0, NULL, 0) == 0));
+ case M_SCORE:
+ return (pat->not ^ (h->score >= pat->min && (pat->max == M_MAXRANGE ||
+ h->score <= pat->max)));
+ case M_SIZE:
+ return (pat->not ^ (h->content->length > pat->min && (pat->max == M_MAXRANGE || h->content->length < pat->max)));
+ case M_REFERENCE:
+ return (pat->not ^ match_reference (pat->rx, h->env->references));
+ case M_ADDRESS:
+ return (pat->not ^ (match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS, h->env->from) ||
+ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS, h->env->sender) ||
+ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS, h->env->to) ||
+ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS, h->env->cc)));
+ break;
+ case M_RECIPIENT:
+ return (pat->not ^ (match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS, h->env->to) ||
+ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS, h->env->cc)));
+ break;
+ case M_LIST:
+ return (pat->not ^ (mutt_is_list_recipient (h->env->to) ||
+ mutt_is_list_recipient (h->env->cc)));
+ case M_PERSONAL_RECIP:
+ return (pat->not ^ (match_user (h->env->to) || match_user (h->env->cc)));
+ break;
+ case M_PERSONAL_FROM:
+ return (pat->not ^ (match_user (h->env->from)));
+ break;
+ }
+ mutt_error ("error: unknown op %d (report this error).", pat->op);
+ return (-1);
+}
+
+/* convert a simple search into a real request */
+void mutt_check_simple (char *s, size_t len, const char *simple)
+{
+ char tmp[LONG_STRING];
+
+ if (!strchr (s, '~')) /* yup, so spoof a real request */
+ {
+ /* convert old tokens into the new format */
+ if (strcasecmp ("all", s) == 0 ||
+ !strcmp ("^", s) || !strcmp (".", s)) /* ~A is more efficient */
+ strfcpy (s, "~A", len);
+ else if (strcasecmp ("del", s) == 0)
+ strfcpy (s, "~D", len);
+ else if (strcasecmp ("flag", s) == 0)
+ strfcpy (s, "~F", len);
+ else if (strcasecmp ("new", s) == 0)
+ strfcpy (s, "~N", len);
+ else if (strcasecmp ("old", s) == 0)
+ strfcpy (s, "~O", len);
+ else if (strcasecmp ("repl", s) == 0)
+ strfcpy (s, "~Q", len);
+ else if (strcasecmp ("read", s) == 0)
+ strfcpy (s, "~R", len);
+ else if (strcasecmp ("tag", s) == 0)
+ strfcpy (s, "~T", len);
+ else if (strcasecmp ("unread", s) == 0)
+ strfcpy (s, "~U", len);
+ else
+ {
+ const char *p = s;
+ int i = 0;
+
+ tmp[i++] = '"';
+ while (*p && i < sizeof (tmp) - 2)
+ {
+ if (*p == '\\' || *p == '"')
+ tmp[i++] = '\\';
+ tmp[i++] = *p++;
+ }
+ tmp[i++] = '"';
+ tmp[i] = 0;
+ mutt_expand_fmt (s, len, simple, tmp);
+ }
+ }
+}
+
+int mutt_pattern_func (int op, char *prompt, HEADER *hdr)
+{
+ pattern_t *pat;
+ char buf[LONG_STRING] = "", *simple, error[STRING];
+ BUFFER err;
+ int i;
+
+ if (mutt_get_field (prompt, buf, sizeof (buf), 0) != 0 || !buf[0])
+ return (-1);
+
+ mutt_message ("Compiling search pattern...");
+
+ simple = safe_strdup (buf);
+ mutt_check_simple (buf, sizeof (buf), NONULL (SimpleSearch));
+
+ err.data = error;
+ err.dsize = sizeof (error);
+ if ((pat = mutt_pattern_comp (buf, M_FULL_MSG, &err)) == NULL)
+ {
+ FREE (&simple);
+ mutt_error ("%s", err.data);
+ return (-1);
+ }
+
+ mutt_message ("Executing command on matching messages...");
+
+ if (op == M_LIMIT)
+ {
+ for (i = 0; i < Context->msgcount; i++)
+ Context->hdrs[i]->virtual = -1;
+ Context->vcount = 0;
+ Context->vsize = 0;
+ }
+
+#define this_body Context->hdrs[i]->content
+
+ for (i = 0; i < Context->msgcount; i++)
+ if (mutt_pattern_exec (pat, M_MATCH_FULL_ADDRESS, Context, Context->hdrs[i]))
+ {
+ switch (op)
+ {
+ case M_DELETE:
+ mutt_set_flag (Context, Context->hdrs[i], M_DELETE, 1);
+ break;
+ case M_UNDELETE:
+ mutt_set_flag (Context, Context->hdrs[i], M_DELETE, 0);
+ break;
+ case M_TAG:
+ mutt_set_flag (Context, Context->hdrs[i], M_TAG, 1);
+ break;
+ case M_UNTAG:
+ mutt_set_flag (Context, Context->hdrs[i], M_TAG, 0);
+ break;
+ case M_LIMIT:
+ Context->hdrs[i]->virtual = Context->vcount;
+ Context->v2r[Context->vcount] = i;
+ Context->vcount++;
+ Context->vsize+=this_body->length + this_body->offset -
+ this_body->hdr_offset;
+ break;
+ }
+ }
+#undef this_body
+
+ mutt_clear_error ();
+
+ if (op == M_LIMIT)
+ {
+ safe_free ((void **) &Context->pattern);
+ if (Context->limit_pattern)
+ mutt_pattern_free (&Context->limit_pattern);
+ if (!Context->vcount)
+ {
+ mutt_error ("No messages matched criteria.");
+ /* restore full display */
+ for (i = 0; i < Context->msgcount; i++)
+ {
+ Context->hdrs[i]->virtual = i;
+ Context->v2r[i] = i;
+ }
+
+ Context->vcount = Context->msgcount;
+ }
+ else if (strncmp (buf, "~A", 2) != 0)
+ {
+ Context->pattern = simple;
+ simple = NULL; /* don't clobber it */
+ Context->limit_pattern = mutt_pattern_comp (buf, M_FULL_MSG, &err);
+ }
+ }
+ FREE (&simple);
+ mutt_pattern_free (&pat);
+ return 0;
+}
+
+int mutt_search_command (int cur, int op)
+{
+ int i, j;
+ char buf[STRING];
+ char temp[LONG_STRING];
+ char error[STRING];
+ BUFFER err;
+ int incr;
+ HEADER *h;
+
+ if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE)
+ {
+ strfcpy (buf, LastSearch, sizeof (buf));
+ if (mutt_get_field ((op == OP_SEARCH) ? "Search for: " : "Reverse search for: ",
+ buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
+ return (-1);
+
+ if (op == OP_SEARCH)
+ unset_option (OPTSEARCHREVERSE);
+ else
+ set_option (OPTSEARCHREVERSE);
+
+ /* compare the *expanded* version of the search pattern in case
+ $simple_search has changed while we were searching */
+ strfcpy (temp, buf, sizeof (temp));
+ mutt_check_simple (temp, sizeof (temp), NONULL (SimpleSearch));
+
+ if (!SearchPattern || strcmp (temp, LastSearchExpn))
+ {
+ set_option (OPTSEARCHINVALID);
+ strfcpy (LastSearch, buf, sizeof (LastSearch));
+ mutt_message ("Compiling search pattern...");
+ mutt_pattern_free (&SearchPattern);
+ err.data = error;
+ err.dsize = sizeof (error);
+ if ((SearchPattern = mutt_pattern_comp (temp, M_FULL_MSG, &err)) == NULL)
+ {
+ mutt_error ("%s", error);
+ return (-1);
+ }
+ mutt_clear_error ();
+ }
+ }
+ else if (!SearchPattern)
+ {
+ mutt_error ("No search pattern.");
+ return (-1);
+ }
+
+ if (option (OPTSEARCHINVALID))
+ {
+ for (i = 0; i < Context->msgcount; i++)
+ Context->hdrs[i]->searched = 0;
+ unset_option (OPTSEARCHINVALID);
+ }
+
+ incr = (option (OPTSEARCHREVERSE)) ? -1 : 1;
+ if (op == OP_SEARCH_OPPOSITE)
+ incr = -incr;
+
+ for (i = cur + incr, j = 0 ; j != Context->vcount; j++)
+ {
+ if (i > Context->vcount - 1)
+ {
+ i = 0;
+ if (option (OPTWRAPSEARCH))
+ mutt_message ("Search wrapped to top.");
+ else
+ {
+ mutt_message ("Search hit bottom without finding match");
+ return (-1);
+ }
+ }
+ else if (i < 0)
+ {
+ i = Context->vcount - 1;
+ if (option (OPTWRAPSEARCH))
+ mutt_message ("Search wrapped to bottom.");
+ else
+ {
+ mutt_message ("Search hit top without finding match");
+ return (-1);
+ }
+ }
+
+ h = Context->hdrs[Context->v2r[i]];
+ if (h->searched)
+ {
+ /* if we've already evaulated this message, use the cached value */
+ if (h->matched)
+ return i;
+ }
+ else
+ {
+ /* remember that we've already searched this message */
+ h->searched = 1;
+ if ((h->matched = (mutt_pattern_exec (SearchPattern, M_MATCH_FULL_ADDRESS, Context, h) > 0)))
+ return i;
+ }
+
+ if (Signals & S_INTERRUPT)
+ {
+ mutt_error ("Search interrupted.");
+ Signals &= ~S_INTERRUPT;
+ return (-1);
+ }
+
+ i += incr;
+ }
+
+ mutt_error ("Not found.");
+ return (-1);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file contains all of the PGP routines necessary to sign, encrypt,
+ * verify and decrypt PGP messages in either the new PGP/MIME format, or
+ * in the older Application/Pgp format. It also contains some code to
+ * cache the user's passphrase for repeat use when decrypting or signing
+ * a message.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "pgp.h"
+#include "mime.h"
+#include "parse.h"
+
+#include <sys/wait.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+
+#ifdef _PGPPATH
+
+
+char PgpPass[STRING];
+static time_t PgpExptime = 0; /* when does the cached passphrase expire? */
+
+static struct pgp_vinfo pgp_vinfo[] =
+{
+ { PGP_V2, "pgp2", &PgpV2, &PgpV2Pubring, &PgpV2Secring, &PgpV2Language },
+ { PGP_V3, "pgp3", &PgpV3, &PgpV3Pubring, &PgpV3Secring, &PgpV3Language },
+ { PGP_V3, "pgp5", &PgpV3, &PgpV3Pubring, &PgpV3Secring, &PgpV3Language },
+ { PGP_UNKNOWN, NULL, NULL, NULL, NULL, NULL}
+};
+
+static struct
+{
+ enum pgp_ops op;
+ char **str;
+}
+pgp_opvers[] =
+{
+ { PGP_DECODE, &PgpReceiveVersion },
+ { PGP_VERIFY, &PgpReceiveVersion },
+ { PGP_DECRYPT, &PgpReceiveVersion },
+ { PGP_SIGN, &PgpSendVersion },
+ { PGP_ENCRYPT, &PgpSendVersion },
+ { PGP_VERIFY_KEY, &PgpSendVersion },
+ { PGP_EXTRACT, &PgpKeyVersion },
+ { PGP_EXTRACT_KEY, &PgpKeyVersion },
+ { PGP_LAST_OP, NULL }
+};
+
+
+
+void pgp_void_passphrase (void)
+{
+ memset (PgpPass, 0, sizeof (PgpPass));
+ PgpExptime = 0;
+}
+
+int pgp_valid_passphrase (void)
+{
+ time_t now = time (NULL);
+
+ if (now < PgpExptime) return 1; /* just use the cached copy. */
+ pgp_void_passphrase ();
+
+ if (mutt_get_password ("Enter PGP passphrase:", PgpPass, sizeof (PgpPass)) == 0)
+ {
+ PgpExptime = time (NULL) + PgpTimeout;
+ return (1);
+ }
+ else
+ {
+ PgpExptime = 0;
+ return (0);
+ }
+ /* not reached */
+}
+
+void mutt_forget_passphrase (void)
+{
+ pgp_void_passphrase ();
+ mutt_message ("PGP passphrase forgotten.");
+}
+
+
+struct pgp_vinfo *pgp_get_vinfo(enum pgp_ops op)
+{
+ int i;
+ char *version = "default";
+
+ for(i = 0; pgp_opvers[i].op != PGP_LAST_OP; i++)
+ {
+ if(pgp_opvers[i].op == op)
+ {
+ version = *pgp_opvers[i].str;
+ break;
+ }
+ }
+
+ if (!strcasecmp(version, "default"))
+ version = PgpDefaultVersion;
+
+ for(i = 0; pgp_vinfo[i].name; i++)
+ {
+ if(!strcasecmp(pgp_vinfo[i].name, version))
+ return &pgp_vinfo[i];
+ }
+
+ return NULL;
+}
+
+enum pgp_version pgp_version(enum pgp_ops op)
+{
+ struct pgp_vinfo *info = pgp_get_vinfo(op);
+ return info ? info->v : PGP_UNKNOWN;
+}
+
+
+char *pgp_getring (enum pgp_ops op, int pub)
+{
+ struct pgp_vinfo *info = pgp_get_vinfo(op);
+ if(!info) return NULL;
+ return pub ? *info->pubring : *info->secring;
+}
+
+char *pgp_binary(enum pgp_ops op)
+{
+ struct pgp_vinfo *info = pgp_get_vinfo(op);
+ return info ? *info->binary : NULL;
+}
+
+
+char *pgp_keyid(KEYINFO *k)
+{
+ if((k->flags & KEYFLAG_SUBKEY) && k->mainkey)
+ k = k->mainkey;
+
+ return _pgp_keyid(k);
+}
+
+char *_pgp_keyid(KEYINFO *k)
+{
+ if(option(OPTPGPLONGIDS))
+ return k->keyid;
+ else
+ return (k->keyid + 8);
+}
+
+/* ----------------------------------------------------------------------------
+ * Routines for handing PGP input.
+ */
+
+/* print the current time to avoid spoofing of the signature output */
+static void pgp_current_time (STATE *s)
+{
+ time_t t;
+ char p[STRING];
+
+ state_puts ("[-- PGP output follows (current time: ", s);
+
+ t = time (NULL);
+ strfcpy (p, asctime (localtime (&t)), sizeof (p));
+ p[strlen (p) - 1] = 0; /* kill the newline */
+ state_puts (p, s);
+
+ state_puts (") --]\n", s);
+}
+
+
+/* Support for the Application/PGP Content Type. */
+
+void application_pgp_handler (BODY *m, STATE *s)
+{
+ int needpass = -1, pgp_keyblock = 0;
+ int clearsign = 0;
+ long start_pos = 0;
+ long bytes, last_pos, offset;
+ char buf[HUGE_STRING];
+ char outfile[_POSIX_PATH_MAX];
+ char tmpfname[_POSIX_PATH_MAX];
+ FILE *pgpout = NULL, *pgpin, *pgperr;
+ FILE *tmpfp;
+ pid_t thepid;
+
+
+ fseek (s->fpin, m->offset, 0);
+ last_pos = m->offset;
+
+ for (bytes = m->length; bytes > 0;)
+ {
+ if (fgets (buf, sizeof (buf) - 1, s->fpin) == NULL)
+ break;
+ offset = ftell (s->fpin);
+ bytes -= (offset - last_pos); /* don't rely on strlen(buf) */
+ last_pos = offset;
+
+ if (strncmp ("-----BEGIN PGP ", buf, 15) == 0)
+ {
+ clearsign = 0;
+ start_pos = last_pos;
+
+ if (strcmp ("MESSAGE-----\n", buf + 15) == 0)
+ needpass = 1;
+ else if (strcmp ("SIGNED MESSAGE-----\n", buf + 15) == 0)
+ {
+ clearsign = 1;
+ needpass = 0;
+ }
+ else if (!option(OPTDONTHANDLEPGPKEYS) &&
+ strcmp ("PUBLIC KEY BLOCK-----\n", buf + 15) == 0)
+ {
+ needpass = 0;
+ pgp_keyblock =1;
+ }
+ else
+ {
+ if (s->prefix)
+ state_puts (s->prefix, s);
+ state_puts (buf, s);
+ continue;
+ }
+
+ if(!clearsign || s->flags & M_VERIFY)
+ {
+
+ /* invoke PGP */
+
+ mutt_mktemp (outfile);
+ if ((pgpout = safe_fopen (outfile, "w+")) == NULL)
+ {
+ mutt_perror (outfile);
+ return;
+ }
+
+ mutt_mktemp (tmpfname);
+ if ((tmpfp = safe_fopen(tmpfname, "w+")) == NULL)
+ {
+ mutt_perror(tmpfname);
+ fclose(pgpout); pgpout = NULL;
+ return;
+ }
+
+ fputs (buf, tmpfp);
+ while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL)
+ {
+ offset = ftell (s->fpin);
+ bytes -= (offset - last_pos); /* don't rely on strlen(buf) */
+ last_pos = offset;
+
+ fputs (buf, tmpfp);
+ if ((needpass && strcmp ("-----END PGP MESSAGE-----\n", buf) == 0) ||
+ (!needpass
+ && (strcmp ("-----END PGP SIGNATURE-----\n", buf) == 0
+ || strcmp ("-----END PGP PUBLIC KEY BLOCK-----\n",buf) == 0)))
+ break;
+ }
+
+ fclose(tmpfp);
+
+ if ((thepid = pgp_invoke_decode (&pgpin, NULL, &pgperr, -1,
+ fileno (pgpout), -1, tmpfname, needpass)) == -1)
+ {
+ fclose (pgpout); pgpout = NULL;
+ mutt_unlink(tmpfname);
+ state_puts ("[-- Error: unable to create PGP subprocess --]\n", s);
+ state_puts (buf, s);
+ continue;
+ }
+
+ if (needpass)
+ {
+ if (!pgp_valid_passphrase ())
+ pgp_void_passphrase ();
+ fputs (PgpPass, pgpin);
+ fputc ('\n', pgpin);
+ }
+
+ fclose (pgpin);
+
+ if (s->flags & M_DISPLAY)
+ pgp_current_time (s);
+
+ mutt_wait_filter (thepid);
+
+ mutt_unlink(tmpfname);
+
+ if (s->flags & M_DISPLAY)
+ mutt_copy_stream(pgperr, s->fpout);
+ fclose (pgperr);
+
+ if (s->flags & M_DISPLAY)
+ state_puts ("\n[-- End of PGP output --]\n\n", s);
+ }
+
+ if(s->flags & M_DISPLAY)
+ {
+ if (needpass)
+ state_puts ("[-- BEGIN PGP MESSAGE --]\n\n", s);
+ else if (pgp_keyblock)
+ state_puts ("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n", s);
+ else
+ state_puts ("[-- BEGIN PGP SIGNED MESSAGE --]\n\n", s);
+ }
+
+ /* Use PGP's output if there was no clearsig signature. */
+
+ if(!clearsign)
+ {
+ fflush (pgpout);
+ rewind (pgpout);
+ while (fgets (buf, sizeof (buf) - 1, pgpout) != NULL)
+ {
+ if (s->prefix)
+ state_puts (s->prefix, s);
+ state_puts (buf, s);
+ }
+ }
+
+ /* Close the temporary files iff they were created.
+ * The condition used to be !clearsign || s->flags & M_VERIFY,
+ * but gcc would complain then.
+ */
+
+ if(pgpout)
+ {
+ fclose (pgpout);
+ pgpout = NULL;
+ mutt_unlink(outfile);
+ }
+
+ /* decode clearsign stuff */
+
+ if(clearsign)
+ {
+
+ /* rationale: We want PGP's error messages, but in the times
+ * of PGP 5.0 we can't rely on PGP to do the dash
+ * escape decoding - so we have to do this
+ * ourselves.
+ */
+
+ int armor_header = 1;
+ int complete = 1;
+
+ fseek(s->fpin, start_pos, SEEK_SET);
+ bytes += (last_pos - start_pos);
+ last_pos = start_pos;
+ offset = start_pos;
+ while(bytes > 0 && fgets(buf, sizeof(buf) - 1, s->fpin) != NULL)
+ {
+ offset = ftell(s->fpin);
+ bytes -= (offset - last_pos);
+ last_pos = offset;
+
+ if(complete)
+ {
+ if (!strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
+ break;
+
+ if(armor_header)
+ {
+ if(*buf == '\n')
+ armor_header = 0;
+ }
+ else
+ {
+ if(s->prefix)
+ state_puts(s->prefix, s);
+
+ if(buf[0] == '-' && buf [1] == ' ')
+ state_puts(buf + 2, s);
+ else
+ state_puts(buf, s);
+ }
+ }
+ else
+ {
+ if(!armor_header)
+ state_puts(buf, s);
+ }
+
+ complete = strchr(buf, '\n') != NULL;
+ }
+
+ if (complete && !strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
+ {
+ while(bytes > 0 && fgets(buf, sizeof(buf) - 1, s->fpin) != NULL)
+ {
+ offset = ftell(s->fpin);
+ bytes -= (offset - last_pos);
+ last_pos = offset;
+
+ if(complete && !strcmp(buf, "-----END PGP SIGNATURE-----\n"))
+ break;
+
+ complete = strchr(buf, '\n') != NULL;
+ }
+ }
+ }
+
+ if (s->flags & M_DISPLAY)
+ {
+ if (needpass)
+ state_puts ("\n[-- END PGP MESSAGE --]\n", s);
+ else if (pgp_keyblock)
+ state_puts ("[-- END PGP PUBLIC KEY BLOCK --]\n", s);
+ else
+ state_puts ("\n[-- END PGP SIGNED MESSAGE --]\n", s);
+ }
+ }
+ else
+ {
+ if (s->prefix)
+ state_puts (s->prefix, s);
+ state_puts (buf, s);
+ }
+ }
+
+ if (needpass == -1)
+ {
+ state_puts ("[-- Error! Could not find beginning of PGP message! --]\n\n", s);
+ return;
+ }
+
+}
+
+int pgp_query (BODY *m)
+{
+ char *p;
+ int t = 0;
+
+ /* Check for old-style APPLICATION/PGP messages */
+ if (m->type == TYPEAPPLICATION)
+ {
+ if (!strcasecmp (m->subtype, "pgp") || !strcasecmp (m->subtype, "x-pgp-message"))
+ {
+ if ((p = mutt_get_parameter ("x-action", m->parameter))
+ && (!strcasecmp (p, "sign") || !strcasecmp (p, "signclear")))
+ t |= PGPSIGN;
+ else if((p = mutt_get_parameter ("format", m->parameter))
+ && !strcasecmp(p, "keys-only"))
+ t |= PGPKEY;
+
+ if ((p = mutt_get_parameter ("format", m->parameter)) &&
+ !strcasecmp (p, "keys-only"))
+ t |= PGPKEY;
+
+ if(!t) t |= PGPENCRYPT; /* not necessarily correct, but... */
+ }
+
+ if (!strcasecmp (m->subtype, "pgp-signed"))
+ t |= PGPSIGN;
+
+ if (!strcasecmp (m->subtype, "pgp-keys"))
+ t |= PGPKEY;
+ }
+
+ /* Check for PGP/MIME messages. */
+ if (m->type == TYPEMULTIPART)
+ {
+ if (strcasecmp (m->subtype, "signed") == 0 &&
+ (p = mutt_get_parameter("protocol", m->parameter)) &&
+ strcasecmp (p, "application/pgp-signature") == 0)
+ t |= PGPSIGN;
+ else if ((strcasecmp (m->subtype, "encrypted") == 0) &&
+ (p = mutt_get_parameter ("protocol", m->parameter)) &&
+ strcasecmp (p, "application/pgp-encrypted") == 0)
+ t |= PGPENCRYPT;
+ }
+
+ if(m->type == TYPEMULTIPART || m->type == TYPEMESSAGE)
+ {
+ BODY *p;
+
+ for(p = m->parts; p; p = p->next)
+ t |= pgp_query(p);
+ }
+
+ return t;
+}
+
+/*
+ * This routine verifies a PGP/MIME signed body.
+ */
+void pgp_signed_handler (BODY *a, STATE *s)
+{
+ FILE *fp, *pgpout, *pgperr;
+ char tempfile[_POSIX_PATH_MAX], sigfile[_POSIX_PATH_MAX],
+ pgperrfile[_POSIX_PATH_MAX];
+ int bytes;
+ int hadcr;
+ int c;
+ pid_t thepid;
+
+ a = a->parts;
+
+ /* First do some error checking to make sure that this looks like a valid
+ * multipart/signed body.
+ */
+ if (a && a->next && a->next->type == TYPEAPPLICATION && a->next->subtype &&
+ strcasecmp (a->next->subtype, "pgp-signature") == 0)
+ {
+ if (s->flags & M_DISPLAY)
+ {
+ mutt_mktemp (tempfile);
+
+ if(!(fp = safe_fopen (tempfile, "w")))
+ {
+ mutt_perror(tempfile);
+ return;
+ }
+
+ fseek (s->fpin, a->hdr_offset, 0);
+ bytes = a->length + a->offset - a->hdr_offset;
+ hadcr = 0;
+ while (bytes > 0)
+ {
+ if((c = fgetc(s->fpin)) == EOF)
+ break;
+
+ bytes--;
+
+ if(c == '\r')
+ hadcr = 1;
+ else
+ {
+ if(c == '\n' && !hadcr)
+ fputc('\r', fp);
+
+ hadcr = 0;
+ }
+
+ fputc(c, fp);
+
+ }
+ fclose (fp);
+
+ /* Now grab the signature. Since signature data is required to be 7bit,
+ * we don't have to worry about doing CTE decoding...
+ */
+ snprintf (sigfile, sizeof (sigfile), "%s.asc", tempfile);
+ if(!(fp = safe_fopen (sigfile, "w")))
+ {
+ mutt_perror(sigfile);
+ unlink(tempfile);
+ return;
+ }
+
+ fseek (s->fpin, a->next->offset, 0);
+ mutt_copy_bytes (s->fpin, fp, a->next->length);
+ fclose (fp);
+
+ mutt_mktemp(pgperrfile);
+ if(!(pgperr = safe_fopen(pgperrfile, "w+")))
+ {
+ mutt_perror(pgperrfile);
+ unlink(tempfile);
+ unlink(sigfile);
+ return;
+ }
+
+ pgp_current_time (s);
+
+ if((thepid = pgp_invoke_verify (NULL, &pgpout, NULL, -1, -1, fileno(pgperr),
+ tempfile, sigfile)) != -1)
+ {
+ mutt_copy_stream(pgpout, s->fpout);
+ fclose (pgpout);
+
+ fflush(pgperr);
+ rewind(pgperr);
+ mutt_copy_stream(pgperr, s->fpout);
+ fclose(pgperr);
+
+ mutt_wait_filter (thepid);
+ }
+
+ state_puts ("[-- End of PGP output --]\n\n", s);
+
+ mutt_unlink (tempfile);
+ mutt_unlink (sigfile);
+ mutt_unlink (pgperrfile);
+
+ /* Now display the signed body */
+ state_puts ("[-- The following data is PGP/MIME signed --]\n\n", s);
+ }
+
+ mutt_body_handler (a, s);
+
+ if (s->flags & M_DISPLAY)
+ state_puts ("\n[-- End of PGP/MIME signed data --]\n", s);
+ }
+ else
+ {
+ dprint (1,(debugfile, "pgp_signed_handler: invalid format!\n"));
+ state_puts ("[-- Error! This message does not comply with the PGP/MIME specification! --]\n\n", s);
+ mutt_decode_attachment (a, s); /* just treat the data as text/plain... */
+ }
+}
+
+/* Extract pgp public keys from messages or attachments */
+
+void pgp_extract_keys_from_messages (HEADER *h)
+{
+ int i;
+ STATE s;
+ char tempfname[_POSIX_PATH_MAX];
+
+ if(h)
+ {
+ mutt_parse_mime_message(Context, h);
+ if(h->pgp & PGPENCRYPT && !pgp_valid_passphrase())
+ return;
+ }
+
+ memset(&s, 0, sizeof(STATE));
+
+ mutt_mktemp(tempfname);
+ if(!(s.fpout = safe_fopen(tempfname, "w")))
+ {
+ mutt_perror(tempfname);
+ return;
+ }
+
+ set_option(OPTDONTHANDLEPGPKEYS);
+
+ if(!h)
+ {
+ for(i = 0; i < Context->vcount; i++)
+ {
+ if(Context->hdrs[Context->v2r[i]]->tagged)
+ {
+ mutt_parse_mime_message(Context, Context->hdrs[Context->v2r[i]]);
+ if(Context->hdrs[Context->v2r[i]]->pgp & PGPENCRYPT
+ && !pgp_valid_passphrase())
+ {
+ fclose(s.fpout);
+ goto bailout;
+ }
+ mutt_pipe_message_to_state(Context->hdrs[Context->v2r[i]], &s);
+ }
+ }
+ }
+ else
+ {
+ mutt_parse_mime_message(Context, h);
+ if(h->pgp & PGPENCRYPT && !pgp_valid_passphrase())
+ {
+ fclose(s.fpout);
+ goto bailout;
+ }
+ mutt_pipe_message_to_state(h, &s);
+ }
+
+ fclose(s.fpout);
+ endwin();
+ pgp_invoke_extract(tempfname);
+ mutt_any_key_to_continue(NULL);
+
+ bailout:
+
+ mutt_unlink(tempfname);
+ unset_option(OPTDONTHANDLEPGPKEYS);
+
+}
+
+static void pgp_extract_keys_from_attachment(FILE *fp, BODY *top)
+{
+ STATE s;
+ FILE *tempfp;
+ char tempfname[_POSIX_PATH_MAX];
+
+ mutt_mktemp(tempfname);
+ if(!(tempfp = safe_fopen(tempfname, "w")))
+ {
+ mutt_perror(tempfname);
+ return;
+ }
+
+ memset(&s, 0, sizeof(STATE));
+
+ s.fpin = fp;
+ s.fpout = tempfp;
+
+ mutt_body_handler(top, &s);
+
+ fclose(tempfp);
+
+ pgp_invoke_extract(tempfname);
+ mutt_any_key_to_continue(NULL);
+
+ mutt_unlink(tempfname);
+
+}
+
+void pgp_extract_keys_from_attachment_list (FILE *fp, int tag, BODY *top)
+{
+ if(!fp)
+ {
+ mutt_error("Internal error. Inform <roessler@guug.de>.");
+ return;
+ }
+
+ endwin();
+ set_option(OPTDONTHANDLEPGPKEYS);
+
+ for(; top; top = top->next)
+ {
+ if(!tag || top->tagged)
+ pgp_extract_keys_from_attachment (fp, top);
+
+ if(!tag)
+ break;
+ }
+
+ unset_option(OPTDONTHANDLEPGPKEYS);
+}
+
+BODY *pgp_decrypt_part (BODY *a, STATE *s, FILE *fpout)
+{
+ char buf[LONG_STRING];
+ FILE *pgpin, *pgpout, *pgperr, *pgptmp;
+ struct stat info;
+ BODY *tattach;
+ int len;
+ char pgperrfile[_POSIX_PATH_MAX];
+ char pgptmpfile[_POSIX_PATH_MAX];
+ pid_t thepid;
+
+ mutt_mktemp (pgperrfile);
+ if ((pgperr = safe_fopen (pgperrfile, "w+")) == NULL)
+ {
+ mutt_perror (pgperrfile);
+ return NULL;
+ }
+ unlink (pgperrfile);
+
+ mutt_mktemp (pgptmpfile);
+ if((pgptmp = safe_fopen (pgptmpfile, "w")) == NULL)
+ {
+ mutt_perror (pgptmpfile);
+ fclose(pgperr);
+ return NULL;
+ }
+
+ /* Position the stream at the beginning of the body, and send the data to
+ * the temporary file.
+ */
+
+ fseek (s->fpin, a->offset, 0);
+ mutt_copy_bytes (s->fpin, pgptmp, a->length);
+ fclose (pgptmp);
+
+ if ((thepid = pgp_invoke_decrypt (&pgpin, &pgpout, NULL, -1, -1,
+ fileno (pgperr), pgptmpfile)) == -1)
+ {
+ fclose (pgperr);
+ unlink (pgptmpfile);
+ if (s->flags & M_DISPLAY)
+ state_puts ("[-- Error: could not create a PGP subprocess! --]\n\n", s);
+ return (NULL);
+ }
+
+ /* send the PGP passphrase to the subprocess */
+ fputs (PgpPass, pgpin);
+ fputc ('\n', pgpin);
+ fclose(pgpin);
+
+ /* Read the output from PGP, and make sure to change CRLF to LF, otherwise
+ * read_mime_header has a hard time parsing the message.
+ */
+ while (fgets (buf, sizeof (buf) - 1, pgpout) != NULL)
+ {
+ len = strlen (buf);
+ if (len > 1 && buf[len - 2] == '\r')
+ strcpy (buf + len - 2, "\n");
+ fputs (buf, fpout);
+ }
+
+ fclose (pgpout);
+ mutt_wait_filter (thepid);
+ mutt_unlink(pgptmpfile);
+
+ if (s->flags & M_DISPLAY)
+ {
+ fflush (pgperr);
+ rewind (pgperr);
+ mutt_copy_stream (pgperr, s->fpout);
+ state_puts ("[-- End of PGP output --]\n\n", s);
+ }
+ fclose (pgperr);
+
+ fflush (fpout);
+ rewind (fpout);
+ if ((tattach = mutt_read_mime_header (fpout, 0)) != NULL)
+ {
+ /*
+ * Need to set the length of this body part.
+ */
+ fstat (fileno (fpout), &info);
+ tattach->length = info.st_size - tattach->offset;
+
+ /* See if we need to recurse on this MIME part. */
+
+ if (tattach->type == TYPEMULTIPART)
+ {
+ fseek (fpout, tattach->offset, 0);
+ tattach->parts = mutt_parse_multipart (fpout, mutt_get_parameter ("boundary", tattach->parameter), tattach->offset + tattach->length, strcasecmp ("digest", tattach->subtype) == 0);
+ }
+ else if (tattach->type == TYPEMESSAGE)
+ {
+ fseek (fpout, tattach->offset, 0);
+ tattach->parts = mutt_parse_messageRFC822 (fpout, tattach);
+ }
+ }
+
+ return (tattach);
+}
+
+void pgp_encrypted_handler (BODY *a, STATE *s)
+{
+ char tempfile[_POSIX_PATH_MAX];
+ FILE *fpout, *fpin;
+ BODY *tattach;
+
+ a = a->parts;
+ if (!a || a->type != TYPEAPPLICATION || !a->subtype ||
+ strcasecmp ("pgp-encrypted", a->subtype) != 0 ||
+ !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype ||
+ strcasecmp ("octet-stream", a->next->subtype) != 0)
+ {
+ if (s->flags & M_DISPLAY)
+ state_puts ("[-- Error: malformed PGP/MIME message --]\n\n", s);
+ return;
+ }
+
+ /*
+ * Move forward to the application/pgp-encrypted body.
+ */
+ a = a->next;
+
+ mutt_mktemp (tempfile);
+ if ((fpout = safe_fopen (tempfile, "w+")) == NULL)
+ {
+ if (s->flags & M_DISPLAY)
+ state_puts ("[-- Error: could not create temporary file --]\n", s);
+ return;
+ }
+
+ if (s->flags & M_DISPLAY) pgp_current_time (s);
+
+ if ((tattach = pgp_decrypt_part (a, s, fpout)) != NULL)
+ {
+ if (s->flags & M_DISPLAY)
+ state_puts ("[-- The following data is PGP/MIME encrypted --]\n\n", s);
+
+ fpin = s->fpin;
+ s->fpin = fpout;
+ mutt_body_handler (tattach, s);
+ s->fpin = fpin;
+
+ if (s->flags & M_DISPLAY)
+ state_puts ("\n[-- End of PGP/MIME encrypted data --]\n", s);
+
+ mutt_free_body (&tattach);
+ }
+
+ fclose (fpout);
+ mutt_unlink(tempfile);
+}
+
+/* ----------------------------------------------------------------------------
+ * Routines for sending PGP/MIME messages.
+ */
+
+static void convert_to_7bit (BODY *a)
+{
+ while (a)
+ {
+ if (a->type == TYPEMULTIPART)
+ {
+ if (a->encoding != ENC7BIT)
+ {
+ a->encoding = ENC7BIT;
+ convert_to_7bit (a->parts);
+ }
+ }
+ else if (a->type == TYPEMESSAGE
+ && strcasecmp(a->subtype, "delivery-status"))
+ {
+ if(a->encoding != ENC7BIT)
+ mutt_message_to_7bit(a, NULL);
+ }
+ else if (a->encoding == ENC8BIT)
+ a->encoding = ENCQUOTEDPRINTABLE;
+ else if (a->encoding == ENCBINARY)
+ a->encoding = ENCBASE64;
+ else if (a->content && (a->content->from || (a->content->space && option (OPTPGPSTRICTENC))))
+ a->encoding = ENCQUOTEDPRINTABLE;
+ a = a->next;
+ }
+}
+
+BODY *pgp_sign_message (BODY *a)
+{
+ PARAMETER *p;
+ BODY *t;
+ char buffer[LONG_STRING];
+ char sigfile[_POSIX_PATH_MAX], signedfile[_POSIX_PATH_MAX];
+ FILE *pgpin, *pgpout, *pgperr, *fp, *sfp;
+ int err = 0;
+ int empty = 1;
+ pid_t thepid;
+
+ convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
+
+ mutt_mktemp (sigfile);
+ if ((fp = safe_fopen (sigfile, "w")) == NULL)
+ {
+ return (NULL);
+ }
+
+ mutt_mktemp (signedfile);
+ if ((sfp = safe_fopen(signedfile, "w")) == NULL)
+ {
+ mutt_perror(signedfile);
+ fclose(fp);
+ unlink(sigfile);
+ return NULL;
+ }
+
+ mutt_write_mime_header (a, sfp);
+ fputc ('\n', sfp);
+ mutt_write_mime_body (a, sfp);
+ fclose(sfp);
+
+ if((thepid = pgp_invoke_sign(&pgpin, &pgpout, &pgperr,
+ -1, -1, -1, signedfile)) == -1)
+ {
+ mutt_perror("Can't open PGP subprocess!");
+ fclose(fp);
+ unlink(sigfile);
+ unlink(signedfile);
+ return NULL;
+ }
+
+ fputs(PgpPass, pgpin);
+ fputc('\n', pgpin);
+ fclose(pgpin);
+
+ /*
+ * Read back the PGP signature. Also, change MESSAGE=>SIGNATURE as
+ * recommended for future releases of PGP.
+ */
+ while (fgets (buffer, sizeof (buffer) - 1, pgpout) != NULL)
+ {
+ if (strcmp ("-----BEGIN PGP MESSAGE-----\n", buffer) == 0)
+ fputs ("-----BEGIN PGP SIGNATURE-----\n", fp);
+ else if (strcmp("-----END PGP MESSAGE-----\n", buffer) == 0)
+ fputs ("-----END PGP SIGNATURE-----\n", fp);
+ else
+ fputs (buffer, fp);
+ empty = 0; /* got some output, so we're ok */
+ }
+
+ /* check for errors from PGP */
+ err = 0;
+ while (fgets (buffer, sizeof (buffer) - 1, pgperr) != NULL)
+ {
+ err = 1;
+ fputs (buffer, stdout);
+ }
+
+ mutt_wait_filter (thepid);
+ fclose (pgperr);
+ fclose (pgpout);
+ unlink (signedfile);
+
+ if (fclose (fp) != 0)
+ {
+ mutt_perror ("fclose");
+ unlink (sigfile);
+ return (NULL);
+ }
+
+ if (err)
+ mutt_any_key_to_continue (NULL);
+ if (empty)
+ {
+ unlink (sigfile);
+ return (NULL); /* fatal error while signing */
+ }
+
+ t = mutt_new_body ();
+ t->type = TYPEMULTIPART;
+ t->subtype = safe_strdup ("signed");
+ t->use_disp = 0;
+ t->encoding = ENC7BIT;
+
+ t->parameter = p = mutt_new_parameter ();
+ p->attribute = safe_strdup ("protocol");
+ p->value = safe_strdup ("application/pgp-signature");
+
+ p->next = mutt_new_parameter ();
+ p = p->next;
+ p->attribute = safe_strdup ("micalg");
+ p->value = safe_strdup (PgpSignMicalg);
+
+ p->next = mutt_new_parameter ();
+ p = p->next;
+ p->attribute = safe_strdup ("boundary");
+ p->value = mutt_generate_boundary ();
+
+ t->parts = a;
+ a = t;
+
+ t->parts->next = mutt_new_body ();
+ t = t->parts->next;
+ t->type = TYPEAPPLICATION;
+ t->subtype = safe_strdup ("pgp-signature");
+ t->filename = safe_strdup (sigfile);
+ t->use_disp = 0;
+ t->encoding = ENC7BIT;
+ t->unlink = 1; /* ok to remove this file after sending. */
+
+ return (a);
+}
+
+/* This routine attempts to find the keyids of the recipients of a message.
+ * It returns NULL if any of the keys can not be found.
+ */
+char *pgp_findKeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc)
+{
+ char *pubring = pgp_pubring(PGP_ENCRYPT);
+ char *key, *keylist = NULL;
+ size_t keylist_size = 0;
+ size_t keylist_used = 0;
+ ADDRESS *tmp = NULL;
+ ADDRESS **last = &tmp;
+ ADDRESS *p;
+ int i;
+ KEYINFO *db;
+ KEYINFO *k_info;
+
+ db = pgp_read_keyring(pubring);
+
+ for (i = 0; i < 3; i++)
+ {
+ switch (i)
+ {
+ case 0: p = to; break;
+ case 1: p = cc; break;
+ case 2: p = bcc; break;
+ default: abort ();
+ }
+
+ *last = rfc822_cpy_adr (p);
+ while (*last)
+ last = &((*last)->next);
+ }
+
+ tmp = mutt_remove_duplicates (tmp);
+
+ for (p = tmp; p ; p = p->next)
+ {
+ if ((k_info = ki_getkeybyaddr (p, db, KEYFLAG_CANENCRYPT)) == NULL)
+ {
+ char buf[LONG_STRING];
+ snprintf (buf, sizeof (buf), "Enter keyID for %s: ", p->mailbox);
+
+ if ((key = pgp_ask_for_key (pubring, db, buf, p->mailbox,
+ KEYFLAG_CANENCRYPT, NULL)) == NULL)
+ {
+ pgp_closedb (db);
+ safe_free ((void **)&keylist);
+ rfc822_free_address (&tmp);
+ return NULL;
+ }
+ }
+ else
+ key = pgp_keyid(k_info);
+
+ keylist_size += strlen (key) + 4;
+ safe_realloc ((void **)&keylist, keylist_size);
+ sprintf (keylist + keylist_used, "%s0x%s", keylist_used ? " " : "",
+ key);
+ keylist_used = strlen (keylist);
+ }
+ rfc822_free_address (&tmp);
+ pgp_closedb (db);
+ return (keylist);
+}
+
+BODY *pgp_encrypt_message (BODY *a, char *keylist, int sign)
+{
+ char buf[LONG_STRING];
+ char tempfile[_POSIX_PATH_MAX], pgperrfile[_POSIX_PATH_MAX];
+ char pgpinfile[_POSIX_PATH_MAX];
+ FILE *pgpin, *pgperr, *fpout, *fptmp;
+ BODY *t;
+ PARAMETER *p;
+ int err = 0;
+ int empty;
+ pid_t thepid;
+
+ mutt_mktemp (tempfile);
+ if ((fpout = safe_fopen (tempfile, "w+")) == NULL)
+ {
+ mutt_perror (tempfile);
+ return (NULL);
+ }
+
+ mutt_mktemp (pgperrfile);
+ if ((pgperr = safe_fopen (pgperrfile, "w+")) == NULL)
+ {
+ mutt_perror (pgperrfile);
+ unlink(tempfile);
+ fclose(fpout);
+ return NULL;
+ }
+ unlink (pgperrfile);
+
+ mutt_mktemp(pgpinfile);
+ if((fptmp = safe_fopen(pgpinfile, "w")) == NULL)
+ {
+ mutt_perror(pgpinfile);
+ unlink(tempfile);
+ fclose(fpout);
+ fclose(pgperr);
+ return NULL;
+ }
+
+ if (sign)
+ convert_to_7bit (a);
+
+ mutt_write_mime_header (a, fptmp);
+ fputc ('\n', fptmp);
+ mutt_write_mime_body (a, fptmp);
+ fclose(fptmp);
+
+ if ((thepid = pgp_invoke_encrypt (&pgpin, NULL, NULL, -1,
+ fileno (fpout), fileno (pgperr),
+ pgpinfile, keylist, sign)) == -1)
+ {
+ fclose (pgperr);
+ unlink(pgpinfile);
+ return (NULL);
+ }
+
+ if (sign)
+ {
+ fputs (PgpPass, pgpin);
+ fputc ('\n', pgpin);
+ }
+ fclose(pgpin);
+
+ mutt_wait_filter (thepid);
+ unlink(pgpinfile);
+
+ fflush (fpout);
+ rewind (fpout);
+ empty = (fgetc (fpout) == EOF);
+ fclose (fpout);
+
+ fflush (pgperr);
+ rewind (pgperr);
+ while (fgets (buf, sizeof (buf) - 1, pgperr) != NULL)
+ {
+ err = 1;
+ fputs (buf, stdout);
+ }
+ fclose (pgperr);
+
+ /* pause if there is any error output from PGP */
+ if (err)
+ mutt_any_key_to_continue (NULL);
+
+ if (empty)
+ {
+ /* fatal error while trying to encrypt message */
+ unlink (tempfile);
+ return (NULL);
+ }
+
+ t = mutt_new_body ();
+ t->type = TYPEMULTIPART;
+ t->subtype = safe_strdup ("encrypted");
+ t->encoding = ENC7BIT;
+ t->use_disp = 0;
+
+ t->parameter = p = mutt_new_parameter ();
+ p->attribute = safe_strdup ("protocol");
+ p->value = safe_strdup ("application/pgp-encrypted");
+
+ p->next = mutt_new_parameter ();
+ p = p->next;
+ p->attribute = safe_strdup ("boundary");
+ p->value = mutt_generate_boundary ();
+
+ t->parts = mutt_new_body ();
+ t->parts->type = TYPEAPPLICATION;
+ t->parts->subtype = safe_strdup ("pgp-encrypted");
+ t->parts->encoding = ENC7BIT;
+ t->parts->use_disp = 0;
+
+ t->parts->next = mutt_new_body ();
+ t->parts->next->type = TYPEAPPLICATION;
+ t->parts->next->subtype = safe_strdup ("octet-stream");
+ t->parts->next->encoding = ENC7BIT;
+ t->parts->next->filename = safe_strdup (tempfile);
+ t->parts->next->use_disp = 0;
+ t->parts->next->unlink = 1; /* delete after sending the message */
+
+ mutt_free_body (&a); /* no longer needed! */
+
+ return (t);
+}
+
+int pgp_protect (HEADER *msg)
+{
+ char *pgpkeylist = NULL;
+ BODY *pbody = NULL;
+
+ /* Do a quick check to make sure that we can find all of the encryption
+ * keys if the user has requested this service.
+ */
+
+ set_option (OPTPGPCHECKTRUST);
+
+ if (msg->pgp & PGPENCRYPT)
+ {
+ if ((pgpkeylist = pgp_findKeys (msg->env->to, msg->env->cc, msg->env->bcc)) == NULL)
+ return (-1);
+ }
+
+ if ((msg->pgp & PGPSIGN) && !pgp_valid_passphrase ())
+ return (-1);
+
+ endwin ();
+ if (msg->pgp & PGPENCRYPT)
+ {
+ pbody = pgp_encrypt_message (msg->content, pgpkeylist, msg->pgp & PGPSIGN);
+ safe_free ((void **) &pgpkeylist);
+ if (!pbody)
+ return (-1);
+ }
+ else if (msg->pgp & PGPSIGN)
+ {
+ if ((pbody = pgp_sign_message (msg->content)) == NULL)
+ return (-1);
+ }
+ msg->content = pbody;
+ return 0;
+}
+
+#endif /* _PGPPATH */
--- /dev/null
+/*
+ * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef _PGPPATH
+
+#define PGPENCRYPT 1
+#define PGPSIGN 2
+#define PGPKEY 4
+
+#define KEYFLAG_CANSIGN (1 << 0)
+#define KEYFLAG_CANENCRYPT (1 << 1)
+#define KEYFLAG_EXPIRED (1 << 8)
+#define KEYFLAG_REVOKED (1 << 9)
+#define KEYFLAG_DISABLED (1 << 10)
+#define KEYFLAG_SUBKEY (1 << 11)
+#define KEYFLAG_CRITICAL (1 << 12)
+#define KEYFLAG_PREFER_ENCRYPTION (1 << 13)
+#define KEYFLAG_PREFER_SIGNING (1 << 14)
+
+typedef struct keyinfo
+{
+ char *keyid;
+ LIST *address;
+ short flags;
+ short keylen;
+ unsigned long gen_time;
+ const char *algorithm;
+ struct keyinfo *mainkey;
+ struct keyinfo *next;
+} KEYINFO;
+
+typedef struct pgp_uid
+{
+ char *addr;
+ short trust;
+} PGPUID;
+
+enum pgp_version
+{
+ PGP_V2,
+ PGP_V3,
+ PGP_UNKNOWN
+};
+
+enum pgp_ops
+{
+ PGP_DECODE, /* application/pgp */
+ PGP_VERIFY, /* PGP/MIME, signed */
+ PGP_DECRYPT, /* PGP/MIME, encrypted */
+ PGP_SIGN, /* sign data */
+ PGP_ENCRYPT, /* encrypt data */
+ PGP_EXTRACT, /* extract keys from messages */
+ PGP_VERIFY_KEY, /* verify key when selecting */
+ PGP_EXTRACT_KEY, /* extract keys from key ring */
+ PGP_LAST_OP
+};
+
+struct pgp_vinfo
+{
+ enum pgp_version v;
+ char *name;
+ char **binary;
+ char **pubring;
+ char **secring;
+ char **language;
+};
+
+
+WHERE char *PgpV2;
+WHERE char *PgpV2Language;
+WHERE char *PgpV2Pubring;
+WHERE char *PgpV2Secring;
+
+WHERE char *PgpV3;
+WHERE char *PgpV3Language;
+WHERE char *PgpV3Pubring;
+WHERE char *PgpV3Secring;
+
+WHERE char *PgpSendVersion;
+WHERE char *PgpReceiveVersion;
+WHERE char *PgpKeyVersion;
+WHERE char *PgpDefaultVersion;
+
+WHERE char *PgpSignAs;
+WHERE char *PgpSignMicalg;
+
+WHERE short PgpTimeout;
+
+
+
+BODY *pgp_decrypt_part (BODY *, STATE *, FILE *);
+BODY *pgp_make_key_attachment (char *);
+
+const char *pgp_pkalg_to_mic(const char *);
+
+char *pgp_ask_for_key (const char *, KEYINFO *, char *, char *, short, char **);
+
+char *pgp_binary(enum pgp_ops);
+char *pgp_getring (enum pgp_ops, int);
+char *pgp_keyid(KEYINFO *);
+char *_pgp_keyid(KEYINFO *);
+
+enum pgp_version pgp_version(enum pgp_ops);
+struct pgp_vinfo *pgp_get_vinfo(enum pgp_ops);
+
+int mutt_check_pgp (HEADER *h);
+int mutt_parse_pgp_hdr (char *, int);
+
+int pgp_protect (HEADER *);
+int pgp_query (BODY *);
+int pgp_valid_passphrase (void);
+
+KEYINFO *ki_getkeybyaddr (ADDRESS *, KEYINFO *, short);
+KEYINFO *ki_getkeybystr (char *, KEYINFO *, short);
+KEYINFO *pgp_read_keyring(const char *);
+
+void application_pgp_handler (BODY *, STATE *);
+void mutt_forget_passphrase (void);
+void pgp_closedb (KEYINFO *);
+void pgp_encrypted_handler (BODY *, STATE *);
+void pgp_extract_keys_from_attachment_list (FILE *fp, int tag, BODY *top);
+void pgp_extract_keys_from_messages(HEADER *hdr);
+void pgp_signed_handler (BODY *, STATE *);
+void pgp_void_passphrase (void);
+
+#define pgp_secring(a) pgp_getring(a, 0)
+#define pgp_pubring(a) pgp_getring(a, 1)
+
+
+pid_t pgp_invoke_decode(FILE **, FILE **, FILE **, int, int, int, const char *, int);
+pid_t pgp_invoke_verify(FILE **, FILE **, FILE **, int, int, int, const char *, const char *);
+pid_t pgp_invoke_decrypt(FILE **, FILE **, FILE **, int, int, int, const char *);
+pid_t pgp_invoke_sign(FILE **, FILE **, FILE **, int, int, int, const char *);
+pid_t pgp_invoke_encrypt(FILE **, FILE **, FILE **, int, int, int, const char *, const char *, int);
+void pgp_invoke_extract(const char *);
+pid_t pgp_invoke_verify_key(FILE **, FILE **, FILE **, int, int, int, const char *);
+pid_t pgp_invoke_extract_key(FILE **, FILE **, FILE **, int, int, int, const char *);
+
+
+#endif /* _PGPPATH */
--- /dev/null
+/*
+ * Copyright (C) 1997 Thomas Roessler <roessler@guug.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "mutt.h"
+#include "pgp.h"
+
+
+pid_t pgp_invoke_decode (FILE **pgpin, FILE **pgpout, FILE **pgperr,
+ int pgpinfd, int pgpoutfd, int pgperrfd,
+ const char *fname, int need_passphrase)
+{
+ char cmd[HUGE_STRING];
+ struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_DECODE);
+
+ if(!pgp)
+ {
+ mutt_error("Unknown PGP version.");
+ return -1;
+ }
+
+ switch(pgp->v)
+ {
+ case PGP_V2:
+ snprintf(cmd, sizeof(cmd), "%scat %s%s | "
+ "%s +language=%s +pubring=%s +secring=%s +verbose=0 +batchmode -f",
+ need_passphrase ? "PGPPASSFD=0; export PGPPASSFD; " : "",
+ need_passphrase ? "- " : "",
+ fname,
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring));
+ break;
+
+ case PGP_V3:
+ snprintf(cmd, sizeof(cmd), "%scat %s%s | "
+ "%sv +language=%s +pubring=%s +secring=%s +verbose=0 +batchmode -f "
+ "--OutputInformationFD=2",
+ need_passphrase ? "PGPPASSFD=0; export PGPPASSFD; " : "",
+ need_passphrase ? "- " : "",
+ fname,
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring));
+ break;
+
+ default:
+ mutt_error("Unknown PGP version.");
+ return -1;
+ }
+
+ return mutt_create_filter_fd(cmd, pgpin, pgpout, pgperr,
+ pgpinfd, pgpoutfd, pgperrfd);
+}
+
+
+pid_t pgp_invoke_verify(FILE **pgpin, FILE **pgpout, FILE **pgperr,
+ int pgpinfd, int pgpoutfd, int pgperrfd,
+ const char *signedstuff, const char *sigfile)
+{
+ char cmd[HUGE_STRING];
+ struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_VERIFY);
+
+ if(!pgp)
+ {
+ mutt_error("Unknown PGP version.");
+ return -1;
+ }
+
+ switch(pgp->v)
+ {
+ case PGP_V2:
+ snprintf(cmd, sizeof(cmd),
+ "%s +language=%s +pubring=%s +secring=%s +batchmode +verbose=0 %s %s",
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring), sigfile, signedstuff);
+ break;
+
+ case PGP_V3:
+ snprintf(cmd, sizeof(cmd),
+ "%sv +language=%s +pubring=%s +secring=%s --OutputInformationFD=1 +batchmode +verbose=0 %s %s",
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring), sigfile, signedstuff);
+ break;
+
+ default:
+ mutt_error("Unknown PGP version.");
+ return -1;
+ }
+
+ return mutt_create_filter_fd(cmd, pgpin, pgpout, pgperr,
+ pgpinfd, pgpoutfd, pgperrfd);
+}
+
+
+
+pid_t pgp_invoke_decrypt(FILE **pgpin, FILE **pgpout, FILE **pgperr,
+ int pgpinfd, int pgpoutfd, int pgperrfd,
+ const char *fname)
+{
+ char cmd[HUGE_STRING];
+ struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_DECRYPT);
+
+ if(!pgp)
+ {
+ mutt_error("Unknown PGP version.");
+ return -1;
+ }
+
+ switch(pgp->v)
+ {
+ case PGP_V2:
+ snprintf(cmd, sizeof(cmd),
+ "PGPPASSFD=0; export PGPPASSFD; cat - %s | %s +language=%s +pubring=%s +secring=%s "
+ "+verbose=0 +batchmode -f",
+ fname, NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring));
+ break;
+
+ case PGP_V3:
+ snprintf(cmd, sizeof(cmd),
+ "PGPPASSFD=0; export PGPPASSFD; cat - %s | %sv +language=%s +pubring=%s +secring=%s "
+ "+verbose=0 +batchmode -f --OutputInformationFD=2",
+ fname, NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring));
+ break;
+
+ default:
+ mutt_error("Unknown PGP version.");
+ return -1;
+
+ }
+
+ return mutt_create_filter_fd(cmd, pgpin, pgpout, pgperr,
+ pgpinfd, pgpoutfd, pgperrfd);
+}
+
+
+pid_t pgp_invoke_sign(FILE **pgpin, FILE **pgpout, FILE **pgperr,
+ int pgpinfd, int pgpoutfd, int pgperrfd,
+ const char *fname)
+{
+ char cmd[HUGE_STRING];
+ struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_SIGN);
+
+ if(!pgp)
+ {
+ mutt_error("Unknown PGP version.");
+ return -1;
+ }
+
+ switch(pgp->v)
+ {
+ case PGP_V2:
+ snprintf(cmd, sizeof(cmd),
+ "PGPPASSFD=0; export PGPPASSFD; cat - %s | %s "
+ "+language=%s +pubring=%s +secring=%s +verbose=0 +batchmode -abfst %s %s",
+ fname, NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring),
+ PgpSignAs ? "-u" : "",
+ PgpSignAs ? PgpSignAs : "");
+ break;
+
+ case PGP_V3:
+ snprintf(cmd, sizeof(cmd),
+ "PGPPASSFD=0; export PGPPASSFD; cat - %s | %ss "
+ "+language=%s +pubring=%s +secring=%s +verbose=0 -abft %s %s",
+ fname, NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring),
+ PgpSignAs ? "-u" : "",
+ PgpSignAs ? PgpSignAs : "");
+ break;
+
+ default:
+ mutt_error("Unknown PGP version.");
+ return -1;
+
+ }
+
+ return mutt_create_filter_fd(cmd, pgpin, pgpout, pgperr,
+ pgpinfd, pgpoutfd, pgperrfd);
+}
+
+
+pid_t pgp_invoke_encrypt(FILE **pgpin, FILE **pgpout, FILE **pgperr,
+ int pgpinfd, int pgpoutfd, int pgperrfd,
+ const char *fname, const char *uids, int sign)
+{
+ char cmd[HUGE_STRING];
+ char tmpcmd[HUGE_STRING];
+ char *cp;
+ char *keylist;
+ struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_ENCRYPT);
+
+ if(!pgp)
+ {
+ mutt_error("Unknown PGP version.");
+ return -1;
+ }
+
+ switch(pgp->v)
+ {
+ case PGP_V2:
+ snprintf(cmd, sizeof(cmd),
+ "%scat %s%s | %s +language=%s +pubring=%s +secring=%s +verbose=0 %s +batchmode -aeft%s %s%s %s",
+ sign ? "PGPPASSFD=0; export PGPPASSFD; " : "",
+ sign ? "- " : "",
+ fname,
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring),
+ option(OPTPGPENCRYPTSELF) ? "+encrypttoself" : "",
+ sign ? "s" : "",
+ sign && PgpSignAs ? "-u " : "",
+ sign && PgpSignAs ? PgpSignAs : "",
+ uids);
+ break;
+
+ case PGP_V3:
+ snprintf(cmd, sizeof(cmd),
+ "%scat %s%s | %se +language=%s +pubring=%s +secring=%s +verbose=0 %s +batchmode +nobatchinvalidkeys=off -aft%s %s%s",
+ sign ? "PGPPASSFD=0; export PGPPASSFD; " : "",
+ sign ? "- " : "",
+ fname,
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring),
+ option(OPTPGPENCRYPTSELF) ? "+encrypttoself" : "",
+ sign ? "s" : "",
+ sign && PgpSignAs ? "-u " : "",
+ sign && PgpSignAs ? PgpSignAs : "");
+
+ keylist = safe_strdup(uids);
+ for(cp = strtok(keylist, " "); cp ; cp = strtok(NULL, " "))
+ {
+ snprintf(tmpcmd, sizeof(tmpcmd), "%s -r %s",
+ cmd, cp);
+ strcpy(cmd, tmpcmd);
+ }
+ safe_free((void **) &keylist);
+ break;
+
+ default:
+ mutt_error("Unknown PGP version.");
+ return -1;
+
+ }
+
+ return mutt_create_filter_fd(cmd, pgpin, pgpout, pgperr,
+ pgpinfd, pgpoutfd, pgperrfd);
+}
+
+
+void pgp_invoke_extract(const char *fname)
+{
+ char cmd[HUGE_STRING];
+ struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_EXTRACT);
+
+ if(!pgp)
+ {
+ mutt_error("Unknown PGP version.");
+ return;
+ }
+
+ switch(pgp->v)
+ {
+ case PGP_V2:
+ snprintf(cmd, sizeof(cmd), "%s +language=%s +pubring=%s +secring=%s -ka %s",
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring), fname);
+ break;
+
+ case PGP_V3:
+ snprintf(cmd, sizeof(cmd), "%sk +language=%s +pubring=%s +secring=%s -a --OutputInformationFD=1 %s",
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring), fname);
+ break;
+
+ default:
+ mutt_error("Unknown PGP version.");
+ return;
+
+ }
+ mutt_system(cmd);
+}
+
+
+pid_t pgp_invoke_verify_key(FILE **pgpin, FILE **pgpout, FILE **pgperr,
+ int pgpinfd, int pgpoutfd, int pgperrfd, const char *id)
+{
+ char cmd[HUGE_STRING];
+ struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_VERIFY_KEY);
+
+ if(!pgp)
+ {
+ mutt_error("Unknown PGP version.");
+ return -1;
+ }
+
+ switch(pgp->v)
+ {
+ case PGP_V2:
+ snprintf(cmd, sizeof(cmd), "%s +language=%s +pubring=%s +secring=%s +batchmode -kcc 0x%8s",
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring), id);
+ break;
+
+ case PGP_V3:
+ snprintf(cmd, sizeof(cmd), "%sk +language=%s +pubring=%s +secring=%s +batchmode -c --OutputInformationFD=1 0x%8s",
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring), id);
+ break;
+
+ default:
+ mutt_error("Unknown PGP version.");
+ return -1;
+
+ }
+
+ return mutt_create_filter_fd(cmd, pgpin, pgpout, pgperr,
+ pgpinfd, pgpoutfd, pgperrfd);
+}
+
+
+pid_t pgp_invoke_extract_key(FILE **pgpin, FILE **pgpout, FILE **pgperr,
+ int pgpinfd, int pgpoutfd, int pgperrfd, const char *id)
+{
+ char cmd[HUGE_STRING];
+ struct pgp_vinfo *pgp = pgp_get_vinfo(PGP_EXTRACT_KEY);
+
+ if(!pgp)
+ {
+ mutt_error("Unknown PGP version.");
+ return -1;
+ }
+
+ switch(pgp->v)
+ {
+ case PGP_V2:
+ snprintf(cmd, sizeof(cmd), "%s -kxaf +language=%s +pubring=%s +secring=%s 0x%8s",
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring), id);
+ break;
+
+ case PGP_V3:
+ snprintf(cmd, sizeof(cmd), "%sk -xa +language=%s +pubring=%s +secring=%s --OutputInformationFD=1 0x%8s",
+ NONULL (*pgp->binary), NONULL (*pgp->language), NONULL (*pgp->pubring), NONULL (*pgp->secring), id);
+ break;
+
+ default:
+ mutt_error("Unknown PGP version.");
+ return -1;
+
+ }
+
+ return mutt_create_filter_fd(cmd, pgpin, pgpout, pgperr,
+ pgpinfd, pgpoutfd, pgperrfd);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mutt_menu.h"
+#include "mime.h"
+#include "pgp.h"
+#include "pager.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#ifdef _PGPPATH
+
+static struct pgp_cache {
+ char *what;
+ char *dflt;
+ struct pgp_cache *next;
+} * id_defaults = NULL;
+
+typedef struct
+{
+ KEYINFO *k;
+ PGPUID *a;
+} pgp_key_t;
+
+static char trust_flags[] = "?- +";
+
+static char *pgp_key_abilities(int flags)
+{
+ static char buff[3];
+
+ if(!(flags & KEYFLAG_CANENCRYPT))
+ buff[0] = '-';
+ else if(flags & KEYFLAG_PREFER_SIGNING)
+ buff[0] = '.';
+ else
+ buff[0] = 'e';
+
+ if(!(flags & KEYFLAG_CANSIGN))
+ buff[1] = '-';
+ else if(flags & KEYFLAG_PREFER_ENCRYPTION)
+ buff[1] = '.';
+ else
+ buff[1] = 's';
+
+ buff[2] = '\0';
+
+ return buff;
+}
+
+static void pgp_entry (char *s, size_t l, MUTTMENU *menu, int num)
+{
+ pgp_key_t *KeyTable = (pgp_key_t *) menu->data;
+
+ snprintf (s, l, "%4d %c%c %4d/0x%s %-4s %2s %s",
+ num + 1,
+ trust_flags[KeyTable[num].a->trust & 0x03],
+ (KeyTable[num].k->flags & KEYFLAG_CRITICAL ?
+ 'c' : ' '),
+ KeyTable[num].k->keylen,
+ _pgp_keyid(KeyTable[num].k),
+ KeyTable[num].k->algorithm,
+ pgp_key_abilities(KeyTable[num].k->flags),
+ KeyTable[num].a->addr);
+}
+
+static int pgp_search (MUTTMENU *m, regex_t *re, int n)
+{
+ char buf[LONG_STRING];
+
+ pgp_entry (buf, sizeof (buf), m, n);
+ return (regexec (re, buf, 0, NULL, 0));
+}
+
+static int pgp_compare (const void *a, const void *b)
+{
+ int r;
+
+ pgp_key_t *s = (pgp_key_t *) a;
+ pgp_key_t *t = (pgp_key_t *) b;
+
+ if((r = strcasecmp (s->a->addr, t->a->addr)) != 0)
+ return r;
+ else
+ return strcasecmp(pgp_keyid(s->k), pgp_keyid(t->k));
+}
+
+static KEYINFO *pgp_select_key (LIST *keys, ADDRESS *p, const char *s)
+{
+ int keymax;
+ pgp_key_t *KeyTable;
+ MUTTMENU *menu;
+ LIST *a;
+ int i;
+ int done = 0;
+ LIST *l;
+ char helpstr[SHORT_STRING], buf[LONG_STRING];
+ char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
+ FILE *fp, *devnull;
+ pid_t thepid;
+ KEYINFO *info;
+
+
+ for (i = 0, l = keys; l; l = l->next)
+ {
+ int did_main_key = 0;
+
+ info = (KEYINFO *) l->data;
+ a = info->address;
+ retry1:
+ for (; a; i++, a = a->next)
+ ;
+
+ if(!did_main_key && info->flags & KEYFLAG_SUBKEY && info->mainkey)
+ {
+ did_main_key = 1;
+ a = info->mainkey->address;
+ goto retry1;
+ }
+ }
+
+ if (i == 0) return NULL;
+
+ keymax = i;
+ KeyTable = safe_malloc (sizeof (pgp_key_t) * i);
+
+ for (i = 0, l = keys; l; l = l->next)
+ {
+ int did_main_key = 0;
+ info = (KEYINFO *)l->data;
+ a = info->address;
+ retry2:
+ for (; a ; i++, a = a->next)
+ {
+ KeyTable[i].k = (KEYINFO *) l->data;
+ KeyTable[i].a = (PGPUID *)a->data;
+ }
+ if(!did_main_key && info->flags & KEYFLAG_SUBKEY && info->mainkey)
+ {
+ did_main_key = 1;
+ a = info->mainkey->address;
+ goto retry2;
+ }
+ }
+
+ qsort (KeyTable, i, sizeof (pgp_key_t), pgp_compare);
+
+ helpstr[0] = 0;
+ mutt_make_help (buf, sizeof (buf), "Exit ", MENU_PGP, OP_EXIT);
+ strcat (helpstr, buf);
+ mutt_make_help (buf, sizeof (buf), "Select ", MENU_PGP,
+ OP_GENERIC_SELECT_ENTRY);
+ strcat (helpstr, buf);
+ mutt_make_help (buf, sizeof (buf), "Check key ", MENU_PGP, OP_VERIFY_KEY);
+ strcat (helpstr, buf);
+ mutt_make_help (buf, sizeof (buf), "Help", MENU_PGP, OP_HELP);
+ strcat (helpstr, buf);
+
+ menu = mutt_new_menu ();
+ menu->max = keymax;
+ menu->make_entry = pgp_entry;
+ menu->search = pgp_search;
+ menu->menu = MENU_PGP;
+ menu->help = helpstr;
+ menu->data = KeyTable;
+
+ strfcpy (buf, "PGP keys matching ", sizeof (buf));
+ if (p)
+ strfcpy (buf, p->mailbox, sizeof (buf) - strlen (buf));
+ else
+ strcat (buf, s);
+ menu->title = buf;
+
+ info = NULL;
+
+ while (!done)
+ {
+ switch (mutt_menuLoop (menu))
+ {
+
+ case OP_VERIFY_KEY:
+
+ mutt_mktemp (tempfile);
+ if ((devnull = fopen ("/dev/null", "w")) == NULL)
+ {
+ mutt_perror ("Can't open /dev/null");
+ break;
+ }
+ if ((fp = safe_fopen (tempfile, "w")) == NULL)
+ {
+ fclose (devnull);
+ mutt_perror ("Can't create temporary file");
+ break;
+ }
+
+ mutt_message ("Invoking PGP...");
+
+ if((thepid = pgp_invoke_verify_key(NULL, NULL, NULL, -1,
+ fileno(fp), fileno(devnull),
+ pgp_keyid(KeyTable[menu->current].k))) == -1)
+ {
+ mutt_perror ("Can't create filter");
+ unlink (tempfile);
+ fclose (fp);
+ fclose (devnull);
+ }
+
+ mutt_wait_filter (thepid);
+ fclose (fp);
+ fclose (devnull);
+ mutt_clear_error ();
+ snprintf(cmd, sizeof(cmd), "Key ID: 0x%s", pgp_keyid(KeyTable[menu->current].k));
+ mutt_do_pager (cmd, tempfile, 0, NULL);
+ menu->redraw = REDRAW_FULL;
+
+ break;
+
+ case OP_VIEW_ID:
+
+ mutt_message (KeyTable[menu->current].a->addr);
+ break;
+
+ case OP_GENERIC_SELECT_ENTRY:
+
+ if (option (OPTPGPCHECKTRUST) &&
+ (KeyTable[menu->current].a->trust & 0x03) < 3)
+ {
+ char *s = "";
+ char buff[LONG_STRING];
+
+ switch (KeyTable[menu->current].a->trust & 0x03)
+ {
+ case 0: s = "This ID's trust level is undefined."; break;
+ case 1: s = "This ID is not trusted."; break;
+ case 2: s = "This ID is only marginally trusted."; break;
+ }
+
+ snprintf (buff, sizeof(buff), "%s Do you really want to use it?", s);
+
+ if (mutt_yesorno (buff, 0) != 1)
+ {
+ mutt_clear_error ();
+ break;
+ }
+ }
+
+ info = KeyTable[menu->current].k;
+ done = 1;
+ break;
+
+ case OP_EXIT:
+
+ info = NULL;
+ done = 1;
+ break;
+ }
+ }
+
+ mutt_menuDestroy (&menu);
+ safe_free ((void **) &KeyTable);
+
+ return (info);
+}
+
+char *pgp_ask_for_key (const char *ringfile, KEYINFO *udb, char *tag, char *whatfor,
+ short abilities, char **alg)
+{
+ KEYINFO *db;
+ KEYINFO *key;
+ char *key_id;
+ char resp[SHORT_STRING];
+ struct pgp_cache *l = NULL;
+
+ db = udb ? udb : pgp_read_keyring(ringfile);
+
+ resp[0] = 0;
+ if (whatfor)
+ {
+
+ for (l = id_defaults; l; l = l->next)
+ if (!strcasecmp (whatfor, l->what))
+ {
+ strcpy (resp, l->dflt);
+ break;
+ }
+ }
+
+
+ FOREVER
+ {
+ if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0)
+ {
+ if (!udb) pgp_closedb (db);
+ return NULL;
+ }
+
+ if (whatfor)
+ {
+ if (l)
+ {
+ safe_free ((void **)&l->dflt);
+ l->dflt = safe_strdup (resp);
+ }
+ else
+ {
+ l = safe_malloc (sizeof (struct pgp_cache));
+ l->next = id_defaults;
+ id_defaults = l;
+ l->what = safe_strdup (whatfor);
+ l->dflt = safe_strdup (resp);
+ }
+ }
+
+ if ((key = ki_getkeybystr (resp, db, abilities)))
+ {
+ key_id = safe_strdup(pgp_keyid (key));
+
+ if (alg)
+ *alg = safe_strdup(pgp_pkalg_to_mic(key->algorithm));
+
+ if (!udb) pgp_closedb (db);
+ return (key_id);
+ }
+ BEEP ();
+ }
+ /* not reached */
+}
+
+/* generate a public key attachment */
+
+BODY *pgp_make_key_attachment (char * tempf)
+{
+ BODY *att;
+ char buff[LONG_STRING];
+ char tempfb[_POSIX_PATH_MAX];
+ char *id;
+ FILE *tempfp;
+ FILE *devnull;
+ struct stat sb;
+ pid_t thepid;
+
+ unset_option (OPTPGPCHECKTRUST);
+
+ if (!(id = pgp_ask_for_key (pgp_pubring(PGP_EXTRACT),
+ NULL, "Please enter the key ID: ", NULL, 0, NULL)))
+ return NULL;
+
+ if (!tempf) {
+ mutt_mktemp (tempfb);
+ tempf = tempfb;
+ }
+
+ if ((tempfp = safe_fopen (tempf, tempf == tempfb ? "w" : "a")) == NULL) {
+ mutt_perror ("Can't create temporary file");
+ safe_free ((void **)&id);
+ return NULL;
+ }
+
+ if ((devnull = fopen ("/dev/null", "w")) == NULL) {
+ mutt_perror ("Can't open /dev/null");
+ safe_free ((void **)&id);
+ fclose (tempfp);
+ if (tempf == tempfb) unlink (tempf);
+ return NULL;
+ }
+
+ if ((thepid = pgp_invoke_extract_key(NULL, NULL, NULL, -1,
+ fileno(tempfp), fileno(devnull), id)) == -1)
+ {
+ mutt_perror ("Can't create filter");
+ unlink (tempf);
+ fclose (tempfp);
+ fclose (devnull);
+ safe_free ((void **)&id);
+ return NULL;
+ }
+
+ mutt_wait_filter (thepid);
+
+ fclose (tempfp);
+ fclose (devnull);
+
+ att = mutt_new_body ();
+ att->filename = safe_strdup (tempf);
+ att->unlink = 1;
+ att->type = TYPEAPPLICATION;
+ att->subtype = safe_strdup ("pgp-keys");
+ snprintf (buff, sizeof (buff), "PGP Key 0x%s.", id);
+ att->description = safe_strdup (buff);
+ mutt_update_encoding (att);
+
+ stat (tempf, &sb);
+ att->length = sb.st_size;
+
+ safe_free ((void **)&id);
+ return att;
+}
+
+static char *mutt_stristr (char *haystack, char *needle)
+{
+ char *p, *q;
+
+ if (!haystack)
+ return NULL;
+ if (!needle)
+ return (haystack);
+
+ while (*(p = haystack))
+ {
+ for (q = needle ; *p && *q && tolower (*p) == tolower (*q) ; p++, q++)
+ ;
+ if (!*q)
+ return (haystack);
+ haystack++;
+ }
+ return NULL;
+}
+
+KEYINFO *ki_getkeybyaddr (ADDRESS *a, KEYINFO *k, short abilities)
+{
+ ADDRESS *r, *p;
+ LIST *l = NULL, *t = NULL;
+ LIST *q;
+ int weak = 0;
+ int weak_association;
+ int match;
+ int did_main_key;
+ PGPUID *u;
+
+ for ( ; k ; k = k->next)
+ {
+ if(k->flags & (KEYFLAG_REVOKED | KEYFLAG_EXPIRED | KEYFLAG_DISABLED))
+ continue;
+
+ if(abilities && !(k->flags & abilities))
+ continue;
+
+ q = k->address;
+ did_main_key = 0;
+ weak_association = 1;
+ match = 0;
+
+ retry:
+
+ for (;q ; q = q->next)
+ {
+ u = (PGPUID *) q->data;
+ r = rfc822_parse_adrlist(NULL, u->addr);
+
+ for(p = r; p && weak_association; p = p->next)
+ {
+ if ((p->mailbox && a->mailbox &&
+ strcasecmp (p->mailbox, a->mailbox) == 0) ||
+ (a->personal && p->personal &&
+ strcasecmp (a->personal, p->personal) == 0))
+ {
+ match = 1;
+
+ if(((u->trust & 0x03) == 3) &&
+ (p->mailbox && a->mailbox && !strcasecmp(p->mailbox, a->mailbox)))
+ weak_association = 0;
+ }
+ }
+ rfc822_free_address(&r);
+ }
+
+ if(match)
+ {
+ t = mutt_new_list ();
+ t->data = (void *) k;
+ t->next = l;
+ l = t;
+
+ if(weak_association)
+ weak = 1;
+
+ }
+
+ if(!did_main_key && !match && k->flags & KEYFLAG_SUBKEY && k->mainkey)
+ {
+ did_main_key = 1;
+ q = k->mainkey->address;
+ goto retry;
+ }
+ }
+
+ if (l)
+ {
+ if (l->next || weak)
+ {
+ /* query for which key the user wants */
+ k = pgp_select_key (l, a, NULL);
+ }
+ else
+ k = (KEYINFO *)l->data;
+
+ /* mutt_free_list() frees the .data member, so clear the pointers */
+
+ for(t = l; t; t = t->next)
+ t->data = NULL;
+
+ mutt_free_list (&l);
+ }
+
+ return (k);
+}
+
+KEYINFO *ki_getkeybystr (char *p, KEYINFO *k, short abilities)
+{
+ LIST *t = NULL, *l = NULL;
+ LIST *a;
+
+ for(; k; k = k->next)
+ {
+ int did_main_key = 0;
+
+ if(k->flags & (KEYFLAG_REVOKED | KEYFLAG_EXPIRED | KEYFLAG_DISABLED))
+ continue;
+
+ if(abilities && !(k->flags & abilities))
+ continue;
+
+ a = k->address;
+
+ retry:
+
+ for(; a ; a = a->next)
+ {
+
+ if (!*p || strcasecmp (p, pgp_keyid(k)) == 0 ||
+ (!strncasecmp(p, "0x", 2) && !strcasecmp(p+2, pgp_keyid(k))) ||
+ (option(OPTPGPLONGIDS) && !strncasecmp(p, "0x", 2) &&
+ !strcasecmp(p+2, k->keyid+8)) ||
+ mutt_stristr(((PGPUID *)a->data)->addr,p))
+ {
+ t = mutt_new_list ();
+ t->data = (void *)k;
+ t->next = l;
+ l = t;
+ break;
+ }
+ }
+
+ if(!did_main_key && k->flags & KEYFLAG_SUBKEY && k->mainkey)
+ {
+ did_main_key = 1;
+ a = k->mainkey->address;
+ goto retry;
+ }
+ }
+
+ if (l)
+ {
+ k = pgp_select_key (l, NULL, p);
+ set_option(OPTNEEDREDRAW);
+
+ for(t = l; t; t = t->next)
+ t->data = NULL;
+
+ mutt_free_list (&l);
+ }
+
+ return (k);
+}
+
+
+
+#endif /* _PGPPATH */
--- /dev/null
+/*
+ * Copyright (C) 1997 Thomas Roessler <roessler@guug.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "sha.h"
+#include "mutt.h"
+#include "pgp.h"
+
+#define CHUNKSIZE 1024
+
+static unsigned char *pbuf = NULL;
+static size_t plen = 0;
+
+enum packet_tags {
+ PT_RES0 = 0, /* reserved */
+ PT_ESK, /* Encrypted Session Key */
+ PT_SIG, /* Signature Packet */
+ PT_CESK, /* Conventionally Encrypted Session Key Packet */
+ PT_OPS, /* One-Pass Signature Packet */
+ PT_SECKEY, /* Secret Key Packet */
+ PT_PUBKEY, /* Public Key Packet */
+ PT_SUBSECKEY, /* Secret Subkey Packet */
+ PT_COMPRESSED, /* Compressed Data Packet */
+ PT_SKE, /* Symmetrically Encrypted Data Packet */
+ PT_MARKER, /* Marker Packet */
+ PT_LITERAL, /* Literal Data Packet */
+ PT_TRUST, /* Trust Packet */
+ PT_NAME, /* Name Packet */
+ PT_SUBKEY, /* Subkey Packet */
+ PT_RES15, /* Reserved */
+ PT_COMMENT /* Comment Packet */
+};
+
+const char *pgp_packet_name[] = {
+ "reserved",
+ "Encrypted Session Key",
+ "Signature Packet",
+ "Conventionally Encrypted Session Key Packet",
+ "One-Pass Signature Packet",
+ "Secret Key Packet",
+ "Public Key Packet",
+ "Secret Subkey Packet",
+ "Compressed Data Packet",
+ "Symmetrically Encrypted Data Packet",
+ "Marker Packet",
+ "Literal Data Packet",
+ "Trust Packet",
+ "Name Packet",
+ "Subkey Packet",
+ "Reserved",
+ "Comment Packet"
+};
+
+static const char *pkalgbytype(unsigned char type)
+{
+ switch(type)
+ {
+ case 1: return "RSA";
+ case 2: return "RSA";
+ case 3: return "RSA";
+ case 16: return "ElG";
+ case 17: return "DSA";
+ default: return "unk";
+ }
+}
+
+
+static struct
+{
+ char *pkalg;
+ char *micalg;
+}
+pktomic[] =
+{
+ { "RSA", "pgp-md5" },
+ { "ElG", "pgp-rmd160" },
+ { "DSA", "pgp-sha1" },
+ { NULL, "x-unknown" }
+};
+
+
+const char *pgp_pkalg_to_mic(const char *alg)
+{
+ int i;
+
+ for(i = 0; pktomic[i].pkalg; i++)
+ {
+ if(!strcasecmp(pktomic[i].pkalg, alg))
+ break;
+ }
+
+ return pktomic[i].micalg;
+}
+
+
+/* unused */
+
+#if 0
+
+static const char *hashalgbytype(unsigned char type)
+{
+ switch(type)
+ {
+ case 1: return "MD5";
+ case 2: return "SHA1";
+ case 3: return "RIPE-MD/160";
+ case 4: return "HAVAL";
+ default: return "unknown";
+ }
+}
+
+#endif
+
+static short canencrypt(unsigned char type)
+{
+ switch(type)
+ {
+ case 1:
+ case 2:
+ case 16:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static short cansign(unsigned char type)
+{
+ switch(type)
+ {
+ case 1:
+ case 3:
+ case 17:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* return values:
+ *
+ * 1 = sign only
+ * 2 = encrypt only
+ * 3 = both
+ */
+
+static short get_abilities(unsigned char type)
+{
+ return (canencrypt(type) << 1) | cansign(type);
+}
+
+static int read_material(size_t material, size_t *used, FILE *fp)
+{
+ if(*used + material >= plen)
+ {
+ unsigned char *p;
+ size_t nplen;
+
+ nplen = *used + material + CHUNKSIZE;
+
+ if(!(p = realloc(pbuf, nplen)))
+ {
+ mutt_perror("realloc");
+ return -1;
+ }
+ plen = nplen;
+ pbuf = p;
+ }
+
+ if(fread(pbuf + *used, 1, material, fp) < material)
+ {
+ mutt_perror("fread");
+ return -1;
+ }
+
+ *used += material;
+ return 0;
+}
+
+static unsigned char *pgp_read_packet(FILE *fp, size_t *len)
+{
+ size_t used = 0;
+ long startpos;
+ unsigned char ctb;
+ unsigned char b;
+ size_t material;
+
+ startpos = ftell(fp);
+
+ if(!plen)
+ {
+ plen = CHUNKSIZE;
+ pbuf = safe_malloc(plen);
+ }
+
+ if(fread(&ctb, 1, 1, fp) < 1)
+ {
+ if(!feof(fp))
+ mutt_perror("fread");
+ goto bail;
+ }
+
+ if(!(ctb & 0x80))
+ {
+ goto bail;
+ }
+
+ if(ctb & 0x40) /* handle PGP 5.0 packets. */
+ {
+ int partial = 0;
+ pbuf[0] = ctb; used++;
+
+ do {
+ if(fread(&b, 1, 1, fp) < 1)
+ {
+ mutt_perror("fread");
+ goto bail;
+ }
+
+ if(b < 192)
+ {
+ material = b;
+ partial = 0;
+ material -= 1;
+ }
+ else if(192 <= b && b <= 223)
+ {
+ material = (b - 192) * 256;
+ if(fread(&b, 1, 1, fp) < 1)
+ {
+ mutt_perror("fread");
+ goto bail;
+ }
+ material += b + 192;
+ partial = 0;
+ material -= 2;
+ }
+ else
+ {
+ material = 1 << (b & 0x1f);
+ partial = 1;
+ material -= 1;
+ }
+
+ if(read_material(material, &used, fp) == -1)
+ goto bail;
+
+ } while (partial);
+ }
+ else /* Old-Style PGP */
+ {
+ int bytes = 0;
+ pbuf[0] = 0x80 | ((ctb >> 2) & 0x0f);
+ used++;
+
+ switch(ctb & 0x03)
+ {
+ case 0:
+ {
+ if(fread(&b, 1, 1, fp) < 1)
+ {
+ mutt_perror("fread");
+ goto bail;
+ }
+
+ material = b;
+ break;
+ }
+
+ case 1:
+ bytes = 2;
+
+ case 2:
+ {
+ int i;
+
+ if(!bytes) bytes = 4;
+
+ material = 0;
+
+ for(i = 0; i < bytes; i++)
+ {
+ if(fread(&b, 1, 1, fp) < 1)
+ {
+ mutt_perror("fread");
+ goto bail;
+ }
+
+ material = (material << 8) + b;
+ }
+ break;
+ }
+
+ default:
+ goto bail;
+ }
+
+ if(read_material(material, &used, fp) == -1)
+ goto bail;
+ }
+
+ if(len)
+ *len = used;
+
+ return pbuf;
+
+ bail:
+
+ fseek(fp, startpos, SEEK_SET);
+ return NULL;
+}
+
+static KEYINFO *pgp_new_keyinfo(void)
+{
+ KEYINFO *p;
+
+ p = safe_malloc(sizeof(KEYINFO));
+ p->keyid = NULL;
+ p->address = NULL;
+ p->flags = 0;
+ p->next = NULL;
+ p->keylen = 0;
+
+ return p;
+}
+
+static KEYINFO *pgp_parse_pgp2_key(unsigned char *buff, size_t l)
+{
+ KEYINFO *p;
+ unsigned char alg;
+ size_t expl;
+ unsigned long id;
+ unsigned long gen_time = 0;
+ unsigned short exp_days = 0;
+ size_t j;
+ int i, k;
+ unsigned char scratch[LONG_STRING];
+
+ if(l < 12)
+ return NULL;
+
+ p = pgp_new_keyinfo();
+
+ for(i = 0, j = 2; i < 4; i++)
+ gen_time = (gen_time << 8) + buff[j++];
+
+ for(i = 0; i < 2; i++)
+ exp_days = (exp_days << 8) + buff[j++];
+
+ if(exp_days && time(NULL) > gen_time + exp_days * 24 * 3600)
+ p->flags |= KEYFLAG_EXPIRED;
+
+ alg = buff[j++];
+
+ p->algorithm = pkalgbytype(alg);
+ p->flags |= get_abilities(alg);
+
+ expl = 0;
+ for(i = 0; i < 2; i++)
+ expl = (expl << 8) + buff[j++];
+
+ p->keylen = expl;
+
+ expl = (expl + 7)/ 8;
+ if(expl < 4)
+ goto bailout;
+
+
+ j += expl - 8;
+
+ for(k = 0; k < 2; k++)
+ {
+ for(id = 0, i = 0; i < 4; i++)
+ id = (id << 8) + buff[j++];
+
+ snprintf((char *)scratch + k * 8, sizeof(scratch) - k * 8,
+ "%08lX", id);
+ }
+
+ p->keyid = safe_strdup((char *)scratch);
+
+ return p;
+
+ bailout:
+
+ safe_free((void **)&p);
+ return NULL;
+}
+
+static void pgp_make_pgp3_fingerprint(unsigned char *buff, size_t l,
+ unsigned char *digest)
+{
+ unsigned char dummy;
+ SHA_CTX context;
+
+ SHA1_Init(&context);
+
+ dummy = buff[0] & 0x3f;
+
+ if(dummy == PT_SUBSECKEY || dummy == PT_SUBKEY || dummy == PT_SECKEY)
+ dummy = PT_PUBKEY;
+
+ dummy = (dummy << 2) | 0x81;
+ SHA1_Update(&context, &dummy, 1);
+ dummy = ((l - 1) >> 8) & 0xff;
+ SHA1_Update(&context, &dummy, 1);
+ dummy = (l - 1) & 0xff;
+ SHA1_Update(&context, &dummy, 1);
+ SHA1_Update(&context, buff + 1, l - 1);
+ SHA1_Final(digest, &context);
+
+}
+
+static void skip_bignum(unsigned char *buff, size_t l, size_t j,
+ size_t *toff, size_t n)
+{
+ size_t len;
+
+ do
+ {
+ len = (buff[j] << 8) + buff[j+1];
+ j += (len + 7) / 8 + 2;
+ } while(j <= l && --n > 0);
+
+ if(toff) *toff = j;
+}
+
+
+static KEYINFO *pgp_parse_pgp3_key(unsigned char *buff, size_t l)
+{
+ KEYINFO *p;
+ unsigned char alg;
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ unsigned char scratch[LONG_STRING];
+ unsigned long gen_time = 0;
+ unsigned long id;
+ int i, k;
+ short len;
+ size_t j;
+
+ p = pgp_new_keyinfo();
+ j = 2;
+
+ for(i = 0; i < 4; i++)
+ gen_time = (gen_time << 8) + buff[j++];
+
+ p->gen_time = gen_time;
+
+ alg = buff[j++];
+
+ p->algorithm = pkalgbytype(alg);
+ p->flags |= get_abilities(alg);
+
+ if (alg == 17)
+ skip_bignum(buff, l, j, &j, 3);
+ else if(alg == 16)
+ skip_bignum(buff, l, j, &j, 2);
+
+ len = (buff[j] << 8) + buff[j+1];
+ p->keylen = len;
+
+ if (alg >=1 && alg <= 3)
+ skip_bignum(buff, l, j, &j, 2);
+ else if(alg == 17 || alg == 16)
+ skip_bignum(buff, l, j, &j, 1);
+
+ pgp_make_pgp3_fingerprint(buff, j, digest);
+
+ for(k = 0; k < 2; k++)
+ {
+ for(id = 0, i = SHA_DIGEST_LENGTH - 8 + k*4;
+ i < SHA_DIGEST_LENGTH + (k - 1) * 4; i++)
+ id = (id << 8) + digest[i];
+
+ snprintf((char *)scratch + k * 8, sizeof(scratch) - k * 8, "%08lX", id);
+ }
+
+ p->keyid = safe_strdup((char *)scratch);
+
+
+ return p;
+}
+
+static KEYINFO *pgp_parse_keyinfo(unsigned char *buff, size_t l)
+{
+ if(!buff || l < 2)
+ return NULL;
+
+ switch(buff[1])
+ {
+ case 2:
+ case 3:
+ return pgp_parse_pgp2_key(buff, l);
+ case 4:
+ return pgp_parse_pgp3_key(buff, l);
+ default:
+ return NULL;
+ }
+}
+
+static int pgp_parse_pgp2_sig(unsigned char *buff, size_t l, KEYINFO *p)
+{
+ unsigned char sigtype;
+ unsigned char pkalg;
+ unsigned char hashalg;
+ long sig_gen_time;
+ unsigned long signerid;
+ size_t j;
+ int i;
+
+ if(l < 22)
+ return -1;
+
+ j = 3;
+ sigtype = buff[j++];
+
+ sig_gen_time = 0;
+ for(i = 0; i < 4; i++)
+ sig_gen_time = (sig_gen_time << 8) + buff[j++];
+
+ j += 4;
+ signerid = 0;
+ for(i = 0; i < 4; i++)
+ signerid = (signerid << 8) + buff[j++];
+
+ pkalg = buff[j++];
+ hashalg = buff[j++];
+
+ if(sigtype == 0x20 || sigtype == 0x28)
+ p->flags |= KEYFLAG_REVOKED;
+
+ return 0;
+}
+
+static int pgp_parse_pgp3_sig(unsigned char *buff, size_t l, KEYINFO *p)
+{
+ unsigned char sigtype;
+ unsigned char pkalg;
+ unsigned char hashalg;
+ unsigned char skt;
+ long sig_gen_time = -1;
+ long validity = -1;
+ long key_validity = -1;
+ long signerid = 0;
+ size_t ml;
+ size_t j;
+ int i;
+ short ii;
+ short have_critical_spks=0;
+
+ if(l < 7)
+ return -1;
+
+ j = 2;
+
+ sigtype = buff[j++];
+ pkalg = buff[j++];
+ hashalg = buff[j++];
+
+ for(ii = 0; ii < 2; ii++)
+ {
+ size_t skl;
+ size_t nextone;
+
+ ml = (buff[j] << 8) + buff[j+1];
+ j += 2;
+
+ if(j + ml > l) break;
+
+ nextone = j;
+ while(ml)
+ {
+ j = nextone;
+ skl = buff[j++];
+ if(!--ml) break;
+
+ if(skl >= 192)
+ {
+ skl = (skl - 192) * 256 + buff[j++] + 192;
+ if(!--ml) break;
+ }
+
+ ml -= skl;
+ if(ml < 0)
+ break;
+
+ nextone = j + skl;
+ skt = buff[j++];
+
+ switch(skt & 0x7f)
+ {
+ case 2: /* creation time */
+ {
+ if(skl < 4)
+ break;
+ sig_gen_time = 0;
+ for(i = 0; i < 4; i++)
+ sig_gen_time = (sig_gen_time << 8) + buff[j++];
+
+ break;
+ }
+ case 3: /* expiration time */
+ {
+ if(skl < 4)
+ break;
+ validity = 0;
+ for(i = 0; i < 4; i++)
+ validity = (validity << 8) + buff[j++];
+ break;
+ }
+ case 9: /* key expiration time */
+ {
+ if(skl < 4)
+ break;
+ key_validity = 0;
+ for(i = 0; i < 4; i++)
+ key_validity = (key_validity << 8) + buff[j++];
+ break;
+ }
+ case 16: /* issuer key ID */
+ {
+ if(skl < 8)
+ break;
+ j += 4;
+ signerid = 0;
+ for(i = 0; i < 4; i++)
+ signerid = (signerid << 8) + buff[j++];
+ break;
+ }
+ case 10: /* CMR key */ break;
+ case 4: /* exportable */
+ case 5: /* trust */
+ case 6: /* regexp */
+ case 7: /* revocable */
+ case 11: /* Pref. symm. alg. */
+ case 12: /* revocation key */
+ case 20: /* notation data */
+ case 21: /* pref. hash */
+ case 22: /* pref. comp.alg. */
+ case 23: /* key server prefs. */
+ case 24: /* pref. key server */
+ default:
+ {
+ if(skt & 0x80)
+ have_critical_spks = 1;
+ }
+ }
+ }
+ j = nextone;
+ }
+
+ if(sigtype == 0x20 || sigtype == 0x28)
+ p->flags |= KEYFLAG_REVOKED;
+ if(key_validity != -1 && time(NULL) > p->gen_time + key_validity)
+ p->flags |= KEYFLAG_EXPIRED;
+ if(have_critical_spks)
+ p->flags |= KEYFLAG_CRITICAL;
+
+ return 0;
+
+}
+
+
+static int pgp_parse_sig(unsigned char *buff, size_t l, KEYINFO *p)
+{
+ if(!buff || l < 2 || !p)
+ return -1;
+
+ switch(buff[1])
+ {
+ case 2:
+ case 3:
+ return pgp_parse_pgp2_sig(buff, l, p);
+ case 4:
+ return pgp_parse_pgp3_sig(buff, l, p);
+ default:
+ return -1;
+ }
+}
+
+
+KEYINFO *pgp_read_keyring(const char *fname)
+{
+ FILE *fp;
+ unsigned char *buff;
+ unsigned char pt = 0;
+ unsigned char last_pt;
+ size_t l;
+ KEYINFO *db = NULL, **end, *p = NULL;
+ KEYINFO *supkey = NULL;
+ PGPUID *uid = NULL;
+ LIST **addr = NULL;
+
+ if(!(fp = fopen(fname, "r")))
+ {
+ mutt_perror("fopen");
+ return NULL;
+
+ }
+
+ end = &db;
+
+ while((buff = pgp_read_packet(fp, &l)) != NULL)
+ {
+ last_pt = pt;
+ pt = buff[0] & 0x3f;
+
+ if(l < 1)
+ continue;
+
+ switch(pt)
+ {
+ case PT_SECKEY:
+ case PT_PUBKEY:
+ case PT_SUBKEY:
+ case PT_SUBSECKEY:
+ {
+ if(p)
+ end = &(p->next);
+
+ if(!(*end = p = pgp_parse_keyinfo(buff, l)))
+ break;
+
+ addr = &p->address;
+
+ if(pt == PT_SUBKEY || pt == PT_SUBSECKEY)
+ {
+ p->flags |= KEYFLAG_SUBKEY;
+ p->mainkey = supkey;
+ }
+ else
+ supkey = p;
+
+ break;
+ }
+
+ case PT_SIG:
+ {
+ pgp_parse_sig(buff, l, p);
+ break;
+ }
+ case PT_TRUST:
+ {
+ if(last_pt == PT_SECKEY || last_pt == PT_PUBKEY ||
+ last_pt == PT_SUBKEY || last_pt == PT_SUBSECKEY)
+ {
+ if(buff[1] & 0x20)
+ p->flags |= KEYFLAG_DISABLED;
+ }
+ else if(last_pt == PT_NAME)
+ uid->trust = buff[1];
+ break;
+ }
+ case PT_NAME:
+ {
+ char *chr;
+ chr = safe_malloc(l);
+ memcpy(chr, buff + 1, l - 1);
+ chr[l-1] = '\0';
+ *addr = mutt_new_list();
+ (*addr)->data = safe_malloc(sizeof(PGPUID));
+ uid = (PGPUID *) (*addr)->data;
+ uid->addr = chr;
+ uid->trust = 0;
+ addr = &(*addr)->next;
+
+ /* the following tags are generated by
+ * pgp 2.6.3in.
+ */
+
+ if(strstr(chr, "ENCR"))
+ p->flags |= KEYFLAG_PREFER_ENCRYPTION;
+ if(strstr(chr, "SIGN"))
+ p->flags |= KEYFLAG_PREFER_SIGNING;
+
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ return db;
+}
+
+void pgp_closedb (KEYINFO *k)
+{
+ KEYINFO *tmp;
+ LIST *q;
+
+ while (k)
+ {
+ if (k->keyid) safe_free ((void **)&k->keyid);
+ for(q = k->address; q; q = q-> next)
+ safe_free((void **)&q->data);
+ tmp = k;
+ k = k->next;
+ safe_free ((void **)&tmp);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Rather crude POP3 support.
+ */
+
+#include "mutt.h"
+#include "mailbox.h"
+#include "mx.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+
+static int getLine (int fd, char *s, int len)
+{
+ char ch;
+ int bytes = 0;
+
+ while (read (fd, &ch, 1) > 0)
+ {
+ *s++ = ch;
+ bytes++;
+ if (ch == '\n')
+ {
+ *s = 0;
+ return (bytes);
+ }
+ /* make sure not to overwrite the buffer */
+ if (bytes == len - 1)
+ {
+ *s = 0;
+ return bytes;
+ }
+ }
+ *s = 0;
+ return (-1);
+}
+
+static int getPass (void)
+{
+ if (!PopPass)
+ {
+ char tmp[SHORT_STRING];
+ if (mutt_get_password ("POP Password: ", tmp, sizeof (tmp)) != 0)
+ return 0;
+ PopPass = safe_strdup (tmp);
+ }
+ return 1;
+}
+
+void mutt_fetchPopMail (void)
+{
+ struct sockaddr_in sin;
+#if SIZEOF_LONG == 4
+ long n;
+#else
+ int n;
+#endif
+ struct hostent *he;
+ char buffer[2048];
+ char msgbuf[SHORT_STRING];
+ int s, i, msgs, bytes, err = 0;
+ CONTEXT ctx;
+ MESSAGE *msg = NULL;
+
+ if (!PopHost)
+ {
+ mutt_error ("POP host is not defined.");
+ return;
+ }
+
+ if (!PopUser)
+ {
+ mutt_error ("No POP username is defined.");
+ return;
+ }
+
+ if (!getPass ()) return;
+
+ s = socket (AF_INET, SOCK_STREAM, IPPROTO_IP);
+
+ memset ((char *) &sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons (PopPort);
+
+ if ((n = inet_addr (PopHost)) == -1)
+ {
+ /* Must be a DNS name */
+ if ((he = gethostbyname (PopHost)) == NULL)
+ {
+ mutt_error ("Could not find address for host %s.", PopHost);
+ return;
+ }
+ memcpy ((void *)&sin.sin_addr, *(he->h_addr_list), he->h_length);
+ }
+ else
+ memcpy ((void *)&sin.sin_addr, (void *)&n, sizeof(n));
+
+ mutt_message ("Connecting to %s", inet_ntoa (sin.sin_addr));
+
+ if (connect (s, (struct sockaddr *) &sin, sizeof (struct sockaddr_in)) == -1)
+ {
+ mutt_perror ("connect");
+ return;
+ }
+
+ if (getLine (s, buffer, sizeof (buffer)) == -1)
+ goto fail;
+
+ if (strncmp (buffer, "+OK", 3) != 0)
+ {
+ mutt_remove_trailing_ws (buffer);
+ mutt_error (buffer);
+ goto finish;
+ }
+
+ sprintf (buffer, "user %s\r\n", PopUser);
+ write (s, buffer, strlen (buffer));
+
+ if (getLine (s, buffer, sizeof (buffer)) == -1)
+ goto fail;
+
+ if (strncmp (buffer, "+OK", 3) != 0)
+ {
+ mutt_remove_trailing_ws (buffer);
+ mutt_error (buffer);
+ goto finish;
+ }
+
+ sprintf (buffer, "pass %s\r\n", PopPass);
+ write (s, buffer, strlen (buffer));
+
+ if (getLine (s, buffer, sizeof (buffer)) == -1)
+ goto fail;
+
+ if (strncmp (buffer, "+OK", 3) != 0)
+ {
+ PopPass[0] = 0; /* void the given password */
+ mutt_remove_trailing_ws (buffer);
+ mutt_error (buffer[0] ? buffer : "Server closed connection!");
+ goto finish;
+ }
+
+ /* find out how many messages are in the mailbox. */
+ write (s, "stat\r\n", 6);
+
+ if (getLine (s, buffer, sizeof (buffer)) == -1)
+ goto fail;
+
+ if (strncmp (buffer, "+OK", 3) != 0)
+ {
+ mutt_remove_trailing_ws (buffer);
+ mutt_error (buffer);
+ goto finish;
+ }
+
+ sscanf (buffer, "+OK %d %d", &msgs, &bytes);
+
+ if (msgs == 0)
+ {
+ mutt_message ("No new mail in POP mailbox.");
+ goto finish;
+ }
+
+ if (mx_open_mailbox (Spoolfile, M_APPEND, &ctx) == NULL)
+ goto finish;
+
+ snprintf (msgbuf, sizeof (msgbuf),
+ "Reading %d new message%s (%d bytes)...", msgs, msgs > 1 ? "s" : "", bytes);
+ mutt_message (msgbuf);
+
+ for (i = 1 ; i <= msgs ; i++)
+ {
+ sprintf (buffer, "retr %d\r\n", i);
+ write (s, buffer, strlen (buffer));
+
+ if (getLine (s, buffer, sizeof (buffer)) == -1)
+ {
+ mx_fastclose_mailbox (&ctx);
+ goto fail;
+ }
+
+ if (strncmp (buffer, "+OK", 3) != 0)
+ {
+ mutt_remove_trailing_ws (buffer);
+ mutt_error (buffer);
+ break;
+ }
+
+ if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL)
+ {
+ err = 1;
+ break;
+ }
+
+ /* Now read the actual message. */
+ FOREVER
+ {
+ char *p;
+ int chunk;
+
+ if ((chunk = getLine (s, buffer, sizeof (buffer))) == -1)
+ {
+ mutt_error ("Error reading message!");
+ err = 1;
+ break;
+ }
+
+ /* check to see if we got a full line */
+ if (buffer[chunk-2] == '\r' && buffer[chunk-1] == '\n')
+ {
+ if (strcmp(".\r\n", buffer) == 0)
+ {
+ /* end of message */
+ break;
+ }
+
+ /* change CRLF to just LF */
+ buffer[chunk-2] = '\n';
+ buffer[chunk-1] = 0;
+ chunk--;
+
+ /* see if the line was byte-stuffed */
+ if (buffer[0] == '.')
+ {
+ p = buffer + 1;
+ chunk--;
+ }
+ else
+ p = buffer;
+ }
+ else
+ p = buffer;
+
+ fwrite (p, 1, chunk, msg->fp);
+ }
+
+ if (mx_close_message (&msg) != 0)
+ {
+ mutt_error ("Error while writing mailbox!");
+ err = 1;
+ }
+
+ if (err)
+ break;
+
+ if (option (OPTPOPDELETE))
+ {
+ /* delete the message on the server */
+ sprintf (buffer, "dele %d\r\n", i);
+ write (s, buffer, strlen (buffer));
+
+ /* eat the server response */
+ getLine (s, buffer, sizeof (buffer));
+ if (strncmp (buffer, "+OK", 3) != 0)
+ {
+ err = 1;
+ mutt_remove_trailing_ws (buffer);
+ mutt_error (buffer);
+ break;
+ }
+ }
+
+ mutt_message ("%s [%d messages read]", msgbuf, i);
+ }
+
+ if (msg)
+ mx_close_message (&msg);
+ mx_close_mailbox (&ctx);
+
+ if (err)
+ {
+ /* make sure no messages get deleted */
+ write (s, "rset\r\n", 6);
+ getLine (s, buffer, sizeof (buffer)); /* snarf the response */
+ }
+
+finish:
+
+ /* exit gracefully */
+ write (s, "quit\r\n", 6);
+ getLine (s, buffer, sizeof (buffer)); /* snarf the response */
+ close (s);
+ return;
+
+ /* not reached */
+
+fail:
+
+ mutt_error ("Server closed connection!");
+ close (s);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_menu.h"
+#include "rfc1524.h"
+#include "mime.h"
+#include "mailbox.h"
+#include "mapping.h"
+
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+
+static struct mapping_t PostponeHelp[] = {
+ { "Exit", OP_EXIT },
+ { "Del", OP_DELETE },
+ { "Undel", OP_UNDELETE },
+ { "Help", OP_HELP },
+ { NULL }
+};
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif /* _PGPPATH */
+
+
+
+static short PostCount = 0;
+static time_t LastModify = 0;
+static CONTEXT *PostContext = NULL;
+
+int mutt_num_postponed (void)
+{
+ struct stat st;
+ CONTEXT ctx;
+
+ if (!Postponed || stat (Postponed, &st) == -1)
+ {
+ PostCount = 0;
+ LastModify = 0;
+ return (0);
+ }
+ else if (S_ISDIR (st.st_mode))
+ {
+ /* if we have a maildir mailbox, we need to stat the "new" dir */
+
+ char buf[_POSIX_PATH_MAX];
+
+ snprintf (buf, sizeof (buf), "%s/new", Postponed);
+ if (access (buf, F_OK) == 0 && stat (buf, &st) == -1)
+ {
+ PostCount = 0;
+ LastModify = 0;
+ return 0;
+ }
+ }
+
+ if (LastModify < st.st_mtime)
+ {
+ LastModify = st.st_mtime;
+
+ if (access (Postponed, R_OK | F_OK) != 0)
+ return (PostCount = 0);
+ if (mx_open_mailbox (Postponed, M_NOSORT | M_QUIET, &ctx) == NULL)
+ PostCount = 0;
+ else
+ PostCount = ctx.msgcount;
+ mx_fastclose_mailbox (&ctx);
+ }
+
+ return (PostCount);
+}
+
+static void post_entry (char *s, size_t slen, MUTTMENU *menu, int entry)
+{
+ CONTEXT *ctx = (CONTEXT *) menu->data;
+
+ mutt_make_string (s, slen, NONULL (HdrFmt), ctx->hdrs[entry]);
+}
+
+static HEADER *select_msg (void)
+{
+ MUTTMENU *menu;
+ int i, done=0, r=-1;
+ char helpstr[SHORT_STRING];
+
+ menu = mutt_new_menu ();
+ menu->make_entry = post_entry;
+ menu->menu = MENU_POST;
+ menu->max = PostContext->msgcount;
+ menu->title = "Postponed Messages";
+ menu->data = PostContext;
+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_POST, PostponeHelp);
+
+ while (!done)
+ {
+ switch (i = mutt_menuLoop (menu))
+ {
+ case OP_DELETE:
+ case OP_UNDELETE:
+ mutt_set_flag (PostContext, PostContext->hdrs[menu->current], M_DELETE, (i == OP_DELETE) ? 1 : 0);
+ PostCount = PostContext->msgcount - PostContext->deleted;
+ if (option (OPTRESOLVE) && menu->current < menu->max - 1)
+ {
+ menu->oldcurrent = menu->current;
+ menu->current++;
+ if (menu->current >= menu->top + menu->pagelen)
+ {
+ menu->top = menu->current;
+ menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+ }
+ else
+ menu->redraw |= REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw = REDRAW_CURRENT;
+ break;
+
+ case OP_GENERIC_SELECT_ENTRY:
+ r = menu->current;
+ done = 1;
+ break;
+
+ case OP_EXIT:
+ done = 1;
+ break;
+ }
+ }
+
+ mutt_menuDestroy (&menu);
+ return (r > -1 ? PostContext->hdrs[r] : NULL);
+}
+
+/* args:
+ * ctx Context info, used when recalling a message to which
+ * we reply.
+ * hdr envelope/attachment info for recalled message
+ * cur if message was a reply, `cur' is set to the message which
+ * `hdr' is in reply to
+ *
+ * return vals:
+ * -1 error/no messages
+ * 0 normal exit
+ * SENDREPLY recalled message is a reply
+ */
+int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur)
+{
+ HEADER *h;
+ MESSAGE *msg;
+ int code = SENDPOSTPONED;
+ LIST *tmp;
+ LIST *last = NULL;
+ LIST *next;
+ char file[_POSIX_PATH_MAX];
+ char *p;
+ int opt_delete;
+
+ if (!Postponed)
+ return (-1);
+
+ if ((PostContext = mx_open_mailbox (Postponed, M_NOSORT, NULL)) == NULL)
+ {
+ PostCount = 0;
+ mutt_error ("No postponed messages.");
+ return (-1);
+ }
+
+ if (! PostContext->msgcount)
+ {
+ PostCount = 0;
+ mx_close_mailbox (PostContext);
+ safe_free ((void **) &PostContext);
+ mutt_error ("No postponed messages.");
+ return (-1);
+ }
+
+ if (PostContext->msgcount == 1)
+ {
+ /* only one message, so just use that one. */
+ h = PostContext->hdrs[0];
+ }
+ else if ((h = select_msg ()) == NULL)
+ {
+ mx_close_mailbox (PostContext);
+ safe_free ((void **) &PostContext);
+ return (-1);
+ }
+
+ if ((msg = mx_open_message (PostContext, h->msgno)) == NULL)
+ {
+ mx_close_mailbox (PostContext);
+ safe_free ((void **) &PostContext);
+ return (-1);
+ }
+
+ fseek (msg->fp, h->offset, 0);
+ hdr->env = mutt_read_rfc822_header (msg->fp, NULL);
+
+ if (h->content->type == TYPEMESSAGE || h->content->type == TYPEMULTIPART)
+ {
+ BODY *b;
+
+ fseek (msg->fp, h->content->offset, 0);
+
+ if (h->content->type == TYPEMULTIPART)
+ {
+ h->content->parts = mutt_parse_multipart (msg->fp,
+ mutt_get_parameter ("boundary", h->content->parameter),
+ h->content->offset + h->content->length,
+ strcasecmp ("digest", h->content->subtype) == 0);
+ }
+ else
+ h->content->parts = mutt_parse_messageRFC822 (msg->fp, h->content);
+
+ /* Now that we know what was in the other message, convert to the new
+ * message.
+ */
+ hdr->content = h->content->parts;
+ b = h->content->parts;
+ while (b != NULL)
+ {
+ file[0] = '\0';
+ if (b->filename)
+ strfcpy (file, b->filename, sizeof (file));
+ mutt_adv_mktemp (file);
+ if (mutt_save_attachment (msg->fp, b, file, 0) == -1)
+ {
+ mutt_free_envelope (&hdr->env);
+ mutt_free_body (&hdr->content);
+ mx_close_message (&msg);
+ mx_fastclose_mailbox (PostContext);
+ safe_free ((void **) &PostContext);
+ return (-1);
+ }
+ safe_free ((void *) &b->filename);
+ b->filename = safe_strdup (file);
+ b->unlink = 1;
+ mutt_free_body (&b->parts);
+ b = b->next;
+ }
+ h->content->parts = NULL;
+ }
+ else
+ {
+ mutt_mktemp (file);
+ if (mutt_save_attachment (msg->fp, h->content, file, 0) == -1)
+ {
+ mutt_free_envelope (&hdr->env);
+ mx_close_message (&msg);
+ mx_fastclose_mailbox (PostContext);
+ safe_free ((void **) &PostContext);
+ return (-1);
+ }
+ hdr->content = mutt_make_attach (file);
+ hdr->content->use_disp = 0; /* no content-disposition */
+ hdr->content->unlink = 1; /* delete when we are done */
+ }
+
+ mx_close_message (&msg);
+
+ /* finished with this message, so delete it. */
+ mutt_set_flag (PostContext, h, M_DELETE, 1);
+
+ /* update the count for the status display */
+ PostCount = PostContext->msgcount - PostContext->deleted;
+
+ /* avoid the "purge deleted messages" prompt */
+ opt_delete = quadoption (OPT_DELETE);
+ set_quadoption (OPT_DELETE, M_YES);
+ mx_close_mailbox (PostContext);
+ set_quadoption (OPT_DELETE, opt_delete);
+
+ safe_free ((void **) &PostContext);
+
+ for (tmp = hdr->env->userhdrs; tmp; )
+ {
+ if (strncasecmp ("X-Mutt-References:", tmp->data, 18) == 0)
+ {
+ if (ctx)
+ {
+ /* if a mailbox is currently open, look to see if the orignal message
+ the user attempted to reply to is in this mailbox */
+ p = tmp->data + 18;
+ SKIPWS (p);
+ *cur = hash_find (ctx->id_hash, p);
+ }
+
+ /* Remove the X-Mutt-References: header field. */
+ next = tmp->next;
+ if (last)
+ last->next = tmp->next;
+ else
+ hdr->env->userhdrs = tmp->next;
+ tmp->next = NULL;
+ mutt_free_list (&tmp);
+ tmp = next;
+ if (*cur)
+ code |= SENDREPLY;
+ }
+
+
+
+#ifdef _PGPPATH
+ else if (strncmp ("Pgp:", tmp->data, 4) == 0)
+ {
+ hdr->pgp = mutt_parse_pgp_hdr (tmp->data+4, 1);
+
+ /* remove the pgp field */
+ next = tmp->next;
+ if (last)
+ last->next = tmp->next;
+ else
+ hdr->env->userhdrs = tmp->next;
+ tmp->next = NULL;
+ mutt_free_list (&tmp);
+ tmp = next;
+ }
+#endif /* _PGPPATH */
+
+
+
+ else
+ {
+ last = tmp;
+ tmp = tmp->next;
+ }
+ }
+ return (code);
+}
+
+
+
+#ifdef _PGPPATH
+
+int mutt_parse_pgp_hdr (char *p, int set_signas)
+{
+ int pgp = 0;
+ char pgp_sign_as[LONG_STRING] = "\0", *q;
+ char pgp_sign_micalg[LONG_STRING] = "\0";
+
+ SKIPWS (p);
+ for (; *p; p++)
+ {
+
+ switch (*p)
+ {
+ case 'e':
+ case 'E':
+ pgp |= PGPENCRYPT;
+ break;
+
+ case 's':
+ case 'S':
+ pgp |= PGPSIGN;
+ q = pgp_sign_as;
+
+ if (*(p+1) == '<')
+ {
+ for (p += 2;
+ *p && *p != '>' && q < pgp_sign_as + sizeof (pgp_sign_as) - 1;
+ *q++ = *p++)
+ ;
+
+ if (*p!='>')
+ {
+ mutt_error ("Illegal PGP header");
+ return 0;
+ }
+ }
+
+ *q = '\0';
+ break;
+
+ case 'm':
+ case 'M':
+ q = pgp_sign_micalg;
+
+ if(*(p+1) == '<')
+ {
+ for(p += 2; *p && *p != '>' && q < pgp_sign_micalg + sizeof(pgp_sign_micalg) - 1;
+ *q++ = *p++)
+ ;
+
+ if(*p != '>')
+ {
+ mutt_error("Illegal PGP header");
+ return 0;
+ }
+ }
+
+ *q = '\0';
+ break;
+
+ default:
+ mutt_error ("Illegal PGP header");
+ return 0;
+ }
+
+ }
+
+ if (set_signas || *pgp_sign_as)
+ {
+ safe_free((void **) &PgpSignAs);
+ PgpSignAs = safe_strdup(pgp_sign_as);
+ }
+
+ if (set_signas || *pgp_sign_micalg)
+ {
+ safe_free((void **) &PgpSignMicalg);
+ PgpSignMicalg = safe_strdup(pgp_sign_micalg);
+ }
+
+ return pgp;
+}
+#endif /* _PGPPATH */
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define FOREVER while (1)
+
+#ifdef DEBUG
+#define dprint(N,X) if(debuglevel>=N) fprintf X
+#else
+#define dprint(N,X)
+#endif
+
+#define NONULL(x) x?x:""
+
+#define MoreArgs(p) (*p->dptr && *p->dptr != ';' && *p->dptr != '#')
+
+#define mutt_make_string(A,B,C,D) _mutt_make_string(A,B,C,D,0)
+void _mutt_make_string (char *, size_t, const char *, HEADER *, format_flag);
+
+int mutt_extract_token (BUFFER *, BUFFER *, int);
+
+int mutt_add_string (BUFFER *, const char *);
+int mutt_add_char (BUFFER *, char);
+
+#define mutt_system(x) _mutt_system(x,0)
+int _mutt_system (const char *, int);
+
+#define mutt_next_thread(x) _mutt_aside_thread(x,1,0)
+#define mutt_previous_thread(x) _mutt_aside_thread(x,0,0)
+#define mutt_next_subthread(x) _mutt_aside_thread(x,1,1)
+#define mutt_previous_subthread(x) _mutt_aside_thread(x,0,1)
+int _mutt_aside_thread (HEADER *, short, short);
+
+#define ISSPACE(c) isspace((unsigned char)c)
+
+#define mutt_new_parameter() safe_calloc (1, sizeof (PARAMETER))
+#define mutt_new_header() safe_calloc (1, sizeof (HEADER))
+#define mutt_new_envelope() safe_calloc (1, sizeof (ENVELOPE))
+
+typedef const char * format_t (char *, size_t, char, const char *, const char *, const char *, const char *, unsigned long, format_flag);
+
+void mutt_FormatString (char *, size_t, const char *, format_t *, unsigned long, format_flag);
+
+void mutt_update_encoding (BODY *a);
+
+FILE *mutt_open_read (const char *, pid_t *);
+
+void set_quadoption (int, int);
+int query_quadoption (int, const char *);
+int quadoption (int);
+
+ADDRESS *mutt_remove_duplicates (ADDRESS *);
+ADDRESS *mutt_expand_aliases (ADDRESS *);
+ADDRESS *mutt_parse_adrlist (ADDRESS *, const char *);
+
+BODY *mutt_dup_body (BODY *);
+BODY *mutt_make_attach (const char *);
+BODY *mutt_make_multipart (BODY *);
+BODY *mutt_new_body (void);
+BODY *mutt_parse_multipart (FILE *, const char *, long, int);
+BODY *mutt_parse_messageRFC822 (FILE *, BODY *);
+BODY *mutt_read_mime_header (FILE *, int);
+
+ENVELOPE *mutt_read_rfc822_header (FILE *, HEADER *);
+HEADER *mutt_dup_header (HEADER *);
+
+ATTACHPTR **mutt_gen_attach_list (BODY *, ATTACHPTR **, short *, short *, int, int);
+
+time_t mutt_local_tz (void);
+time_t mutt_mktime (struct tm *, int);
+time_t is_from (const char *, char *, size_t);
+
+char *mutt_expand_path (char *, size_t);
+char *mutt_find_hook (int, const char *);
+char *mutt_generate_boundary (void);
+char *mutt_gen_msgid (void);
+char *mutt_get_name (ADDRESS *);
+char *mutt_get_parameter (const char *, PARAMETER *);
+char *mutt_read_line (char *, size_t *, FILE *, int *);
+char *mutt_strlower (char *);
+char *mutt_skip_whitespace (char *);
+char *mutt_substrcpy (char *, const char *, const char *, size_t);
+char *mutt_substrdup (const char *, const char *);
+
+void mutt_add_child_pid (pid_t);
+void mutt_alias_menu (char *, size_t, ALIAS *);
+void mutt_block_signals (void);
+void mutt_block_signals_system (void);
+void mutt_body_handler (BODY *, STATE *);
+void mutt_bounce_message (HEADER *, ADDRESS *);
+void mutt_buffy (char *);
+void mutt_check_rescore (CONTEXT *ctx);
+void mutt_clear_error (void);
+void mutt_create_alias (ENVELOPE *, ADDRESS *);
+void mutt_decode_attachment (BODY *, STATE *);
+void mutt_default_save (char *, size_t, HEADER *);
+void mutt_display_address (ADDRESS *);
+void mutt_edit_file (const char *, const char *);
+void mutt_edit_headers (const char *, const char *, HEADER *, char *, size_t);
+void mutt_curses_error (const char *, ...);
+void mutt_enter_command (void);
+void mutt_exit (int);
+void mutt_expand_fmt (char *, size_t, const char *, const char *);
+void mutt_expand_link (char *, const char *, const char *);
+void mutt_fetchPopMail (void);
+void mutt_folder_hook (char *);
+void mutt_free_alias (ALIAS **);
+void mutt_free_body (BODY **);
+void mutt_free_color (int fg, int bg);
+void mutt_free_envelope (ENVELOPE **);
+void mutt_free_header (HEADER **);
+void mutt_free_parameter (PARAMETER **);
+void mutt_generate_header (char *, size_t, HEADER *, int);
+void mutt_help (int);
+void mutt_index_menu (void);
+void mutt_init_history (void);
+void mutt_linearize_tree (CONTEXT *, int);
+void mutt_make_help (char *, size_t, char *, int, int);
+void mutt_message (const char *, ...);
+void mutt_message_to_7bit (BODY *, FILE *);
+void mutt_mktemp (char *);
+void mutt_nocurses_error (const char *, ...);
+void mutt_normalize_time (struct tm *);
+void mutt_parse_mime_message (CONTEXT *ctx, HEADER *);
+void mutt_pipe_message_to_state (HEADER *, STATE *);
+void mutt_perror (const char *);
+void mutt_pretty_mailbox (char *);
+void mutt_pretty_size (char *, size_t, long);
+void mutt_print_message (HEADER *);
+void mutt_remove_trailing_ws (char *);
+void mutt_query_exit (void);
+void mutt_query_menu (char *, size_t);
+void mutt_safe_path (char *s, size_t l, ADDRESS *a);
+void mutt_save_path (char *s, size_t l, ADDRESS *a);
+void mutt_score_message (HEADER *);
+void mutt_select_fcc (char *, size_t, HEADER *);
+void mutt_select_file (char *, size_t, int);
+void mutt_send_hook (HEADER *);
+void mutt_set_flag (CONTEXT *, HEADER *, int, int);
+void mutt_shell_escape (void);
+void mutt_show_error (void);
+void mutt_signal_init (void);
+void mutt_tabs_to_spaces (char *);
+void mutt_tag_set_flag (int, int);
+void mutt_unblock_signals (void);
+void mutt_unblock_signals_system (int);
+void mutt_unlink (const char *);
+void mutt_update_encoding (BODY *a);
+void mutt_update_tree (ATTACHPTR **, short);
+void mutt_version (void);
+void mutt_view_attachments (HEADER *);
+
+int mutt_addr_is_user (ADDRESS *);
+int mutt_alias_complete (char *, size_t);
+int mutt_alloc_color (int fg, int bg);
+int mutt_any_key_to_continue (const char *);
+int mutt_buffy_check (int);
+int mutt_buffy_notify (void);
+int mutt_builtin_editor (const char *, HEADER *, HEADER *);
+int mutt_can_decode (BODY *);
+int mutt_change_flag (HEADER *, int);
+int mutt_check_encoding (const char *);
+int mutt_check_key (const char *);
+int mutt_check_menu (const char *);
+int mutt_check_mime_type (const char *);
+int mutt_check_month (const char *);
+int mutt_check_overwrite (const char *, const char *, char *, size_t, int);
+int mutt_complete (char *);
+int mutt_compose_attachment (BODY *a);
+int mutt_copy_bytes (FILE *, FILE *, size_t);
+int mutt_copy_stream (FILE *, FILE *);
+int mutt_decode_save_attachment (FILE *, BODY *, char *, int, int);
+int mutt_display_message (HEADER *h);
+int mutt_edit_attachment(BODY *, int);
+int mutt_enter_fname (const char *, char *, size_t, int *, int);
+int mutt_enter_string (unsigned char *, size_t, int, int, int);
+int mutt_get_field (char *, char *, size_t, int);
+int mutt_get_password (char *, char *, size_t);
+int mutt_get_postponed (CONTEXT *, HEADER *, HEADER **);
+int mutt_is_autoview (char *);
+int mutt_is_mail_list (ADDRESS *);
+int mutt_is_list_recipient (ADDRESS *a);
+int mutt_is_text_type (int, char *);
+int mutt_is_valid_mailbox (const char *);
+int mutt_needs_mailcap (BODY *);
+int mutt_num_postponed (void);
+int mutt_parse_bind (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+int mutt_parse_color (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+int mutt_parse_uncolor (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+int mutt_parse_hook (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+int mutt_parse_macro (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+int mutt_parse_mailboxes (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+int mutt_parse_mono (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+int mutt_parse_push (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+int mutt_parse_rc_line (/* const */ char *, BUFFER *, BUFFER *);
+int mutt_parse_score (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+int mutt_parse_unscore (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+int mutt_pattern_func (int, char *, HEADER *);
+int mutt_pipe_attachment (FILE *, BODY *, const char *, char *);
+int mutt_pipe_message (HEADER *);
+int mutt_print_attachment (FILE *, BODY *);
+int mutt_query_complete (char *, size_t);
+int mutt_save_attachment (FILE *, BODY *, char *, int);
+int mutt_save_message (HEADER *, int, int, int *);
+int mutt_search_command (int, int);
+int mutt_send_menu (HEADER *, char *, size_t, HEADER *);
+int mutt_send_message (HEADER *, const char *);
+int mutt_strcmp (const char *, const char *);
+int mutt_thread_set_flag (HEADER *, int, int, int);
+int mutt_view_attachment (FILE*, BODY *, int);
+int mutt_wait_filter (pid_t);
+int mutt_which_case (const char *);
+int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int);
+int mutt_write_mime_body (BODY *, FILE *);
+int mutt_write_mime_header (BODY *, FILE *);
+int mutt_write_rfc822_header (FILE *, ENVELOPE *, BODY *, int);
+int mutt_yesorno (const char *, int);
+void mutt_cache_index_colors(CONTEXT *);
+void mutt_set_header_color(CONTEXT *, HEADER *);
+
+int mh_valid_message (const char *);
+
+pid_t mutt_create_filter (const char *, FILE **, FILE **, FILE **);
+pid_t mutt_create_filter_fd (const char *, FILE **, FILE **, FILE **, int, int, int);
+
+#define FREE(x) safe_free((void **)x)
+
+char *safe_strdup (const char *);
+void *safe_calloc (size_t, size_t);
+void *safe_malloc (unsigned int);
+void safe_realloc (void **, size_t);
+void safe_free (void **);
+
+FILE *safe_fopen (const char *, const char *);
+
+ADDRESS *alias_reverse_lookup (ADDRESS *);
+
+#define strfcpy(A,B,C) strncpy(A,B,C), *(A+(C)-1)=0
+
+/* this macro must check for *c == 0 since isspace(0) has unreliable behavior
+ on some systems */
+#define SKIPWS(c) while (*(c) && isspace ((unsigned char) *(c))) c++;
+
+#ifdef LOCALES_HACK
+#define IsPrint(c) (isprint((unsigned char)(c)) || \
+ ((unsigned char)(c) >= 0xa0))
+#else
+#define IsPrint(c) (isprint((unsigned char)(c)) || \
+ (option (OPTLOCALES) ? 0 : \
+ ((unsigned char)(c) >= 0xa0)))
+#endif
+
+#define new_pattern() calloc(1, sizeof (pattern_t))
+
+int mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h);
+pattern_t *mutt_pattern_comp (/* const */ char *s, int flags, BUFFER *err);
+void mutt_check_simple (char *s, size_t len, const char *simple);
+void mutt_pattern_free (pattern_t **pat);
+
+/* ----------------------------------------------------------------------------
+ * Prototypes for broken systems
+ */
+
+#ifdef HAVE_SRAND48
+#define LRAND lrand48
+#define SRAND srand48
+#define DRAND drand48
+#else
+#define LRAND rand
+#define SRAND srand
+#define DRAND (double)rand
+#endif /* HAVE_SRAND48 */
+
+#ifdef HAVE_SETEGID
+#define SETEGID setegid
+#else
+#define SETEGID setgid
+#endif
+
+int getdnsdomainname (char *, size_t);
+
+/* According to SCO support, this is how to detect SCO */
+#if defined (_M_UNIX) || defined (M_OS)
+#define SCO
+#endif
+
+/* SCO Unix uses chsize() instead of ftruncate() */
+#ifndef HAVE_FTRUNCATE
+#define ftruncate chsize
+#endif
+
+#ifndef HAVE_SNPRINTF
+extern int snprintf (char *, size_t, const char *, ...);
+#endif
+
+#ifndef HAVE_VSNPRINTF
+extern int vsnprintf (char *, size_t, const char *, va_list);
+#endif
+
+#ifndef HAVE_STRERROR
+#ifndef STDC_HEADERS
+extern int sys_nerr;
+extern char *sys_errlist[];
+#endif
+
+#define strerror(x) ((x) > 0 && (x) < sys_nerr) ? sys_errlist[(x)] : 0
+#endif /* !HAVE_STRERROR */
+
+/* AIX doesn't define these in any headers (sigh) */
+int strcasecmp (const char *, const char *);
+int strncasecmp (const char *, const char *, size_t);
+
+#ifdef _AIX
+int setegid (gid_t);
+#endif /* _AIX */
+
+#ifndef STDC_HEADERS
+extern FILE *fdopen ();
+extern int system ();
+extern int puts ();
+extern int fputs ();
+extern int fputc ();
+extern int fseek ();
+extern char *strchr ();
+extern int getopt ();
+extern int fputs ();
+extern int fputc ();
+extern int fclose ();
+extern int fprintf();
+extern int printf ();
+extern int fgetc ();
+extern int tolower ();
+extern int toupper ();
+extern int sscanf ();
+extern size_t fread ();
+extern size_t fwrite ();
+extern int system ();
+extern int rename ();
+extern time_t time ();
+extern struct tm *localtime ();
+extern char *asctime ();
+extern char *strpbrk ();
+extern int fflush ();
+extern long lrand48 ();
+extern void srand48 ();
+extern time_t mktime ();
+extern int vsprintf ();
+extern int ungetc ();
+extern char *mktemp ();
+extern int ftruncate ();
+extern void *memset ();
+extern int pclose ();
+extern int socket ();
+extern int connect ();
+extern size_t strftime ();
+extern int lstat ();
+extern void rewind ();
+extern int readlink ();
+
+/* IRIX barfs on empty var decls because the system include file uses elipsis
+ in the declaration. So declare all the args to avoid compiler errors. This
+ should be harmless on other systems. */
+int ioctl (int, int, ...);
+
+#endif
+
+/* unsorted */
+void ci_bounce_message (HEADER *, int *);
+void ci_send_message (int, HEADER *, char *, CONTEXT *, HEADER *);
+void ci_attach (BODY *);
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_menu.h"
+#include "sort.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+typedef struct query
+{
+ ADDRESS *addr;
+ char *name;
+ char *other;
+ struct query *next;
+} QUERY;
+
+typedef struct entry
+{
+ int tagged;
+ QUERY *data;
+} ENTRY;
+
+static struct mapping_t QueryHelp[] = {
+ { "Exit", OP_EXIT },
+ { "Mail", OP_MAIL },
+ { "New Query", OP_QUERY },
+ { "Make Alias", OP_CREATE_ALIAS },
+ { "Search", OP_SEARCH },
+ { "Help", OP_HELP },
+ { NULL }
+};
+
+/* Variables for outsizing output format */
+static int FirstColumn;
+static int SecondColumn;
+
+static void query_menu (char *buf, size_t buflen, QUERY *results, int retbuf);
+
+static ADDRESS *result_to_addr (QUERY *r)
+{
+ static ADDRESS tmp;
+
+ tmp = *r->addr;
+
+ if(!tmp.next && !tmp.personal)
+ tmp.personal = r->name;
+
+ return &tmp;
+}
+
+static QUERY *run_query (char *s, int quiet)
+{
+ FILE *fp;
+ QUERY *first = NULL;
+ QUERY *cur = NULL;
+ char cmd[_POSIX_PATH_MAX];
+ char buf[STRING];
+ char msg[STRING];
+ char *p;
+ pid_t thepid;
+ int l;
+
+
+ snprintf (cmd, sizeof (cmd), QueryCmd, s);
+
+ if ((thepid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
+ {
+ dprint (1, (debugfile, "unable to fork command: %s", cmd));
+ return 0;
+ }
+ if (!quiet)
+ mutt_message ("Waiting for response...");
+ fgets (msg, sizeof (msg) - 1, fp);
+ while (fgets(buf, sizeof (buf) - 1, fp))
+ {
+ if (first == NULL)
+ {
+ FirstColumn = 0;
+ SecondColumn = 0;
+ first = (QUERY *) safe_calloc (1, sizeof (QUERY));
+ cur = first;
+ }
+ else
+ {
+ cur->next = (QUERY *) safe_calloc (1, sizeof (QUERY));
+ cur = cur->next;
+ }
+ p = strtok(buf, "\t\n");
+ if (p)
+ {
+ l = strlen (p);
+ if (l > SecondColumn)
+ SecondColumn = l;
+
+ cur->addr = rfc822_parse_adrlist (cur->addr, p);
+ p = strtok(NULL, "\t\n");
+ if (p)
+ {
+ l = strlen (p);
+ if (l > FirstColumn)
+ FirstColumn = l;
+ cur->name = safe_strdup (p);
+ p = strtok(NULL, "\t\n");
+ if (p)
+ {
+ cur->other = safe_strdup (p);
+ }
+ }
+ }
+ }
+ fclose (fp);
+ if (mutt_wait_filter (thepid))
+ {
+ dprint (1, (debugfile, "Error: %s\n", msg));
+ if (!quiet)
+ mutt_error (msg);
+ }
+ else
+ {
+ if (!quiet)
+ mutt_message (msg);
+ }
+
+ return first;
+}
+
+int query_search (MUTTMENU *m, regex_t *re, int n)
+{
+ ENTRY *table = (ENTRY *) m->data;
+
+ return (regexec (re, table[n].data->name, 0, NULL, 0));
+}
+
+/* This is the callback routine from mutt_menuLoop() which is used to generate
+ * a menu entry for the requested item number.
+ */
+void query_entry (char *s, size_t slen, MUTTMENU *m, int num)
+{
+ ENTRY *table = (ENTRY *) m->data;
+ char buf[SHORT_STRING] = "";
+
+ while (FirstColumn + SecondColumn > 70)
+ {
+ FirstColumn = FirstColumn * 3 / 4;
+ SecondColumn = SecondColumn * 3 / 4;
+ }
+
+ rfc822_write_address (buf, sizeof (buf), table[num].data->addr);
+
+ snprintf (s, slen, " %c %3d %-*.*s %-*.*s %s",
+ table[num].tagged ? '*':' ',
+ num+1,
+ FirstColumn+2,
+ FirstColumn+2,
+ table[num].data->name,
+ SecondColumn+2,
+ SecondColumn+2,
+ buf,
+ table[num].data->other);
+}
+
+int query_tag (MUTTMENU *menu, int n)
+{
+ return (((ENTRY *) menu->data)[n].tagged = !((ENTRY *) menu->data)[n].tagged);
+}
+
+int mutt_query_complete (char *buf, size_t buflen)
+{
+ QUERY *results = NULL;
+
+ results = run_query (buf, 1);
+ if (results)
+ {
+ /* only one response? */
+ if (results->next == NULL)
+ {
+ buf[0] = '\0';
+ rfc822_write_address (buf, buflen, result_to_addr(results));
+ mutt_clear_error ();
+ return (0);
+ }
+ /* multiple results, choose from query menu */
+ query_menu (buf, buflen, results, 1);
+ }
+ return (0);
+}
+
+void mutt_query_menu (char *buf, size_t buflen)
+{
+ if (buf == NULL)
+ {
+ char buffer[STRING] = "";
+
+ query_menu (buffer, sizeof (buffer), NULL, 0);
+ }
+ else
+ {
+ query_menu (buf, buflen, NULL, 1);
+ }
+}
+
+static void query_menu (char *buf, size_t buflen, QUERY *results, int retbuf)
+{
+ MUTTMENU *menu;
+ HEADER *msg = NULL;
+ ENTRY *QueryTable = NULL;
+ QUERY *queryp = NULL;
+ int i, done = 0;
+ int op;
+ char helpstr[SHORT_STRING];
+ char title[STRING] = "Query";
+
+ menu = mutt_new_menu ();
+ menu->make_entry = query_entry;
+ menu->search = query_search;
+ menu->tag = query_tag;
+ menu->menu = MENU_QUERY;
+ menu->title = title;
+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_QUERY, QueryHelp);
+
+ if (results == NULL)
+ {
+ if (!QueryCmd)
+ {
+ mutt_error ("Query command not defined.");
+ return;
+ }
+ /* Prompt for Query */
+ if (mutt_get_field ("Query: ", buf, buflen, 0) == 0 && buf[0])
+ {
+ results = run_query (buf, 0);
+ }
+ }
+
+ if (results)
+ {
+ /* tell whoever called me to redraw the screen when I return */
+ set_option (OPTNEEDREDRAW);
+
+ snprintf (title, sizeof (title), "Query '%s'", buf);
+
+ /* count the number of results */
+ for (queryp = results; queryp; queryp = queryp->next)
+ menu->max++;
+
+ menu->data = QueryTable = (ENTRY *) safe_calloc (menu->max, sizeof (ENTRY));
+
+ for (i = 0, queryp = results; queryp; queryp = queryp->next, i++)
+ QueryTable[i].data = queryp;
+
+ while (!done)
+ {
+ switch ((op = mutt_menuLoop (menu)))
+ {
+ case OP_QUERY_APPEND:
+ case OP_QUERY:
+ if (mutt_get_field ("Query: ", buf, buflen, 0) == 0 && buf[0])
+ {
+ QUERY *newresults = NULL;
+
+ newresults = run_query (buf, 0);
+
+ menu->redraw = REDRAW_FULL;
+ if (newresults)
+ {
+ snprintf (title, sizeof (title), "Query '%s'", buf);
+
+ if (op == OP_QUERY)
+ {
+ queryp = results;
+ while (queryp)
+ {
+ rfc822_free_address (&queryp->addr);
+ safe_free ((void **)&queryp->name);
+ safe_free ((void **)&queryp->other);
+ results = queryp->next;
+ safe_free ((void **)&queryp);
+ queryp = results;
+ }
+ results = newresults;
+ safe_free ((void **) &QueryTable);
+ }
+ else
+ {
+ /* append */
+ for (queryp = results; queryp->next; queryp = queryp->next);
+
+ queryp->next = newresults;
+ }
+
+
+ menu->current = 0;
+ mutt_menuDestroy (&menu);
+ menu = mutt_new_menu ();
+ menu->make_entry = query_entry;
+ menu->search = query_search;
+ menu->tag = query_tag;
+ menu->menu = MENU_QUERY;
+ menu->title = title;
+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_QUERY, QueryHelp);
+
+ /* count the number of results */
+ for (queryp = results; queryp; queryp = queryp->next)
+ menu->max++;
+
+ if (op == OP_QUERY)
+ {
+ menu->data = QueryTable =
+ (ENTRY *) safe_calloc (menu->max, sizeof (ENTRY));
+
+ for (i = 0, queryp = results; queryp;
+ queryp = queryp->next, i++)
+ QueryTable[i].data = queryp;
+ }
+ else
+ {
+ int clear = 0;
+
+ /* append */
+ safe_realloc ((void **)&QueryTable, menu->max * sizeof (ENTRY));
+
+ menu->data = QueryTable;
+
+ for (i = 0, queryp = results; queryp;
+ queryp = queryp->next, i++)
+ {
+ /* once we hit new entries, clear/init the tag */
+ if (queryp == newresults)
+ clear = 1;
+
+ QueryTable[i].data = queryp;
+ if (clear)
+ QueryTable[i].tagged = 0;
+ }
+ }
+ }
+ }
+ break;
+
+ case OP_CREATE_ALIAS:
+ if (menu->tagprefix)
+ {
+ ADDRESS *naddr = NULL;
+
+ for (i = 0; i < menu->max; i++)
+ if (QueryTable[i].tagged)
+ rfc822_append (&naddr, result_to_addr(QueryTable[i].data));
+
+ mutt_create_alias (NULL, naddr);
+ }
+ else
+ {
+ mutt_create_alias (NULL, result_to_addr(QueryTable[menu->current].data));
+ }
+ break;
+
+ case OP_GENERIC_SELECT_ENTRY:
+ if (retbuf)
+ {
+ done = 2;
+ break;
+ }
+ /* fall through to OP_MAIL */
+
+ case OP_MAIL:
+ msg = mutt_new_header ();
+ msg->env = mutt_new_envelope ();
+ if (!menu->tagprefix)
+ {
+ msg->env->to =
+ rfc822_cpy_adr (result_to_addr(QueryTable[menu->current].data));
+ }
+ else
+ {
+ for (i = 0; i < menu->max; i++)
+ if (QueryTable[i].tagged)
+ rfc822_append (&msg->env->to, result_to_addr(QueryTable[i].data));
+ }
+ ci_send_message (0, msg, NULL, Context, NULL);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_EXIT:
+ done = 1;
+ break;
+ }
+ }
+
+ /* if we need to return the selected entries */
+ if (retbuf && (done == 2))
+ {
+ int tagged = 0;
+ size_t curpos = 0;
+
+ memset (buf, 0, buflen);
+
+ /* check for tagged entries */
+ for (i = 0; i < menu->max; i++)
+ {
+ if (QueryTable[i].tagged)
+ {
+ if (curpos == 0)
+ {
+ tagged = 1;
+ rfc822_write_address (buf, buflen, result_to_addr(QueryTable[i].data));
+ curpos = strlen (buf);
+ }
+ else if (curpos + 2 < buflen)
+ {
+ strcat (buf, ", ");
+ rfc822_write_address ((char *) buf + curpos + 1, buflen - curpos - 1,
+ result_to_addr(QueryTable[i].data));
+ curpos = strlen (buf);
+ }
+ }
+ }
+ /* then enter current message */
+ if (!tagged)
+ {
+ rfc822_write_address (buf, buflen, result_to_addr(QueryTable[menu->current].data));
+ }
+ }
+
+ queryp = results;
+ while (queryp)
+ {
+ rfc822_free_address (&queryp->addr);
+ safe_free ((void **)&queryp->name);
+ safe_free ((void **)&queryp->other);
+ results = queryp->next;
+ safe_free ((void **)&queryp);
+ queryp = results;
+ }
+ safe_free ((void **) &QueryTable);
+ }
+
+ mutt_menuDestroy (&menu);
+}
--- /dev/null
+#!/usr/local/bin/perl
+#
+# A small script to strip out any "illegal" PGP code to make sure it is
+# safe for International export.
+#
+$word = shift;
+$illegal = 0;
+$count = 0;
+while (<>)
+{
+ if (/^#if/)
+ {
+ if (/${word}/) { $illegal = 1; }
+ if ($illegal) { $count++; }
+ }
+ elsif ($illegal && /^#endif/)
+ {
+ $count--;
+ if ($count == 0)
+ {
+ $illegal = 0;
+ next;
+ }
+ }
+ print if (! $illegal);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mutt_menu.h"
+#include "rfc1524.h"
+#include "mime.h"
+#include "mailbox.h"
+#include "attach.h"
+#include "mapping.h"
+#include "mx.h"
+#include "copy.h"
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <errno.h>
+
+static struct mapping_t AttachHelp[] = {
+ { "Exit", OP_EXIT },
+ { "Save", OP_SAVE },
+ { "Pipe", OP_PIPE },
+ { "Print", OP_PRINT },
+ { "Help", OP_HELP },
+ { NULL }
+};
+
+void mutt_update_tree (ATTACHPTR **idx, short idxlen)
+{
+ char buf[STRING];
+ char *s;
+ int x;
+
+ for (x = 0; x < idxlen; x++)
+ {
+ if (2 * (idx[x]->level + 2) < sizeof (buf))
+ {
+ if (idx[x]->level)
+ {
+ s = buf + 2 * (idx[x]->level - 1);
+ *s++ = (idx[x]->content->next) ? M_TREE_LTEE : M_TREE_LLCORNER;
+ *s++ = M_TREE_HLINE;
+ *s++ = M_TREE_RARROW;
+ }
+ else
+ s = buf;
+ *s = 0;
+ }
+
+ if (idx[x]->tree)
+ {
+ if (strcmp (idx[x]->tree, buf) != 0)
+ {
+ safe_free ((void **) &idx[x]->tree);
+ idx[x]->tree = safe_strdup (buf);
+ }
+ }
+ else
+ idx[x]->tree = safe_strdup (buf);
+
+ if (2 * (idx[x]->level + 2) < sizeof (buf) && idx[x]->level)
+ {
+ s = buf + 2 * (idx[x]->level - 1);
+ *s++ = (idx[x]->content->next) ? '\005' : '\006';
+ *s++ = '\006';
+ }
+ }
+}
+
+ATTACHPTR **mutt_gen_attach_list (BODY *m,
+ ATTACHPTR **idx,
+ short *idxlen,
+ short *idxmax,
+ int level,
+ int compose)
+{
+ ATTACHPTR *new;
+
+ for (; m; m = m->next)
+ {
+ if (*idxlen == *idxmax)
+ safe_realloc ((void **) &idx, sizeof (ATTACHPTR *) * (*idxmax += 5));
+
+ if (m->type == TYPEMULTIPART && m->parts)
+ {
+ idx = mutt_gen_attach_list (m->parts, idx, idxlen, idxmax, level, compose);
+ }
+ else
+ {
+ new = idx[(*idxlen)++] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
+ new->content = m;
+ new->level = level;
+
+ /* We don't support multipart messages in the compose menu yet */
+ if (!compose && m->type == TYPEMESSAGE &&
+ (!strcasecmp (m->subtype, "rfc822") ||
+ !strcasecmp (m->subtype, "news")) && is_multipart (m->parts))
+ {
+ idx = mutt_gen_attach_list (m->parts, idx, idxlen, idxmax, level + 1, compose);
+ }
+ }
+ }
+
+ if (level == 0)
+ mutt_update_tree (idx, *idxlen);
+
+ return (idx);
+}
+
+void attach_entry (char *b, size_t blen, MUTTMENU *menu, int num)
+{
+ char t[SHORT_STRING];
+ char s[SHORT_STRING];
+ char size[SHORT_STRING];
+ ATTACHPTR **idx = (ATTACHPTR **) menu->data;
+ BODY *m;
+
+ m = idx[num]->content;
+ s[0] = 0;
+ if (m->type == TYPEMESSAGE && (!strcasecmp ("rfc822", m->subtype) ||
+ !strcasecmp ("news", m->subtype)) && MsgFmt[0])
+ _mutt_make_string (s, sizeof (s), MsgFmt, m->hdr,
+ M_FORMAT_FORCESUBJ | M_FORMAT_MAKEPRINT);
+
+ mutt_pretty_size (size, sizeof (size), m->length);
+ snprintf (t, sizeof (t), "[%.7s/%.10s, %.6s, %s]",
+ TYPE (m->type), m->subtype, ENCODING (m->encoding), size);
+ snprintf (b, blen, " %c%c %2d %-34.34s %s%s",
+ m->deleted ? 'D' : ' ',
+ m->tagged ? '*' : ' ',
+ num + 1,
+ t,
+ idx[num]->tree ? idx[num]->tree : "",
+ s[0] ? s : (m->description ? m->description :
+ (m->filename ? m->filename : "<no description>")));
+}
+
+int mutt_tag_attach (MUTTMENU *menu, int n)
+{
+ return (((ATTACHPTR **) menu->data)[n]->content->tagged = !((ATTACHPTR **) menu->data)[n]->content->tagged);
+}
+
+static void mutt_query_save_attachment (FILE *fp, BODY *body)
+{
+ char buf[_POSIX_PATH_MAX], tfile[_POSIX_PATH_MAX];
+
+ if (fp && body->filename)
+ strfcpy (buf, body->filename, sizeof (buf));
+ else
+ buf[0] = 0;
+ if (mutt_get_field ("Save to file: ", buf, sizeof (buf), M_FILE | M_CLEAR) != 0 || !buf[0])
+ return;
+ mutt_expand_path (buf, sizeof (buf));
+ if (mutt_check_overwrite (body->filename, buf, tfile, sizeof (tfile), 0))
+ return;
+ mutt_message ("Saving...");
+ if (mutt_save_attachment (fp, body, tfile, 0) == 0)
+ mutt_message ("Attachment saved.");
+}
+
+void mutt_save_attachment_list (FILE *fp, int tag, BODY *top)
+{
+ for (; top; top = top->next)
+ {
+ if (!tag || top->tagged)
+ mutt_query_save_attachment (fp, top);
+ else if (top->parts)
+ mutt_save_attachment_list (fp, 1, top->parts);
+ if (!tag)
+ return;
+ }
+}
+
+static void
+mutt_query_pipe_attachment (char *command, FILE *fp, BODY *body, int filter)
+{
+ char tfile[_POSIX_PATH_MAX];
+ char warning[STRING+_POSIX_PATH_MAX];
+
+ if (filter)
+ {
+ snprintf (warning, sizeof (warning),
+ "WARNING! You are about to overwrite %s, continue?",
+ body->filename);
+ if (mutt_yesorno (warning, M_NO) != M_YES) {
+ CLEARLINE (LINES-1);
+ return;
+ }
+ mutt_mktemp (tfile);
+ }
+ else
+ tfile[0] = 0;
+
+ if (mutt_pipe_attachment (fp, body, command, tfile))
+ {
+ if (filter)
+ {
+ mutt_unlink (body->filename);
+ mutt_rename_file (tfile, body->filename);
+ mutt_update_encoding (body);
+ mutt_message ("Attachment filtered.");
+ }
+ }
+ else
+ {
+ if (filter && tfile[0])
+ mutt_unlink (tfile);
+ }
+}
+
+static void
+pipe_attachment_list (char *command, FILE *fp, int tag, BODY *top, int filter)
+{
+ for (; top; top = top->next)
+ {
+ if (!tag || top->tagged)
+ mutt_query_pipe_attachment (command, fp, top, filter);
+ else if (top->parts)
+ pipe_attachment_list (command, fp, tag, top->parts, filter);
+ if (!tag)
+ break;
+ }
+}
+
+void mutt_pipe_attachment_list (FILE *fp, int tag, BODY *top, int filter)
+{
+ char buf[SHORT_STRING];
+
+ if (fp)
+ filter = 0; /* sanity check: we can't filter in the recv case yet */
+
+ buf[0] = 0;
+ if (mutt_get_field ((filter ? "Filter through: " : "Pipe to: "),
+ buf, sizeof (buf), 0) != 0 || !buf[0])
+ return;
+ mutt_expand_path (buf, sizeof (buf));
+ pipe_attachment_list (buf, fp, tag, top, filter);
+}
+
+static void print_attachment_list (FILE *fp, int tag, BODY *top)
+{
+ for (; top; top = top->next)
+ {
+ if (!tag || top->tagged)
+ mutt_print_attachment (fp, top);
+ else if (top->parts)
+ mutt_print_attachment_list (fp, tag, top->parts);
+ if (!tag)
+ return;
+ }
+}
+
+void mutt_print_attachment_list (FILE *fp, int tag, BODY *top)
+{
+ if (query_quadoption (OPT_PRINT, tag ? "Print tagged attachment(s)?" : "Print attachment?") != M_YES)
+ return;
+ print_attachment_list (fp, tag, top);
+}
+
+int mutt_is_message_type (int type, char *subtype)
+{
+ if (type != TYPEMESSAGE)
+ return 0;
+ if (strcasecmp (subtype, "rfc822") == 0 || strcasecmp (subtype, "news") == 0)
+ return 1;
+ return 0;
+}
+
+static void
+bounce_attachment_list (ADDRESS *adr, int tag, BODY *body, HEADER *hdr)
+{
+ for (; body; body = body->next)
+ {
+ if (!tag || body->tagged)
+ {
+ if (!mutt_is_message_type (body->type, body->subtype))
+ {
+ mutt_error ("You may only bounce message/rfc822 parts.");
+ continue;
+ }
+ body->hdr->msgno = hdr->msgno;
+ mutt_bounce_message (body->hdr, adr);
+ }
+ else if (body->parts)
+ bounce_attachment_list (adr, tag, body->parts, hdr);
+ if (!tag)
+ break;
+ }
+}
+
+static void query_bounce_attachment (int tag, BODY *top, HEADER *hdr)
+{
+ char prompt[SHORT_STRING];
+ char buf[HUGE_STRING];
+ ADDRESS *adr = NULL;
+ int rc;
+
+ buf[0] = 0;
+ snprintf (prompt, sizeof (prompt), "Bounce %smessage%s to: ",
+ tag ? "tagged " : "", tag ? "s" : "");
+ rc = mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS);
+
+ if (rc || !buf[0])
+ return;
+
+ adr = rfc822_parse_adrlist (adr, buf);
+ adr = mutt_expand_aliases (adr);
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), adr);
+ snprintf (prompt, sizeof (prompt), "Bounce message%s to %s...?", (tag ? "s" : ""), buf);
+ if (mutt_yesorno (prompt, 1) != 1)
+ {
+ rfc822_free_address (&adr);
+ CLEARLINE (LINES-1);
+ return;
+ }
+ bounce_attachment_list (adr, tag, top, hdr);
+ rfc822_free_address (&adr);
+}
+
+static void
+copy_tagged_attachments (FILE *fpout, FILE *fpin, const char *boundary, BODY *bdy)
+{
+ for (; bdy; bdy = bdy->next)
+ {
+ if (bdy->tagged)
+ {
+ fprintf (fpout, "--%s\n", boundary);
+ fseek (fpin, bdy->hdr_offset, 0);
+ mutt_copy_bytes (fpin, fpout, bdy->length + bdy->offset - bdy->hdr_offset);
+ }
+ else if (bdy->parts)
+ copy_tagged_attachments (fpout, fpin, boundary, bdy->parts);
+ }
+}
+
+static int
+create_tagged_message (const char *tempfile,
+ int tag,
+ CONTEXT *ctx,
+ HEADER *cur,
+ BODY *body)
+{
+ char *boundary;
+ MESSAGE *msg, *src;
+ CONTEXT tmpctx;
+ int magic;
+
+ magic = DefaultMagic;
+ DefaultMagic = M_MBOX;
+ mx_open_mailbox (tempfile, M_APPEND, &tmpctx);
+ msg = mx_open_new_message (&tmpctx, cur, M_ADD_FROM);
+ src = mx_open_message (ctx, cur->msgno);
+
+ if (tag)
+ {
+ mutt_copy_header (src->fp, cur, msg->fp, CH_XMIT, NULL);
+ boundary = mutt_get_parameter ("boundary", cur->content->parameter);
+ copy_tagged_attachments (msg->fp, src->fp, boundary, cur->content->parts);
+ fprintf (msg->fp, "--%s--\n", boundary);
+ }
+ else
+ {
+ /* single attachment */
+ mutt_copy_header (src->fp, cur, msg->fp, CH_XMIT | CH_MIME | CH_NONEWLINE, NULL);
+ fputs ("Mime-Version: 1.0\n", msg->fp);
+ mutt_write_mime_header (body, msg->fp);
+ fputc ('\n', msg->fp);
+ fseek (src->fp, body->offset, 0);
+ mutt_copy_bytes (src->fp, msg->fp, body->length);
+ }
+
+ mx_close_message (&msg);
+ mx_close_message (&src);
+ mx_close_mailbox (&tmpctx);
+ DefaultMagic = magic;
+ return 0;
+}
+
+/* op flag to ci_send_message()
+ tag operate on tagged attachments?
+ hdr current message
+ body current attachment */
+static void reply_attachment_list (int op, int tag, HEADER *hdr, BODY *body)
+{
+ HEADER *hn;
+ char tempfile[_POSIX_PATH_MAX];
+ CONTEXT *ctx;
+
+ if (!tag && body->hdr)
+ {
+ hn = body->hdr;
+ hn->msgno = hdr->msgno; /* required for MH/maildir */
+ ctx = Context;
+ }
+ else
+ {
+ /* build a fake message which consists of only the tagged attachments */
+ mutt_mktemp (tempfile);
+ create_tagged_message (tempfile, tag, Context, hdr, body);
+ ctx = mx_open_mailbox (tempfile, M_QUIET, NULL);
+ hn = ctx->hdrs[0];
+ }
+
+ ci_send_message (op, NULL, NULL, ctx, hn);
+
+ if (hn->replied && !hdr->replied)
+ mutt_set_flag (Context, hdr, M_REPLIED, 1);
+
+ if (ctx != Context)
+ {
+ mx_fastclose_mailbox (ctx);
+ safe_free ((void **) &ctx);
+ unlink (tempfile);
+ }
+}
+
+void
+mutt_attach_display_loop (MUTTMENU *menu, int op, FILE *fp, ATTACHPTR **idx)
+{
+ int old_optweed = option (OPTWEED);
+
+ set_option (OPTWEED);
+ do
+ {
+ switch (op)
+ {
+ case OP_DISPLAY_HEADERS:
+ toggle_option (OPTWEED);
+ /* fall through */
+
+ case OP_VIEW_ATTACH:
+ op = mutt_view_attachment (fp, idx[menu->current]->content, M_REGULAR);
+ break;
+
+ case OP_NEXT_ENTRY:
+ case OP_MAIN_NEXT_UNDELETED: /* hack */
+ if (menu->current < menu->max - 1)
+ {
+ menu->current++;
+ op = OP_VIEW_ATTACH;
+ }
+ else
+ op = OP_NULL;
+ break;
+ case OP_PREV_ENTRY:
+ case OP_MAIN_PREV_UNDELETED: /* hack */
+ if (menu->current > 0)
+ {
+ menu->current--;
+ op = OP_VIEW_ATTACH;
+ }
+ else
+ op = OP_NULL;
+ break;
+ default:
+ op = OP_NULL;
+ }
+ }
+ while (op != OP_NULL);
+
+ if (option (OPTWEED) != old_optweed)
+ toggle_option (OPTWEED);
+}
+
+void mutt_view_attachments (HEADER *hdr)
+{
+
+
+
+#ifdef _PGPPATH
+ char tempfile[_POSIX_PATH_MAX];
+ int pgp = 0;
+#endif
+
+
+
+ char helpstr[SHORT_STRING];
+ MUTTMENU *menu;
+ BODY *cur;
+ MESSAGE *msg;
+ FILE *fp;
+ ATTACHPTR **idx = NULL;
+ short idxlen = 0;
+ short idxmax = 0;
+ int flags = 0;
+ int op;
+
+ /* make sure we have parsed this message */
+ mutt_parse_mime_message (Context, hdr);
+
+ if ((msg = mx_open_message (Context, hdr->msgno)) == NULL)
+ return;
+
+
+
+#ifdef _PGPPATH
+
+ if((hdr->pgp & PGPENCRYPT) && !pgp_valid_passphrase())
+ {
+ mx_close_message(&msg);
+ return;
+ }
+
+ if ((hdr->pgp & PGPENCRYPT) && hdr->content->type == TYPEMULTIPART)
+ {
+ STATE s;
+
+ memset (&s, 0, sizeof (s));
+ s.fpin = msg->fp;
+ mutt_mktemp (tempfile);
+ if ((fp = safe_fopen (tempfile, "w+")) == NULL)
+ {
+ mutt_perror (tempfile);
+ mx_close_message (&msg);
+ return;
+ }
+ cur = pgp_decrypt_part (hdr->content->parts->next, &s, fp);
+ rewind (fp);
+
+ pgp = 1;
+ }
+ else
+#endif /* _PGPPATH */
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ fp = msg->fp;
+ cur = hdr->content;
+ }
+
+ idx = mutt_gen_attach_list (cur, idx, &idxlen, &idxmax, 0, 0);
+
+ menu = mutt_new_menu ();
+ menu->max = idxlen;
+ menu->make_entry = attach_entry;
+ menu->tag = mutt_tag_attach;
+ menu->menu = MENU_ATTACH;
+ menu->title = "Attachments";
+ menu->data = idx;
+ menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_ATTACH, AttachHelp);
+
+ FOREVER
+ {
+ switch (op = mutt_menuLoop (menu))
+ {
+ case OP_DISPLAY_HEADERS:
+ case OP_VIEW_ATTACH:
+ mutt_attach_display_loop (menu, op, fp, idx);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_ATTACH_VIEW_MAILCAP:
+ mutt_view_attachment (fp, idx[menu->current]->content, M_MAILCAP);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_ATTACH_VIEW_TEXT:
+ mutt_view_attachment (fp, idx[menu->current]->content, M_AS_TEXT);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+
+
+#ifdef _PGPPATH
+ case OP_EXTRACT_KEYS:
+ pgp_extract_keys_from_attachment_list (fp, menu->tagprefix, menu->tagprefix ? cur : idx[menu->current]->content);
+ menu->redraw = REDRAW_FULL;
+ break;
+#endif
+
+
+
+ case OP_PRINT:
+ mutt_print_attachment_list (fp, menu->tagprefix, menu->tagprefix ? cur : idx[menu->current]->content);
+ break;
+
+ case OP_PIPE:
+ mutt_pipe_attachment_list (fp, menu->tagprefix, menu->tagprefix ? cur : idx[menu->current]->content, 0);
+ break;
+
+ case OP_SAVE:
+ mutt_save_attachment_list (fp, menu->tagprefix, menu->tagprefix ? cur : idx[menu->current]->content);
+ if (option (OPTRESOLVE) && menu->current < menu->max - 1)
+ {
+ menu->current++;
+ menu->redraw = REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw = REDRAW_CURRENT;
+ break;
+
+ case OP_DELETE:
+
+ if (menu->max == 1)
+ {
+ mutt_message ("Only deletion of multipart attachments is supported.");
+ }
+ else
+ {
+#ifdef _PGPPATH
+ if (hdr->pgp)
+ {
+ mutt_message (
+ "Deletion of attachments from PGP messages is unsupported.");
+ }
+ else
+#endif
+ {
+ if (!menu->tagprefix)
+ {
+ idx[menu->current]->content->deleted = 1;
+ if (option (OPTRESOLVE) && menu->current < menu->max - 1)
+ {
+ menu->current++;
+ menu->redraw = REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw = REDRAW_CURRENT;
+ }
+ else
+ {
+ int x;
+
+ for (x = 0; x < menu->max; x++)
+ {
+ if (idx[x]->content->tagged)
+ {
+ idx[x]->content->deleted = 1;
+ menu->redraw = REDRAW_INDEX;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case OP_UNDELETE:
+ if (!menu->tagprefix)
+ {
+ idx[menu->current]->content->deleted = 0;
+ if (option (OPTRESOLVE) && menu->current < menu->max - 1)
+ {
+ menu->current++;
+ menu->redraw = REDRAW_MOTION_RESYNCH;
+ }
+ else
+ menu->redraw = REDRAW_CURRENT;
+ }
+ else
+ {
+ int x;
+
+ for (x = 0; x < menu->max; x++)
+ {
+ if (idx[x]->content->tagged)
+ {
+ idx[x]->content->deleted = 0;
+ menu->redraw = REDRAW_INDEX;
+ }
+ }
+ }
+ break;
+
+ case OP_BOUNCE_MESSAGE:
+ query_bounce_attachment (menu->tagprefix, menu->tagprefix ? cur : idx[menu->current]->content, hdr);
+ break;
+
+ case OP_REPLY:
+ case OP_GROUP_REPLY:
+ case OP_LIST_REPLY:
+ case OP_FORWARD_MESSAGE:
+
+
+
+#ifdef _PGPPATH
+ if ((hdr->pgp & PGPENCRYPT) && hdr->content->type == TYPEMULTIPART)
+ {
+ mutt_error (
+ "This operation is not currently supported for PGP messages.");
+ break;
+ }
+#endif
+
+
+
+ if (op == OP_FORWARD_MESSAGE)
+ flags = SENDFORWARD;
+ else
+ flags = SENDREPLY |
+ (op == OP_GROUP_REPLY ? SENDGROUPREPLY : 0) |
+ (op == OP_LIST_REPLY ? SENDLISTREPLY : 0);
+ reply_attachment_list (flags,
+ menu->tagprefix,
+ hdr,
+ menu->tagprefix ? cur : idx[menu->current]->content);
+ menu->redraw = REDRAW_FULL;
+ break;
+
+ case OP_EXIT:
+ mx_close_message (&msg);
+ hdr->attach_del = 0;
+ while (idxlen-- > 0)
+ {
+ if (idx[idxlen]->content->deleted)
+ hdr->attach_del = 1;
+ safe_free ((void **) &idx[idxlen]->tree);
+ safe_free ((void **) &idx[idxlen]);
+ }
+ if (hdr->attach_del)
+ hdr->changed = 1;
+ safe_free ((void **) &idx);
+ idxmax = 0;
+
+
+
+
+
+
+
+
+
+
+
+#ifdef _PGPPATH
+ if (pgp)
+ {
+ fclose (fp);
+ mutt_free_body (&cur);
+ unlink (tempfile);
+ }
+#endif /* _PGPPATH */
+
+
+
+ mutt_menuDestroy (&menu);
+ return;
+ }
+ }
+
+ /* not reached */
+}
--- /dev/null
+const char *ReleaseDate = "1998-05-14";
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+/* this routine should be called after receiving SIGWINCH */
+void mutt_resize_screen (void)
+{
+ char *cp;
+ int fd;
+ struct winsize w;
+#ifdef HAVE_RESIZETERM
+ int SLtt_Screen_Rows, SLtt_Screen_Cols;
+#endif
+
+ SLtt_Screen_Rows = -1;
+ SLtt_Screen_Cols = -1;
+ if ((fd = open ("/dev/tty", O_RDONLY)) != -1)
+ {
+ if (ioctl (fd, TIOCGWINSZ, &w) != -1)
+ {
+ SLtt_Screen_Rows = w.ws_row;
+ SLtt_Screen_Cols = w.ws_col;
+ }
+ close (fd);
+ }
+ if (SLtt_Screen_Rows <= 0)
+ {
+ if ((cp = getenv ("LINES")) != NULL)
+ {
+ SLtt_Screen_Rows = atoi (cp);
+ }
+ else
+ SLtt_Screen_Rows = 24;
+ }
+ if (SLtt_Screen_Cols <= 0)
+ {
+ if ((cp = getenv ("COLUMNS")) != NULL)
+ SLtt_Screen_Cols = atoi (cp);
+ else
+ SLtt_Screen_Cols = 80;
+ }
+#ifdef USE_SLANG_CURSES
+ delwin (stdscr);
+ SLsmg_reset_smg ();
+ SLsmg_init_smg ();
+ stdscr = newwin (0, 0, 0, 0);
+ keypad (stdscr, TRUE);
+#else
+ resizeterm (SLtt_Screen_Rows, SLtt_Screen_Cols);
+#endif
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * rfc1524 defines a format for the Multimedia Mail Configuration, which
+ * is the standard mailcap file format under Unix which specifies what
+ * external programs should be used to view/compose/edit multimedia files
+ * based on content type.
+ *
+ * This file contains various functions for implementing a fair subset of
+ * rfc1524.
+ */
+
+#include "mutt.h"
+#include "rfc1524.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <string.h>
+
+/* The command semantics include the following:
+ * %s is the filename that contains the mail body data
+ * %t is the content type, like text/plain
+ * %{parameter} is replaced by the parameter value from the content-type field
+ * \% is %
+ * Unsupported rfc1524 parameters: these would probably require some doing
+ * by mutt, and can probably just be done by piping the message to metamail
+ * %n is the integer number of sub-parts in the multipart
+ * %F is "content-type filename" repeated for each sub-part
+ *
+ * In addition, this function returns a 0 if the command works on a file,
+ * and 1 if the command works on a pipe.
+ */
+int rfc1524_expand_command (BODY *a, char *filename, char *type,
+ char *command, int clen)
+{
+ int x=0,y=0;
+ int needspipe = TRUE;
+ char buf[LONG_STRING];
+
+ while (command[x] && x<clen && y<sizeof(buf)) {
+ if (command[x] == '\\') {
+ x++;
+ buf[y++] = command[x++];
+ }
+ else if (command[x] == '%') {
+ x++;
+ if (command[x] == '{') {
+ char param[STRING];
+ int z = 0;
+ char *ret = NULL;
+
+ x++;
+ while (command[x] && command[x] != '}' && z<sizeof(param))
+ param[z++] = command[x++];
+ param[z] = '\0';
+ dprint(2,(debugfile,"Parameter: %s Returns: %s\n",param,ret));
+ ret = mutt_get_parameter(param,a->parameter);
+ dprint(2,(debugfile,"Parameter: %s Returns: %s\n",param,ret));
+ z = 0;
+ while (ret && ret[z] && y<sizeof(buf))
+ buf[y++] = ret[z++];
+ }
+ else if (command[x] == 's' && filename != NULL)
+ {
+ char *fn = filename;
+
+ while (*fn && y < sizeof (buf))
+ buf[y++] = *fn++;
+ needspipe = FALSE;
+ }
+ else if (command[x] == 't')
+ {
+ while (*type && y < sizeof (buf))
+ buf[y++] = *type++;
+ }
+ x++;
+ }
+ else
+ buf[y++] = command[x++];
+ }
+ buf[y] = '\0';
+ strfcpy (command, buf, clen);
+
+ return needspipe;
+}
+
+/* NUL terminates a rfc 1524 field,
+ * returns start of next field or NULL */
+static char *get_field (char *s)
+{
+ char *ch;
+
+ if (!s)
+ return NULL;
+
+ while ((ch = strpbrk (s, ";\\")) != NULL)
+ {
+ if (*ch == '\\')
+ {
+ s = ch + 1;
+ if (*s)
+ s++;
+ }
+ else
+ {
+ *ch++ = 0;
+ SKIPWS (ch);
+ break;
+ }
+ }
+ mutt_remove_trailing_ws (s);
+ return ch;
+}
+
+static int get_field_text (char *field, char **entry,
+ char *type, char *filename, int line)
+{
+ field = mutt_skip_whitespace (field);
+ if (*field == '=')
+ {
+ if (entry)
+ {
+ field = mutt_skip_whitespace (++field);
+ safe_free ((void **) entry);
+ *entry = safe_strdup (field);
+ }
+ return 1;
+ }
+ else
+ {
+ mutt_error ("Improperly formated entry for type %s in \"%s\" line %d",
+ type, filename, line);
+ return 0;
+ }
+}
+
+static int rfc1524_mailcap_parse (BODY *a,
+ char *filename,
+ char *type,
+ rfc1524_entry *entry,
+ int opt)
+{
+ FILE *fp;
+ char *buf = NULL;
+ size_t buflen;
+ char *ch;
+ char *field;
+ int found = FALSE;
+ int copiousoutput;
+ int composecommand;
+ int editcommand;
+ int printcommand;
+ int btlen;
+ int line = 0;
+
+ /* rfc1524 mailcap file is of the format:
+ * base/type; command; extradefs
+ * type can be * for matching all
+ * base with no /type is an implicit wild
+ * command contains a %s for the filename to pass, default to pipe on stdin
+ * extradefs are of the form:
+ * def1="definition"; def2="define \;";
+ * line wraps with a \ at the end of the line
+ * # for comments
+ */
+
+ /* find length of basetype */
+ if ((ch = strchr (type, '/')) == NULL)
+ return FALSE;
+ btlen = ch - type;
+
+ if ((fp = fopen (filename, "r")) != NULL)
+ {
+ while (!found && (buf = mutt_read_line (buf, &buflen, fp, &line)) != NULL)
+ {
+ /* ignore comments */
+ if (*buf == '#')
+ continue;
+ dprint (2, (debugfile, "mailcap entry: %s\n", buf));
+
+ /* check type */
+ ch = get_field (buf);
+ if (strcasecmp (buf, type) &&
+ (strncasecmp (buf, type, btlen) ||
+ (buf[btlen] != 0 && /* implicit wild */
+ strcmp (buf + btlen, "/*")))) /* wildsubtype */
+ continue;
+
+ /* next field is the viewcommand */
+ field = ch;
+ ch = get_field (ch);
+ if (entry)
+ entry->command = safe_strdup (field);
+
+ /* parse the optional fields */
+ found = TRUE;
+ copiousoutput = FALSE;
+ composecommand = FALSE;
+ editcommand = FALSE;
+ printcommand = FALSE;
+
+ while (ch)
+ {
+ field = ch;
+ ch = get_field (ch);
+ dprint (2, (debugfile, "field: %s\n", field));
+
+ if (!strcasecmp (field, "needsterminal"))
+ {
+ if (entry)
+ entry->needsterminal = TRUE;
+ }
+ else if (!strcasecmp (field, "copiousoutput"))
+ {
+ copiousoutput = TRUE;
+ if (entry)
+ entry->copiousoutput = TRUE;
+ }
+ else if (!strncasecmp (field, "composetyped", 12))
+ {
+ /* this compare most occur before compose to match correctly */
+ if (get_field_text (field + 12, entry ? &entry->composetypecommand : NULL,
+ type, filename, line))
+ composecommand = TRUE;
+ }
+ else if (!strncasecmp (field, "compose", 7))
+ {
+ if (get_field_text (field + 7, entry ? &entry->composecommand : NULL,
+ type, filename, line))
+ composecommand = TRUE;
+ }
+ else if (!strncasecmp (field, "print", 5))
+ {
+ if (get_field_text (field + 5, entry ? &entry->printcommand : NULL,
+ type, filename, line))
+ printcommand = TRUE;
+ }
+ else if (!strncasecmp (field, "edit", 4))
+ {
+ if (get_field_text (field + 4, entry ? &entry->editcommand : NULL,
+ type, filename, line))
+ editcommand = TRUE;
+ }
+ else if (!strncasecmp (field, "nametemplate", 12))
+ {
+ get_field_text (field + 12, entry ? &entry->nametemplate : NULL,
+ type, filename, line);
+ }
+ else if (!strncasecmp (field, "x-convert", 9))
+ {
+ get_field_text (field + 9, entry ? &entry->convert : NULL,
+ type, filename, line);
+ }
+ else if (!strncasecmp (field, "test", 4))
+ {
+ /*
+ * This routine executes the given test command to determine
+ * if this is the right entry.
+ */
+ char *test_command = NULL;
+ size_t len;
+
+ if (get_field_text (field + 4, &test_command, type, filename, line)
+ && test_command)
+ {
+ len = strlen (test_command) + STRING;
+ safe_realloc ((void **) &test_command, len);
+ rfc1524_expand_command (a, NULL, type, test_command, len);
+ if (mutt_system (test_command))
+ {
+ /* a non-zero exit code means test failed */
+ found = FALSE;
+ }
+ free (test_command);
+ }
+ }
+ } /* while (ch) */
+
+ if (opt == M_AUTOVIEW)
+ {
+ if (!copiousoutput)
+ found = FALSE;
+ }
+ else if (opt == M_COMPOSE)
+ {
+ if (!composecommand)
+ found = FALSE;
+ }
+ else if (opt == M_EDIT)
+ {
+ if (!editcommand)
+ found = FALSE;
+ }
+ else if (opt == M_PRINT)
+ {
+ if (!printcommand)
+ found = FALSE;
+ }
+
+ if (!found)
+ {
+ /* reset */
+ if (entry)
+ {
+ safe_free ((void **) &entry->command);
+ safe_free ((void **) &entry->composecommand);
+ safe_free ((void **) &entry->composetypecommand);
+ safe_free ((void **) &entry->editcommand);
+ safe_free ((void **) &entry->printcommand);
+ safe_free ((void **) &entry->nametemplate);
+ safe_free ((void **) &entry->convert);
+ entry->needsterminal = 0;
+ entry->copiousoutput = 0;
+ }
+ }
+ } /* while (!found && (buf = mutt_read_line ())) */
+ fclose (fp);
+ } /* if ((fp = fopen ())) */
+ safe_free ((void **) &buf);
+ return found;
+}
+
+rfc1524_entry *rfc1524_new_entry()
+{
+ rfc1524_entry *tmp;
+
+ tmp = (rfc1524_entry *)safe_malloc(sizeof(rfc1524_entry));
+ memset(tmp,0,sizeof(rfc1524_entry));
+
+ return tmp;
+}
+
+void rfc1524_free_entry(rfc1524_entry **entry)
+{
+ rfc1524_entry *p = *entry;
+
+ safe_free((void **)&p->command);
+ safe_free((void **)&p->testcommand);
+ safe_free((void **)&p->composecommand);
+ safe_free((void **)&p->composetypecommand);
+ safe_free((void **)&p->editcommand);
+ safe_free((void **)&p->printcommand);
+ safe_free((void **)&p->nametemplate);
+ safe_free((void **)entry);
+}
+
+/*
+ * rfc1524_mailcap_lookup attempts to find the given type in the
+ * list of mailcap files. On success, this returns the entry information
+ * in *entry, and returns 1. On failure (not found), returns 0.
+ * If entry == NULL just return 1 if the given type is found.
+ */
+int rfc1524_mailcap_lookup (BODY *a, char *type, rfc1524_entry *entry, int opt)
+{
+ char path[_POSIX_PATH_MAX];
+ int x;
+ int found = FALSE;
+ char *curr = MailcapPath;
+
+ /* rfc1524 specifies that a path of mailcap files should be searched.
+ * joy. They say
+ * $HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap, etc
+ * and overriden by the MAILCAPS environment variable, and, just to be nice,
+ * we'll make it specifiable in .muttrc
+ */
+ if (!*curr)
+ {
+ mutt_error ("No mailcap path specified");
+ return 0;
+ }
+
+ while (!found && *curr)
+ {
+ x = 0;
+ while (*curr && *curr != ':' && x < sizeof (path) - 1)
+ {
+ path[x++] = *curr;
+ curr++;
+ }
+ if (*curr)
+ curr++;
+
+ if (!x)
+ continue;
+
+ path[x] = '\0';
+ mutt_expand_path (path, sizeof (path));
+
+ dprint(2,(debugfile,"Checking mailcap file: %s\n",path));
+ found = rfc1524_mailcap_parse (a, path, type, entry, opt);
+ }
+
+ if (entry && !found)
+ mutt_error ("mailcap entry for type %s not found", type);
+
+ return found;
+}
+
+/* Modified by blong to accept a "suggestion" for file name. If
+ * that file exists, then construct one with unique name but
+ * keep any extension. This might fail, I guess.
+ * Renamed to mutt_adv_mktemp so I only have to change where it's
+ * called, and not all possible cases.
+ */
+void mutt_adv_mktemp (char *s)
+{
+ char buf[_POSIX_PATH_MAX];
+ char tmp[_POSIX_PATH_MAX];
+ char *period;
+
+ strfcpy (buf, NONULL (Tempdir), sizeof (buf));
+ mutt_expand_path (buf, sizeof (buf));
+ if (s[0] == '\0')
+ {
+ sprintf (s, "%s/muttXXXXXX", buf);
+ mktemp (s);
+ }
+ else
+ {
+ strfcpy (tmp, s, sizeof (tmp));
+ sprintf (s, "%s/%s", buf, tmp);
+ if (access (s, F_OK) != 0)
+ return;
+ if ((period = strrchr (tmp, '.')) != NULL)
+ *period = 0;
+ sprintf (s, "%s/%s.XXXXXX", buf, tmp);
+ mktemp (s);
+ if (period != NULL)
+ {
+ *period = '.';
+ strcat (s, period);
+ }
+ }
+}
+
+/* This routine expands the filename given to match the format of the
+ * nametemplate given. It returns various values based on what operations
+ * it performs.
+ *
+ * Returns 0 if oldfile is fine as is.
+ * Returns 1 if newfile specified
+ */
+
+int rfc1524_expand_filename (char *nametemplate,
+ char *oldfile,
+ char *newfile,
+ size_t nflen)
+{
+ int z = 0;
+ int i = 0, j = 0;
+ int lmatch = TRUE;
+ int match = TRUE;
+ size_t len = 0;
+ char *s;
+
+ newfile[0] = 0;
+
+ if (nametemplate && (s = strrchr (nametemplate, '/')))
+ nametemplate = s + 1;
+
+ if (oldfile && (s = strrchr (oldfile, '/')))
+ {
+ len = s - oldfile + 1;
+ if (len > nflen)
+ len = nflen;
+ strfcpy (newfile, oldfile, len + 1);
+ oldfile += len;
+ }
+
+ /* If nametemplate is NULL, create a newfile from oldfile and return 0 */
+ if (!nametemplate)
+ {
+ if (oldfile)
+ strfcpy (newfile, oldfile, nflen);
+ mutt_adv_mktemp (newfile);
+ return 0;
+ }
+
+ /* If oldfile is NULL, just return a newfile name */
+ if (!oldfile)
+ {
+ snprintf (newfile, nflen, nametemplate, "mutt");
+ mutt_adv_mktemp (newfile);
+ return 0;
+ }
+
+ /* Next, attempt to determine if the oldfile already matches nametemplate */
+ /* Nametemplate is of the form pre%spost, only replace pre or post if
+ * they don't already match the oldfilename */
+ /* Test pre */
+
+ if ((s = strrchr (nametemplate, '%')) != NULL)
+ {
+ newfile[len] = '\0';
+
+ z = s - nametemplate;
+
+ for (i = 0; i < z && i < nflen; i++)
+ {
+ if (oldfile[i] != nametemplate[i])
+ {
+ lmatch=FALSE;
+ break;
+ }
+ }
+
+ if (!lmatch)
+ {
+ match = FALSE;
+ i = nflen - len;
+ if (i > z)
+ i = z;
+ strfcpy (newfile + len, nametemplate, i);
+ }
+
+ strfcpy (newfile + strlen (newfile),
+ oldfile, nflen - strlen (newfile));
+
+ dprint (1, (debugfile,"template: %s, oldfile: %s, newfile: %s\n",
+ nametemplate, oldfile, newfile));
+
+ /* test post */
+ lmatch = TRUE;
+
+ for (z += 2, i = strlen (oldfile) - 1, j = strlen (nametemplate) - 1;
+ i && j > z; i--, j--)
+ if (oldfile[i] != nametemplate[j])
+ {
+ lmatch = FALSE;
+ break;
+ }
+
+ if (!lmatch)
+ {
+ match = FALSE;
+ strfcpy (newfile + strlen (newfile),
+ nametemplate + z, nflen - strlen (newfile));
+ }
+
+ if (match)
+ return 0;
+
+ return 1;
+ }
+ else
+ {
+ /* no %s in nametemplate, graft unto path of oldfile */
+ strfcpy (newfile, nametemplate, nflen);
+ return 1;
+ }
+}
+
+/* For nametemplate support, we may need to rename a file.
+ * If rfc1524_expand_command() is used on a recv'd message, then
+ * the filename doesn't exist yet, but if its used while sending a message,
+ * then we need to rename the existing file.
+ *
+ * This function returns 0 on successful move, 1 on old file doesn't exist,
+ * 2 on new file already exists, and 3 on other failure.
+ */
+int mutt_rename_file (char *oldfile, char *newfile)
+{
+ FILE *ofp, *nfp;
+
+ if (access (oldfile, F_OK) != 0)
+ return 1;
+ if (access (newfile, F_OK) == 0)
+ return 2;
+ if ((ofp = fopen (oldfile,"r")) == NULL)
+ return 3;
+ if ((nfp = safe_fopen (newfile,"w")) == NULL)
+ {
+ fclose(ofp);
+ return 3;
+ }
+ mutt_copy_stream (ofp,nfp);
+ fclose (nfp);
+ fclose (ofp);
+ mutt_unlink (oldfile);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _RFC1524_H
+#define _RFC1524_H
+
+typedef struct rfc1524_mailcap_entry {
+/* char *contenttype; */ /* we don't need this, as we search for it */
+ char *command;
+ char *testcommand;
+ char *composecommand;
+ char *composetypecommand;
+ char *editcommand;
+ char *printcommand;
+ char *nametemplate;
+ char *convert;
+/* char *description; */ /* we don't need this */
+ unsigned int needsterminal : 1; /* endwin() and system */
+ unsigned int copiousoutput : 1; /* needs pager, basically */
+} rfc1524_entry;
+
+rfc1524_entry *rfc1524_new_entry ();
+void rfc1524_free_entry (rfc1524_entry **);
+int rfc1524_expand_command (BODY *, char *, char *, char *, int);
+int rfc1524_expand_filename (char *, char *, char *, size_t);
+int rfc1524_mailcap_lookup (BODY *, char *, rfc1524_entry *, int);
+void mutt_adv_mktemp (char *);
+int mutt_rename_file (char *, char *);
+
+#endif /* _RFC1524_H */
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mime.h"
+#include "rfc2047.h"
+
+#include <ctype.h>
+#include <string.h>
+
+typedef void encode_t (char *, size_t, const unsigned char *);
+
+extern char MimeSpecials[];
+extern char B64Chars[];
+
+static void q_encode_string (char *d, size_t dlen, const unsigned char *s)
+{
+ char charset[SHORT_STRING];
+ size_t cslen, wordlen;
+ char *wptr = d;
+
+ snprintf (charset, sizeof (charset), "=?%s?Q?",
+ strcasecmp ("us-ascii", charset) == 0 ? "unknown-8bit" : Charset);
+ cslen = strlen (charset);
+
+ strcpy (wptr, charset);
+ wptr += cslen;
+ wordlen = cslen;
+ dlen -= cslen;
+
+ dlen -= 3; /* save room for the word terminator */
+
+ while (*s && dlen > 0)
+ {
+ if (wordlen >= 72)
+ {
+ if (dlen < 4 + cslen)
+ break;
+
+ strcpy (wptr, "?=\n ");
+ wptr += 4;
+ dlen -= 4;
+ strcpy (wptr, charset);
+ wptr += cslen;
+ wordlen = cslen;
+ dlen -= cslen;
+ }
+
+ if (*s == ' ')
+ {
+ *wptr++ = '_';
+ wordlen++;
+ dlen--;
+ }
+ else if ((*s & 0x80) || *s == '\t' || strchr (MimeSpecials, *s))
+ {
+ if (wordlen >= 70)
+ {
+ if (dlen < 4 + cslen)
+ break;
+
+ strcpy (wptr, "?=\n ");
+ wptr += 4;
+ dlen -= 4;
+
+ strcpy (wptr, charset);
+ wptr += cslen;
+ wordlen = cslen;
+ dlen -= cslen;
+ }
+
+ if (dlen < 3)
+ break;
+ sprintf (wptr, "=%02X", *s);
+ wptr += 3;
+ wordlen += 3;
+ dlen -= 3;
+ }
+ else
+ {
+ *wptr++ = *s;
+ wordlen++;
+ dlen--;
+ }
+ s++;
+ }
+
+ strcpy (wptr, "?=");
+}
+
+static void b_encode_string (char *d, size_t dlen, const unsigned char *s)
+{
+ char charset[SHORT_STRING];
+ char *wptr = d;
+ int cslen;
+ int wordlen;
+
+ snprintf (charset, sizeof (charset), "=?%s?B?", Charset);
+ cslen = strlen (charset);
+ strcpy (wptr, charset);
+ wptr += cslen;
+ wordlen = cslen;
+ dlen -= cslen;
+
+ dlen -= 3; /* save room for the word terminator */
+
+ while (*s && dlen >= 4)
+ {
+ if (wordlen >= 71)
+ {
+ if (dlen < 4 + cslen)
+ break;
+
+ strcpy (wptr, "?=\n ");
+ wptr += 4;
+ dlen -= 4;
+
+ strcpy (wptr, charset);
+ wptr += cslen;
+ wordlen = cslen;
+ dlen -= cslen;
+ }
+
+ *wptr++ = B64Chars[ (*s >> 2) & 0x3f ];
+ *wptr++ = B64Chars[ ((*s & 0x3) << 4) | ((*(s+1) >> 4) & 0xf) ];
+ s++;
+ if (*s)
+ {
+ *wptr++ = B64Chars[ ((*s & 0xf) << 2) | ((*(s+1) >> 6) & 0x3) ];
+ s++;
+ if (*s)
+ {
+ *wptr++ = B64Chars[ *s & 0x3f ];
+ s++;
+ }
+ else
+ *wptr++ = '=';
+ }
+ else
+ {
+ *wptr++ = '=';
+ *wptr++ = '=';
+ }
+
+ wordlen += 4;
+ dlen -= 4;
+ }
+
+ strcpy (wptr, "?=");
+}
+
+void rfc2047_encode_string (char *d, size_t dlen, const unsigned char *s)
+{
+ int count = 0;
+ int len;
+ const unsigned char *p = s;
+ encode_t *encoder;
+
+ /* First check to see if there are any 8-bit characters */
+ for (; *p; p++)
+ {
+ if (*p & 0x80)
+ count++;
+ else if (*p == '=' && *p == '?')
+ {
+ count += 2;
+ p++;
+ }
+ }
+ if (!count)
+ {
+ strfcpy (d, (const char *)s, dlen);
+ return;
+ }
+
+ if (strcasecmp("us-ascii", Charset) == 0 ||
+ strncasecmp("iso-8859", Charset, 8) == 0)
+ encoder = q_encode_string;
+ else
+ {
+ /* figure out which encoding generates the most compact representation */
+ len = strlen ((char *) s);
+ if ((count * 2) + len <= (4 * len) / 3)
+ encoder = q_encode_string;
+ else
+ encoder = b_encode_string;
+ }
+
+ /* Hack to pull the Re: and Fwd: out of the encoded word for better
+ handling by agents which do not support RFC2047. */
+ if (!strncasecmp ("re: ", (char *) s, 4))
+ {
+ strncpy (d, (char *) s, 4);
+ d += 4;
+ dlen -= 4;
+ s += 4;
+ }
+ else if (!strncasecmp ("fwd: ", (char *) s, 5))
+ {
+ strncpy (d, (char *) s, 5);
+ d += 5;
+ dlen -= 5;
+ s += 5;
+ }
+
+ (*encoder) (d, dlen, s);
+}
+
+void rfc2047_encode_adrlist (ADDRESS *addr)
+{
+ ADDRESS *ptr = addr;
+ char buffer[STRING];
+
+ while (ptr)
+ {
+ if (ptr->personal)
+ {
+ rfc2047_encode_string (buffer, sizeof (buffer), (const unsigned char *)ptr->personal);
+ safe_free ((void **) &ptr->personal);
+ ptr->personal = safe_strdup (buffer);
+ }
+ ptr = ptr->next;
+ }
+}
+
+static int rfc2047_decode_word (char *d, const char *s, size_t len)
+{
+ char *p = safe_strdup (s);
+ char *pp = p;
+ char *pd = d;
+ int enc = 0, filter = 0, count = 0, c1, c2, c3, c4;
+
+ while ((pp = strtok (pp, "?")) != NULL)
+ {
+ count++;
+ switch (count)
+ {
+ case 2:
+ if (strcasecmp (pp, Charset) != 0)
+ filter = 1;
+ break;
+ case 3:
+ if (toupper (*pp) == 'Q')
+ enc = ENCQUOTEDPRINTABLE;
+ else if (toupper (*pp) == 'B')
+ enc = ENCBASE64;
+ else
+ return (-1);
+ break;
+ case 4:
+ if (enc == ENCQUOTEDPRINTABLE)
+ {
+ while (*pp && len > 0)
+ {
+ if (*pp == '_')
+ {
+ *pd++ = ' ';
+ len--;
+ }
+ else if (*pp == '=')
+ {
+ *pd++ = (hexval(pp[1]) << 4) | hexval(pp[2]);
+ len--;
+ pp += 2;
+ }
+ else
+ {
+ *pd++ = *pp;
+ len--;
+ }
+ pp++;
+ }
+ *pd = 0;
+ }
+ else if (enc == ENCBASE64)
+ {
+ while (*pp && len > 0)
+ {
+ c1 = Index_64[(int) pp[0]];
+ c2 = Index_64[(int) pp[1]];
+ *pd++ = (c1 << 2) | ((c2 >> 4) & 0x3);
+ if (--len == 0) break;
+
+ if (pp[2] == '=') break;
+
+ c3 = Index_64[(int) pp[2]];
+ *pd++ = ((c2 & 0xf) << 4) | ((c3 >> 2) & 0xf);
+ if (--len == 0)
+ break;
+
+ if (pp[3] == '=')
+ break;
+
+ c4 = Index_64[(int) pp[3]];
+ *pd++ = ((c3 & 0x3) << 6) | c4;
+ if (--len == 0)
+ break;
+
+ pp += 4;
+ }
+ *pd = 0;
+ }
+ break;
+ }
+ pp = 0;
+ }
+ safe_free ((void **) &p);
+ if (filter)
+ {
+ pd = d;
+ while (*pd)
+ {
+ if (!IsPrint ((unsigned char) *pd))
+ *pd = '?';
+ pd++;
+ }
+ }
+ return (0);
+}
+
+/* try to decode anything that looks like a valid RFC2047 encoded
+ * header field, ignoring RFC822 parsing rules
+ */
+void rfc2047_decode (char *d, const char *s, size_t dlen)
+{
+ const char *p, *q;
+ size_t n;
+ int found_encoded = 0;
+
+ dlen--; /* save room for the terminal nul */
+
+ while (*s && dlen > 0)
+ {
+ if ((p = strstr (s, "=?")) == NULL ||
+ (q = strchr (p + 2, '?')) == NULL ||
+ (q = strchr (q + 1, '?')) == NULL ||
+ (q = strstr (q + 1, "?=")) == NULL)
+ {
+ /* no encoded words */
+ if (d != s)
+ strfcpy (d, s, dlen + 1);
+ return;
+ }
+
+ if (p != s)
+ {
+ n = (size_t) (p - s);
+ /* ignore spaces between encoded words */
+ if (!found_encoded || strspn (s, " \t\r\n") != n)
+ {
+ if (n > dlen)
+ n = dlen;
+ if (d != s)
+ memcpy (d, s, n);
+ d += n;
+ dlen -= n;
+ }
+ }
+
+ rfc2047_decode_word (d, p, dlen);
+ found_encoded = 1;
+ s = q + 2;
+ n = strlen (d);
+ dlen -= n;
+ d += n;
+ }
+ *d = 0;
+}
+
+void rfc2047_decode_adrlist (ADDRESS *a)
+{
+ while (a)
+ {
+ if (a->personal && strstr (a->personal, "=?") != NULL)
+ rfc2047_decode (a->personal, a->personal, strlen (a->personal) + 1);
+ a = a->next;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+void rfc2047_encode_string (char *, size_t, const unsigned char *);
+void rfc2047_encode_adrlist (ADDRESS *);
+
+void rfc2047_decode (char *, const char *, size_t);
+void rfc2047_decode_adrlist (ADDRESS *);
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#ifndef TESTING
+#include "mutt.h"
+#else
+#define safe_strdup strdup
+#define safe_malloc malloc
+#define SKIPWS(x) while(isspace(*x))x++
+#define FREE(x) safe_free(x)
+#define ISSPACE isspace
+#define strfcpy(a,b,c) {if (c) {strncpy(a,b,c);a[c-1]=0;}}
+#include "rfc822.h"
+#endif
+
+const char RFC822Specials[] = "@.,:;<>[]\\\"()";
+#define is_special(x) strchr(RFC822Specials,x)
+
+int RFC822Error = 0;
+
+/* these must defined in the same order as the numerated errors given in rfc822.h */
+const char *RFC822Errors[] = {
+ "out of memory",
+ "mismatched parenthesis",
+ "mismatched quotes",
+ "bad route in <>",
+ "bad address in <>",
+ "bad address spec"
+};
+
+void rfc822_dequote_comment (char *s)
+{
+ char *w = s;
+
+ for (; *s; s++)
+ {
+ if (*s == '\\')
+ {
+ if (!*++s)
+ break; /* error? */
+ *w++ = *s;
+ }
+ else if (*s != '\"')
+ {
+ if (w != s)
+ *w = *s;
+ w++;
+ }
+ }
+ *w = 0;
+}
+
+void rfc822_free_address (ADDRESS **p)
+{
+ ADDRESS *t;
+
+ while (*p)
+ {
+ t = *p;
+ *p = (*p)->next;
+#ifdef EXACT_ADDRESS
+ FREE (&t->val);
+#endif
+ FREE (&t->personal);
+ FREE (&t->mailbox);
+ FREE (&t);
+ }
+}
+
+static const char *
+parse_comment (const char *s,
+ char *comment, size_t *commentlen, size_t commentmax)
+{
+ int level = 1;
+
+ while (*s && level)
+ {
+ if (*s == '(')
+ level++;
+ else if (*s == ')')
+ {
+ if (--level == 0)
+ {
+ s++;
+ break;
+ }
+ }
+ else if (*s == '\\')
+ {
+ if (!*++s)
+ break;
+ }
+ if (*commentlen < commentmax)
+ comment[(*commentlen)++] = *s;
+ s++;
+ }
+ if (level)
+ {
+ RFC822Error = ERR_MISMATCH_PAREN;
+ return NULL;
+ }
+ return s;
+}
+
+static const char *
+parse_quote (const char *s, char *token, size_t *tokenlen, size_t tokenmax)
+{
+ if (*tokenlen < tokenmax)
+ token[(*tokenlen)++] = '"';
+ while (*s)
+ {
+ if (*tokenlen < tokenmax)
+ token[(*tokenlen)++] = *s;
+ if (*s == '"')
+ return (s + 1);
+ if (*s == '\\')
+ {
+ if (!*++s)
+ break;
+ }
+ s++;
+ }
+ RFC822Error = ERR_MISMATCH_QUOTE;
+ return NULL;
+}
+
+static const char *
+next_token (const char *s, char *token, size_t *tokenlen, size_t tokenmax)
+{
+ if (*s == '(')
+ return (parse_comment (s + 1, token, tokenlen, tokenmax));
+ if (*s == '"')
+ return (parse_quote (s + 1, token, tokenlen, tokenmax));
+ if (is_special (*s))
+ {
+ if (*tokenlen < tokenmax)
+ token[(*tokenlen)++] = *s;
+ return (s + 1);
+ }
+ while (*s)
+ {
+ if (ISSPACE (*s) || is_special (*s))
+ break;
+ if (*tokenlen < tokenmax)
+ token[(*tokenlen)++] = *s;
+ s++;
+ }
+ return s;
+}
+
+static const char *
+parse_mailboxdomain (const char *s, const char *nonspecial,
+ char *mailbox, size_t *mailboxlen, size_t mailboxmax,
+ char *comment, size_t *commentlen, size_t commentmax)
+{
+ const char *ps;
+
+ while (*s)
+ {
+ SKIPWS (s);
+ if (strchr (nonspecial, *s) == NULL && is_special (*s))
+ return s;
+
+ if (*s == '(')
+ {
+ if (*commentlen && *commentlen < commentmax)
+ comment[(*commentlen)++] = ' ';
+ ps = next_token (s, comment, commentlen, commentmax);
+ }
+ else
+ ps = next_token (s, mailbox, mailboxlen, mailboxmax);
+ if (!ps)
+ return NULL;
+ s = ps;
+ }
+
+ return s;
+}
+
+static const char *
+parse_address (const char *s,
+ char *comment, size_t *commentlen, size_t commentmax,
+ ADDRESS *addr)
+{
+ char token[128];
+ size_t tokenlen = 0;
+
+ s = parse_mailboxdomain (s, ".\"(\\",
+ token, &tokenlen, sizeof (token) - 1,
+ comment, commentlen, commentmax);
+ if (!s)
+ return NULL;
+
+ if (*s == '@')
+ {
+ if (tokenlen < sizeof (token) - 1)
+ token[tokenlen++] = '@';
+ s = parse_mailboxdomain (s + 1, ".([]\\",
+ token, &tokenlen, sizeof (token) - 1,
+ comment, commentlen, commentmax);
+ if (!s)
+ return NULL;
+ }
+
+ token[tokenlen] = 0;
+ addr->mailbox = safe_strdup (token);
+
+ if (*commentlen && !addr->personal)
+ {
+ comment[*commentlen] = 0;
+ addr->personal = safe_strdup (comment);
+ }
+
+ return s;
+}
+
+static const char *
+parse_route_addr (const char *s,
+ char *comment, size_t *commentlen, size_t commentmax,
+ ADDRESS *addr)
+{
+ SKIPWS (s);
+
+ /* find the end of the route */
+ if (*s == '@')
+ {
+ char token[128];
+ size_t tokenlen = 0;
+
+ while (s && *s == '@')
+ s = parse_mailboxdomain (s + 1, ".[](\\", token,
+ &tokenlen, sizeof (token) - 1,
+ comment, commentlen, commentmax);
+ if (!s || *s != ':')
+ {
+ RFC822Error = ERR_BAD_ROUTE;
+ return NULL; /* invalid route */
+ }
+ s++;
+ }
+
+ if ((s = parse_address (s, comment, commentlen, commentmax, addr)) == NULL)
+ return NULL;
+
+ if (*s != '>' || !addr->mailbox)
+ {
+ RFC822Error = ERR_BAD_ROUTE_ADDR;
+ return NULL;
+ }
+
+ s++;
+ return s;
+}
+
+static const char *
+parse_addr_spec (const char *s,
+ char *comment, size_t *commentlen, size_t commentmax,
+ ADDRESS *addr)
+{
+ s = parse_address (s, comment, commentlen, commentmax, addr);
+ if (s && *s && *s != ',' && *s != ';')
+ {
+ RFC822Error = ERR_BAD_ADDR_SPEC;
+ return NULL;
+ }
+ return s;
+}
+
+static void
+add_addrspec (ADDRESS **top, ADDRESS **last, const char *phrase,
+ char *comment, size_t *commentlen, size_t commentmax)
+{
+ ADDRESS *cur = rfc822_new_address ();
+
+ if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL)
+ return;
+
+ if (*last)
+ (*last)->next = cur;
+ else
+ *top = cur;
+ *last = cur;
+}
+
+ADDRESS *rfc822_parse_adrlist (ADDRESS *top, const char *s)
+{
+ const char *begin, *ps;
+ char comment[128], phrase[128];
+ size_t phraselen = 0, commentlen = 0;
+ ADDRESS *cur, *last = NULL;
+
+ RFC822Error = 0;
+
+ last = top;
+ while (last && last->next)
+ last = last->next;
+
+ SKIPWS (s);
+ begin = s;
+ while (*s)
+ {
+ if (*s == ',')
+ {
+ if (phraselen)
+ {
+ phrase[phraselen] = 0;
+ add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
+ }
+ else if (commentlen && last && !last->personal)
+ {
+ comment[commentlen] = 0;
+ last->personal = safe_strdup (comment);
+ }
+
+#ifdef EXACT_ADDRESS
+ if (last)
+ last->val = mutt_substrdup (begin, s);
+#endif
+ commentlen = 0;
+ phraselen = 0;
+ s++;
+ begin = s;
+ SKIPWS (begin);
+ }
+ else if (*s == '(')
+ {
+ if (commentlen && commentlen < sizeof (comment) - 1)
+ comment[commentlen++] = ' ';
+ if ((ps = next_token (s, comment, &commentlen, sizeof (comment) - 1)) == NULL)
+ {
+ rfc822_free_address (&top);
+ return NULL;
+ }
+ s = ps;
+ }
+ else if (*s == ':')
+ {
+ cur = rfc822_new_address ();
+ phrase[phraselen] = 0;
+ cur->mailbox = safe_strdup (phrase);
+ cur->group = 1;
+
+ if (last)
+ last->next = cur;
+ else
+ top = cur;
+ last = cur;
+
+#ifdef EXACT_ADDRESS
+ last->val = mutt_substrdup (begin, s);
+#endif
+
+ phraselen = 0;
+ commentlen = 0;
+ s++;
+ begin = s;
+ SKIPWS (begin);
+ }
+ else if (*s == ';')
+ {
+ if (phraselen)
+ {
+ phrase[phraselen] = 0;
+ add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
+ }
+ else if (commentlen && !last->personal)
+ {
+ comment[commentlen] = 0;
+ last->personal = safe_strdup (comment);
+ }
+#ifdef EXACT_ADDRESS
+ if (last)
+ last->val = mutt_substrdup (begin, s);
+#endif
+
+ /* add group terminator */
+ cur = rfc822_new_address ();
+ if (last)
+ {
+ last->next = cur;
+ last = cur;
+ }
+
+ phraselen = 0;
+ commentlen = 0;
+ s++;
+ begin = s;
+ SKIPWS (begin);
+ }
+ else if (*s == '<')
+ {
+ phrase[phraselen] = 0;
+ cur = rfc822_new_address ();
+ if (phraselen)
+ {
+ if (cur->personal)
+ free (cur->personal);
+ /* if we get something like "Michael R. Elkins" remove the quotes */
+ rfc822_dequote_comment (phrase);
+ cur->personal = safe_strdup (phrase);
+ }
+ if ((ps = parse_route_addr (s + 1, comment, &commentlen, sizeof (comment) - 1, cur)) == NULL)
+ {
+ rfc822_free_address (&top);
+ rfc822_free_address (&cur);
+ return NULL;
+ }
+
+ if (last)
+ last->next = cur;
+ else
+ top = cur;
+ last = cur;
+
+ phraselen = 0;
+ commentlen = 0;
+ s = ps;
+ }
+ else
+ {
+ if (phraselen && phraselen < sizeof (phrase) - 1)
+ phrase[phraselen++] = ' ';
+ if ((ps = next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL)
+ {
+ rfc822_free_address (&top);
+ return NULL;
+ }
+ s = ps;
+ }
+ SKIPWS (s);
+ }
+
+ if (phraselen)
+ {
+ phrase[phraselen] = 0;
+ comment[commentlen] = 0;
+ add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
+ }
+ else if (commentlen && last && !last->personal)
+ {
+ comment[commentlen] = 0;
+ last->personal = safe_strdup (comment);
+ }
+#ifdef EXACT_ADDRESS
+ if (last)
+ last->val = mutt_substrdup (begin, s);
+#endif
+
+ return top;
+}
+
+void rfc822_qualify (ADDRESS *addr, const char *host)
+{
+ char *p;
+
+ for (; addr; addr = addr->next)
+ if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL)
+ {
+ if (!(p = malloc (strlen (addr->mailbox) + strlen (host) + 2)))
+ return;
+ sprintf (p, "%s@%s", addr->mailbox, host);
+ free (addr->mailbox);
+ addr->mailbox = p;
+ }
+}
+
+void
+rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
+{
+ if (strpbrk (value, specials))
+ {
+ char tmp[256], *pc = tmp;
+ size_t tmplen = sizeof (tmp) - 3;
+
+ *pc++ = '"';
+ for (; *value && tmplen; value++)
+ {
+ if (*value == '\\' || *value == '"')
+ {
+ *pc++ = '\\';
+ tmplen--;
+ }
+ *pc++ = *value;
+ tmplen--;
+ }
+ *pc++ = '"';
+ *pc = 0;
+ strfcpy (buf, tmp, buflen);
+ }
+ else
+ strfcpy (buf, value, buflen);
+}
+
+void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS *addr)
+{
+ size_t len;
+ char *pbuf = buf;
+ char *pc;
+
+ if (!addr)
+ return;
+
+ buflen--; /* save room for the terminal nul */
+
+#ifdef EXACT_ADDRESS
+ if (addr->val)
+ {
+ if (!buflen)
+ goto done;
+ strfcpy (pbuf, addr->val, buflen);
+ len = strlen (pbuf);
+ pbuf += len;
+ buflen -= len;
+ if (addr->group)
+ {
+ if (!buflen)
+ goto done;
+ *pbuf++ = ':';
+ buflen--;
+ *pbuf = 0;
+ }
+ return;
+ }
+#endif
+
+ if (addr->personal)
+ {
+ if (strpbrk (addr->personal, RFC822Specials))
+ {
+ if (!buflen)
+ goto done;
+ *pbuf++ = '"';
+ buflen--;
+ for (pc = addr->personal; *pc && buflen > 0; pc++)
+ {
+ if (*pc == '"' || *pc == '\\')
+ {
+ if (!buflen)
+ goto done;
+ *pbuf++ = '\\';
+ buflen--;
+ }
+ if (!buflen)
+ goto done;
+ *pbuf++ = *pc;
+ buflen--;
+ }
+ if (!buflen)
+ goto done;
+ *pbuf++ = '"';
+ buflen--;
+ }
+ else
+ {
+ if (!buflen)
+ goto done;
+ strfcpy (pbuf, addr->personal, buflen);
+ len = strlen (pbuf);
+ pbuf += len;
+ buflen -= len;
+ }
+
+ if (!buflen)
+ goto done;
+ *pbuf++ = ' ';
+ buflen--;
+
+ if (!buflen)
+ goto done;
+ *pbuf++ = '<';
+ buflen--;
+ }
+
+ if (addr->mailbox)
+ {
+ if (!buflen)
+ goto done;
+ strfcpy (pbuf, addr->mailbox, buflen);
+ len = strlen (pbuf);
+ pbuf += len;
+ buflen -= len;
+
+ if (addr->personal)
+ {
+ if (!buflen)
+ goto done;
+ *pbuf++ = '>';
+ buflen--;
+ }
+
+ if (addr->group)
+ {
+ if (!buflen)
+ goto done;
+ *pbuf++ = ':';
+ buflen--;
+ if (!buflen)
+ goto done;
+ *pbuf++ = ' ';
+ buflen--;
+ }
+ }
+ else
+ {
+ if (!buflen)
+ goto done;
+ *pbuf++ = ';';
+ buflen--;
+ }
+done:
+ /* no need to check for length here since we already save space at the
+ beginning of this routine */
+ *pbuf = 0;
+}
+
+/* note: it is assumed that `buf' is nul terminated! */
+void rfc822_write_address (char *buf, size_t buflen, ADDRESS *addr)
+{
+ char *pbuf = buf;
+ size_t len = strlen (buf);
+
+ buflen--; /* save room for the terminal nul */
+
+ if (len > 0)
+ {
+ if (len > buflen)
+ return; /* safety check for bogus arguments */
+
+ pbuf += len;
+ buflen -= len;
+ if (!buflen)
+ goto done;
+ *pbuf++ = ',';
+ buflen--;
+ if (!buflen)
+ goto done;
+ *pbuf++ = ' ';
+ buflen--;
+ }
+
+ for (; addr && buflen > 0; addr = addr->next)
+ {
+ /* use buflen+1 here because we already saved space for the trailing
+ nul char, and the subroutine can make use of it */
+ rfc822_write_address_single (pbuf, buflen + 1, addr);
+
+ /* this should be safe since we always have at least 1 char passed into
+ the above call, which means `pbuf' should always be nul terminated */
+ len = strlen (pbuf);
+ pbuf += len;
+ buflen -= len;
+ if (addr->next && !addr->group)
+ {
+ if (!buflen)
+ goto done;
+ *pbuf++ = ',';
+ buflen--;
+ if (!buflen)
+ goto done;
+ *pbuf++ = ' ';
+ buflen--;
+ }
+ }
+done:
+ *pbuf = 0;
+}
+
+/* this should be rfc822_cpy_adr */
+ADDRESS *rfc822_cpy_adr_real (ADDRESS *addr)
+{
+ ADDRESS *p = rfc822_new_address ();
+
+#ifdef EXACT_ADDRESS
+ p->val = safe_strdup (addr->val);
+#endif
+ p->personal = safe_strdup (addr->personal);
+ p->mailbox = safe_strdup (addr->mailbox);
+ p->group = addr->group;
+ return p;
+}
+
+/* this should be rfc822_cpy_adrlist */
+ADDRESS *rfc822_cpy_adr (ADDRESS *addr)
+{
+ ADDRESS *top = NULL, *last = NULL;
+
+ for (; addr; addr = addr->next)
+ {
+ if (last)
+ {
+ last->next = rfc822_cpy_adr_real (addr);
+ last = last->next;
+ }
+ else
+ top = last = rfc822_cpy_adr_real (addr);
+ }
+ return top;
+}
+
+/* append list 'b' to list 'a' and return the last element in the new list */
+ADDRESS *rfc822_append (ADDRESS **a, ADDRESS *b)
+{
+ ADDRESS *tmp = *a;
+
+ while (tmp && tmp->next)
+ tmp = tmp->next;
+ if (!b)
+ return tmp;
+ if (tmp)
+ tmp->next = rfc822_cpy_adr (b);
+ else
+ tmp = *a = rfc822_cpy_adr (b);
+ while (tmp && tmp->next)
+ tmp = tmp->next;
+ return tmp;
+}
+
+#ifdef TESTING
+int safe_free (void **p)
+{
+ free(*p);
+ *p = 0;
+}
+
+int main (int argc, char **argv)
+{
+ ADDRESS *list;
+ char buf[256];
+ char *str = "aaaaaaaaaaaaaa <bbbbbbbbbbbbbbbb>, ccccccc <dddddddddddddddd>,\n\
+ eeeeeeeeee <ffffffffffffffff>, ggggggggggg <hhhhhhhhhhhhhhhhhhh>,\n\
+ iiiiiiiiiiiiiiii <jjjjjjjjjjjjjjjjjjjj>,\n\
+ kkkkkkkkkkkkkk <lllllllllllllllll>,\n\
+ mmmmmmmmmmmmm <nnnnnnnnnnnnnnnnnn>, ooooooooooo <pppppppppppppp>,\n\
+ qqqqqqqqqqqqq <rrrrrrrrrrrrrrrr>\n";
+
+ list = rfc822_parse_adrlist (NULL, str);
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), list);
+ rfc822_free_address (&list);
+ puts (buf);
+ exit (0);
+}
+#endif
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef rfc822_h
+#define rfc822_h
+
+#include "config.h"
+
+/* possible values for RFC822Error */
+enum
+{
+ ERR_MEMORY = 1,
+ ERR_MISMATCH_PAREN,
+ ERR_MISMATCH_QUOTE,
+ ERR_BAD_ROUTE,
+ ERR_BAD_ROUTE_ADDR,
+ ERR_BAD_ADDR_SPEC
+};
+
+typedef struct address_t
+{
+#ifdef EXACT_ADDRESS
+ char *val; /* value of address as parsed */
+#endif
+ char *personal; /* real name of address */
+ char *mailbox; /* mailbox and host address */
+ int group; /* group mailbox? */
+ struct address_t *next;
+}
+ADDRESS;
+
+void rfc822_free_address (ADDRESS **);
+void rfc822_qualify (ADDRESS *, const char *);
+ADDRESS *rfc822_parse_adrlist (ADDRESS *, const char *s);
+ADDRESS *rfc822_cpy_adr (ADDRESS *addr);
+ADDRESS *rfc822_cpy_adr_real (ADDRESS *addr);
+ADDRESS *rfc822_append (ADDRESS **a, ADDRESS *b);
+void rfc822_write_address (char *, size_t, ADDRESS *);
+void rfc822_write_list (char *, size_t, ADDRESS *);
+void rfc822_free_address (ADDRESS **addr);
+void rfc822_cat (char *, size_t, const char *, const char *);
+
+extern int RFC822Error;
+extern const char *RFC822Errors[];
+
+#define rfc822_error(x) RFC822Errors[x]
+#define rfc822_new_address() calloc(1,sizeof(ADDRESS))
+
+#endif /* rfc822_h */
--- /dev/null
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+# Generated automatically from Makefile.in by configure.
+#
+# A simplified Makefile for use with Mutt.
+#
+# Michael Elkins <me@cs.hmc.edu>
+# 1/23/97
+#
+OBJS= hashrexp.o rx.o rxanal.o rxbasic.o rxbitset.o rxcset.o \
+ rxgnucomp.o rxhash.o rxnfa.o rxnode.o rxposix.o rxsimp.o \
+ rxspencer.o rxstr.o rxsuper.o rxunfa.o
+
+
+
+librx.a: $(OBJS)
+ ar rc librx.a $(OBJS)
+ -ranlib librx.a
+
+clean:
+ rm -f *.o librx.a *~
--- /dev/null
+#
+# A simplified Makefile for use with Mutt.
+#
+# Michael Elkins <me@cs.hmc.edu>
+# 1/23/97
+#
+OBJS= hashrexp.o rx.o rxanal.o rxbasic.o rxbitset.o rxcset.o \
+ rxgnucomp.o rxhash.o rxnfa.o rxnode.o rxposix.o rxsimp.o \
+ rxspencer.o rxstr.o rxsuper.o rxunfa.o
+
+VPATH=@srcdir@
+@SET_MAKE@
+
+librx.a: $(OBJS)
+ ar rc librx.a $(OBJS)
+ -ranlib librx.a
+
+clean:
+ rm -f *.o librx.a *~
--- /dev/null
+/* classes: h_files */
+
+#ifndef _RXH
+#define _RXH
+
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+#include <sys/types.h>
+#include "rxhash.h"
+#include "rxcset.h"
+
+\f
+
+struct rx_cache;
+struct rx_superset;
+struct rx;
+struct rx_se_list;
+
+\f
+
+/* Suppose that from some NFA state and next character, more than one
+ * path through side-effect edges is possible. In what order should
+ * the paths be tried? A function of type rx_se_list_order answers
+ * that question. It compares two lists of side effects, and says
+ * which list comes first.
+ */
+
+#ifdef __STDC__
+typedef int (*rx_se_list_order) (struct rx *,
+ struct rx_se_list *,
+ struct rx_se_list *);
+#else
+typedef int (*rx_se_list_order) ();
+#endif
+
+
+
+/* Struct RX holds an NFA and cache state for the corresponding super NFA.
+ */
+struct rx
+{
+ /* The compiler assigns a unique id to every pattern.
+ * Like sequence numbers in X, there is a subtle bug here
+ * if you use Rx in a system that runs for a long time.
+ * But, because of the way the caches work out, it is almost
+ * impossible to trigger the Rx version of this bug.
+ *
+ * The id is used to validate superstates found in a cache
+ * of superstates. It isn't sufficient to let a superstate
+ * point back to the rx for which it was compiled -- the caller
+ * may be re-using a `struct rx' in which case the superstate
+ * is not really valid. So instead, superstates are validated
+ * by checking the sequence number of the pattern for which
+ * they were built.
+ */
+ int rx_id;
+
+ /* This is memory mgt. state for superstates. This may be
+ * shared by more than one struct rx.
+ */
+ struct rx_cache * cache;
+
+ /* Every nfa defines the size of its own character set.
+ * Each superstate has an array of this size, with each element
+ * a `struct rx_inx'. So, don't make this number too large.
+ * In particular, don't make it 2^16.
+ */
+ int local_cset_size;
+
+ /* Lists of side effects as stored in the NFA are `hash consed'..meaning
+ * that lists with the same elements are ==. During compilation,
+ * this table facilitates hash-consing.
+ */
+ struct rx_hash se_list_memo;
+
+ /* Lists of NFA states are also hashed.
+ */
+ struct rx_hash set_list_memo;
+
+
+
+ /* The compiler and matcher must build a number of instruction frames.
+ * The format of these frames is fixed (c.f. struct rx_inx). The values
+ * of the instruction opcodes is not fixed.
+ *
+ * An enumerated type (enum rx_opcode) defines the set of instructions
+ * that the compiler or matcher might generate. When filling an instruction
+ * frame, the INX field is found by indexing this instruction table
+ * with an opcode:
+ */
+ void ** instruction_table;
+
+ /* The list of all states in an NFA.
+ * The NEXT field of NFA states links this list.
+ */
+ struct rx_nfa_state *nfa_states;
+ struct rx_nfa_state *start_nfa_states;
+ struct rx_superset * start_set;
+
+ /* This orders the search through super-nfa paths.
+ * See the comment near the typedef of rx_se_list_order.
+ */
+ rx_se_list_order se_list_cmp;
+
+ int next_nfa_id;
+};
+
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ * `re_match_2' returns information about at least this many registers
+ * the first time a `regs' structure is passed.
+ *
+ * Also, this is the greatest number of backreferenced subexpressions
+ * allowed in a pattern being matched without caller-supplied registers.
+ */
+#ifndef RE_NREGS
+#define RE_NREGS 30
+#endif
+
+
+#ifndef emacs
+#define CHARBITS 8
+#define CHAR_SET_SIZE (1 << CHARBITS)
+#define Sword 1
+#define SYNTAX(c) re_syntax_table[c]
+extern char re_syntax_table[CHAR_SET_SIZE];
+#endif
+
+
+/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we
+ * use `alloca' instead of `malloc' for the backtracking stack.
+ *
+ * Emacs will die miserably if we don't do this.
+ */
+
+#ifdef REGEX_MALLOC
+#define REGEX_ALLOCATE malloc
+#else /* not REGEX_MALLOC */
+#define REGEX_ALLOCATE alloca
+#endif /* not REGEX_MALLOC */
+
+#undef MAX
+#undef MIN
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+extern void * rx_id_instruction_table[];
+extern struct rx_cache * rx_default_cache;
+
+\f
+#ifdef __STDC__
+
+#else /* STDC */
+
+#endif /* STDC */
+
+
+#endif /* _RXH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+#include "rxall.h"
+#include "rxnode.h"
+#include "rxhash.h"
+
+#ifdef __STDC__
+static int
+rexp_node_equal (void * va, void * vb)
+#else
+static int
+rexp_node_equal (va, vb)
+ void * va;
+ void * vb;
+#endif
+{
+ struct rexp_node * a;
+ struct rexp_node * b;
+
+ a = (struct rexp_node *)va;
+ b = (struct rexp_node *)vb;
+
+ return ( (va == vb)
+ || ( (a->type == b->type)
+ && (a->params.intval == b->params.intval)
+ && (a->params.intval2 == b->params.intval2)
+ && rx_bitset_is_equal (a->params.cset_size, a->params.cset, b->params.cset)
+ && rexp_node_equal (a->params.pair.left, b->params.pair.left)
+ && rexp_node_equal (a->params.pair.right, b->params.pair.right)));
+}
+
+
--- /dev/null
+/* classes: h_files */
+
+#ifndef INST_RXPOSIXH
+#define INST_RXPOSIXH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+struct rx_posix_regex
+{
+ struct rexp_node * pattern;
+ struct rexp_node ** subexps;
+ size_t re_nsub;
+ unsigned char * translate;
+ unsigned int newline_anchor:1;/* If true, an anchor at a newline matches.*/
+ unsigned int no_sub:1; /* If set, don't return register offsets. */
+ unsigned int is_anchored:1;
+ unsigned int is_nullable:1;
+ unsigned char fastmap[256];
+ void * owner_data;
+};
+
+typedef struct rx_posix_regex regex_t;
+
+/* Type for byte offsets within the string. POSIX mandates this. */
+typedef int regoff_t;
+
+typedef struct rx_registers
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+ regoff_t final_tag; /* data from the cut operator (only pmatch[0]) */
+} regmatch_t;
+
+\f
+
+/* If any error codes are removed, changed, or added, update the
+ * `rx_error_msg' table.
+ */
+#define REG_NOERROR 0 /* Success. */
+#define REG_NOMATCH 1 /* Didn't find a match (for regexec). */
+
+/* POSIX regcomp return error codes.
+ * (In the order listed in the standard.)
+ */
+#define REG_BADPAT 2 /* Invalid pattern. */
+#define REG_ECOLLATE 3 /* Not implemented. */
+#define REG_ECTYPE 4 /* Invalid character class name. */
+#define REG_EESCAPE 5 /* Trailing backslash. */
+#define REG_ESUBREG 6 /* Invalid back reference. */
+#define REG_EBRACK 7 /* Unmatched left bracket. */
+#define REG_EPAREN 8 /* Parenthesis imbalance. */
+#define REG_EBRACE 9 /* Unmatched \{. */
+#define REG_BADBR 10 /* Invalid contents of \{\}. */
+#define REG_ERANGE 11 /* Invalid range end. */
+#define REG_ESPACE 12 /* Ran out of memory. */
+#define REG_BADRPT 13 /* No preceding re for repetition op. */
+
+/* Error codes we've added.
+ */
+#define REG_EEND 14 /* Premature end. */
+#define REG_ESIZE 15 /* Compiled pattern bigger than 2^16 bytes. */
+#define REG_ERPAREN 16 /* Unmatched ) or \); not returned from regcomp. */
+
+\f
+/*
+ * POSIX `cflags' bits (i.e., information for `regcomp').
+ */
+
+/* If this bit is set, then use extended regular expression syntax.
+ * If not set, then use basic regular expression syntax.
+ */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ * If not set, then case is significant.
+ */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ * characters in the string.
+ * If not set, then anchors do match at newlines.
+ */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+ * If not set, then returns differ between not matching and errors.
+ */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/*
+ * POSIX `eflags' bits (i.e., information for regexec).
+ */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ * the beginning of the string (presumably because it's not the
+ * beginning of a line).
+ * If not set, then the beginning-of-line operator does match the
+ * beginning of the string.
+ */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line.
+ */
+#define REG_NOTEOL (REG_NOTBOL << 1)
+
+/* For regnexec only. Allocate register storage and return that. */
+#define REG_ALLOC_REGS (REG_NOTEOL << 1)
+
+\f
+#ifdef __STDC__
+extern int regncomp (regex_t * preg,
+ const char * pattern, int len,
+ int cflags);
+extern int regcomp (regex_t * preg, const char * pattern, int cflags);
+extern size_t regerror (int errcode,
+ const regex_t *preg,
+ char *errbuf, size_t errbuf_size);
+extern int regnexec (const regex_t *preg,
+ const char *string, int len,
+ size_t nmatch, regmatch_t **pmatch,
+ int eflags);
+extern int regexec (const regex_t *preg,
+ const char *string,
+ size_t nmatch, regmatch_t pmatch[],
+ int eflags);
+extern void regfree (regex_t *preg);
+
+#else /* STDC */
+extern int regncomp ();
+extern int regcomp ();
+extern size_t regerror ();
+extern int regnexec ();
+extern int regexec ();
+extern void regfree ();
+
+#endif /* STDC */
+#endif /* INST_RXPOSIXH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+#include "rx.h"
+#include "rxall.h"
+#include "rxhash.h"
+#include "rxnfa.h"
+#include "rxsuper.h"
+
+\f
+
+const char rx_version_string[] = "GNU Rx version 1.5";
+
+\f
+#ifdef __STDC__
+struct rx *
+rx_make_rx (int cset_size)
+#else
+struct rx *
+rx_make_rx (cset_size)
+ int cset_size;
+#endif
+{
+ static int rx_id = 0;
+ struct rx * new_rx;
+ new_rx = (struct rx *)malloc (sizeof (*new_rx));
+ rx_bzero ((char *)new_rx, sizeof (*new_rx));
+ new_rx->rx_id = rx_id++;
+ new_rx->cache = rx_default_cache;
+ new_rx->local_cset_size = cset_size;
+ new_rx->instruction_table = rx_id_instruction_table;
+ new_rx->next_nfa_id = 0;
+ return new_rx;
+}
+
+#ifdef __STDC__
+void
+rx_free_rx (struct rx * rx)
+#else
+void
+rx_free_rx (rx)
+ struct rx * rx;
+#endif
+{
+ if (rx->start_set)
+ rx->start_set->starts_for = 0;
+ rx_free_nfa (rx);
+ free (rx);
+}
+
+
+#ifdef __STDC__
+void
+rx_bzero (char * mem, int size)
+#else
+void
+rx_bzero (mem, size)
+ char * mem;
+ int size;
+#endif
+{
+ while (size)
+ {
+ *mem = 0;
+ ++mem;
+ --size;
+ }
+}
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXH
+#define RXH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+#include "rxhash.h"
+
+
+\f
+
+extern const char rx_version_string[];
+
+\f
+#ifdef __STDC__
+extern struct rx * rx_make_rx (int cset_size);
+extern void rx_free_rx (struct rx * rx);
+extern void rx_bzero (char * mem, int size);
+
+#else /* STDC */
+extern struct rx * rx_make_rx ();
+extern void rx_free_rx ();
+extern void rx_bzero ();
+
+#endif /* STDC */
+
+
+
+
+
+#endif /* RXH */
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXALLH
+#define RXALLH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+#if 0
+#include <stdlib.h>
+#include "malloc.h"
+#endif
+\f
+#ifdef __STDC__
+
+#else /* STDC */
+
+#endif /* STDC */
+
+
+#endif /* RXALLH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+#include "rxall.h"
+#include "rxanal.h"
+#include "rxbitset.h"
+#include "rxsuper.h"
+
+\f
+
+
+#ifdef __STDC__
+int
+rx_posix_analyze_rexp (struct rexp_node *** subexps,
+ size_t * re_nsub,
+ struct rexp_node * node,
+ int id)
+#else
+int
+rx_posix_analyze_rexp (subexps, re_nsub, node, id)
+ struct rexp_node *** subexps;
+ size_t * re_nsub;
+ struct rexp_node * node;
+ int id;
+#endif
+{
+ if (node)
+ {
+ size_t this_subexp;
+ if (node->type == r_parens)
+ {
+ if (node->params.intval >= 0)
+ {
+ this_subexp = *re_nsub;
+ ++*re_nsub;
+ if (!*subexps)
+ *subexps = (struct rexp_node **)malloc (sizeof (struct rexp_node *) * *re_nsub);
+ else
+ *subexps = (struct rexp_node **)realloc (*subexps,
+ sizeof (struct rexp_node *) * *re_nsub);
+ }
+ }
+ if (node->params.pair.left)
+ id = rx_posix_analyze_rexp (subexps, re_nsub, node->params.pair.left, id);
+ if (node->params.pair.right)
+ id = rx_posix_analyze_rexp (subexps, re_nsub, node->params.pair.right, id);
+ switch (node->type)
+ {
+ case r_cset:
+ node->len = 1;
+ node->observed = 0;
+ break;
+ case r_string:
+ node->len = node->params.cstr.len;
+ node->observed = 0;
+ break;
+ case r_cut:
+ node->len = 0;
+ node->observed = 0;
+ break;
+ case r_concat:
+ case r_alternate:
+ {
+ int lob, rob;
+ int llen, rlen;
+ lob = (!node->params.pair.left ? 0 : node->params.pair.left->observed);
+ rob = (!node->params.pair.right ? 0 : node->params.pair.right->observed);
+ llen = (!node->params.pair.left ? 0 : node->params.pair.left->len);
+ rlen = (!node->params.pair.right ? 0 : node->params.pair.right->len);
+ node->len = ((llen >= 0) && (rlen >= 0)
+ ? ((node->type == r_concat)
+ ? llen + rlen
+ : ((llen == rlen) ? llen : -1))
+ : -1);
+ node->observed = lob || rob;
+ break;
+ }
+ case r_opt:
+ case r_star:
+ case r_plus:
+ node->len = -1;
+ node->observed = (node->params.pair.left
+ ? node->params.pair.left->observed
+ : 0);
+ break;
+
+ case r_interval:
+ node->len = -1;
+ node->observed = 1;
+ break;
+
+ case r_parens:
+ if (node->params.intval >= 0)
+ {
+ node->observed = 1;
+ (*subexps)[this_subexp] = node;
+ }
+ else
+ node->observed = (node->params.pair.left
+ ? node->params.pair.left->observed
+ : 0);
+ node->len = (node->params.pair.left
+ ? node->params.pair.left->len
+ : 0);
+ break;
+ case r_context:
+ switch (node->params.intval)
+ {
+ default:
+ node->observed = 1;
+ node->len = -1;
+ break;
+ case '^':
+ case '$':
+ case '=':
+ case '<':
+ case '>':
+ case 'b':
+ case 'B':
+ case '`':
+ case '\'':
+ node->observed = 1;
+ node->len = 0;
+ break;
+ }
+ break;
+ }
+ if (node->observed)
+ node->id = id++;
+ return id;
+ }
+ return id;
+}
+\f
+/* Returns 0 unless the pattern can match the empty string. */
+#ifdef __STDC__
+int
+rx_fill_in_fastmap (int cset_size, unsigned char * map, struct rexp_node * exp)
+#else
+int
+rx_fill_in_fastmap (cset_size, map, exp)
+ int cset_size;
+ unsigned char * map;
+ struct rexp_node * exp;
+#endif
+{
+ if (!exp)
+ {
+ can_match_empty:
+ {
+ int x;
+ for (x = 0; x < cset_size; ++x)
+ map[x] = 1;
+ }
+ return 1;
+ }
+
+ switch (exp->type)
+ {
+ case r_cset:
+ {
+ int x;
+ int most;
+
+ most = exp->params.cset_size;
+ for (x = 0; x < most; ++x)
+ if (RX_bitset_member (exp->params.cset, x))
+ map[x] = 1;
+ }
+ return 0;
+
+ case r_string:
+ if (exp->params.cstr.len)
+ {
+ map[exp->params.cstr.contents[0]] = 1;
+ return 0;
+ }
+ else
+ return 1;
+
+ case r_cut:
+ return 1;
+
+
+ case r_concat:
+ return rx_fill_in_fastmap (cset_size, map, exp->params.pair.left);
+
+ /* Why not the right branch? If the left branch
+ * can't be empty it doesn't matter. If it can, then
+ * the fastmap is already saturated, and again, the
+ * right branch doesn't matter.
+ */
+
+
+ case r_alternate:
+ return ( rx_fill_in_fastmap (cset_size, map, exp->params.pair.left)
+ | rx_fill_in_fastmap (cset_size, map, exp->params.pair.right));
+
+ case r_parens:
+ case r_plus:
+ return rx_fill_in_fastmap (cset_size, map, exp->params.pair.left);
+
+ case r_opt:
+ case r_star:
+ goto can_match_empty;
+ /* Why not the subtree? These operators already saturate
+ * the fastmap.
+ */
+
+ case r_interval:
+ if (exp->params.intval == 0)
+ goto can_match_empty;
+ else
+ return rx_fill_in_fastmap (cset_size, map, exp->params.pair.left);
+
+ case r_context:
+ goto can_match_empty;
+ }
+
+ /* this should never happen but gcc seems to like it */
+ return 0;
+
+}
+\f
+
+#ifdef __STDC__
+int
+rx_is_anchored_p (struct rexp_node * exp)
+#else
+int
+rx_is_anchored_p (exp)
+ struct rexp_node * exp;
+#endif
+{
+ if (!exp)
+ return 0;
+
+ switch (exp->type)
+ {
+ case r_opt:
+ case r_star:
+ case r_cset:
+ case r_string:
+ case r_cut:
+ return 0;
+
+ case r_parens:
+ case r_plus:
+ case r_concat:
+ return rx_is_anchored_p (exp->params.pair.left);
+
+ case r_alternate:
+ return ( rx_is_anchored_p (exp->params.pair.left)
+ && rx_is_anchored_p (exp->params.pair.right));
+
+
+ case r_interval:
+ if (exp->params.intval == 0)
+ return 0;
+ else
+ return rx_is_anchored_p (exp->params.pair.left);
+
+ case r_context:
+ return (exp->params.intval == '^');
+ }
+
+ /* this should never happen but gcc seems to like it */
+ return 0;
+}
+
+\f
+
+#ifdef __STDC__
+enum rx_answers
+rx_start_superstate (struct rx_classical_system * frame)
+#else
+enum rx_answers
+rx_start_superstate (frame)
+ struct rx_classical_system * frame;
+#endif
+{
+ struct rx_superset * start_contents;
+ struct rx_nfa_state_set * start_nfa_set;
+
+ if (frame->rx->start_set)
+ start_contents = frame->rx->start_set;
+ else
+ {
+ {
+ struct rx_possible_future * futures;
+ futures = rx_state_possible_futures (frame->rx, frame->rx->start_nfa_states);
+
+ if (!futures)
+ return rx_bogus;
+
+ if (futures->next)
+ return rx_start_state_with_too_many_futures;
+
+ start_nfa_set = futures->destset;
+ }
+
+ start_contents =
+ rx_superstate_eclosure_union (frame->rx,
+ rx_superset_cons (frame->rx, 0, 0),
+ start_nfa_set);
+
+ if (!start_contents)
+ return rx_bogus;
+
+ start_contents->starts_for = frame->rx;
+ frame->rx->start_set = start_contents;
+ }
+
+ if ( start_contents->superstate
+ && (start_contents->superstate->rx_id == frame->rx->rx_id))
+ {
+ if (frame->state)
+ {
+ rx_unlock_superstate (frame->rx, frame->state);
+ }
+ frame->state = start_contents->superstate;
+ /* The cached superstate may be in a semifree state.
+ * We need to lock it and preserve the invariant
+ * that a locked superstate is never semifree.
+ * So refresh it.
+ */
+ rx_refresh_this_superstate (frame->rx->cache, frame->state);
+ rx_lock_superstate (frame->rx, frame->state);
+ return rx_yes;
+ }
+ else
+ {
+ struct rx_superstate * state;
+
+ rx_protect_superset (frame->rx, start_contents);
+ state = rx_superstate (frame->rx, start_contents);
+ rx_release_superset (frame->rx, start_contents);
+ if (!state)
+ return rx_bogus;
+ if (frame->state)
+ {
+ rx_unlock_superstate (frame->rx, frame->state);
+ }
+ frame->state = state;
+ rx_lock_superstate (frame->rx, frame->state);
+ return rx_yes;
+ }
+}
+
+\f
+
+
+#ifdef __STDC__
+enum rx_answers
+rx_fit_p (struct rx_classical_system * frame, unsigned const char * burst, int len)
+#else
+enum rx_answers
+rx_fit_p (frame, burst, len)
+ struct rx_classical_system * frame;
+ unsigned const char * burst;
+ int len;
+#endif
+{
+ struct rx_inx * inx_table;
+ struct rx_inx * inx;
+
+ if (!frame->state)
+ return rx_bogus;
+
+ if (!len)
+ {
+ frame->final_tag = frame->state->contents->is_final;
+ return (frame->state->contents->is_final
+ ? rx_yes
+ : rx_no);
+ }
+
+ inx_table = frame->state->transitions;
+ rx_unlock_superstate (frame->rx, frame->state);
+
+ while (len--)
+ {
+ struct rx_inx * next_table;
+
+ inx = inx_table + *burst;
+ next_table = (struct rx_inx *)inx->data;
+ while (!next_table)
+ {
+ struct rx_superstate * state;
+ state = ((struct rx_superstate *)
+ ((char *)inx_table
+ - ((unsigned long)
+ ((struct rx_superstate *)0)->transitions)));
+
+ switch ((int)inx->inx)
+ {
+ case rx_backtrack:
+ /* RX_BACKTRACK means that we've reached the empty
+ * superstate, indicating that match can't succeed
+ * from this point.
+ */
+ frame->state = 0;
+ return rx_no;
+
+ case rx_cache_miss:
+ /* Because the superstate NFA is lazily constructed,
+ * and in fact may erode from underneath us, we sometimes
+ * have to construct the next instruction from the hard way.
+ * This invokes one step in the lazy-conversion.
+ */
+ inx =
+ rx_handle_cache_miss
+ (frame->rx, state, *burst, inx->data_2);
+
+ if (!inx)
+ {
+ frame->state = 0;
+ return rx_bogus;
+ }
+ next_table = (struct rx_inx *)inx->data;
+ continue;
+
+
+ /* No other instructions are legal here.
+ * (In particular, this function does not handle backtracking
+ * or the related instructions.)
+ */
+ default:
+ frame->state = 0;
+ return rx_bogus;
+ }
+ }
+ inx_table = next_table;
+ ++burst;
+ }
+
+ if (inx->data_2) /* indicates a final superstate */
+ {
+ frame->final_tag = (int)inx->data_2;
+ frame->state = ((struct rx_superstate *)
+ ((char *)inx_table
+ - ((unsigned long)
+ ((struct rx_superstate *)0)->transitions)));
+ rx_lock_superstate (frame->rx, frame->state);
+ return rx_yes;
+ }
+ frame->state = ((struct rx_superstate *)
+ ((char *)inx_table
+ - ((unsigned long)
+ ((struct rx_superstate *)0)->transitions)));
+ rx_lock_superstate (frame->rx, frame->state);
+ return rx_no;
+}
+
+\f
+
+
+#ifdef __STDC__
+enum rx_answers
+rx_advance (struct rx_classical_system * frame, unsigned const char * burst, int len)
+#else
+enum rx_answers
+rx_advance (frame, burst, len)
+ struct rx_classical_system * frame;
+ unsigned const char * burst;
+ int len;
+#endif
+{
+ struct rx_inx * inx_table;
+
+ if (!frame->state)
+ return rx_bogus;
+
+ if (!len)
+ return rx_yes;
+
+ inx_table = frame->state->transitions;
+ rx_unlock_superstate (frame->rx, frame->state);
+
+ while (len--)
+ {
+ struct rx_inx * inx;
+ struct rx_inx * next_table;
+
+ inx = inx_table + *burst;
+ next_table = (struct rx_inx *)inx->data;
+ while (!next_table)
+ {
+ struct rx_superstate * state;
+ state = ((struct rx_superstate *)
+ ((char *)inx_table
+ - ((unsigned long)
+ ((struct rx_superstate *)0)->transitions)));
+
+ switch ((int)inx->inx)
+ {
+ case rx_backtrack:
+ /* RX_BACKTRACK means that we've reached the empty
+ * superstate, indicating that match can't succeed
+ * from this point.
+ */
+ frame->state = 0;
+ return rx_no;
+
+ case rx_cache_miss:
+ /* Because the superstate NFA is lazily constructed,
+ * and in fact may erode from underneath us, we sometimes
+ * have to construct the next instruction from the hard way.
+ * This invokes one step in the lazy-conversion.
+ */
+ inx =
+ rx_handle_cache_miss
+ (frame->rx, state, *burst, inx->data_2);
+
+ if (!inx)
+ {
+ frame->state = 0;
+ return rx_bogus;
+ }
+ next_table = (struct rx_inx *)inx->data;
+ continue;
+
+
+ /* No other instructions are legal here.
+ * (In particular, this function does not handle backtracking
+ * or the related instructions.)
+ */
+ default:
+ frame->state = 0;
+ return rx_bogus;
+ }
+ }
+ inx_table = next_table;
+ ++burst;
+ }
+
+ frame->state = ((struct rx_superstate *)
+ ((char *)inx_table
+ - ((unsigned long)
+ ((struct rx_superstate *)0)->transitions)));
+ rx_lock_superstate (frame->rx, frame->state);
+ return rx_yes;
+}
+
+\f
+
+#ifdef __STDC__
+int
+rx_advance_to_final (struct rx_classical_system * frame, unsigned const char * burst, int len)
+#else
+int
+rx_advance_to_final (frame, burst, len)
+ struct rx_classical_system * frame;
+ unsigned const char * burst;
+ int len;
+#endif
+{
+ int initial_len;
+ struct rx_inx * inx_table;
+ struct rx_superstate * this_state;
+
+ if (!frame->state)
+ return 0;
+
+ if (!len)
+ {
+ frame->final_tag = frame->state->contents->is_final;
+ return 0;
+ }
+
+ inx_table = frame->state->transitions;
+
+ initial_len = len;
+
+ this_state = frame->state;
+
+ while (len--)
+ {
+ struct rx_inx * inx;
+ struct rx_inx * next_table;
+
+ /* this_state holds the state for the position we're
+ * leaving. this_state is locked.
+ */
+ inx = inx_table + *burst;
+ next_table = (struct rx_inx *)inx->data;
+
+ while (!next_table)
+ {
+ struct rx_superstate * state;
+
+ state = ((struct rx_superstate *)
+ ((char *)inx_table
+ - ((unsigned long)
+ ((struct rx_superstate *)0)->transitions)));
+
+ switch ((int)inx->inx)
+ {
+ case rx_backtrack:
+ /* RX_BACKTRACK means that we've reached the empty
+ * superstate, indicating that match can't succeed
+ * from this point.
+ *
+ * Return to the state for the position prior to what
+ * we failed at, and return that position.
+ */
+ frame->state = this_state;
+ frame->final_tag = this_state->contents->is_final;
+ return (initial_len - len) - 1;
+
+ case rx_cache_miss:
+ /* Because the superstate NFA is lazily constructed,
+ * and in fact may erode from underneath us, we sometimes
+ * have to construct the next instruction from the hard way.
+ * This invokes one step in the lazy-conversion.
+ */
+ inx = rx_handle_cache_miss
+ (frame->rx, state, *burst, inx->data_2);
+
+ if (!inx)
+ {
+ rx_unlock_superstate (frame->rx, this_state);
+ frame->state = 0;
+ return -1;
+ }
+ next_table = (struct rx_inx *)inx->data;
+ continue;
+
+
+ /* No other instructions are legal here.
+ * (In particular, this function does not handle backtracking
+ * or the related instructions.)
+ */
+ default:
+ rx_unlock_superstate (frame->rx, this_state);
+ frame->state = 0;
+ return -1;
+ }
+ }
+
+ /* Release the superstate for the preceeding position: */
+ rx_unlock_superstate (frame->rx, this_state);
+
+ /* Compute the superstate for the new position: */
+ inx_table = next_table;
+ this_state = ((struct rx_superstate *)
+ ((char *)inx_table
+ - ((unsigned long)
+ ((struct rx_superstate *)0)->transitions)));
+
+ /* Lock it (see top-of-loop invariant): */
+ rx_lock_superstate (frame->rx, this_state);
+
+ /* Check to see if we should stop: */
+ if (this_state->contents->is_final)
+ {
+ frame->final_tag = this_state->contents->is_final;
+ frame->state = this_state;
+ return (initial_len - len);
+ }
+
+ ++burst;
+ }
+
+ /* Consumed all of the characters. */
+ frame->state = this_state;
+ frame->final_tag = this_state->contents->is_final;
+
+ /* state already locked (see top-of-loop invariant) */
+ return initial_len;
+}
+
+
+\f
+
+#ifdef __STDC__
+void
+rx_terminate_system (struct rx_classical_system * frame)
+#else
+void
+rx_terminate_system (frame)
+ struct rx_classical_system * frame;
+#endif
+{
+ if (frame->state)
+ {
+ rx_unlock_superstate (frame->rx, frame->state);
+ frame->state = 0;
+ }
+}
+
+\f
+
+
+
+
+
+
+
+#ifdef __STDC__
+void
+rx_init_system (struct rx_classical_system * frame, struct rx * rx)
+#else
+void
+rx_init_system (frame, rx)
+ struct rx_classical_system * frame;
+ struct rx * rx;
+#endif
+{
+ frame->rx = rx;
+ frame->state = 0;
+}
+
+\f
+
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXANALH
+#define RXANALH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+#include "rxcset.h"
+#include "rxnode.h"
+#include "rxsuper.h"
+
+\f
+
+
+enum rx_answers
+{
+ rx_yes = 0,
+ rx_no = 1,
+ rx_bogus = -1,
+ rx_start_state_with_too_many_futures = rx_bogus - 1
+ /* n < 0 -- error */
+};
+
+struct rx_classical_system
+{
+ struct rx * rx;
+ struct rx_superstate * state;
+ int final_tag;
+};
+
+
+\f
+#ifdef __STDC__
+extern int rx_posix_analyze_rexp (struct rexp_node *** subexps,
+ size_t * re_nsub,
+ struct rexp_node * node,
+ int id);
+extern int rx_fill_in_fastmap (int cset_size, unsigned char * map, struct rexp_node * exp);
+extern int rx_is_anchored_p (struct rexp_node * exp);
+extern enum rx_answers rx_start_superstate (struct rx_classical_system * frame);
+extern enum rx_answers rx_fit_p (struct rx_classical_system * frame, unsigned const char * burst, int len);
+extern enum rx_answers rx_advance (struct rx_classical_system * frame, unsigned const char * burst, int len);
+extern int rx_advance_to_final (struct rx_classical_system * frame, unsigned const char * burst, int len);
+extern void rx_terminate_system (struct rx_classical_system * frame);
+extern void rx_init_system (struct rx_classical_system * frame, struct rx * rx);
+
+#else /* STDC */
+extern int rx_posix_analyze_rexp ();
+extern int rx_fill_in_fastmap ();
+extern int rx_is_anchored_p ();
+extern enum rx_answers rx_start_superstate ();
+extern enum rx_answers rx_fit_p ();
+extern enum rx_answers rx_advance ();
+extern int rx_advance_to_final ();
+extern void rx_terminate_system ();
+extern void rx_init_system ();
+
+#endif /* STDC */
+
+
+
+#endif /* RXANALH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+#include "rxall.h"
+#include "rxbasic.h"
+#include "rxstr.h"
+
+
+\f
+
+int rx_basic_unfaniverse_delay = RX_DEFAULT_NFA_DELAY;
+static struct rx_unfaniverse * rx_basic_uv = 0;
+
+\f
+
+static int
+init_basic_once ()
+{
+ if (rx_basic_uv)
+ return 0;
+ rx_basic_uv = rx_make_unfaniverse (rx_basic_unfaniverse_delay);
+ return (rx_basic_uv ? 0 : -1);
+}
+
+\f
+#ifdef __STDC__
+struct rx_unfaniverse *
+rx_basic_unfaniverse (void)
+#else
+struct rx_unfaniverse *
+rx_basic_unfaniverse ()
+#endif
+{
+ if (init_basic_once ())
+ return 0;
+ return rx_basic_uv;
+}
+\f
+
+static char * silly_hack = 0;
+
+#ifdef __STDC__
+struct rx_solutions *
+rx_basic_make_solutions (struct rx_registers * regs, struct rexp_node * expression, struct rexp_node ** subexps, int start, int end, struct rx_context_rules * rules, const unsigned char * str)
+#else
+struct rx_solutions *
+rx_basic_make_solutions (regs, expression, subexps, start, end, rules, str)
+ struct rx_registers * regs;
+ struct rexp_node * expression;
+ struct rexp_node ** subexps;
+ int start;
+ int end;
+ struct rx_context_rules * rules;
+ const unsigned char * str;
+#endif
+{
+ struct rx_str_closure * closure;
+ if (init_basic_once ())
+ return 0; /* bogus but rare */
+ if ( expression
+ && (expression->len >= 0)
+ && (expression->len != (end - start)))
+ return &rx_no_solutions;
+ if (silly_hack)
+ {
+ closure = (struct rx_str_closure *)silly_hack;
+ silly_hack = 0;
+ }
+ else
+ closure = (struct rx_str_closure *)malloc (sizeof (*closure));
+ if (!closure)
+ return 0;
+ closure->str = str;
+ closure->len = end;
+ closure->rules = *rules;
+ return rx_make_solutions (regs, rx_basic_uv, expression, subexps, 256,
+ start, end, rx_str_vmfn, rx_str_contextfn,
+ (void *)closure);
+}
+
+
+
+#ifdef __STDC__
+void
+rx_basic_free_solutions (struct rx_solutions * solns)
+#else
+ void
+ rx_basic_free_solutions (solns)
+ struct rx_solutions * solns;
+#endif
+{
+ if (solns == &rx_no_solutions)
+ return;
+
+ if (!silly_hack)
+ silly_hack = (char *)solns->closure;
+ else
+ free (solns->closure);
+ solns->closure = 0;
+ rx_free_solutions (solns);
+}
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXBASICH
+#define RXBASICH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+#include "rxcontext.h"
+#include "rxnode.h"
+#include "rxspencer.h"
+#include "rxunfa.h"
+\f
+
+
+#ifndef RX_DEFAULT_NFA_DELAY
+/* This value bounds the number of NFAs kept in a cache of recent NFAs.
+ * This value is used whenever the rx_basic_* entry points are used,
+ * for example, when the Posix entry points are invoked.
+ */
+#define RX_DEFAULT_NFA_DELAY 64
+#endif
+
+\f
+#ifdef __STDC__
+extern struct rx_unfaniverse * rx_basic_unfaniverse (void);
+extern struct rx_solutions * rx_basic_make_solutions (struct rx_registers * regs, struct rexp_node * expression, struct rexp_node ** subexps, int start, int end, struct rx_context_rules * rules, const unsigned char * str);
+extern void rx_basic_free_solutions (struct rx_solutions * solns);
+
+#else /* STDC */
+extern struct rx_unfaniverse * rx_basic_unfaniverse ();
+extern struct rx_solutions * rx_basic_make_solutions ();
+extern void rx_basic_free_solutions ();
+
+#endif /* STDC */
+
+
+
+
+
+
+
+
+#endif /* RXBASICH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+/*
+ * Tom Lord (lord@cygnus.com, lord@gnu.ai.mit.edu)
+ */
+\f
+
+#include "rxall.h"
+#include "rxbitset.h"
+
+
+#ifdef __STDC__
+int
+rx_bitset_is_equal (int size, rx_Bitset a, rx_Bitset b)
+#else
+int
+rx_bitset_is_equal (size, a, b)
+ int size;
+ rx_Bitset a;
+ rx_Bitset b;
+#endif
+{
+ int x;
+ RX_subset s;
+
+ if (size == 0)
+ return 1;
+
+ s = b[0];
+ b[0] = ~a[0];
+
+ for (x = rx_bitset_numb_subsets(size) - 1; a[x] == b[x]; --x)
+ ;
+
+ b[0] = s;
+ return !x && (s == a[0]);
+}
+
+
+#ifdef __STDC__
+int
+rx_bitset_is_subset (int size, rx_Bitset a, rx_Bitset b)
+#else
+int
+rx_bitset_is_subset (size, a, b)
+ int size;
+ rx_Bitset a;
+ rx_Bitset b;
+#endif
+{
+ int x;
+ x = rx_bitset_numb_subsets(size) - 1;
+ while (x-- && ((a[x] & b[x]) == a[x]));
+ return x == -1;
+}
+
+
+#ifdef __STDC__
+int
+rx_bitset_empty (int size, rx_Bitset set)
+#else
+int
+rx_bitset_empty (size, set)
+ int size;
+ rx_Bitset set;
+#endif
+{
+ int x;
+ RX_subset s;
+ s = set[0];
+ set[0] = 1;
+ for (x = rx_bitset_numb_subsets(size) - 1; !set[x]; --x)
+ ;
+ set[0] = s;
+ return !s;
+}
+
+#ifdef __STDC__
+void
+rx_bitset_null (int size, rx_Bitset b)
+#else
+void
+rx_bitset_null (size, b)
+ int size;
+ rx_Bitset b;
+#endif
+{
+ rx_bzero ((char *)b, rx_sizeof_bitset(size));
+}
+
+
+#ifdef __STDC__
+void
+rx_bitset_universe (int size, rx_Bitset b)
+#else
+void
+rx_bitset_universe (size, b)
+ int size;
+ rx_Bitset b;
+#endif
+{
+ int x = rx_bitset_numb_subsets (size);
+ while (x--)
+ *b++ = ~(RX_subset)0;
+}
+
+
+#ifdef __STDC__
+void
+rx_bitset_complement (int size, rx_Bitset b)
+#else
+void
+rx_bitset_complement (size, b)
+ int size;
+ rx_Bitset b;
+#endif
+{
+ int x = rx_bitset_numb_subsets (size);
+ while (x--)
+ {
+ *b = ~*b;
+ ++b;
+ }
+}
+
+
+#ifdef __STDC__
+void
+rx_bitset_assign (int size, rx_Bitset a, rx_Bitset b)
+#else
+void
+rx_bitset_assign (size, a, b)
+ int size;
+ rx_Bitset a;
+ rx_Bitset b;
+#endif
+{
+ int x;
+ for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+ a[x] = b[x];
+}
+
+
+#ifdef __STDC__
+void
+rx_bitset_union (int size, rx_Bitset a, rx_Bitset b)
+#else
+void
+rx_bitset_union (size, a, b)
+ int size;
+ rx_Bitset a;
+ rx_Bitset b;
+#endif
+{
+ int x;
+ for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+ a[x] |= b[x];
+}
+
+
+#ifdef __STDC__
+void
+rx_bitset_intersection (int size,
+ rx_Bitset a, rx_Bitset b)
+#else
+void
+rx_bitset_intersection (size, a, b)
+ int size;
+ rx_Bitset a;
+ rx_Bitset b;
+#endif
+{
+ int x;
+ for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+ a[x] &= b[x];
+}
+
+
+#ifdef __STDC__
+void
+rx_bitset_difference (int size, rx_Bitset a, rx_Bitset b)
+#else
+void
+rx_bitset_difference (size, a, b)
+ int size;
+ rx_Bitset a;
+ rx_Bitset b;
+#endif
+{
+ int x;
+ for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+ a[x] &= ~ b[x];
+}
+
+
+#ifdef __STDC__
+void
+rx_bitset_revdifference (int size,
+ rx_Bitset a, rx_Bitset b)
+#else
+void
+rx_bitset_revdifference (size, a, b)
+ int size;
+ rx_Bitset a;
+ rx_Bitset b;
+#endif
+{
+ int x;
+ for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+ a[x] = ~a[x] & b[x];
+}
+
+#ifdef __STDC__
+void
+rx_bitset_xor (int size, rx_Bitset a, rx_Bitset b)
+#else
+void
+rx_bitset_xor (size, a, b)
+ int size;
+ rx_Bitset a;
+ rx_Bitset b;
+#endif
+{
+ int x;
+ for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+ a[x] ^= b[x];
+}
+
+
+#ifdef __STDC__
+unsigned long
+rx_bitset_hash (int size, rx_Bitset b)
+#else
+unsigned long
+rx_bitset_hash (size, b)
+ int size;
+ rx_Bitset b;
+#endif
+{
+ int x;
+ unsigned long answer;
+
+ answer = 0;
+
+ for (x = 0; x < size; ++x)
+ {
+ if (RX_bitset_member (b, x))
+ answer += (answer << 3) + x;
+ }
+ return answer;
+}
+
+
+RX_subset rx_subset_singletons [RX_subset_bits] =
+{
+ 0x1,
+ 0x2,
+ 0x4,
+ 0x8,
+ 0x10,
+ 0x20,
+ 0x40,
+ 0x80,
+ 0x100,
+ 0x200,
+ 0x400,
+ 0x800,
+ 0x1000,
+ 0x2000,
+ 0x4000,
+ 0x8000,
+ 0x10000,
+ 0x20000,
+ 0x40000,
+ 0x80000,
+ 0x100000,
+ 0x200000,
+ 0x400000,
+ 0x800000,
+ 0x1000000,
+ 0x2000000,
+ 0x4000000,
+ 0x8000000,
+ 0x10000000,
+ 0x20000000,
+ 0x40000000,
+ 0x80000000
+};
+
+
+/*
+ * (define l (let loop ((x 0) (l '())) (if (eq? x 256) l (loop (+ x 1) (cons x l)))))
+ * (define lb (map (lambda (n) (number->string n 2)) l))
+ * (define lc (map string->list lb))
+ * (define ln (map (lambda (l) (map (lambda (c) (if (eq? c #\1) 1 0)) l)) lc))
+ * (define lt (map (lambda (l) (apply + l)) ln))
+ */
+
+static int char_pops[256] =
+{
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+};
+
+#define RX_char_population(C) (char_pops[C])
+
+#ifdef __STDC__
+int
+rx_bitset_population (int size, rx_Bitset a)
+#else
+int
+rx_bitset_population (size, a)
+ int size;
+ rx_Bitset a;
+#endif
+{
+ int x;
+ int total;
+ unsigned char s;
+
+ if (size == 0)
+ return 0;
+
+ total = 0;
+ x = sizeof (RX_subset) * rx_bitset_numb_subsets(size) - 1;
+ while (x >= 0)
+ {
+ s = ((unsigned char *)a)[x];
+ --x;
+ total = total + RX_char_population (s);
+ }
+ return total;
+}
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXBITSETH
+#define RXBITSETH
+
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+
+typedef unsigned long RX_subset;
+#define RX_subset_bits (8 * sizeof (RX_subset))
+#define RX_subset_mask (RX_subset_bits - 1)
+typedef RX_subset * rx_Bitset;
+
+#ifdef __STDC__
+typedef void (*rx_bitset_iterator) (rx_Bitset, int member_index);
+#else
+typedef void (*rx_bitset_iterator) ();
+#endif
+
+/* Return the index of the word containing the Nth bit.
+ */
+#define rx_bitset_subset(N) ((N) / RX_subset_bits)
+
+/* Return the conveniently-sized supset containing the Nth bit.
+ */
+#define rx_bitset_subset_val(B,N) ((B)[rx_bitset_subset(N)])
+
+
+/* Genericly combine the word containing the Nth bit with a 1 bit mask
+ * of the Nth bit position within that word.
+ */
+#define RX_bitset_access(B,N,OP) \
+ ((B)[rx_bitset_subset(N)] OP rx_subset_singletons[(N) & RX_subset_mask])
+
+#define RX_bitset_member(B,N) RX_bitset_access(B, N, &)
+#define RX_bitset_enjoin(B,N) RX_bitset_access(B, N, |=)
+#define RX_bitset_remove(B,N) RX_bitset_access(B, N, &= ~)
+#define RX_bitset_toggle(B,N) RX_bitset_access(B, N, ^= )
+
+/* How many words are needed for N bits?
+ */
+#define rx_bitset_numb_subsets(N) (((N) + RX_subset_bits - 1) / RX_subset_bits)
+
+/* How much memory should be allocated for a bitset with N bits?
+ */
+#define rx_sizeof_bitset(N) (rx_bitset_numb_subsets(N) * sizeof(RX_subset))
+\f
+
+extern RX_subset rx_subset_singletons[];
+
+
+\f
+#ifdef __STDC__
+extern int rx_bitset_is_equal (int size, rx_Bitset a, rx_Bitset b);
+extern int rx_bitset_is_subset (int size, rx_Bitset a, rx_Bitset b);
+extern int rx_bitset_empty (int size, rx_Bitset set);
+extern void rx_bitset_null (int size, rx_Bitset b);
+extern void rx_bitset_universe (int size, rx_Bitset b);
+extern void rx_bitset_complement (int size, rx_Bitset b);
+extern void rx_bitset_assign (int size, rx_Bitset a, rx_Bitset b);
+extern void rx_bitset_union (int size, rx_Bitset a, rx_Bitset b);
+extern void rx_bitset_intersection (int size,
+ rx_Bitset a, rx_Bitset b);
+extern void rx_bitset_difference (int size, rx_Bitset a, rx_Bitset b);
+extern void rx_bitset_revdifference (int size,
+ rx_Bitset a, rx_Bitset b);
+extern void rx_bitset_xor (int size, rx_Bitset a, rx_Bitset b);
+extern unsigned long rx_bitset_hash (int size, rx_Bitset b);
+extern int rx_bitset_population (int size, rx_Bitset a);
+
+#else /* STDC */
+extern int rx_bitset_is_equal ();
+extern int rx_bitset_is_subset ();
+extern int rx_bitset_empty ();
+extern void rx_bitset_null ();
+extern void rx_bitset_universe ();
+extern void rx_bitset_complement ();
+extern void rx_bitset_assign ();
+extern void rx_bitset_union ();
+extern void rx_bitset_intersection ();
+extern void rx_bitset_difference ();
+extern void rx_bitset_revdifference ();
+extern void rx_bitset_xor ();
+extern unsigned long rx_bitset_hash ();
+extern int rx_bitset_population ();
+
+#endif /* STDC */
+
+
+
+#endif /* RXBITSETH */
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXCONTEXTH
+#define RXCONTEXTH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+struct rx_context_rules
+{
+ unsigned int newline_anchor:1;/* If true, an anchor at a newline matches.*/
+ unsigned int not_bol:1; /* If set, the anchors ('^' and '$') don't */
+ unsigned int not_eol:1; /* match at the ends of the string. */
+ unsigned int case_indep:1;
+};
+
+\f
+#ifdef __STDC__
+
+#else /* STDC */
+
+#endif /* STDC */
+
+
+#endif /* RXCONTEXTH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+/*
+ * Tom Lord (lord@cygnus.com, lord@gnu.ai.mit.edu)
+ */
+\f
+
+
+#include "rxall.h"
+#include "rxcset.h"
+/* Utilities for manipulating bitset represntations of characters sets. */
+
+#ifdef __STDC__
+rx_Bitset
+rx_cset (int size)
+#else
+rx_Bitset
+rx_cset (size)
+ int size;
+#endif
+{
+ rx_Bitset b;
+ b = (rx_Bitset) malloc (rx_sizeof_bitset (size));
+ if (b)
+ rx_bitset_null (size, b);
+ return b;
+}
+
+
+#ifdef __STDC__
+rx_Bitset
+rx_copy_cset (int size, rx_Bitset a)
+#else
+rx_Bitset
+rx_copy_cset (size, a)
+ int size;
+ rx_Bitset a;
+#endif
+{
+ rx_Bitset cs;
+ cs = rx_cset (size);
+
+ if (cs)
+ rx_bitset_union (size, cs, a);
+
+ return cs;
+}
+
+
+#ifdef __STDC__
+void
+rx_free_cset (rx_Bitset c)
+#else
+void
+rx_free_cset (c)
+ rx_Bitset c;
+#endif
+{
+ if (c)
+ free ((char *)c);
+}
+
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXCSETH
+#define RXCSETH
+
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* lord Sun May 7 12:34:11 1995 */
+
+\f
+#include "rxbitset.h"
+
+\f
+#ifdef __STDC__
+extern rx_Bitset rx_cset (int size);
+extern rx_Bitset rx_copy_cset (int size, rx_Bitset a);
+extern void rx_free_cset (rx_Bitset c);
+
+#else /* STDC */
+extern rx_Bitset rx_cset ();
+extern rx_Bitset rx_copy_cset ();
+extern void rx_free_cset ();
+
+#endif /* STDC */
+
+
+
+#endif /* RXCSETH */
--- /dev/null
+/* Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+#include <sys/types.h>
+#include "rxall.h"
+#include "rxgnucomp.h"
+#include "inst-rxposix.h"
+
+\f
+/* {A Syntax Table}
+ */
+
+/* Define the syntax basics for \<, \>, etc.
+ */
+
+#ifndef emacs
+#define CHARBITS 8
+#define CHAR_SET_SIZE (1 << CHARBITS)
+#define Sword 1
+#define SYNTAX(c) re_syntax_table[c]
+char re_syntax_table[CHAR_SET_SIZE];
+
+#ifdef __STDC__
+static void
+init_syntax_once (void)
+#else
+static void
+init_syntax_once ()
+#endif
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+
+ rx_bzero ((char *)re_syntax_table, sizeof re_syntax_table);
+
+ for (c = 'a'; c <= 'z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = 'A'; c <= 'Z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = '0'; c <= '9'; c++)
+ re_syntax_table[c] = Sword;
+
+ re_syntax_table['_'] = Sword;
+
+ done = 1;
+}
+#endif /* not emacs */
+
+\f
+
+
+
+const char *rx_error_msg[] =
+{
+ 0, /* REG_NOUT */
+ "No match", /* REG_NOMATCH */
+ "Invalid regular expression", /* REG_BADPAT */
+ "Invalid collation character", /* REG_ECOLLATE */
+ "Invalid character class name", /* REG_ECTYPE */
+ "Trailing backslash", /* REG_EESCAPE */
+ "Invalid back reference", /* REG_ESUBREG */
+ "Unmatched [ or [^", /* REG_EBRACK */
+ "Unmatched ( or \\(", /* REG_EPAREN */
+ "Unmatched \\{", /* REG_EBRACE */
+ "Invalid content of \\{\\}", /* REG_BADBR */
+ "Invalid range end", /* REG_ERANGE */
+ "Memory exhausted", /* REG_ESPACE */
+ "Invalid preceding regular expression", /* REG_BADRPT */
+ "Premature end of regular expression", /* REG_EEND */
+ "Regular expression too big", /* REG_ESIZE */
+ "Unmatched ) or \\)", /* REG_ERPAREN */
+};
+
+
+\f
+/*
+ * Macros used while compiling patterns.
+ *
+ * By convention, PEND points just past the end of the uncompiled pattern,
+ * P points to the read position in the pattern. `translate' is the name
+ * of the translation table (`TRANSLATE' is the name of a macro that looks
+ * things up in `translate').
+ */
+
+
+/*
+ * Fetch the next character in the uncompiled pattern---translating it
+ * if necessary. *Also cast from a signed character in the constant
+ * string passed to us by the user to an unsigned char that we can use
+ * as an array index (in, e.g., `translate').
+ */
+#define PATFETCH(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ c = translate[c]; \
+ } while (0)
+
+/*
+ * Fetch the next character in the uncompiled pattern, with no
+ * translation.
+ */
+#define PATFETCH_RAW(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ } while (0)
+
+/* Go backwards one character in the pattern. */
+#define PATUNFETCH p--
+
+
+#define TRANSLATE(d) translate[(unsigned char) (d)]
+
+typedef int regnum_t;
+
+/* Since offsets can go either forwards or backwards, this type needs to
+ * be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1.
+ */
+typedef int pattern_offset_t;
+
+typedef struct
+{
+ struct rexp_node ** top_expression;
+ struct rexp_node ** last_expression;
+ struct rexp_node ** last_non_regular_expression;
+ pattern_offset_t inner_group_offset;
+ regnum_t regnum;
+} compile_stack_elt_t;
+
+typedef struct
+{
+ compile_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size)
+
+/* The next available element. */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list. */
+#define SET_LIST_BIT(c) \
+ (b[((unsigned char) (c)) / CHARBITS] \
+ |= 1 << (((unsigned char) c) % CHARBITS))
+
+/* Get the next unsigned number in the uncompiled pattern. */
+#define GET_UNSIGNED_NUMBER(num) \
+ { if (p != pend) \
+ { \
+ PATFETCH (c); \
+ while (isdigit (c)) \
+ { \
+ if (num < 0) \
+ num = 0; \
+ num = num * 10 + c - '0'; \
+ if (p == pend) \
+ break; \
+ PATFETCH (c); \
+ } \
+ } \
+ }
+
+#define CHAR_CLASS_MAX_LENGTH 64
+
+#define IS_CHAR_CLASS(string) \
+ (!strcmp (string, "alpha") || !strcmp (string, "upper") \
+ || !strcmp (string, "lower") || !strcmp (string, "digit") \
+ || !strcmp (string, "alnum") || !strcmp (string, "xdigit") \
+ || !strcmp (string, "space") || !strcmp (string, "print") \
+ || !strcmp (string, "punct") || !strcmp (string, "graph") \
+ || !strcmp (string, "cntrl") || !strcmp (string, "blank"))
+
+\f
+/* These predicates are used in regex_compile. */
+
+/* P points to just after a ^ in PATTERN. Return true if that ^ comes
+ * after an alternative or a begin-subexpression. We assume there is at
+ * least one character before the ^.
+ */
+
+#ifdef __STDC__
+static int
+at_begline_loc_p (const char *pattern, const char * p, unsigned long syntax)
+#else
+static int
+at_begline_loc_p (pattern, p, syntax)
+ const char *pattern;
+ const char * p;
+ unsigned long syntax;
+#endif
+{
+ const char *prev = p - 2;
+ int prev_prev_backslash = ((prev > pattern) && (prev[-1] == '\\'));
+
+ return
+
+ (/* After a subexpression? */
+ ((*prev == '(') && ((syntax & RE_NO_BK_PARENS) || prev_prev_backslash))
+ ||
+ /* After an alternative? */
+ ((*prev == '|') && ((syntax & RE_NO_BK_VBAR) || prev_prev_backslash))
+ );
+}
+
+/* The dual of at_begline_loc_p. This one is for $. We assume there is
+ * at least one character after the $, i.e., `P < PEND'.
+ */
+
+#ifdef __STDC__
+static int
+at_endline_loc_p (const char *p, const char *pend, int syntax)
+#else
+static int
+at_endline_loc_p (p, pend, syntax)
+ const char *p;
+ const char *pend;
+ int syntax;
+#endif
+{
+ const char *next = p;
+ int next_backslash = (*next == '\\');
+ const char *next_next = (p + 1 < pend) ? (p + 1) : 0;
+
+ return
+ (
+ /* Before a subexpression? */
+ ((syntax & RE_NO_BK_PARENS)
+ ? (*next == ')')
+ : (next_backslash && next_next && (*next_next == ')')))
+ ||
+ /* Before an alternative? */
+ ((syntax & RE_NO_BK_VBAR)
+ ? (*next == '|')
+ : (next_backslash && next_next && (*next_next == '|')))
+ );
+}
+\f
+
+unsigned char rx_id_translation[256] =
+{
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+ 250, 251, 252, 253, 254, 255
+};
+
+/* The compiler keeps an inverted translation table.
+ * This looks up/inititalize elements.
+ * VALID is an array of booleans that validate CACHE.
+ */
+
+#ifdef __STDC__
+static rx_Bitset
+inverse_translation (int * n_members, int cset_size, char * valid, rx_Bitset cache, unsigned char * translate, int c)
+#else
+static rx_Bitset
+inverse_translation (n_members, cset_size, valid, cache, translate, c)
+ int * n_members;
+ int cset_size;
+ char * valid;
+ rx_Bitset cache;
+ unsigned char * translate;
+ int c;
+#endif
+{
+ rx_Bitset cs;
+ cs = cache + c * rx_bitset_numb_subsets (cset_size);
+
+ if (!valid[c])
+ {
+ int x;
+ int c_tr;
+ int membs;
+
+ c_tr = TRANSLATE(c);
+ rx_bitset_null (cset_size, cs);
+ membs = 0;
+ for (x = 0; x < 256; ++x)
+ if (TRANSLATE(x) == c_tr)
+ {
+ RX_bitset_enjoin (cs, x);
+ membs++;
+ }
+ valid[c] = 1;
+ n_members[c] = membs;
+ }
+ return cs;
+}
+
+\f
+
+
+/* More subroutine declarations and macros for regex_compile. */
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
+ * false if it's not.
+ */
+
+#ifdef __STDC__
+static int
+group_in_compile_stack (compile_stack_type compile_stack, regnum_t regnum)
+#else
+static int
+group_in_compile_stack (compile_stack, regnum)
+ compile_stack_type compile_stack;
+ regnum_t regnum;
+#endif
+{
+ int this_element;
+
+ for (this_element = compile_stack.avail - 1;
+ this_element >= 0;
+ this_element--)
+ if (compile_stack.stack[this_element].regnum == regnum)
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Read the ending character of a range (in a bracket expression) from the
+ * uncompiled pattern *P_PTR (which ends at PEND). We assume the
+ * starting character is in `P[-2]'. (`P[-1]' is the character `-'.)
+ * Then we set the translation of all bits between the starting and
+ * ending characters (inclusive) in the compiled pattern B.
+ *
+ * Return an error code.
+ *
+ * We use these short variable names so we can use the same macros as
+ * `regex_compile' itself.
+ */
+
+#ifdef __STDC__
+static int
+compile_range (int * n_members, int cset_size, rx_Bitset cs, const char ** p_ptr, const char * pend, unsigned char * translate, unsigned long syntax, rx_Bitset inv_tr, char * valid_inv_tr)
+#else
+static int
+compile_range (n_members, cset_size, cs, p_ptr, pend, translate, syntax, inv_tr, valid_inv_tr)
+ int * n_members;
+ int cset_size;
+ rx_Bitset cs;
+ const char ** p_ptr;
+ const char * pend;
+ unsigned char * translate;
+ unsigned long syntax;
+ rx_Bitset inv_tr;
+ char * valid_inv_tr;
+#endif
+{
+ unsigned this_char;
+
+ const char *p = *p_ptr;
+
+ unsigned char range_end;
+ unsigned char range_start = TRANSLATE(p[-2]);
+
+ if (p == pend)
+ return REG_ERANGE;
+
+ PATFETCH (range_end);
+
+ (*p_ptr)++;
+
+ if (range_start > range_end)
+ return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+ for (this_char = range_start; this_char <= range_end; this_char++)
+ {
+ rx_Bitset it =
+ inverse_translation (n_members, cset_size, valid_inv_tr, inv_tr, translate, this_char);
+ rx_bitset_union (cset_size, cs, it);
+ }
+
+ return REG_NOERROR;
+}
+\f
+
+#ifdef __STDC__
+static int
+pointless_if_repeated (struct rexp_node * node)
+#else
+static int
+pointless_if_repeated (node)
+ struct rexp_node * node;
+#endif
+{
+ if (!node)
+ return 1;
+ switch (node->type)
+ {
+ case r_cset:
+ case r_string:
+ case r_cut:
+ return 0;
+ case r_concat:
+ case r_alternate:
+ return (pointless_if_repeated (node->params.pair.left)
+ && pointless_if_repeated (node->params.pair.right));
+ case r_opt:
+ case r_star:
+ case r_interval:
+ case r_parens:
+ return pointless_if_repeated (node->params.pair.left);
+ case r_context:
+ switch (node->params.intval)
+ {
+ case '=':
+ case '<':
+ case '^':
+ case 'b':
+ case 'B':
+ case '`':
+ case '\'':
+ return 1;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+\f
+
+#ifdef __STDC__
+static int
+factor_string (struct rexp_node *** lastp, int cset_size)
+#else
+static int
+factor_string (lastp, cset_size)
+ struct rexp_node *** lastp;
+ int cset_size;
+#endif
+{
+ struct rexp_node ** expp;
+ struct rexp_node * exp;
+ rx_Bitset cs;
+ struct rexp_node * cset_node;
+
+ expp = *lastp;
+ exp = *expp; /* presumed r_string */
+
+ cs = rx_cset (cset_size);
+ if (!cs)
+ return -1;
+ RX_bitset_enjoin (cs, exp->params.cstr.contents[exp->params.cstr.len - 1]);
+ cset_node = rx_mk_r_cset (r_cset, cset_size, cs);
+ if (!cset_node)
+ {
+ rx_free_cset (cs);
+ return -1;
+ }
+ if (exp->params.cstr.len == 1)
+ {
+ rx_free_rexp (exp);
+ *expp = cset_node;
+ /* lastp remains the same */
+ return 0;
+ }
+ else
+ {
+ struct rexp_node * concat_node;
+ exp->params.cstr.len--;
+ concat_node = rx_mk_r_binop (r_concat, exp, cset_node);
+ if (!concat_node)
+ {
+ rx_free_rexp (cset_node);
+ return -1;
+ }
+ *expp = concat_node;
+ *lastp = &concat_node->params.pair.right;
+ return 0;
+ }
+}
+
+\f
+
+#define isa_blank(C) (((C) == ' ') || ((C) == '\t'))
+
+#ifdef __STDC__
+int
+rx_parse (struct rexp_node ** rexp_p,
+ const char *pattern,
+ int size,
+ unsigned long syntax,
+ int cset_size,
+ unsigned char *translate)
+#else
+int
+rx_parse (rexp_p, pattern, size, syntax, cset_size, translate)
+ struct rexp_node ** rexp_p;
+ const char *pattern;
+ int size;
+ unsigned long syntax;
+ int cset_size;
+ unsigned char *translate;
+#endif
+{
+ int compile_error;
+ RX_subset
+ inverse_translate [CHAR_SET_SIZE * rx_bitset_numb_subsets(CHAR_SET_SIZE)];
+ char validate_inv_tr [CHAR_SET_SIZE];
+ int n_members [CHAR_SET_SIZE];
+
+ /* We fetch characters from PATTERN here. Even though PATTERN is
+ * `char *' (i.e., signed), we declare these variables as unsigned, so
+ * they can be reliably used as array indices.
+ */
+ register unsigned char c;
+ register unsigned char c1;
+
+ /* A random tempory spot in PATTERN. */
+ const char *p1;
+
+ /* Keeps track of unclosed groups. */
+ compile_stack_type compile_stack;
+
+ /* Points to the current (ending) position in the pattern. */
+ const char *p;
+ const char *pend;
+
+ /* When parsing is done, this will hold the expression tree. */
+ struct rexp_node * rexp;
+
+ /* This and top_expression are saved on the compile stack. */
+ struct rexp_node ** top_expression;
+ struct rexp_node ** last_non_regular_expression;
+ struct rexp_node ** last_expression;
+
+ /* Parameter to `goto append_node' */
+ struct rexp_node * append;
+
+ /* Counts open-groups as they are encountered. This is the index of the
+ * innermost group being compiled.
+ */
+ regnum_t regnum;
+
+ /* True iff the sub-expression just started
+ * is purely syntactic. Otherwise, a regmatch data
+ * slot is allocated for the subexpression.
+ */
+ int syntax_only_parens;
+
+ /* Place in the uncompiled pattern (i.e., the {) to
+ * which to go back if the interval is invalid.
+ */
+ const char *beg_interval;
+
+ int side;
+
+\f
+
+ if (!translate)
+ translate = rx_id_translation;
+
+ /* Points to the current (ending) position in the pattern. */
+ p = pattern;
+ pend = pattern + size;
+
+ /* When parsing is done, this will hold the expression tree. */
+ rexp = 0;
+
+ /* In the midst of compilation, this holds onto the regexp
+ * first parst while rexp goes on to aquire additional constructs.
+ */
+ top_expression = &rexp;
+ last_non_regular_expression = top_expression;
+ last_expression = top_expression;
+
+ /* Counts open-groups as they are encountered. This is the index of the
+ * innermost group being compiled.
+ */
+ regnum = 0;
+
+ rx_bzero ((char *)validate_inv_tr, sizeof (validate_inv_tr));
+
+
+ /* Initialize the compile stack. */
+ compile_stack.stack = (( compile_stack_elt_t *) malloc ((INIT_COMPILE_STACK_SIZE) * sizeof ( compile_stack_elt_t)));
+ if (compile_stack.stack == 0)
+ return REG_ESPACE;
+
+ compile_stack.size = INIT_COMPILE_STACK_SIZE;
+ compile_stack.avail = 0;
+
+#if !defined (emacs) && !defined (SYNTAX_TABLE)
+ /* Initialize the syntax table. */
+ init_syntax_once ();
+#endif
+
+ /* Loop through the uncompiled pattern until we're at the end. */
+ while (p != pend)
+ {
+ PATFETCH (c);
+
+ switch (c)
+ {
+ case '^':
+ {
+ if ( /* If at start of pattern, it's an operator. */
+ p == pattern + 1
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's come before. */
+ || at_begline_loc_p (pattern, p, syntax))
+ {
+ struct rexp_node * n
+ = rx_mk_r_int (r_context, '^');
+ if (!n)
+ goto space_error;
+ append = n;
+ goto append_node;
+ }
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '$':
+ {
+ if ( /* If at end of pattern, it's an operator. */
+ p == pend
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's next. */
+ || at_endline_loc_p (p, pend, syntax))
+ {
+ struct rexp_node * n
+ = rx_mk_r_int (r_context, '$');
+ if (!n)
+ goto space_error;
+ append = n;
+ goto append_node;
+ }
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '+':
+ case '?':
+ if ((syntax & RE_BK_PLUS_QM)
+ || (syntax & RE_LIMITED_OPS))
+ goto normal_char;
+
+ handle_plus:
+ case '*':
+ /* If there is no previous pattern... */
+ if (pointless_if_repeated (*last_expression))
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ {
+ compile_error = REG_BADRPT;
+ goto error_return;
+ }
+ else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ }
+
+ {
+ /* 1 means zero (many) matches is allowed. */
+ char zero_times_ok = 0, many_times_ok = 0;
+
+ /* If there is a sequence of repetition chars, collapse it
+ down to just one (the right one). We can't combine
+ interval operators with these because of, e.g., `a{2}*',
+ which should only match an even number of `a's. */
+
+ for (;;)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+
+ if (p == pend)
+ break;
+
+ PATFETCH (c);
+
+ if (c == '*'
+ || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+ ;
+
+ else if (syntax & RE_BK_PLUS_QM && c == '\\')
+ {
+ if (p == pend)
+ {
+ compile_error = REG_EESCAPE;
+ goto error_return;
+ }
+
+ PATFETCH (c1);
+ if (!(c1 == '+' || c1 == '?'))
+ {
+ PATUNFETCH;
+ PATUNFETCH;
+ break;
+ }
+
+ c = c1;
+ }
+ else
+ {
+ PATUNFETCH;
+ break;
+ }
+
+ /* If we get here, we found another repeat character. */
+ }
+
+ /* Now we know whether or not zero matches is allowed
+ * and also whether or not two or more matches is allowed.
+ */
+
+ {
+ struct rexp_node * inner_exp;
+ struct rexp_node * star;
+
+ if (*last_expression && ((*last_expression)->type == r_string))
+ if (factor_string (&last_expression, cset_size))
+ goto space_error;
+ inner_exp = *last_expression;
+ star = rx_mk_r_monop ((many_times_ok
+ ? (zero_times_ok ? r_star : r_plus)
+ : r_opt),
+ inner_exp);
+ if (!star)
+ goto space_error;
+ *last_expression = star;
+ }
+ }
+ break;
+
+
+ case '.':
+ {
+ rx_Bitset cs;
+ struct rexp_node * n;
+ cs = rx_cset (cset_size);
+ if (!cs)
+ goto space_error;
+ n = rx_mk_r_cset (r_cset, cset_size, cs);
+ if (!n)
+ {
+ rx_free_cset (cs);
+ goto space_error;
+ }
+ rx_bitset_universe (cset_size, cs);
+ if (!(syntax & RE_DOT_NEWLINE))
+ RX_bitset_remove (cs, '\n');
+ if (syntax & RE_DOT_NOT_NULL)
+ RX_bitset_remove (cs, 0);
+
+ append = n;
+ goto append_node;
+ break;
+ }
+
+
+ case '[':
+ if (p == pend)
+ {
+ compile_error = REG_EBRACK;
+ goto error_return;
+ }
+ {
+ int had_char_class;
+ rx_Bitset cs;
+ struct rexp_node * node;
+ int is_inverted;
+
+ had_char_class = 0;
+ is_inverted = *p == '^';
+ cs = rx_cset (cset_size);
+ if (!cs)
+ goto space_error;
+ node = rx_mk_r_cset (r_cset, cset_size ,cs);
+ if (!node)
+ {
+ rx_free_cset (cs);
+ goto space_error;
+ }
+
+ /* This branch of the switch is normally exited with
+ *`goto append_node'
+ */
+ append = node;
+
+ if (is_inverted)
+ p++;
+
+ /* Remember the first position in the bracket expression. */
+ p1 = p;
+
+ /* Read in characters and ranges, setting map bits. */
+ for (;;)
+ {
+ if (p == pend)
+ {
+ compile_error = REG_EBRACK;
+ goto error_return;
+ }
+
+ PATFETCH (c);
+
+ /* \ might escape characters inside [...] and [^...]. */
+ if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+ {
+ if (p == pend)
+ {
+ compile_error = REG_EESCAPE;
+ goto error_return;
+ }
+
+ PATFETCH (c1);
+ {
+ rx_Bitset it = inverse_translation (n_members,
+ cset_size,
+ validate_inv_tr,
+ inverse_translate,
+ translate,
+ c1);
+ rx_bitset_union (cset_size, cs, it);
+ }
+ continue;
+ }
+
+ /* Could be the end of the bracket expression. If it's
+ not (i.e., when the bracket expression is `[]' so
+ far), the ']' character bit gets set way below. */
+ if (c == ']' && p != p1 + 1)
+ goto finalize_class_and_append;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character class. */
+ if (had_char_class && c == '-' && *p != ']')
+ {
+ compile_error = REG_ERANGE;
+ goto error_return;
+ }
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character: if this is a hyphen not at the
+ beginning or the end of a list, then it's the range
+ operator. */
+ if (c == '-'
+ && !(p - 2 >= pattern && p[-2] == '[')
+ && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+ && *p != ']')
+ {
+ int ret
+ = compile_range (n_members, cset_size, cs, &p, pend, translate, syntax,
+ inverse_translate, validate_inv_tr);
+ if (ret != REG_NOERROR)
+ {
+ compile_error = ret;
+ goto error_return;
+ }
+ }
+
+ else if (p[0] == '-' && p[1] != ']')
+ { /* This handles ranges made up of characters only. */
+ int ret;
+
+ /* Move past the `-'. */
+ PATFETCH (c1);
+
+ ret = compile_range (n_members, cset_size, cs, &p, pend, translate, syntax,
+ inverse_translate, validate_inv_tr);
+ if (ret != REG_NOERROR)
+ {
+ compile_error = ret;
+ goto error_return;
+ }
+ }
+
+ /* See if we're at the beginning of a possible character
+ class. */
+
+ else if ((syntax & RE_CHAR_CLASSES)
+ && (c == '[') && (*p == ':'))
+ {
+ char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+ PATFETCH (c);
+ c1 = 0;
+
+ /* If pattern is `[[:'. */
+ if (p == pend)
+ {
+ compile_error = REG_EBRACK;
+ goto error_return;
+ }
+
+ for (;;)
+ {
+ PATFETCH (c);
+ if (c == ':' || c == ']' || p == pend
+ || c1 == CHAR_CLASS_MAX_LENGTH)
+ break;
+ str[c1++] = c;
+ }
+ str[c1] = '\0';
+
+ /* If isn't a word bracketed by `[:' and:`]':
+ undo the ending character, the letters, and leave
+ the leading `:' and `[' (but set bits for them). */
+ if (c == ':' && *p == ']')
+ {
+ if (!strncmp (str, "cut", 3))
+ {
+ int val;
+ if (1 != sscanf (str + 3, " %d", &val))
+ {
+ compile_error = REG_ECTYPE;
+ goto error_return;
+ }
+ /* Throw away the ]] */
+ PATFETCH (c);
+ PATFETCH (c);
+ {
+ struct rexp_node * cut;
+ cut = rx_mk_r_int (r_cut, val);
+ append = cut;
+ goto append_node;
+ }
+ }
+ else if (!strncmp (str, "(", 1))
+ {
+ /* Throw away the ]] */
+ PATFETCH (c);
+ PATFETCH (c);
+ syntax_only_parens = 1;
+ goto handle_open;
+ }
+ else if (!strncmp (str, ")", 1))
+ {
+ /* Throw away the ]] */
+ PATFETCH (c);
+ PATFETCH (c);
+ syntax_only_parens = 1;
+ goto handle_close;
+ }
+ else
+ {
+ int ch;
+ int is_alnum = !strcmp (str, "alnum");
+ int is_alpha = !strcmp (str, "alpha");
+ int is_blank = !strcmp (str, "blank");
+ int is_cntrl = !strcmp (str, "cntrl");
+ int is_digit = !strcmp (str, "digit");
+ int is_graph = !strcmp (str, "graph");
+ int is_lower = !strcmp (str, "lower");
+ int is_print = !strcmp (str, "print");
+ int is_punct = !strcmp (str, "punct");
+ int is_space = !strcmp (str, "space");
+ int is_upper = !strcmp (str, "upper");
+ int is_xdigit = !strcmp (str, "xdigit");
+
+ if (!IS_CHAR_CLASS (str))
+ {
+ compile_error = REG_ECTYPE;
+ goto error_return;
+ }
+
+ /* Throw away the ] at the end of the character
+ class. */
+ PATFETCH (c);
+
+ if (p == pend) { compile_error = REG_EBRACK; goto error_return; }
+
+ for (ch = 0; ch < 1 << CHARBITS; ch++)
+ {
+ if ( (is_alnum && isalnum (ch))
+ || (is_alpha && isalpha (ch))
+ || (is_blank && isa_blank (ch))
+ || (is_cntrl && iscntrl (ch))
+ || (is_digit && isdigit (ch))
+ || (is_graph && isgraph (ch))
+ || (is_lower && islower (ch))
+ || (is_print && isprint (ch))
+ || (is_punct && ispunct (ch))
+ || (is_space && isspace (ch))
+ || (is_upper && isupper (ch))
+ || (is_xdigit && isxdigit (ch)))
+ {
+ rx_Bitset it =
+ inverse_translation (n_members,
+ cset_size,
+ validate_inv_tr,
+ inverse_translate,
+ translate,
+ ch);
+ rx_bitset_union (cset_size,
+ cs, it);
+ }
+ }
+ had_char_class = 1;
+ }
+ }
+ else
+ {
+ c1++;
+ while (c1--)
+ PATUNFETCH;
+ {
+ rx_Bitset it =
+ inverse_translation (n_members,
+ cset_size,
+ validate_inv_tr,
+ inverse_translate,
+ translate,
+ '[');
+ rx_bitset_union (cset_size,
+ cs, it);
+ }
+ {
+ rx_Bitset it =
+ inverse_translation (n_members,
+ cset_size,
+ validate_inv_tr,
+ inverse_translate,
+ translate,
+ ':');
+ rx_bitset_union (cset_size,
+ cs, it);
+ }
+ had_char_class = 0;
+ }
+ }
+ else
+ {
+ had_char_class = 0;
+ {
+ rx_Bitset it = inverse_translation (n_members,
+ cset_size,
+ validate_inv_tr,
+ inverse_translate,
+ translate,
+ c);
+ rx_bitset_union (cset_size, cs, it);
+ }
+ }
+ }
+
+ finalize_class_and_append:
+ if (is_inverted)
+ {
+ rx_bitset_complement (cset_size, cs);
+ if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+ RX_bitset_remove (cs, '\n');
+ }
+ goto append_node;
+ }
+ break;
+
+
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ {
+ syntax_only_parens = 0;
+ goto handle_open;
+ }
+ else
+ goto normal_char;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ {
+ syntax_only_parens = 0;
+ goto handle_close;
+ }
+ else
+ goto normal_char;
+
+
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '|':
+ if (syntax & RE_NO_BK_VBAR)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '{':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ goto handle_interval;
+ else
+ goto normal_char;
+
+
+ case '\\':
+ if (p == pend) { compile_error = REG_EESCAPE; goto error_return; }
+
+ /* Do not translate the character after the \, so that we can
+ distinguish, e.g., \B from \b, even if we normally would
+ translate, e.g., B to b. */
+ PATFETCH_RAW (c);
+
+ switch (c)
+ {
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto normal_backslash;
+
+ syntax_only_parens = 0;
+
+ handle_open:
+ if (!syntax_only_parens)
+ regnum++;
+
+ if (COMPILE_STACK_FULL)
+ {
+ compile_stack.stack
+ = ((compile_stack_elt_t *)
+ realloc (compile_stack.stack,
+ (compile_stack.size << 1) * sizeof (compile_stack_elt_t)));
+ if (compile_stack.stack == 0)
+ goto space_error;
+ compile_stack.size <<= 1;
+ }
+
+ if (*last_non_regular_expression)
+ {
+ struct rexp_node * concat;
+ concat = rx_mk_r_binop (r_concat, *last_non_regular_expression, 0);
+ if (!concat)
+ goto space_error;
+ *last_non_regular_expression = concat;
+ last_non_regular_expression = &concat->params.pair.right;
+ last_expression = last_non_regular_expression;
+ }
+
+ /*
+ * These are the values to restore when we hit end of this
+ * group.
+ */
+ COMPILE_STACK_TOP.top_expression = top_expression;
+ COMPILE_STACK_TOP.last_expression = last_expression;
+ COMPILE_STACK_TOP.last_non_regular_expression = last_non_regular_expression;
+
+ if (syntax_only_parens)
+ COMPILE_STACK_TOP.regnum = -1;
+ else
+ COMPILE_STACK_TOP.regnum = regnum;
+
+ compile_stack.avail++;
+
+ top_expression = last_non_regular_expression;
+ break;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+ syntax_only_parens = 0;
+
+ handle_close:
+ /* See similar code for backslashed left paren above. */
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_char;
+ else
+ { compile_error = REG_ERPAREN; goto error_return; }
+
+ /* Since we just checked for an empty stack above, this
+ * ``can't happen''.
+ */
+
+ {
+ /* We don't just want to restore into `regnum', because
+ * later groups should continue to be numbered higher,
+ * as in `(ab)c(de)' -- the second group is #2.
+ */
+ regnum_t this_group_regnum;
+ struct rexp_node ** inner;
+ struct rexp_node * parens;
+
+ inner = top_expression;
+ compile_stack.avail--;
+
+ if (!!syntax_only_parens != (COMPILE_STACK_TOP.regnum == -1))
+ { compile_error = REG_ERPAREN; goto error_return; }
+
+ top_expression = COMPILE_STACK_TOP.top_expression;
+ last_expression = COMPILE_STACK_TOP.last_expression;
+ last_non_regular_expression = COMPILE_STACK_TOP.last_non_regular_expression;
+ this_group_regnum = COMPILE_STACK_TOP.regnum;
+
+ {
+ parens = rx_mk_r_monop (r_parens, *inner);
+ if (!parens)
+ goto space_error;
+ parens->params.intval = this_group_regnum;
+ *inner = parens;
+ break;
+ }
+ }
+
+ case '|': /* `\|'. */
+ if ((syntax & RE_LIMITED_OPS) || (syntax & RE_NO_BK_VBAR))
+ goto normal_backslash;
+ handle_alt:
+ if (syntax & RE_LIMITED_OPS)
+ goto normal_char;
+
+ {
+ struct rexp_node * alt;
+
+ alt = rx_mk_r_binop (r_alternate, *top_expression, 0);
+ if (!alt)
+ goto space_error;
+ *top_expression = alt;
+ last_expression = &alt->params.pair.right;
+ last_non_regular_expression = &alt->params.pair.right;
+ }
+ break;
+
+
+ case '{':
+ /* If \{ is a literal. */
+ if (!(syntax & RE_INTERVALS)
+ /* If we're at `\{' and it's not the open-interval
+ operator. */
+ || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ || (p - 2 == pattern && p == pend))
+ goto normal_backslash;
+
+ handle_interval:
+ {
+ /* If got here, then the syntax allows intervals.
+ */
+
+ /* At least (most) this many matches must be made.
+ */
+ int lower_bound;
+ int upper_bound;
+
+ lower_bound = -1;
+ upper_bound = -1;
+
+ /* We're about to parse the bounds of the interval.
+ * It may turn out that this isn't an interval after
+ * all, in which case these same characters will have
+ * to be reparsed as literals. This remembers
+ * the backtrack point in the parse:
+ */
+ beg_interval = p - 1;
+
+ if (p == pend)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ { compile_error = REG_EBRACE; goto error_return; }
+ }
+
+ GET_UNSIGNED_NUMBER (lower_bound);
+
+ if (c == ',')
+ {
+ GET_UNSIGNED_NUMBER (upper_bound);
+ if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+ }
+ else
+ /* Interval such as `{n}' => match exactly n times.
+ */
+ upper_bound = lower_bound;
+
+ if (lower_bound < 0
+ || upper_bound > RE_DUP_MAX
+ || lower_bound > upper_bound)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ { compile_error = REG_BADBR; goto error_return; }
+ }
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (c != '\\') { compile_error = REG_EBRACE; goto error_return; }
+ PATFETCH (c);
+ }
+
+ if (c != '}')
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ { compile_error = REG_BADBR; goto error_return; }
+ }
+
+ /* We just parsed a valid interval.
+ * lower_bound and upper_bound are set.
+ */
+
+ /* If it's invalid to have no preceding re.
+ */
+ if (pointless_if_repeated (*last_expression))
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ { compile_error = REG_BADRPT; goto error_return; }
+ else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+ /* treat the interval spec as literal chars. */
+ goto unfetch_interval;
+ }
+
+ {
+ struct rexp_node * interval;
+
+ if (*last_expression && ((*last_expression)->type == r_string))
+ if (factor_string (&last_expression, cset_size))
+ goto space_error;
+ interval = rx_mk_r_monop (r_interval, *last_expression);
+ if (!interval)
+ goto space_error;
+ interval->params.intval = lower_bound;
+ interval->params.intval2 = upper_bound;
+ *last_expression = interval;
+ last_non_regular_expression = last_expression;
+ }
+ beg_interval = 0;
+ }
+ break;
+
+ unfetch_interval:
+ /* If an invalid interval, match the characters as literals. */
+ p = beg_interval;
+ beg_interval = 0;
+
+ /* normal_char and normal_backslash need `c'. */
+ PATFETCH (c);
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if ((p > pattern) && (p[-1] == '\\'))
+ goto normal_backslash;
+ }
+ goto normal_char;
+
+#ifdef emacs
+ /* There is no way to specify the before_dot and after_dot
+ * operators. rms says this is ok. --karl
+ */
+ case '=':
+ side = '=';
+ goto add_side_effect;
+ break;
+
+ case 's':
+ case 'S':
+ {
+ rx_Bitset cs;
+ struct rexp_node * set;
+
+ cs = rx_cset (&cset_size);
+ if (!cs)
+ goto space_error;
+ set = rx_mk_r_cset (r_cset, cset_size, cs);
+ if (!set)
+ {
+ rx_free_cset (cs);
+ goto space_error;
+ }
+ if (c == 'S')
+ rx_bitset_universe (cset_size, cs);
+
+ PATFETCH (c);
+ {
+ int x;
+ enum syntaxcode code = syntax_spec_code [c];
+ for (x = 0; x < 256; ++x)
+ {
+
+ if (SYNTAX (x) == code)
+ {
+ rx_Bitset it =
+ inverse_translation (n_members,
+ cset_size, validate_inv_tr,
+ inverse_translate,
+ translate, x);
+ rx_bitset_xor (cset_size, cs, it);
+ }
+ }
+ }
+ append = set;
+ goto append_node;
+ }
+ break;
+#endif /* emacs */
+
+
+ case 'w':
+ case 'W':
+ {
+ rx_Bitset cs;
+ struct rexp_node * n;
+
+ cs = rx_cset (cset_size);
+ n = (cs ? rx_mk_r_cset (r_cset, cset_size, cs) : 0);
+ if (!(cs && n))
+ {
+ if (cs)
+ rx_free_cset (cs);
+ goto space_error;
+ }
+ if (c == 'W')
+ rx_bitset_universe (cset_size ,cs);
+ {
+ int x;
+ for (x = cset_size - 1; x > 0; --x)
+ if (SYNTAX(x) & Sword)
+ RX_bitset_toggle (cs, x);
+ }
+ append = n;
+ goto append_node;
+ }
+ break;
+
+ case '<':
+ side = '<';
+ goto add_side_effect;
+ break;
+
+ case '>':
+ side = '>';
+ goto add_side_effect;
+ break;
+
+ case 'b':
+ side = 'b';
+ goto add_side_effect;
+ break;
+
+ case 'B':
+ side = 'B';
+ goto add_side_effect;
+ break;
+
+ case '`':
+ side = '`';
+ goto add_side_effect;
+ break;
+
+ case '\'':
+ side = '\'';
+ goto add_side_effect;
+ break;
+
+ add_side_effect:
+ {
+ struct rexp_node * se;
+ se = rx_mk_r_int (r_context, side);
+ if (!se)
+ goto space_error;
+ append = se;
+ goto append_node;
+ }
+ break;
+
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (syntax & RE_NO_BK_REFS)
+ goto normal_char;
+
+ c1 = c - '0';
+
+ /* Can't back reference to a subexpression if inside of it. */
+ if (group_in_compile_stack (compile_stack, c1))
+ goto normal_char;
+
+ if (c1 > regnum)
+ { compile_error = REG_ESUBREG; goto error_return; }
+
+ side = c;
+ goto add_side_effect;
+ break;
+
+ case '+':
+ case '?':
+ if (syntax & RE_BK_PLUS_QM)
+ goto handle_plus;
+ else
+ goto normal_backslash;
+
+ default:
+ normal_backslash:
+ /* You might think it would be useful for \ to mean
+ * not to translate; but if we don't translate it
+ * it will never match anything.
+ */
+ c = TRANSLATE (c);
+ goto normal_char;
+ }
+ break;
+
+
+ default:
+ /* Expects the character in `c'. */
+ normal_char:
+ {
+ rx_Bitset cs;
+ struct rexp_node * match;
+ rx_Bitset it;
+
+ it = inverse_translation (n_members,
+ cset_size, validate_inv_tr,
+ inverse_translate, translate, c);
+
+ if (1 != n_members[c])
+ {
+ cs = rx_cset (cset_size);
+ match = (cs ? rx_mk_r_cset (r_cset, cset_size, cs) : 0);
+ if (!(cs && match))
+ {
+ if (cs)
+ rx_free_cset (cs);
+ goto space_error;
+ }
+ rx_bitset_union (CHAR_SET_SIZE, cs, it);
+ append = match;
+ goto append_node;
+ }
+ else
+ {
+ if (*last_expression && (*last_expression)->type == r_string)
+ {
+ if (rx_adjoin_string (&((*last_expression)->params.cstr), c))
+ goto space_error;
+ break;
+ }
+ else
+ {
+ append = rx_mk_r_str (r_string, c);
+ if(!append)
+ goto space_error;
+ goto append_node;
+ }
+ }
+ break;
+
+ append_node:
+ /* This genericly appends the rexp APPEND to *LAST_EXPRESSION
+ * and then parses the next character normally.
+ */
+ if (RX_regular_node_type (append->type))
+ {
+ if (!*last_expression)
+ *last_expression = append;
+ else
+ {
+ struct rexp_node * concat;
+ concat = rx_mk_r_binop (r_concat,
+ *last_expression, append);
+ if (!concat)
+ goto space_error;
+ *last_expression = concat;
+ last_expression = &concat->params.pair.right;
+ }
+ }
+ else
+ {
+ if (!*last_non_regular_expression)
+ {
+ *last_non_regular_expression = append;
+ last_expression = last_non_regular_expression;
+ }
+ else
+ {
+ struct rexp_node * concat;
+ concat = rx_mk_r_binop (r_concat,
+ *last_non_regular_expression, append);
+ if (!concat)
+ goto space_error;
+ *last_non_regular_expression = concat;
+ last_non_regular_expression = &concat->params.pair.right;
+ last_expression = last_non_regular_expression;
+ }
+ }
+ }
+ } /* switch (c) */
+ } /* while p != pend */
+
+
+ /* Through the pattern now. */
+
+ if (!COMPILE_STACK_EMPTY)
+ { compile_error = REG_EPAREN; goto error_return; }
+ free (compile_stack.stack);
+
+
+ *rexp_p = rexp;
+ return REG_NOERROR;
+
+ space_error:
+ compile_error = REG_ESPACE;
+
+ error_return:
+ free (compile_stack.stack);
+ /* Free expressions pushed onto the compile stack! */
+ if (rexp)
+ rx_free_rexp (rexp);
+ return compile_error;
+}
+
+
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXGNUCOMPH
+#define RXGNUCOMPH
+/* Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+#include "rxcset.h"
+#include "rxnode.h"
+
+
+\f
+
+/* This is an array of error messages corresponding to the error codes.
+ */
+extern const char *rx_error_msg[];
+
+
+\f
+/* {Syntax Bits}
+ */
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+
+enum RE_SYNTAX_BITS
+{
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+ RE_BACKSLASH_ESCAPE_IN_LISTS = (1),
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+ RE_BK_PLUS_QM = (RE_BACKSLASH_ESCAPE_IN_LISTS << 1),
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+ RE_CHAR_CLASSES = (RE_BK_PLUS_QM << 1),
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions, of course).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX draft 11.2 says that * etc. in leading positions is undefined.
+ We already implemented a previous draft which made those constructs
+ invalid, though, so we haven't changed the code back. */
+ RE_CONTEXT_INDEP_ANCHORS = (RE_CHAR_CLASSES << 1),
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+ RE_CONTEXT_INDEP_OPS = (RE_CONTEXT_INDEP_ANCHORS << 1),
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator. */
+ RE_CONTEXT_INVALID_OPS = (RE_CONTEXT_INDEP_OPS << 1),
+
+/* If this bit is set, then . matches newline.
+ If not set, then it doesn't. */
+ RE_DOT_NEWLINE = (RE_CONTEXT_INVALID_OPS << 1),
+
+/* If this bit is set, then . doesn't match NUL.
+ If not set, then it does. */
+ RE_DOT_NOT_NULL = (RE_DOT_NEWLINE << 1),
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+ RE_HAT_LISTS_NOT_NEWLINE = (RE_DOT_NOT_NULL << 1),
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+ RE_INTERVALS = (RE_HAT_LISTS_NOT_NEWLINE << 1),
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+ RE_LIMITED_OPS = (RE_INTERVALS << 1),
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+ RE_NEWLINE_ALT = (RE_LIMITED_OPS << 1),
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+ are literals.
+ If not set, then `\{...\}' defines an interval. */
+ RE_NO_BK_BRACES = (RE_NEWLINE_ALT << 1),
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+ RE_NO_BK_PARENS = (RE_NO_BK_BRACES << 1),
+
+/* If this bit is set, then \<digit> matches <digit>.
+ If not set, then \<digit> is a back-reference. */
+ RE_NO_BK_REFS = (RE_NO_BK_PARENS << 1),
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+ RE_NO_BK_VBAR = (RE_NO_BK_REFS << 1),
+
+/* If this bit is set, then an ending range point collating higher
+ than the starting range point, as in [z-a], is invalid.
+ If not set, then when ending range point collates higher than the
+ starting range point, the range is ignored. */
+ RE_NO_EMPTY_RANGES = (RE_NO_BK_VBAR << 1),
+
+/* If this bit is set, then an unmatched ) is ordinary.
+ If not set, then an unmatched ) is invalid. */
+ RE_UNMATCHED_RIGHT_PAREN_ORD = (RE_NO_EMPTY_RANGES << 1),
+
+ RE_SYNTAX_EMACS = 0,
+
+ RE_SYNTAX_AWK = (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS
+ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES
+ | RE_UNMATCHED_RIGHT_PAREN_ORD),
+
+ RE_SYNTAX_GREP = (RE_BK_PLUS_QM | RE_CHAR_CLASSES
+ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS
+ | RE_NEWLINE_ALT),
+
+ RE_SYNTAX_EGREP = (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS
+ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE
+ | RE_NEWLINE_ALT | RE_NO_BK_PARENS
+ | RE_NO_BK_VBAR),
+
+ RE_SYNTAX_POSIX_EGREP = (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES),
+
+ /* Syntax bits common to both basic and extended POSIX regex syntax. */
+ _RE_SYNTAX_POSIX_COMMON = (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL
+ | RE_INTERVALS | RE_NO_EMPTY_RANGES),
+
+ RE_SYNTAX_POSIX_BASIC = (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM),
+
+ /* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+ RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. */
+
+ RE_SYNTAX_POSIX_MINIMAL_BASIC = (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS),
+
+ RE_SYNTAX_POSIX_EXTENDED = (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS
+ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES
+ | RE_NO_BK_PARENS | RE_NO_BK_VBAR
+ | RE_UNMATCHED_RIGHT_PAREN_ORD),
+
+ /* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+ replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */
+ RE_SYNTAX_POSIX_MINIMAL_EXTENDED = (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS
+ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS
+ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD),
+
+ RE_SYNTAX_SED = RE_SYNTAX_POSIX_BASIC,
+
+ RE_SYNTAX_POSIX_AWK = (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+};
+\f
+
+/* Maximum number of duplicates an interval can allow. Some systems
+ (erroneously) define this in other header files, but we want our
+ value, so remove any previous define. */
+#undef RE_DUP_MAX
+#define RE_DUP_MAX ((1 << 15) - 1)
+
+
+\f
+#ifdef __STDC__
+extern int rx_parse (struct rexp_node ** rexp_p,
+ const char *pattern,
+ int size,
+ unsigned long syntax,
+ int cset_size,
+ unsigned char *translate);
+
+#else /* STDC */
+extern int rx_parse ();
+
+#endif /* STDC */
+
+
+#endif /* RXGNUCOMPH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+/*
+ * Tom Lord (lord@cygnus.com, lord@gnu.ai.mit.edu)
+ */
+\f
+
+#include "rxall.h"
+#include "rxhash.h"
+
+\f
+
+#ifdef __STDC__
+static struct rx_hash *
+default_hash_alloc (struct rx_hash_rules * rules)
+#else
+static struct rx_hash *
+default_hash_alloc (rules)
+ struct rx_hash_rules * rules;
+#endif
+{
+ return (struct rx_hash *)malloc (sizeof (struct rx_hash));
+}
+
+
+#ifdef __STDC__
+static struct rx_hash_item *
+default_hash_item_alloc (struct rx_hash_rules * rules, void * value)
+#else
+static struct rx_hash_item *
+default_hash_item_alloc (rules, value)
+ struct rx_hash_rules * rules;
+ void * value;
+#endif
+{
+ struct rx_hash_item * it;
+ it = (struct rx_hash_item *)malloc (sizeof (*it));
+ if (it)
+ {
+ it->data = value;
+ it->binding = 0;
+ }
+ return it;
+}
+
+
+#ifdef __STDC__
+static void
+default_free_hash (struct rx_hash * tab,
+ struct rx_hash_rules * rules)
+#else
+static void
+default_free_hash (tab, rules)
+ struct rx_hash * tab;
+ struct rx_hash_rules * rules;
+#endif
+{
+ free ((char *)tab);
+}
+
+
+#ifdef __STDC__
+static void
+default_free_hash_item (struct rx_hash_item * item,
+ struct rx_hash_rules * rules)
+#else
+static void
+default_free_hash_item (item, rules)
+ struct rx_hash_item * item;
+ struct rx_hash_rules * rules;
+#endif
+{
+ free ((char *)item);
+}
+
+#ifdef __STDC__
+static int
+default_eq (void * va, void * vb)
+#else
+static int
+default_eq (va, vb)
+ void * va;
+ void * vb;
+#endif
+{
+ return va == vb;
+}
+
+\f
+
+#define EQ(rules) ((rules && rules->eq) ? rules->eq : default_eq)
+#define HASH_ALLOC(rules) ((rules && rules->hash_alloc) ? rules->hash_alloc : default_hash_alloc)
+#define FREE_HASH(rules) ((rules && rules->free_hash) ? rules->free_hash : default_free_hash)
+#define ITEM_ALLOC(rules) ((rules && rules->hash_item_alloc) ? rules->hash_item_alloc : default_hash_item_alloc)
+#define FREE_HASH_ITEM(rules) ((rules && rules->free_hash_item) ? rules->free_hash_item : default_free_hash_item)
+
+\f
+static unsigned long rx_hash_masks[4] =
+{
+ 0x12488421,
+ 0x96699669,
+ 0xbe7dd7eb,
+ 0xffffffff
+};
+
+/* hash to bucket */
+#define JOIN_BYTE(H, B) (((H) + ((H) << 3) + (B)) & 0xf)
+
+#define H2B(X) JOIN_BYTE (JOIN_BYTE (JOIN_BYTE ((X & 0xf), ((X>>8) & 0xf)), ((X>>16) & 0xf)), ((X>>24) & 0xf))
+
+#define BKTS 16
+
+/* Hash tables */
+#ifdef __STDC__
+struct rx_hash_item *
+rx_hash_find (struct rx_hash * table,
+ unsigned long hash,
+ void * value,
+ struct rx_hash_rules * rules)
+#else
+struct rx_hash_item *
+rx_hash_find (table, hash, value, rules)
+ struct rx_hash * table;
+ unsigned long hash;
+ void * value;
+ struct rx_hash_rules * rules;
+#endif
+{
+ rx_hash_eq eq = EQ (rules);
+ int maskc = 0;
+ long mask = rx_hash_masks [0];
+ int bucket = H2B(hash & mask);
+
+ while (RX_bitset_member (&table->nested_p, bucket))
+ {
+ table = (struct rx_hash *)(table->children [bucket]);
+ ++maskc;
+ mask = rx_hash_masks[maskc];
+ bucket = H2B (hash & mask);
+ }
+
+ {
+ struct rx_hash_item * it;
+ it = (struct rx_hash_item *)(table->children[bucket]);
+ while (it)
+ if (eq (it->data, value))
+ return it;
+ else
+ it = it->next_same_hash;
+ }
+
+ return 0;
+}
+
+
+#ifdef __STDC__
+static int
+listlen (struct rx_hash_item * bucket)
+#else
+static int
+listlen (bucket)
+ struct rx_hash_item * bucket;
+#endif
+{
+ int i;
+ for (i = 0; bucket; ++i, bucket = bucket->next_same_hash)
+ ;
+ return i;
+}
+
+#ifdef __STDC__
+static int
+overflows (struct rx_hash_item * bucket)
+#else
+static int
+overflows (bucket)
+ struct rx_hash_item * bucket;
+#endif
+{
+ return !( bucket
+ && bucket->next_same_hash
+ && bucket->next_same_hash->next_same_hash
+ && bucket->next_same_hash->next_same_hash->next_same_hash);
+}
+
+
+#ifdef __STDC__
+struct rx_hash_item *
+rx_hash_store (struct rx_hash * table,
+ unsigned long hash,
+ void * value,
+ struct rx_hash_rules * rules)
+#else
+struct rx_hash_item *
+rx_hash_store (table, hash, value, rules)
+ struct rx_hash * table;
+ unsigned long hash;
+ void * value;
+ struct rx_hash_rules * rules;
+#endif
+{
+ rx_hash_eq eq = EQ (rules);
+ int maskc = 0;
+ long mask = rx_hash_masks [0];
+ int bucket = H2B(hash & mask);
+ int depth = 0;
+
+ while (RX_bitset_member (&table->nested_p, bucket))
+ {
+ table = (struct rx_hash *)(table->children [bucket]);
+ ++maskc;
+ mask = rx_hash_masks[maskc];
+ bucket = H2B(hash & mask);
+ ++depth;
+ }
+
+ {
+ struct rx_hash_item * it;
+ it = (struct rx_hash_item *)(table->children[bucket]);
+ while (it)
+ if (eq (it->data, value))
+ return it;
+ else
+ it = it->next_same_hash;
+ }
+
+ {
+ if ( (depth < 3)
+ && (overflows ((struct rx_hash_item *)table->children [bucket])))
+ {
+ struct rx_hash * newtab;
+ newtab = (struct rx_hash *) HASH_ALLOC(rules) (rules);
+ if (!newtab)
+ goto add_to_bucket;
+ rx_bzero ((char *)newtab, sizeof (*newtab));
+ newtab->parent = table;
+ {
+ struct rx_hash_item * them;
+ unsigned long newmask;
+ them = (struct rx_hash_item *)table->children[bucket];
+ newmask = rx_hash_masks[maskc + 1];
+ while (them)
+ {
+ struct rx_hash_item * save = them->next_same_hash;
+ int new_buck = H2B(them->hash & newmask);
+ them->next_same_hash = ((struct rx_hash_item *)
+ newtab->children[new_buck]);
+ ((struct rx_hash_item **)newtab->children)[new_buck] = them;
+ them->table = newtab;
+ them = save;
+ ++newtab->refs;
+ --table->refs;
+ }
+ ((struct rx_hash **)table->children)[bucket] = newtab;
+ RX_bitset_enjoin (&table->nested_p, bucket);
+ ++table->refs;
+ table = newtab;
+ bucket = H2B(hash & newmask);
+ }
+ }
+ }
+ add_to_bucket:
+ {
+ struct rx_hash_item * it = ((struct rx_hash_item *)
+ ITEM_ALLOC(rules) (rules, value));
+ if (!it)
+ return 0;
+ it->hash = hash;
+ it->table = table;
+ /* DATA and BINDING are to be set in hash_item_alloc */
+ it->next_same_hash = (struct rx_hash_item *)table->children [bucket];
+ ((struct rx_hash_item **)table->children)[bucket] = it;
+ ++table->refs;
+ return it;
+ }
+}
+
+
+#ifdef __STDC__
+void
+rx_hash_free (struct rx_hash_item * it, struct rx_hash_rules * rules)
+#else
+void
+rx_hash_free (it, rules)
+ struct rx_hash_item * it;
+ struct rx_hash_rules * rules;
+#endif
+{
+ if (it)
+ {
+ struct rx_hash * table = it->table;
+ unsigned long hash = it->hash;
+ int depth = (table->parent
+ ? (table->parent->parent
+ ? (table->parent->parent->parent
+ ? 3
+ : 2)
+ : 1)
+ : 0);
+ int bucket = H2B (hash & rx_hash_masks [depth]);
+ struct rx_hash_item ** pos
+ = (struct rx_hash_item **)&table->children [bucket];
+
+ while (*pos != it)
+ pos = &(*pos)->next_same_hash;
+ *pos = it->next_same_hash;
+ FREE_HASH_ITEM(rules) (it, rules);
+ --table->refs;
+ while (!table->refs && depth)
+ {
+ struct rx_hash * save = table;
+ table = table->parent;
+ --depth;
+ bucket = H2B(hash & rx_hash_masks [depth]);
+ --table->refs;
+ table->children[bucket] = 0;
+ RX_bitset_remove (&table->nested_p, bucket);
+ FREE_HASH (rules) (save, rules);
+ }
+ }
+}
+
+#ifdef __STDC__
+void
+rx_free_hash_table (struct rx_hash * tab, rx_hash_freefn freefn,
+ struct rx_hash_rules * rules)
+#else
+void
+rx_free_hash_table (tab, freefn, rules)
+ struct rx_hash * tab;
+ rx_hash_freefn freefn;
+ struct rx_hash_rules * rules;
+#endif
+{
+ int x;
+
+ for (x = 0; x < BKTS; ++x)
+ if (RX_bitset_member (&tab->nested_p, x))
+ {
+ rx_free_hash_table ((struct rx_hash *)tab->children[x],
+ freefn, rules);
+ FREE_HASH (rules) ((struct rx_hash *)tab->children[x], rules);
+ }
+ else
+ {
+ struct rx_hash_item * them = (struct rx_hash_item *)tab->children[x];
+ while (them)
+ {
+ struct rx_hash_item * that = them;
+ them = that->next_same_hash;
+ freefn (that);
+ FREE_HASH_ITEM (rules) (that, rules);
+ }
+ }
+}
+
+
+
+#ifdef __STDC__
+int
+rx_count_hash_nodes (struct rx_hash * st)
+#else
+int
+rx_count_hash_nodes (st)
+ struct rx_hash * st;
+#endif
+{
+ int x;
+ int count = 0;
+ for (x = 0; x < BKTS; ++x)
+ count += ((RX_bitset_member (&st->nested_p, x))
+ ? rx_count_hash_nodes ((struct rx_hash *)st->children[x])
+ : listlen ((struct rx_hash_item *)(st->children[x])));
+
+ return count;
+}
+
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXHASHH
+#define RXHASHH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+/*
+ * Tom Lord (lord@cygnus.com, lord@gnu.ai.mit.edu)
+ */
+\f
+
+#include "rxbitset.h"
+
+/* giant inflatable hash trees */
+
+struct rx_hash_item
+{
+ struct rx_hash_item * next_same_hash;
+ struct rx_hash * table;
+ unsigned long hash;
+ void * data;
+ void * binding;
+};
+
+struct rx_hash
+{
+ struct rx_hash * parent;
+ int refs;
+ RX_subset nested_p;
+ void ** children[16];
+};
+
+struct rx_hash_rules;
+
+/* rx_hash_eq should work like the == operator. */
+
+#ifdef __STDC__
+typedef int (*rx_hash_eq)(void *, void *);
+typedef struct rx_hash * (*rx_alloc_hash)(struct rx_hash_rules *);
+typedef void (*rx_free_hash)(struct rx_hash *,
+ struct rx_hash_rules *);
+typedef struct rx_hash_item * (*rx_alloc_hash_item)(struct rx_hash_rules *,
+ void *);
+typedef void (*rx_free_hash_item)(struct rx_hash_item *,
+ struct rx_hash_rules *);
+typedef void (*rx_hash_freefn) (struct rx_hash_item * it);
+#else
+typedef int (*rx_hash_eq)();
+typedef struct rx_hash * (*rx_alloc_hash)();
+typedef void (*rx_free_hash)();
+typedef struct rx_hash_item * (*rx_alloc_hash_item)();
+typedef void (*rx_free_hash_item)();
+typedef void (*rx_hash_freefn) ();
+#endif
+
+struct rx_hash_rules
+{
+ rx_hash_eq eq;
+ rx_alloc_hash hash_alloc;
+ rx_free_hash free_hash;
+ rx_alloc_hash_item hash_item_alloc;
+ rx_free_hash_item free_hash_item;
+};
+
+\f
+#ifdef __STDC__
+extern struct rx_hash_item * rx_hash_find (struct rx_hash * table,
+ unsigned long hash,
+ void * value,
+ struct rx_hash_rules * rules);
+extern struct rx_hash_item * rx_hash_store (struct rx_hash * table,
+ unsigned long hash,
+ void * value,
+ struct rx_hash_rules * rules);
+extern void rx_hash_free (struct rx_hash_item * it, struct rx_hash_rules * rules);
+extern void rx_free_hash_table (struct rx_hash * tab, rx_hash_freefn freefn,
+ struct rx_hash_rules * rules);
+extern int rx_count_hash_nodes (struct rx_hash * st);
+
+#else /* STDC */
+extern struct rx_hash_item * rx_hash_find ();
+extern struct rx_hash_item * rx_hash_store ();
+extern void rx_hash_free ();
+extern void rx_free_hash_table ();
+extern int rx_count_hash_nodes ();
+
+#endif /* STDC */
+
+
+
+
+
+#endif /* RXHASHH */
+
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+/*
+ * Tom Lord (lord@cygnus.com, lord@gnu.ai.mit.edu)
+ */
+\f
+
+#include "rxall.h"
+#include "rxnfa.h"
+
+\f
+
+#define remalloc(M, S) (M ? realloc (M, S) : malloc (S))
+
+\f
+/* {Low Level Data Structure Code}
+ */
+
+/* Constructs a new nfa node. */
+#ifdef __STDC__
+struct rx_nfa_state *
+rx_nfa_state (struct rx *rx)
+#else
+struct rx_nfa_state *
+rx_nfa_state (rx)
+ struct rx *rx;
+#endif
+{
+ struct rx_nfa_state * n = (struct rx_nfa_state *)malloc (sizeof (*n));
+ if (!n)
+ return 0;
+ rx_bzero ((char *)n, sizeof (*n));
+ n->next = rx->nfa_states;
+ rx->nfa_states = n;
+ return n;
+}
+
+
+#ifdef __STDC__
+static void
+rx_free_nfa_state (struct rx_nfa_state * n)
+#else
+static void
+rx_free_nfa_state (n)
+ struct rx_nfa_state * n;
+#endif
+{
+ free ((char *)n);
+}
+
+
+/* This adds an edge between two nodes, but doesn't initialize the
+ * edge label.
+ */
+
+#ifdef __STDC__
+struct rx_nfa_edge *
+rx_nfa_edge (struct rx *rx,
+ enum rx_nfa_etype type,
+ struct rx_nfa_state *start,
+ struct rx_nfa_state *dest)
+#else
+struct rx_nfa_edge *
+rx_nfa_edge (rx, type, start, dest)
+ struct rx *rx;
+ enum rx_nfa_etype type;
+ struct rx_nfa_state *start;
+ struct rx_nfa_state *dest;
+#endif
+{
+ struct rx_nfa_edge *e;
+ e = (struct rx_nfa_edge *)malloc (sizeof (*e));
+ if (!e)
+ return 0;
+ e->next = start->edges;
+ start->edges = e;
+ e->type = type;
+ e->dest = dest;
+ return e;
+}
+
+
+#ifdef __STDC__
+static void
+rx_free_nfa_edge (struct rx_nfa_edge * e)
+#else
+static void
+rx_free_nfa_edge (e)
+ struct rx_nfa_edge * e;
+#endif
+{
+ free ((char *)e);
+}
+
+
+/* This constructs a POSSIBLE_FUTURE, which is a kind epsilon-closure
+ * of an NFA. These are added to an nfa automaticly by eclose_nfa.
+ */
+
+#ifdef __STDC__
+static struct rx_possible_future *
+rx_possible_future (struct rx * rx,
+ struct rx_se_list * effects)
+#else
+static struct rx_possible_future *
+rx_possible_future (rx, effects)
+ struct rx * rx;
+ struct rx_se_list * effects;
+#endif
+{
+ struct rx_possible_future *ec;
+ ec = (struct rx_possible_future *) malloc (sizeof (*ec));
+ if (!ec)
+ return 0;
+ ec->destset = 0;
+ ec->next = 0;
+ ec->effects = effects;
+ return ec;
+}
+
+
+#ifdef __STDC__
+static void
+rx_free_possible_future (struct rx_possible_future * pf)
+#else
+static void
+rx_free_possible_future (pf)
+ struct rx_possible_future * pf;
+#endif
+{
+ free ((char *)pf);
+}
+
+
+#ifdef __STDC__
+static void
+rx_free_nfa_graph (struct rx *rx)
+#else
+static void
+rx_free_nfa_graph (rx)
+ struct rx *rx;
+#endif
+{
+ while (rx->nfa_states)
+ {
+ while (rx->nfa_states->edges)
+ {
+ switch (rx->nfa_states->edges->type)
+ {
+ case ne_cset:
+ rx_free_cset (rx->nfa_states->edges->params.cset);
+ break;
+ default:
+ break;
+ }
+ {
+ struct rx_nfa_edge * e;
+ e = rx->nfa_states->edges;
+ rx->nfa_states->edges = rx->nfa_states->edges->next;
+ rx_free_nfa_edge (e);
+ }
+ } /* while (rx->nfa_states->edges) */
+ {
+ /* Iterate over the partial epsilon closures of rx->nfa_states */
+ struct rx_possible_future * pf = rx->nfa_states->futures;
+ while (pf)
+ {
+ struct rx_possible_future * pft = pf;
+ pf = pf->next;
+ rx_free_possible_future (pft);
+ }
+ }
+ {
+ struct rx_nfa_state *n;
+ n = rx->nfa_states;
+ rx->nfa_states = rx->nfa_states->next;
+ rx_free_nfa_state (n);
+ }
+ }
+}
+
+
+\f
+/* {Translating a Syntax Tree into an NFA}
+ *
+ */
+
+
+/* This is the Thompson regexp->nfa algorithm.
+ * It is modified to allow for `side-effect epsilons.' Those are
+ * edges that are taken whenever a similarly placed epsilon edge
+ * would be, but which also imply that some side effect occurs
+ * when the edge is taken.
+ *
+ * Side effects are used to model parts of the pattern langauge
+ * that are not regular.
+ */
+
+#ifdef __STDC__
+int
+rx_build_nfa (struct rx *rx,
+ struct rexp_node *rexp,
+ struct rx_nfa_state **start,
+ struct rx_nfa_state **end)
+#else
+int
+rx_build_nfa (rx, rexp, start, end)
+ struct rx *rx;
+ struct rexp_node *rexp;
+ struct rx_nfa_state **start;
+ struct rx_nfa_state **end;
+#endif
+{
+ struct rx_nfa_edge *edge;
+
+ /* Start & end nodes may have been allocated by the caller. */
+ *start = *start ? *start : rx_nfa_state (rx);
+
+ if (!*start)
+ return 0;
+
+ if (!rexp)
+ {
+ *end = *start;
+ return 1;
+ }
+
+ *end = *end ? *end : rx_nfa_state (rx);
+
+ if (!*end)
+ {
+ rx_free_nfa_state (*start);
+ return 0;
+ }
+
+ switch (rexp->type)
+ {
+ case r_cset:
+ edge = rx_nfa_edge (rx, ne_cset, *start, *end);
+ (*start)->has_cset_edges = 1;
+ if (!edge)
+ return 0;
+ edge->params.cset = rx_copy_cset (rx->local_cset_size,
+ rexp->params.cset);
+ if (!edge->params.cset)
+ {
+ rx_free_nfa_edge (edge);
+ return 0;
+ }
+ return 1;
+
+ case r_string:
+ {
+ if (rexp->params.cstr.len == 1)
+ {
+ edge = rx_nfa_edge (rx, ne_cset, *start, *end);
+ (*start)->has_cset_edges = 1;
+ if (!edge)
+ return 0;
+ edge->params.cset = rx_cset (rx->local_cset_size);
+ if (!edge->params.cset)
+ {
+ rx_free_nfa_edge (edge);
+ return 0;
+ }
+ RX_bitset_enjoin (edge->params.cset, rexp->params.cstr.contents[0]);
+ return 1;
+ }
+ else
+ {
+ struct rexp_node copied;
+ struct rx_nfa_state * shared;
+
+ copied = *rexp;
+ shared = 0;
+ copied.params.cstr.len--;
+ copied.params.cstr.contents++;
+ if (!rx_build_nfa (rx, &copied, &shared, end))
+ return 0;
+ copied.params.cstr.len = 1;
+ copied.params.cstr.contents--;
+ return rx_build_nfa (rx, &copied, start, &shared);
+ }
+ }
+
+ case r_opt:
+ return (rx_build_nfa (rx, rexp->params.pair.left, start, end)
+ && rx_nfa_edge (rx, ne_epsilon, *start, *end));
+
+ case r_plus:
+ {
+ struct rx_nfa_state * star_start = 0;
+ struct rx_nfa_state * star_end = 0;
+ struct rx_nfa_state * shared;
+
+ shared = 0;
+ if (!rx_build_nfa (rx, rexp->params.pair.left, start, &shared))
+ return 0;
+ return (rx_build_nfa (rx, rexp->params.pair.left,
+ &star_start, &star_end)
+ && star_start
+ && star_end
+ && rx_nfa_edge (rx, ne_epsilon, star_start, star_end)
+ && rx_nfa_edge (rx, ne_epsilon, shared, star_start)
+ && rx_nfa_edge (rx, ne_epsilon, star_end, *end)
+ && rx_nfa_edge (rx, ne_epsilon, star_end, star_start));
+ }
+
+ case r_interval:
+ case r_star:
+ {
+ struct rx_nfa_state * star_start = 0;
+ struct rx_nfa_state * star_end = 0;
+ return (rx_build_nfa (rx, rexp->params.pair.left,
+ &star_start, &star_end)
+ && star_start
+ && star_end
+ && rx_nfa_edge (rx, ne_epsilon, star_start, star_end)
+ && rx_nfa_edge (rx, ne_epsilon, *start, star_start)
+ && rx_nfa_edge (rx, ne_epsilon, star_end, *end)
+
+ && rx_nfa_edge (rx, ne_epsilon, star_end, star_start));
+ }
+
+ case r_cut:
+ {
+ struct rx_nfa_state * cut_end = 0;
+
+ cut_end = rx_nfa_state (rx);
+ if (!(cut_end && rx_nfa_edge (rx, ne_epsilon, *start, cut_end)))
+ {
+ rx_free_nfa_state (*start);
+ rx_free_nfa_state (*end);
+ if (cut_end)
+ rx_free_nfa_state (cut_end);
+ return 0;
+ }
+ cut_end->is_final = rexp->params.intval;
+ return 1;
+ }
+
+ case r_parens:
+ return rx_build_nfa (rx, rexp->params.pair.left, start, end);
+
+ case r_concat:
+ {
+ struct rx_nfa_state *shared = 0;
+ return
+ (rx_build_nfa (rx, rexp->params.pair.left, start, &shared)
+ && rx_build_nfa (rx, rexp->params.pair.right, &shared, end));
+ }
+
+ case r_alternate:
+ {
+ struct rx_nfa_state *ls = 0;
+ struct rx_nfa_state *le = 0;
+ struct rx_nfa_state *rs = 0;
+ struct rx_nfa_state *re = 0;
+ return (rx_build_nfa (rx, rexp->params.pair.left, &ls, &le)
+ && rx_build_nfa (rx, rexp->params.pair.right, &rs, &re)
+ && rx_nfa_edge (rx, ne_epsilon, *start, ls)
+ && rx_nfa_edge (rx, ne_epsilon, *start, rs)
+ && rx_nfa_edge (rx, ne_epsilon, le, *end)
+ && rx_nfa_edge (rx, ne_epsilon, re, *end));
+ }
+
+ case r_context:
+ edge = rx_nfa_edge (rx, ne_side_effect, *start, *end);
+ if (!edge)
+ return 0;
+ edge->params.side_effect = (void *)rexp->params.intval;
+ return 1;
+ }
+
+ /* this should never happen */
+ return 0;
+}
+
+\f
+/* {Low Level Data Structures for the Static NFA->SuperNFA Analysis}
+ *
+ * There are side effect lists -- lists of side effects occuring
+ * along an uninterrupted, acyclic path of side-effect epsilon edges.
+ * Such paths are collapsed to single edges in the course of computing
+ * epsilon closures. The resulting single edges are labled with a list
+ * of all the side effects from the original multi-edge path. Equivalent
+ * lists of side effects are made == by the constructors below.
+ *
+ * There are also nfa state sets. These are used to hold a list of all
+ * states reachable from a starting state for a given type of transition
+ * and side effect list. These are also hash-consed.
+ */
+
+
+
+/* The next several functions compare, construct, etc. lists of side
+ * effects. See ECLOSE_NFA (below) for details.
+ */
+
+/* Ordering of rx_se_list
+ * (-1, 0, 1 return value convention).
+ */
+
+#ifdef __STDC__
+static int
+se_list_cmp (void * va, void * vb)
+#else
+static int
+se_list_cmp (va, vb)
+ void * va;
+ void * vb;
+#endif
+{
+ struct rx_se_list * a = (struct rx_se_list *)va;
+ struct rx_se_list * b = (struct rx_se_list *)vb;
+
+ return ((va == vb)
+ ? 0
+ : (!va
+ ? -1
+ : (!vb
+ ? 1
+ : ((long)a->car < (long)b->car
+ ? 1
+ : ((long)a->car > (long)b->car
+ ? -1
+ : se_list_cmp ((void *)a->cdr, (void *)b->cdr))))));
+}
+
+
+#ifdef __STDC__
+static int
+se_list_equal (void * va, void * vb)
+#else
+static int
+se_list_equal (va, vb)
+ void * va;
+ void * vb;
+#endif
+{
+ return !(se_list_cmp (va, vb));
+}
+
+static struct rx_hash_rules se_list_hash_rules = { se_list_equal, 0, 0, 0, 0 };
+
+
+#ifdef __STDC__
+static struct rx_se_list *
+side_effect_cons (struct rx * rx,
+ void * se, struct rx_se_list * list)
+#else
+static struct rx_se_list *
+side_effect_cons (rx, se, list)
+ struct rx * rx;
+ void * se;
+ struct rx_se_list * list;
+#endif
+{
+ struct rx_se_list * l;
+ l = ((struct rx_se_list *) malloc (sizeof (*l)));
+ if (!l)
+ return 0;
+ l->car = se;
+ l->cdr = list;
+ return l;
+}
+
+
+#ifdef __STDC__
+static struct rx_se_list *
+hash_cons_se_prog (struct rx * rx,
+ struct rx_hash * memo,
+ void * car, struct rx_se_list * cdr)
+#else
+static struct rx_se_list *
+hash_cons_se_prog (rx, memo, car, cdr)
+ struct rx * rx;
+ struct rx_hash * memo;
+ void * car;
+ struct rx_se_list * cdr;
+#endif
+{
+ long hash = (long)car ^ (long)cdr;
+ struct rx_se_list template;
+
+ template.car = car;
+ template.cdr = cdr;
+ {
+ struct rx_hash_item * it = rx_hash_store (memo, hash,
+ (void *)&template,
+ &se_list_hash_rules);
+ if (!it)
+ return 0;
+ if (it->data == (void *)&template)
+ {
+ struct rx_se_list * consed;
+ consed = (struct rx_se_list *) malloc (sizeof (*consed));
+ *consed = template;
+ it->data = (void *)consed;
+ }
+ return (struct rx_se_list *)it->data;
+ }
+}
+
+
+#ifdef __STDC__
+static struct rx_se_list *
+hash_se_prog (struct rx * rx, struct rx_hash * memo, struct rx_se_list * prog)
+#else
+static struct rx_se_list *
+hash_se_prog (rx, memo, prog)
+ struct rx * rx;
+ struct rx_hash * memo;
+ struct rx_se_list * prog;
+#endif
+{
+ struct rx_se_list * answer = 0;
+ while (prog)
+ {
+ answer = hash_cons_se_prog (rx, memo, prog->car, answer);
+ if (!answer)
+ return 0;
+ prog = prog->cdr;
+ }
+ return answer;
+}
+
+
+
+/* {Constructors, etc. for NFA State Sets}
+ */
+
+#ifdef __STDC__
+static int
+nfa_set_cmp (void * va, void * vb)
+#else
+static int
+nfa_set_cmp (va, vb)
+ void * va;
+ void * vb;
+#endif
+{
+ struct rx_nfa_state_set * a = (struct rx_nfa_state_set *)va;
+ struct rx_nfa_state_set * b = (struct rx_nfa_state_set *)vb;
+
+ return ((va == vb)
+ ? 0
+ : (!va
+ ? -1
+ : (!vb
+ ? 1
+ : (a->car->id < b->car->id
+ ? 1
+ : (a->car->id > b->car->id
+ ? -1
+ : nfa_set_cmp ((void *)a->cdr, (void *)b->cdr))))));
+}
+
+#ifdef __STDC__
+static int
+nfa_set_equal (void * va, void * vb)
+#else
+static int
+nfa_set_equal (va, vb)
+ void * va;
+ void * vb;
+#endif
+{
+ return !nfa_set_cmp (va, vb);
+}
+
+static struct rx_hash_rules nfa_set_hash_rules = { nfa_set_equal, 0, 0, 0, 0 };
+
+
+#ifdef __STDC__
+static struct rx_nfa_state_set *
+nfa_set_cons (struct rx * rx,
+ struct rx_hash * memo, struct rx_nfa_state * state,
+ struct rx_nfa_state_set * set)
+#else
+static struct rx_nfa_state_set *
+nfa_set_cons (rx, memo, state, set)
+ struct rx * rx;
+ struct rx_hash * memo;
+ struct rx_nfa_state * state;
+ struct rx_nfa_state_set * set;
+#endif
+{
+ struct rx_nfa_state_set template;
+ struct rx_hash_item * node;
+ template.car = state;
+ template.cdr = set;
+ node = rx_hash_store (memo,
+ (((long)state) >> 8) ^ (long)set,
+ &template, &nfa_set_hash_rules);
+ if (!node)
+ return 0;
+ if (node->data == &template)
+ {
+ struct rx_nfa_state_set * l;
+ l = (struct rx_nfa_state_set *) malloc (sizeof (*l));
+ node->data = (void *) l;
+ if (!l)
+ return 0;
+ *l = template;
+ }
+ return (struct rx_nfa_state_set *)node->data;
+}
+
+
+#ifdef __STDC__
+static struct rx_nfa_state_set *
+nfa_set_enjoin (struct rx * rx,
+ struct rx_hash * memo, struct rx_nfa_state * state,
+ struct rx_nfa_state_set * set)
+#else
+static struct rx_nfa_state_set *
+nfa_set_enjoin (rx, memo, state, set)
+ struct rx * rx;
+ struct rx_hash * memo;
+ struct rx_nfa_state * state;
+ struct rx_nfa_state_set * set;
+#endif
+{
+ if (!set || (state->id < set->car->id))
+ return nfa_set_cons (rx, memo, state, set);
+ if (state->id == set->car->id)
+ return set;
+ else
+ {
+ struct rx_nfa_state_set * newcdr
+ = nfa_set_enjoin (rx, memo, state, set->cdr);
+ if (newcdr != set->cdr)
+ set = nfa_set_cons (rx, memo, set->car, newcdr);
+ return set;
+ }
+}
+
+\f
+/* {Computing Epsilon/Side-effect Closures.}
+ */
+
+struct eclose_frame
+{
+ struct rx_se_list *prog_backwards;
+};
+
+
+/* This is called while computing closures for "outnode".
+ * The current node in the traversal is "node".
+ * "frame" contains a list of a all side effects between
+ * "outnode" and "node" from most to least recent.
+ *
+ * Explores forward from "node", adding new possible
+ * futures to outnode.
+ *
+ * Returns 0 on allocation failure.
+ */
+
+#ifdef __STDC__
+static int
+eclose_node (struct rx *rx, struct rx_nfa_state *outnode,
+ struct rx_nfa_state *node, struct eclose_frame *frame)
+#else
+static int
+eclose_node (rx, outnode, node, frame)
+ struct rx *rx;
+ struct rx_nfa_state *outnode;
+ struct rx_nfa_state *node;
+ struct eclose_frame *frame;
+#endif
+{
+ struct rx_nfa_edge *e = node->edges;
+
+ /* For each node, we follow all epsilon paths to build the closure.
+ * The closure omits nodes that have only epsilon edges.
+ * The closure is split into partial closures -- all the states in
+ * a partial closure are reached by crossing the same list of
+ * of side effects (though not necessarily the same path).
+ */
+ if (node->mark)
+ return 1;
+ node->mark = 1;
+
+ /* If "node" has more than just epsilon and
+ * and side-effect transitions (id >= 0), or is final,
+ * then it has to be added to the possible futures
+ * of "outnode".
+ */
+ if (node->id >= 0 || node->is_final)
+ {
+ struct rx_possible_future **ec;
+ struct rx_se_list * prog_in_order;
+ int cmp;
+
+ prog_in_order = ((struct rx_se_list *)hash_se_prog (rx,
+ &rx->se_list_memo,
+ frame->prog_backwards));
+
+ ec = &outnode->futures;
+
+ while (*ec)
+ {
+ cmp = se_list_cmp ((void *)(*ec)->effects, (void *)prog_in_order);
+ if (cmp <= 0)
+ break;
+ ec = &(*ec)->next;
+ }
+
+ if (!*ec || (cmp < 0))
+ {
+ struct rx_possible_future * pf;
+ pf = rx_possible_future (rx, prog_in_order);
+ if (!pf)
+ return 0;
+ pf->next = *ec;
+ *ec = pf;
+ }
+ if (node->id >= 0)
+ {
+ (*ec)->destset = nfa_set_enjoin (rx, &rx->set_list_memo,
+ node, (*ec)->destset);
+ if (!(*ec)->destset)
+ return 0;
+ }
+ }
+
+ /* Recurse on outgoing epsilon and side effect nodes.
+ */
+ while (e)
+ {
+ switch (e->type)
+ {
+ case ne_epsilon:
+ if (!eclose_node (rx, outnode, e->dest, frame))
+ return 0;
+ break;
+ case ne_side_effect:
+ {
+ frame->prog_backwards = side_effect_cons (rx,
+ e->params.side_effect,
+ frame->prog_backwards);
+ if (!frame->prog_backwards)
+ return 0;
+ if (!eclose_node (rx, outnode, e->dest, frame))
+ return 0;
+ {
+ struct rx_se_list * dying = frame->prog_backwards;
+ frame->prog_backwards = frame->prog_backwards->cdr;
+ free ((char *)dying);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ e = e->next;
+ }
+ node->mark = 0;
+ return 1;
+}
+
+
+#ifdef __STDC__
+struct rx_possible_future *
+rx_state_possible_futures (struct rx * rx, struct rx_nfa_state * n)
+#else
+struct rx_possible_future *
+rx_state_possible_futures (rx, n)
+ struct rx * rx;
+ struct rx_nfa_state * n;
+#endif
+{
+ if (n->futures_computed)
+ return n->futures;
+ else
+ {
+ struct eclose_frame frame;
+ frame.prog_backwards = 0;
+ if (!eclose_node (rx, n, n, &frame))
+ return 0;
+ else
+ {
+ n->futures_computed = 1;
+ return n->futures;
+ }
+ }
+}
+
+
+\f
+/* {Storing the NFA in a Contiguous Region of Memory}
+ */
+
+
+
+#ifdef __STDC__
+static void
+se_memo_freer (struct rx_hash_item * node)
+#else
+static void
+se_memo_freer (node)
+ struct rx_hash_item * node;
+#endif
+{
+ free ((char *)node->data);
+}
+
+
+#ifdef __STDC__
+static void
+nfa_set_freer (struct rx_hash_item * node)
+#else
+static void
+nfa_set_freer (node)
+ struct rx_hash_item * node;
+#endif
+{
+ free ((char *)node->data);
+}
+
+#ifdef __STDC__
+void
+rx_free_nfa (struct rx *rx)
+#else
+void
+rx_free_nfa (rx)
+ struct rx *rx;
+#endif
+{
+ rx_free_hash_table (&rx->se_list_memo, se_memo_freer, &se_list_hash_rules);
+ rx_bzero ((char *)&rx->se_list_memo, sizeof (rx->se_list_memo));
+ rx_free_hash_table (&rx->set_list_memo, nfa_set_freer, &nfa_set_hash_rules);
+ rx_bzero ((char *)&rx->set_list_memo, sizeof (rx->set_list_memo));
+ rx_free_nfa_graph (rx);
+}
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXNFAH
+#define RXNFAH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+/*
+ * Tom Lord (lord@cygnus.com, lord@gnu.ai.mit.edu)
+ */
+\f
+#include "_rx.h"
+#include "rxnode.h"
+\f
+
+/* NFA
+ *
+ * A syntax tree is compiled into an NFA. This page defines the structure
+ * of that NFA.
+ */
+
+struct rx_nfa_state
+{
+ /* These are kept in a list as the NFA is being built.
+ * Here is the link.
+ */
+ struct rx_nfa_state *next;
+
+ /* After the NFA is built, states are given integer id's.
+ * States whose outgoing transitions are all either epsilon or
+ * side effect edges are given ids less than 0. Other states
+ * are given successive non-negative ids starting from 0.
+ *
+ * Here is the id for this state:
+ */
+ int id;
+
+ /* The list of NFA edges that go from this state to some other. */
+ struct rx_nfa_edge *edges;
+
+ /* If you land in this state, then you implicitly land
+ * in all other states reachable by only epsilon translations.
+ * Call the set of maximal loop-less paths to such states the
+ * epsilon closure of this state.
+ *
+ * There may be other states that are reachable by a mixture of
+ * epsilon and side effect edges. Consider the set of maximal loop-less
+ * paths of that sort from this state. Call it the epsilon-side-effect
+ * closure of the state.
+ *
+ * The epsilon closure of the state is a subset of the epsilon-side-
+ * effect closure. It consists of all the paths that contain
+ * no side effects -- only epsilon edges.
+ *
+ * The paths in the epsilon-side-effect closure can be partitioned
+ * into equivalance sets. Two paths are equivalant if they have the
+ * same set of side effects, in the same order. The epsilon-closure
+ * is one of these equivalance sets. Let's call these equivalance
+ * sets: observably equivalant path sets. That name is chosen
+ * because equivalance of two paths means they cause the same side
+ * effects -- so they lead to the same subsequent observations other
+ * than that they may wind up in different target states.
+ *
+ * The superstate nfa, which is derived from this nfa, is based on
+ * the observation that all of the paths in an observably equivalant
+ * path set can be explored at the same time, provided that the
+ * matcher keeps track not of a single nfa state, but of a set of
+ * states. In particular, after following all the paths in an
+ * observably equivalant set, you wind up at a set of target states.
+ * That set of target states corresponds to one state in the
+ * superstate NFA.
+ *
+ * Staticly, before matching begins, it is convenient to analyze the
+ * nfa. Each state is labeled with a list of the observably
+ * equivalant path sets who's union covers all the
+ * epsilon-side-effect paths beginning in this state. This list is
+ * called the possible futures of the state.
+ *
+ * A trivial example is this NFA:
+ * s1
+ * A ---> B
+ *
+ * s2
+ * ---> C
+ *
+ * epsilon s1
+ * ---------> D ------> E
+ *
+ *
+ * In this example, A has two possible futures.
+ * One invokes the side effect `s1' and contains two paths,
+ * one ending in state B, the other in state E.
+ * The other invokes the side effect `s2' and contains only
+ * one path, landing in state C.
+ *
+ * Here is a list of the possible futures of this state:
+ */
+ struct rx_possible_future *futures;
+ int futures_computed:1;
+
+
+ /* There is exactly one distinguished starting state in every NFA: */
+ unsigned int is_start:1;
+
+ int has_cset_edges:1;
+
+ /* There may be many final states if the "cut" operator was used.
+ * each will have a different non-0 value for this field:
+ */
+ int is_final;
+
+
+ /* These are used internally during NFA construction... */
+ unsigned int eclosure_needed:1;
+ unsigned int mark:1;
+};
+
+
+/* An edge in an NFA is typed:
+ */
+enum rx_nfa_etype
+{
+ /* A cset edge is labled with a set of characters one of which
+ * must be matched for the edge to be taken.
+ */
+ ne_cset,
+
+ /* An epsilon edge is taken whenever its starting state is
+ * reached.
+ */
+ ne_epsilon,
+
+ /* A side effect edge is taken whenever its starting state is
+ * reached. Side effects may cause the match to fail or the
+ * position of the matcher to advance.
+ */
+ ne_side_effect
+};
+
+struct rx_nfa_edge
+{
+ struct rx_nfa_edge *next;
+ enum rx_nfa_etype type;
+ struct rx_nfa_state *dest;
+ union
+ {
+ rx_Bitset cset;
+ void * side_effect;
+ } params;
+};
+
+
+
+/* A possible future consists of a list of side effects
+ * and a set of destination states. Below are their
+ * representations. These structures are hash-consed so
+ * that lists with the same elements share a representation
+ * (their addresses are ==).
+ */
+
+struct rx_nfa_state_set
+{
+ struct rx_nfa_state * car;
+ struct rx_nfa_state_set * cdr;
+};
+
+struct rx_se_list
+{
+ void * car;
+ struct rx_se_list * cdr;
+};
+
+struct rx_possible_future
+{
+ struct rx_possible_future *next;
+ struct rx_se_list * effects;
+ struct rx_nfa_state_set * destset;
+};
+
+
+
+\f
+#ifdef __STDC__
+extern struct rx_nfa_state * rx_nfa_state (struct rx *rx);
+extern struct rx_nfa_edge * rx_nfa_edge (struct rx *rx,
+ enum rx_nfa_etype type,
+ struct rx_nfa_state *start,
+ struct rx_nfa_state *dest);
+extern int rx_build_nfa (struct rx *rx,
+ struct rexp_node *rexp,
+ struct rx_nfa_state **start,
+ struct rx_nfa_state **end);
+extern struct rx_possible_future * rx_state_possible_futures (struct rx * rx, struct rx_nfa_state * n);
+extern void rx_free_nfa (struct rx *rx);
+
+#else /* STDC */
+extern struct rx_nfa_state * rx_nfa_state ();
+extern struct rx_nfa_edge * rx_nfa_edge ();
+extern int rx_build_nfa ();
+extern struct rx_possible_future * rx_state_possible_futures ();
+extern void rx_free_nfa ();
+
+#endif /* STDC */
+
+
+
+
+
+
+
+
+
+
+
+#endif /* RXNFAH */
--- /dev/null
+/* classes: src_files */
+
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+#include "rxall.h"
+#include "rxnode.h"
+\f
+
+
+#define INITSIZE 8
+#define EXPANDSIZE 8
+
+#ifdef __STDC__
+static int
+rx_init_string(struct rx_string *thisone, char first)
+#else
+static int
+rx_init_string(thisone, first)
+ struct rx_string *thisone;
+ char first;
+#endif
+{
+ char *tmp;
+
+ tmp = (char *) malloc (INITSIZE);
+
+ if(!tmp)
+ return -1;
+
+ thisone->contents = tmp;
+ thisone->contents[0] = first;
+ thisone->reallen = INITSIZE;
+ thisone->len = 1;
+ return 0;
+}
+
+#ifdef __STDC__
+static void
+rx_free_string (struct rx_string *junk)
+#else
+static void
+rx_free_string (junk)
+ struct rx_string *junk;
+#endif
+{
+ free (junk->contents);
+ junk->len = junk->reallen = 0;
+ junk->contents = 0;
+}
+
+
+#ifdef __STDC__
+int
+rx_adjoin_string (struct rx_string *str, char c)
+#else
+int
+rx_adjoin_string (str, c)
+ struct rx_string *str;
+ char c;
+#endif
+{
+ if (!str->contents)
+ return rx_init_string (str, c);
+
+ if (str->len == str->reallen)
+ {
+ char *temp;
+ temp = (char *) realloc (str->contents, str->reallen + EXPANDSIZE);
+
+ if(!temp)
+ return -1;
+
+ str->contents = temp;
+ str->reallen += EXPANDSIZE;
+ }
+
+ str->contents[str->len++] = c;
+ return 0;
+}
+
+
+#ifdef __STDC__
+static int
+rx_copy_string (struct rx_string *to, struct rx_string *from)
+#else
+static int
+rx_copy_string (to, from)
+ struct rx_string *to;
+ struct rx_string *from;
+#endif
+{
+ char *tmp;
+
+ if (from->len)
+ {
+ tmp = (char *) malloc (from->reallen);
+
+ if (!tmp)
+ return -1;
+ }
+
+ rx_free_string (to);
+ to->len = from->len;
+ to->reallen = from->reallen;
+ to->contents = tmp;
+
+ memcpy (to->contents, from->contents, from->reallen);
+
+ return 0;
+}
+
+
+#ifdef __STDC__
+static int
+rx_compare_rx_strings (struct rx_string *a, struct rx_string *b)
+#else
+static int
+rx_compare_rx_strings (a, b)
+ struct rx_string *a;
+ struct rx_string *b;
+#endif
+{
+ if (a->len != b->len)
+ return 0;
+
+ if (a->len)
+ return !memcmp (a->contents, b->contents, a->len);
+ else
+ return 1; /* trivial case: "" == "" */
+}
+
+
+#ifdef __STDC__
+static unsigned long
+rx_string_hash (unsigned long seed, struct rx_string *str)
+#else
+static unsigned long
+rx_string_hash (seed, str)
+ unsigned long seed;
+ struct rx_string *str;
+#endif
+{
+ /* From Tcl: */
+ unsigned long result;
+ int c;
+ char * string;
+ int len;
+
+ string = str->contents;
+ len = str->len;
+ result = seed;
+
+ while (len--)
+ {
+ c = *string;
+ string++;
+ result += (result<<3) + c;
+ }
+ return result;
+}
+
+
+\f
+
+#ifdef __STDC__
+struct rexp_node *
+rexp_node (int type)
+#else
+struct rexp_node *
+rexp_node (type)
+ int type;
+#endif
+{
+ struct rexp_node *n;
+
+ n = (struct rexp_node *) malloc (sizeof (*n));
+ rx_bzero ((char *)n, sizeof (*n));
+ if (n)
+ {
+ n->type = type;
+ n->id = -1;
+ n->refs = 1;
+ }
+ return n;
+}
+
+
+/* free_rexp_node assumes that the bitset passed to rx_mk_r_cset
+ * can be freed using rx_free_cset.
+ */
+
+#ifdef __STDC__
+struct rexp_node *
+rx_mk_r_cset (int type, int size, rx_Bitset b)
+#else
+struct rexp_node *
+rx_mk_r_cset (type, size, b)
+ int type;
+ int size;
+ rx_Bitset b;
+#endif
+{
+ struct rexp_node * n;
+ n = rexp_node (type);
+ if (n)
+ {
+ n->params.cset = b;
+ n->params.cset_size = size;
+ }
+ return n;
+}
+
+
+#ifdef __STDC__
+struct rexp_node *
+rx_mk_r_int (int type, int intval)
+#else
+struct rexp_node *
+rx_mk_r_int (type, intval)
+ int type;
+ int intval;
+#endif
+{
+ struct rexp_node * n;
+ n = rexp_node (type);
+ if (n)
+ n->params.intval = intval;
+ return n;
+}
+
+
+#ifdef __STDC__
+struct rexp_node *
+rx_mk_r_str (int type, char c)
+#else
+struct rexp_node *
+rx_mk_r_str (type, c)
+ int type;
+ char c;
+#endif
+{
+ struct rexp_node *n;
+ n = rexp_node (type);
+ if (n)
+ rx_init_string (&(n->params.cstr), c);
+ return n;
+}
+
+
+#ifdef __STDC__
+struct rexp_node *
+rx_mk_r_binop (int type, struct rexp_node * a, struct rexp_node * b)
+#else
+struct rexp_node *
+rx_mk_r_binop (type, a, b)
+ int type;
+ struct rexp_node * a;
+ struct rexp_node * b;
+#endif
+{
+ struct rexp_node * n = rexp_node (type);
+ if (n)
+ {
+ n->params.pair.left = a;
+ n->params.pair.right = b;
+ }
+ return n;
+}
+
+
+#ifdef __STDC__
+struct rexp_node *
+rx_mk_r_monop (int type, struct rexp_node * a)
+#else
+struct rexp_node *
+rx_mk_r_monop (type, a)
+ int type;
+ struct rexp_node * a;
+#endif
+{
+ return rx_mk_r_binop (type, a, 0);
+}
+
+
+#ifdef __STDC__
+void
+rx_free_rexp (struct rexp_node * node)
+#else
+void
+rx_free_rexp (node)
+ struct rexp_node * node;
+#endif
+{
+ if (node && !--node->refs)
+ {
+ if (node->params.cset)
+ rx_free_cset (node->params.cset);
+ if (node->params.cstr.reallen)
+ rx_free_string (&(node->params.cstr));
+ rx_free_rexp (node->params.pair.left);
+ rx_free_rexp (node->params.pair.right);
+ rx_free_rexp (node->simplified);
+ free ((char *)node);
+ }
+}
+
+#ifdef __STDC__
+void
+rx_save_rexp (struct rexp_node * node)
+#else
+void
+rx_save_rexp (node)
+ struct rexp_node * node;
+#endif
+{
+ if (node)
+ ++node->refs;
+}
+
+
+#ifdef __STDC__
+struct rexp_node *
+rx_copy_rexp (int cset_size, struct rexp_node *node)
+#else
+struct rexp_node *
+rx_copy_rexp (cset_size, node)
+ int cset_size;
+ struct rexp_node *node;
+#endif
+{
+ if (!node)
+ return 0;
+ else
+ {
+ struct rexp_node *n;
+ n = rexp_node (node->type);
+ if (!n)
+ return 0;
+
+ if (node->params.cset)
+ {
+ n->params.cset = rx_copy_cset (cset_size,
+ node->params.cset);
+ if (!n->params.cset)
+ {
+ rx_free_rexp (n);
+ return 0;
+ }
+ }
+
+ if (node->params.cstr.reallen)
+ if (rx_copy_string (&(n->params.cstr), &(node->params.cstr)))
+ {
+ rx_free_rexp(n);
+ return 0;
+ }
+
+ n->params.intval = node->params.intval;
+ n->params.intval2 = node->params.intval2;
+ n->params.pair.left = rx_copy_rexp (cset_size, node->params.pair.left);
+ n->params.pair.right = rx_copy_rexp (cset_size, node->params.pair.right);
+ if ( (node->params.pair.left && !n->params.pair.left)
+ || (node->params.pair.right && !n->params.pair.right))
+ {
+ rx_free_rexp (n);
+ return 0;
+ }
+ n->id = node->id;
+ n->len = node->len;
+ n->observed = node->observed;
+ return n;
+ }
+}
+
+\f
+
+#ifdef __STDC__
+struct rexp_node *
+rx_shallow_copy_rexp (int cset_size, struct rexp_node *node)
+#else
+struct rexp_node *
+rx_shallow_copy_rexp (cset_size, node)
+ int cset_size;
+ struct rexp_node *node;
+#endif
+{
+ if (!node)
+ return 0;
+ else
+ {
+ struct rexp_node *n;
+ n = rexp_node (node->type);
+ if (!n)
+ return 0;
+
+ if (node->params.cset)
+ {
+ n->params.cset = rx_copy_cset (cset_size,
+ node->params.cset);
+ if (!n->params.cset)
+ {
+ rx_free_rexp (n);
+ return 0;
+ }
+ }
+
+ if (node->params.cstr.reallen)
+ if (rx_copy_string (&(n->params.cstr), &(node->params.cstr)))
+ {
+ rx_free_rexp(n);
+ return 0;
+ }
+
+ n->params.intval = node->params.intval;
+ n->params.intval2 = node->params.intval2;
+ n->params.pair.left = node->params.pair.left;
+ rx_save_rexp (node->params.pair.left);
+ n->params.pair.right = node->params.pair.right;
+ rx_save_rexp (node->params.pair.right);
+ n->id = node->id;
+ n->len = node->len;
+ n->observed = node->observed;
+ return n;
+ }
+}
+
+\f
+
+
+#ifdef __STDC__
+int
+rx_rexp_equal (struct rexp_node * a, struct rexp_node * b)
+#else
+int
+rx_rexp_equal (a, b)
+ struct rexp_node * a;
+ struct rexp_node * b;
+#endif
+{
+ int ret;
+
+ if (a == b)
+ return 1;
+
+ if ((a == 0) || (b == 0))
+ return 0;
+
+ if (a->type != b->type)
+ return 0;
+
+ switch (a->type)
+ {
+ case r_cset:
+ ret = ( (a->params.cset_size == b->params.cset_size)
+ && rx_bitset_is_equal (a->params.cset_size,
+ a->params.cset,
+ b->params.cset));
+ break;
+
+ case r_string:
+ ret = rx_compare_rx_strings (&(a->params.cstr), &(b->params.cstr));
+ break;
+
+ case r_cut:
+ ret = (a->params.intval == b->params.intval);
+ break;
+
+ case r_concat:
+ case r_alternate:
+ ret = ( rx_rexp_equal (a->params.pair.left, b->params.pair.left)
+ && rx_rexp_equal (a->params.pair.right, b->params.pair.right));
+ break;
+ case r_opt:
+ case r_star:
+ case r_plus:
+ ret = rx_rexp_equal (a->params.pair.left, b->params.pair.left);
+ break;
+ case r_interval:
+ ret = ( (a->params.intval == b->params.intval)
+ && (a->params.intval2 == b->params.intval2)
+ && rx_rexp_equal (a->params.pair.left, b->params.pair.left));
+ break;
+ case r_parens:
+ ret = ( (a->params.intval == b->params.intval)
+ && rx_rexp_equal (a->params.pair.left, b->params.pair.left));
+ break;
+
+ case r_context:
+ ret = (a->params.intval == b->params.intval);
+ break;
+ default:
+ return 0;
+ }
+ return ret;
+}
+
+
+\f
+
+
+#ifdef __STDC__
+unsigned long
+rx_rexp_hash (struct rexp_node * node, unsigned long seed)
+#else
+ unsigned long
+ rx_rexp_hash (node, seed)
+ struct rexp_node * node;
+ unsigned long seed;
+#endif
+{
+ if (!node)
+ return seed;
+
+ seed = rx_rexp_hash (node->params.pair.left, seed);
+ seed = rx_rexp_hash (node->params.pair.right, seed);
+ seed = rx_bitset_hash (node->params.cset_size, node->params.cset);
+ seed = rx_string_hash (seed, &(node->params.cstr));
+ seed += (seed << 3) + node->params.intval;
+ seed += (seed << 3) + node->params.intval2;
+ seed += (seed << 3) + node->type;
+ seed += (seed << 3) + node->id;
+#if 0
+ seed += (seed << 3) + node->len;
+ seed += (seed << 3) + node->observed;
+#endif
+ return seed;
+}
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXNODEH
+#define RXNODEH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+/*
+ * Tom Lord (lord@cygnus.com, lord@gnu.ai.mit.edu)
+ */
+
+\f
+#include "rxbitset.h"
+#include "rxcset.h"
+
+\f
+
+enum rexp_node_type
+{
+ r_cset = 0, /* Match from a character set. `a' or `[a-z]'*/
+ r_concat = 1, /* Concat two subexpressions. `ab' */
+ r_alternate = 2, /* Choose one of two subexpressions. `a\|b' */
+ r_opt = 3, /* Optional subexpression. `a?' */
+ r_star = 4, /* Repeated subexpression. `a*' */
+ r_plus = 5, /* Nontrivially repeated subexpression. `a+' */
+ r_string = 6, /* Shorthand for a concatenation of characters */
+ r_cut = 7, /* Generates a tagged, final nfa state. */
+
+ /* see RX_regular_node_type */
+
+ r_interval = 8, /* Counted subexpression. `a{4, 1000}' */
+ r_parens = 9, /* Parenthesized subexpression */
+ r_context = 10 /* Context-sensative operator such as "^" */
+};
+
+#define RX_regular_node_type(T) ((T) <= r_interval)
+
+
+
+struct rx_string
+{
+ unsigned long len;
+ unsigned long reallen;
+ unsigned char *contents;
+};
+
+struct rexp_node
+{
+ int refs;
+ enum rexp_node_type type;
+ struct
+ {
+ int cset_size;
+ rx_Bitset cset;
+ int intval;
+ int intval2;
+ struct
+ {
+ struct rexp_node *left;
+ struct rexp_node *right;
+ } pair;
+ struct rx_string cstr;
+ } params;
+ int id;
+ int len;
+ int observed;
+ struct rexp_node * simplified;
+ struct rx_cached_rexp * cr;
+};
+
+
+\f
+#ifdef __STDC__
+extern int rx_adjoin_string (struct rx_string *str, char c);
+extern struct rexp_node * rexp_node (int type);
+extern struct rexp_node * rx_mk_r_cset (int type, int size, rx_Bitset b);
+extern struct rexp_node * rx_mk_r_int (int type, int intval);
+extern struct rexp_node * rx_mk_r_str (int type, char c);
+extern struct rexp_node * rx_mk_r_binop (int type, struct rexp_node * a, struct rexp_node * b);
+extern struct rexp_node * rx_mk_r_monop (int type, struct rexp_node * a);
+extern void rx_free_rexp (struct rexp_node * node);
+extern void rx_save_rexp (struct rexp_node * node);
+extern struct rexp_node * rx_copy_rexp (int cset_size, struct rexp_node *node);
+extern struct rexp_node * rx_shallow_copy_rexp (int cset_size, struct rexp_node *node);
+extern int rx_rexp_equal (struct rexp_node * a, struct rexp_node * b);
+extern unsigned long rx_rexp_hash (struct rexp_node * node, unsigned long seed);
+
+#else /* STDC */
+extern int rx_adjoin_string ();
+extern struct rexp_node * rexp_node ();
+extern struct rexp_node * rx_mk_r_cset ();
+extern struct rexp_node * rx_mk_r_int ();
+extern struct rexp_node * rx_mk_r_str ();
+extern struct rexp_node * rx_mk_r_binop ();
+extern struct rexp_node * rx_mk_r_monop ();
+extern void rx_free_rexp ();
+extern void rx_save_rexp ();
+extern struct rexp_node * rx_copy_rexp ();
+extern struct rexp_node * rx_shallow_copy_rexp ();
+extern int rx_rexp_equal ();
+extern unsigned long rx_rexp_hash ();
+
+#endif /* STDC */
+
+
+
+
+#endif /* RXNODEH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+#include "rxall.h"
+#include "rxposix.h"
+#include "rxgnucomp.h"
+#include "rxbasic.h"
+#include "rxsimp.h"
+\f
+/* regcomp takes a regular expression as a string and compiles it.
+ *
+ * PATTERN is the address of the pattern string.
+ *
+ * CFLAGS is a series of bits which affect compilation.
+ *
+ * If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ * use POSIX basic syntax.
+ *
+ * If REG_NEWLINE is set, then . and [^...] don't match newline.
+ * Also, regexec will try a match beginning after every newline.
+ *
+ * If REG_ICASE is set, then we considers upper- and lowercase
+ * versions of letters to be equivalent when matching.
+ *
+ * If REG_NOSUB is set, then when PREG is passed to regexec, that
+ * routine will report only success or failure, and nothing about the
+ * registers.
+ *
+ * It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ * the return codes and their meanings.)
+ */
+
+
+#ifdef __STDC__
+int
+regncomp (regex_t * preg, const char * pattern, int len, int cflags)
+#else
+int
+regncomp (preg, pattern, len, cflags)
+ regex_t * preg;
+ const char * pattern;
+ int len;
+ int cflags;
+#endif
+{
+ int ret;
+ unsigned int syntax;
+
+ rx_bzero ((char *)preg, sizeof (*preg));
+ syntax = ((cflags & REG_EXTENDED)
+ ? RE_SYNTAX_POSIX_EXTENDED
+ : RE_SYNTAX_POSIX_BASIC);
+
+ if (!(cflags & REG_ICASE))
+ preg->translate = 0;
+ else
+ {
+ unsigned i;
+
+ preg->translate = (unsigned char *) malloc (256);
+ if (!preg->translate)
+ return (int) REG_ESPACE;
+
+ /* Map uppercase characters to corresponding lowercase ones. */
+ for (i = 0; i < CHAR_SET_SIZE; i++)
+ preg->translate[i] = isupper (i) ? tolower (i) : i;
+ }
+
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (!(cflags & REG_NEWLINE))
+ preg->newline_anchor = 0;
+ else
+ {
+ /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+
+ preg->no_sub = !!(cflags & REG_NOSUB);
+
+ ret = rx_parse (&preg->pattern,
+ pattern, len,
+ syntax,
+ 256,
+ preg->translate);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ * unmatched close-group: both are REG_EPAREN.
+ */
+ if (ret == REG_ERPAREN)
+ ret = REG_EPAREN;
+
+ if (!ret)
+ {
+ preg->re_nsub = 1;
+ preg->subexps = 0;
+ rx_posix_analyze_rexp (&preg->subexps,
+ &preg->re_nsub,
+ preg->pattern,
+ 0);
+ preg->is_nullable = rx_fill_in_fastmap (256,
+ preg->fastmap,
+ preg->pattern);
+
+ preg->is_anchored = rx_is_anchored_p (preg->pattern);
+ }
+
+ return (int) ret;
+}
+
+
+#ifdef __STDC__
+int
+regcomp (regex_t * preg, const char * pattern, int cflags)
+#else
+int
+regcomp (preg, pattern, cflags)
+ regex_t * preg;
+ const char * pattern;
+ int cflags;
+#endif
+{
+ /* POSIX says a null character in the pattern terminates it, so we
+ * can use strlen here in compiling the pattern.
+ */
+
+ return regncomp (preg, pattern, strlen (pattern), cflags);
+}
+
+
+\f
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. */
+
+#ifdef __STDC__
+size_t
+regerror (int errcode, const regex_t *preg,
+ char *errbuf, size_t errbuf_size)
+#else
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+ int errcode;
+ const regex_t *preg;
+ char *errbuf;
+ size_t errbuf_size;
+#endif
+{
+ const char *msg;
+ size_t msg_size;
+
+ msg = rx_error_msg[errcode] == 0 ? "Success" : rx_error_msg[errcode];
+ msg_size = strlen (msg) + 1; /* Includes the 0. */
+ if (errbuf_size != 0)
+ {
+ if (msg_size > errbuf_size)
+ {
+ strncpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+ }
+ else
+ strcpy (errbuf, msg);
+ }
+ return msg_size;
+}
+\f
+
+
+#ifdef __STDC__
+int
+rx_regmatch (regmatch_t pmatch[], const regex_t *preg, struct rx_context_rules * rules, int start, int end, const char *string)
+#else
+int
+rx_regmatch (pmatch, preg, rules, start, end, string)
+ regmatch_t pmatch[];
+ const regex_t *preg;
+ struct rx_context_rules * rules;
+ int start;
+ int end;
+ const char *string;
+#endif
+{
+ struct rx_solutions * solutions;
+ enum rx_answers answer;
+ struct rx_context_rules local_rules;
+ int orig_end;
+ int end_lower_bound;
+ int end_upper_bound;
+
+ local_rules = *rules;
+ orig_end = end;
+
+ if (!preg->pattern)
+ {
+ end_lower_bound = start;
+ end_upper_bound = start;
+ }
+ else if (preg->pattern->len >= 0)
+ {
+ end_lower_bound = start + preg->pattern->len;
+ end_upper_bound = start + preg->pattern->len;
+ }
+ else
+ {
+ end_lower_bound = start;
+ end_upper_bound = end;
+ }
+ end = end_upper_bound;
+ while (end >= end_lower_bound)
+ {
+ local_rules.not_eol = (rules->not_eol
+ ? ( (end == orig_end)
+ || !local_rules.newline_anchor
+ || (string[end] != '\n'))
+ : ( (end != orig_end)
+ && (!local_rules.newline_anchor
+ || (string[end] != '\n'))));
+ solutions = rx_basic_make_solutions (pmatch, preg->pattern, preg->subexps,
+ start, end, &local_rules, string);
+ if (!solutions)
+ return REG_ESPACE;
+
+ answer = rx_next_solution (solutions);
+
+ if (answer == rx_yes)
+ {
+ if (pmatch)
+ {
+ pmatch[0].rm_so = start;
+ pmatch[0].rm_eo = end;
+ pmatch[0].final_tag = solutions->final_tag;
+ }
+ rx_basic_free_solutions (solutions);
+ return 0;
+ }
+ else
+ rx_basic_free_solutions (solutions);
+
+ --end;
+ }
+
+ switch (answer)
+ {
+ default:
+ case rx_bogus:
+ return REG_ESPACE;
+
+ case rx_no:
+ return REG_NOMATCH;
+ }
+}
+
+
+#ifdef __STDC__
+int
+rx_regexec (regmatch_t pmatch[], const regex_t *preg, struct rx_context_rules * rules, int start, int end, const char *string)
+#else
+int
+rx_regexec (pmatch, preg, rules, start, end, string)
+ regmatch_t pmatch[];
+ const regex_t *preg;
+ struct rx_context_rules * rules;
+ int start;
+ int end;
+ const char *string;
+#endif
+{
+ int x;
+ int stat;
+ int anchored;
+ struct rexp_node * simplified;
+ struct rx_unfa * unfa;
+ struct rx_classical_system machine;
+
+ anchored = preg->is_anchored;
+
+ unfa = 0;
+ if ((end - start) > RX_MANY_CASES)
+ {
+ if (0 > rx_simple_rexp (&simplified, 256, preg->pattern, preg->subexps))
+ return REG_ESPACE;
+ unfa = rx_unfa (rx_basic_unfaniverse (), simplified, 256);
+ if (!unfa)
+ {
+ rx_free_rexp (simplified);
+ return REG_ESPACE;
+ }
+ rx_init_system (&machine, unfa->nfa);
+ rx_free_rexp (simplified);
+ }
+
+ for (x = start; x <= end; ++x)
+ {
+ if (preg->is_nullable
+ || ((x < end)
+ && (preg->fastmap[((unsigned char *)string)[x]])))
+ {
+ if ((end - start) > RX_MANY_CASES)
+ {
+ int amt;
+ if (rx_start_superstate (&machine) != rx_yes)
+ {
+ rx_free_unfa (unfa);
+ return REG_ESPACE;
+ }
+ amt = rx_advance_to_final (&machine, string + x, end - start - x);
+ if (!machine.final_tag && (amt < (end - start - x)))
+ goto nomatch;
+ }
+ stat = rx_regmatch (pmatch, preg, rules, x, end, string);
+ if (!stat || (stat != REG_NOMATCH))
+ {
+ rx_free_unfa (unfa);
+ return stat;
+ }
+ }
+ nomatch:
+ if (anchored)
+ if (!preg->newline_anchor)
+ {
+ rx_free_unfa (unfa);
+ return REG_NOMATCH;
+ }
+ else
+ while (x < end)
+ if (string[x] == '\n')
+ break;
+ else
+ ++x;
+ }
+ rx_free_unfa (unfa);
+ return REG_NOMATCH;
+}
+
+\f
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ * string STRING.
+ *
+ * If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ * `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ * least NMATCH elements, and we set them to the offsets of the
+ * corresponding matched substrings.
+ *
+ * EFLAGS specifies `execution flags' which affect matching: if
+ * REG_NOTBOL is set, then ^ does not match at the beginning of the
+ * string; if REG_NOTEOL is set, then $ does not match at the end.
+ *
+ * We return 0 if we find a match and REG_NOMATCH if not.
+ */
+
+#ifdef __STDC__
+int
+regnexec (const regex_t *preg, const char *string, int len, size_t nmatch, regmatch_t **pmatch, int eflags)
+#else
+int
+regnexec (preg, string, len, nmatch, pmatch, eflags)
+ const regex_t *preg;
+ const char *string;
+ int len;
+ size_t nmatch;
+ regmatch_t **pmatch;
+ int eflags;
+#endif
+{
+ int want_reg_info;
+ struct rx_context_rules rules;
+ regmatch_t * regs;
+ size_t nregs;
+ int stat;
+
+ want_reg_info = (!preg->no_sub && (nmatch > 0));
+
+ rules.newline_anchor = preg->newline_anchor;
+ rules.not_bol = !!(eflags & REG_NOTBOL);
+ rules.not_eol = !!(eflags & REG_NOTEOL);
+ rules.case_indep = !!(eflags & REG_ICASE);
+
+ if (nmatch >= preg->re_nsub)
+ {
+ regs = *pmatch;
+ nregs = nmatch;
+ }
+ else
+ {
+ regs = (regmatch_t *)malloc (preg->re_nsub * sizeof (*regs));
+ if (!regs)
+ return REG_ESPACE;
+ nregs = preg->re_nsub;
+ }
+
+ {
+ int x;
+ for (x = 0; x < nregs; ++x)
+ regs[x].rm_so = regs[x].rm_eo = -1;
+ }
+
+
+ stat = rx_regexec (regs, preg, &rules, 0, len, string);
+
+ if (!stat && want_reg_info && pmatch && (regs != *pmatch))
+ {
+ size_t x;
+ for (x = 0; x < nmatch; ++x)
+ (*pmatch)[x] = regs[x];
+ }
+
+ if (!stat && (eflags & REG_ALLOC_REGS))
+ *pmatch = regs;
+ else if (regs && (!pmatch || (regs != *pmatch)))
+ free (regs);
+
+ return stat;
+}
+
+#ifdef __STDC__
+int
+regexec (const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
+#else
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *preg;
+ const char *string;
+ size_t nmatch;
+ regmatch_t pmatch[];
+ int eflags;
+#endif
+{
+ return regnexec (preg,
+ string,
+ strlen (string),
+ nmatch,
+ &pmatch,
+ (eflags & ~REG_ALLOC_REGS));
+}
+
+
+/* Free dynamically allocated space used by PREG. */
+
+#ifdef __STDC__
+void
+regfree (regex_t *preg)
+#else
+void
+regfree (preg)
+ regex_t *preg;
+#endif
+{
+ if (preg->pattern)
+ {
+ rx_free_rexp (preg->pattern);
+ preg->pattern = 0;
+ }
+ if (preg->subexps)
+ {
+ free (preg->subexps);
+ preg->subexps = 0;
+ }
+ if (preg->translate != 0)
+ {
+ free (preg->translate);
+ preg->translate = 0;
+ }
+}
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXPOSIXH
+#define RXPOSIXH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+#include "rxspencer.h"
+#include "rxcontext.h"
+#include "inst-rxposix.h"
+
+#ifdef __STDC__
+extern int regncomp (regex_t * preg, const char * pattern, int len, int cflags);
+extern int regcomp (regex_t * preg, const char * pattern, int cflags);
+extern size_t regerror (int errcode, const regex_t *preg,
+ char *errbuf, size_t errbuf_size);
+extern int rx_regmatch (regmatch_t pmatch[], const regex_t *preg, struct rx_context_rules * rules, int start, int end, const char *string);
+extern int rx_regexec (regmatch_t pmatch[], const regex_t *preg, struct rx_context_rules * rules, int start, int end, const char *string);
+extern int regnexec (const regex_t *preg, const char *string, int len, size_t nmatch, regmatch_t **pmatch, int eflags);
+extern int regexec (const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
+extern void regfree (regex_t *preg);
+
+#else /* STDC */
+extern int regncomp ();
+extern int regcomp ();
+extern size_t regerror ();
+extern int rx_regmatch ();
+extern int rx_regexec ();
+extern int regnexec ();
+extern int regexec ();
+extern void regfree ();
+
+#endif /* STDC */
+
+#endif /* RXPOSIXH */
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXPROTOH
+#define RXPROTOH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+
+#ifdef __STDC__
+#define P(X) X
+#else
+#define P(X) ()
+#endif
+
+
+\f
+#ifdef __STDC__
+
+#else /* STDC */
+
+#endif /* STDC */
+
+
+#endif /* RXPROTOH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+#include "rxall.h"
+#include "rxsimp.h"
+
+\f
+
+
+/* Could reasonably hashcons instead of in rxunfa.c */
+
+#ifdef __STDC__
+int
+rx_simple_rexp (struct rexp_node ** answer,
+ int cset_size,
+ struct rexp_node *node,
+ struct rexp_node ** subexps)
+#else
+int
+rx_simple_rexp (answer, cset_size, node, subexps)
+ struct rexp_node ** answer;
+ int cset_size;
+ struct rexp_node *node;
+ struct rexp_node ** subexps;
+#endif
+{
+ int stat;
+
+ if (!node)
+ {
+ *answer = 0;
+ return 0;
+ }
+
+ if (!node->observed)
+ {
+ rx_save_rexp (node);
+ *answer = node;
+ return 0;
+ }
+
+ if (node->simplified)
+ {
+ rx_save_rexp (node->simplified);
+ *answer = node->simplified;
+ return 0;
+ }
+
+ switch (node->type)
+ {
+ default:
+ case r_cset:
+ case r_string:
+ case r_cut:
+ return -2; /* an internal error, really */
+
+ case r_parens:
+ stat = rx_simple_rexp (answer, cset_size,
+ node->params.pair.left,
+ subexps);
+ break;
+
+ case r_context:
+ if (isdigit (node->params.intval))
+ stat = rx_simple_rexp (answer, cset_size,
+ subexps [node->params.intval - '0'],
+ subexps);
+ else
+ {
+ *answer = 0;
+ stat = 0;
+ }
+ break;
+
+ case r_concat:
+ case r_alternate:
+ case r_opt:
+ case r_star:
+ case r_plus:
+ case r_interval:
+ {
+ struct rexp_node *n;
+ n = rexp_node (node->type);
+ if (!n)
+ return -1;
+
+ if (node->params.cset)
+ {
+ n->params.cset = rx_copy_cset (cset_size,
+ node->params.cset);
+ if (!n->params.cset)
+ {
+ rx_free_rexp (n);
+ return -1;
+ }
+ }
+ n->params.intval = node->params.intval;
+ n->params.intval2 = node->params.intval2;
+ {
+ int s;
+
+ s = rx_simple_rexp (&n->params.pair.left, cset_size,
+ node->params.pair.left, subexps);
+ if (!s)
+ s = rx_simple_rexp (&n->params.pair.right, cset_size,
+ node->params.pair.right, subexps);
+ if (!s)
+ {
+ *answer = n;
+ stat = 0;
+ }
+ else
+ {
+ rx_free_rexp (n);
+ stat = s;
+ }
+ }
+ }
+ break;
+ }
+
+ if (!stat)
+ {
+ node->simplified = *answer;
+ rx_save_rexp (node->simplified);
+ }
+ return stat;
+}
+
+
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXSIMPH
+#define RXSIMPH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+#include "rxcset.h"
+#include "rxnode.h"
+
+\f
+#ifdef __STDC__
+extern int rx_simple_rexp (struct rexp_node ** answer,
+ int cset_size,
+ struct rexp_node *node,
+ struct rexp_node ** subexps);
+
+#else /* STDC */
+extern int rx_simple_rexp ();
+
+#endif /* STDC */
+
+
+
+
+
+#endif /* RXSIMPH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+#include <stdio.h>
+#include "rxall.h"
+#include "rxspencer.h"
+#include "rxsimp.h"
+
+\f
+
+static char * silly_hack_2 = 0;
+
+struct rx_solutions rx_no_solutions;
+
+#ifdef __STDC__
+struct rx_solutions *
+rx_make_solutions (struct rx_registers * regs, struct rx_unfaniverse * verse, struct rexp_node * expression, struct rexp_node ** subexps, int cset_size, int start, int end, rx_vmfn vmfn, rx_contextfn contextfn, void * closure)
+#else
+struct rx_solutions *
+rx_make_solutions (regs, verse, expression, subexps, cset_size,
+ start, end, vmfn, contextfn, closure)
+ struct rx_registers * regs;
+ struct rx_unfaniverse * verse;
+ struct rexp_node * expression;
+ struct rexp_node ** subexps;
+ int cset_size;
+ int start;
+ int end;
+ rx_vmfn vmfn;
+ rx_contextfn contextfn;
+ void * closure;
+#endif
+{
+ struct rx_solutions * solns;
+
+ if ( expression
+ && (expression->len >= 0)
+ && (expression->len != (end - start)))
+ return &rx_no_solutions;
+
+ if (silly_hack_2)
+ {
+ solns = (struct rx_solutions *)silly_hack_2;
+ silly_hack_2 = 0;
+ }
+ else
+ solns = (struct rx_solutions *)malloc (sizeof (*solns));
+ if (!solns)
+ return 0;
+ rx_bzero ((char *)solns, sizeof (*solns));
+
+ solns->step = 0;
+ solns->cset_size = cset_size;
+ solns->subexps = subexps;
+ solns->exp = expression;
+ rx_save_rexp (expression);
+ solns->verse = verse;
+ solns->regs = regs;
+ solns->start = start;
+ solns->end = end;
+ solns->vmfn = vmfn;
+ solns->contextfn = contextfn;
+ solns->closure = closure;
+
+ if (!solns->exp || !solns->exp->observed)
+ {
+ solns->dfa = rx_unfa (verse, expression, cset_size);
+ if (!solns->dfa)
+ goto err_return;
+ rx_init_system (&solns->match_engine, solns->dfa->nfa);
+
+ if (rx_yes != rx_start_superstate (&solns->match_engine))
+ goto err_return;
+ }
+ else
+ {
+ struct rexp_node * simplified;
+ int status;
+ status = rx_simple_rexp (&simplified, cset_size, solns->exp, subexps);
+ if (status)
+ goto err_return;
+ solns->dfa = rx_unfa (verse, simplified, cset_size);
+ if (!solns->dfa)
+ {
+ rx_free_rexp (simplified);
+ goto err_return;
+ }
+ rx_init_system (&solns->match_engine, solns->dfa->nfa);
+ if (rx_yes != rx_start_superstate (&solns->match_engine))
+ goto err_return;
+ rx_free_rexp (simplified);
+ }
+
+ if (expression && ( (expression->type == r_concat)
+ || (expression->type == r_plus)
+ || (expression->type == r_star)
+ || (expression->type == r_interval)))
+ {
+ struct rexp_node * subexp;
+
+ subexp = solns->exp->params.pair.left;
+
+ if (!subexp || !subexp->observed)
+ {
+ solns->left_dfa = rx_unfa (solns->verse, subexp, solns->cset_size);
+ }
+ else
+ {
+ struct rexp_node * simplified;
+ int status;
+ status = rx_simple_rexp (&simplified, solns->cset_size, subexp, solns->subexps);
+ if (status)
+ goto err_return;
+ solns->left_dfa = rx_unfa (solns->verse, simplified, solns->cset_size);
+ rx_free_rexp (simplified);
+ }
+
+ if (!solns->left_dfa)
+ goto err_return;
+ rx_bzero ((char *)&solns->left_match_engine, sizeof (solns->left_match_engine));
+ rx_init_system (&solns->left_match_engine, solns->left_dfa->nfa);
+ }
+
+ return solns;
+
+ err_return:
+ rx_free_rexp (solns->exp);
+ free (solns);
+ return 0;
+}
+
+
+\f
+#ifdef __STDC__
+void
+rx_free_solutions (struct rx_solutions * solns)
+#else
+void
+rx_free_solutions (solns)
+ struct rx_solutions * solns;
+#endif
+{
+ if (!solns)
+ return;
+
+ if (solns == &rx_no_solutions)
+ return;
+
+ if (solns->left)
+ {
+ rx_free_solutions (solns->left);
+ solns->left = 0;
+ }
+
+ if (solns->right)
+ {
+ rx_free_solutions (solns->right);
+ solns->right = 0;
+ }
+
+ if (solns->dfa)
+ {
+ rx_free_unfa (solns->dfa);
+ solns->dfa = 0;
+ }
+ if (solns->left_dfa)
+ {
+ rx_terminate_system (&solns->left_match_engine);
+ rx_free_unfa (solns->left_dfa);
+ solns->left_dfa = 0;
+ }
+
+ rx_terminate_system (&solns->match_engine);
+
+ if (solns->exp)
+ {
+ rx_free_rexp (solns->exp);
+ solns->exp = 0;
+ }
+
+ if (!silly_hack_2)
+ silly_hack_2 = (char *)solns;
+ else
+ free (solns);
+}
+
+\f
+#ifdef __STDC__
+static enum rx_answers
+rx_solution_fit_p (struct rx_solutions * solns)
+#else
+static enum rx_answers
+rx_solution_fit_p (solns)
+ struct rx_solutions * solns;
+#endif
+{
+ unsigned const char * burst;
+ int burst_addr;
+ int burst_len;
+ int burst_end_addr;
+ int rel_pos_in_burst;
+ enum rx_answers vmstat;
+ int current_pos;
+
+ current_pos = solns->start;
+ next_burst:
+ vmstat = solns->vmfn (solns->closure,
+ &burst, &burst_len, &burst_addr,
+ current_pos, solns->end,
+ current_pos);
+
+ if (vmstat != rx_yes)
+ return vmstat;
+
+ rel_pos_in_burst = current_pos - burst_addr;
+ burst_end_addr = burst_addr + burst_len;
+
+ if (burst_end_addr >= solns->end)
+ {
+ enum rx_answers fit_status;
+ fit_status = rx_fit_p (&solns->match_engine,
+ burst + rel_pos_in_burst,
+ solns->end - current_pos);
+ return fit_status;
+ }
+ else
+ {
+ enum rx_answers fit_status;
+ fit_status = rx_advance (&solns->match_engine,
+ burst + rel_pos_in_burst,
+ burst_len - rel_pos_in_burst);
+ if (fit_status != rx_yes)
+ {
+ return fit_status;
+ }
+ else
+ {
+ current_pos += burst_len - rel_pos_in_burst;
+ goto next_burst;
+ }
+ }
+}
+\f
+
+#ifdef __STDC__
+static enum rx_answers
+rx_solution_fit_str_p (struct rx_solutions * solns)
+#else
+static enum rx_answers
+rx_solution_fit_str_p (solns)
+ struct rx_solutions * solns;
+#endif
+{
+ int current_pos;
+ unsigned const char * burst;
+ int burst_addr;
+ int burst_len;
+ int burst_end_addr;
+ int rel_pos_in_burst;
+ enum rx_answers vmstat;
+ int count;
+ unsigned char * key;
+
+
+ current_pos = solns->start;
+ count = solns->exp->params.cstr.len;
+ key = (unsigned char *)solns->exp->params.cstr.contents;
+
+ next_burst:
+ vmstat = solns->vmfn (solns->closure,
+ &burst, &burst_len, &burst_addr,
+ current_pos, solns->end,
+ current_pos);
+
+ if (vmstat != rx_yes)
+ return vmstat;
+
+ rel_pos_in_burst = current_pos - burst_addr;
+ burst_end_addr = burst_addr + burst_len;
+
+ {
+ unsigned const char * pos;
+
+ pos = burst + rel_pos_in_burst;
+
+ if (burst_end_addr >= solns->end)
+ {
+ while (count)
+ {
+ if (*pos != *key)
+ return rx_no;
+ ++pos;
+ ++key;
+ --count;
+ }
+ return rx_yes;
+ }
+ else
+ {
+ int part_count;
+ int part_count_init;
+
+ part_count_init = burst_len - rel_pos_in_burst;
+ part_count = part_count_init;
+ while (part_count)
+ {
+ if (*pos != *key)
+ return rx_no;
+ ++pos;
+ ++key;
+ --part_count;
+ }
+ count -= part_count_init;
+ current_pos += burst_len - rel_pos_in_burst;
+ goto next_burst;
+ }
+ }
+}
+
+
+\f
+
+#if 0
+#ifdef __STDC__
+int
+rx_best_end_guess (struct rx_solutions * solns, struct rexp_node * exp, int bound)
+#else
+int
+rx_best_end_guess (solns, exp, bound)
+ struct rx_solutions * solns;
+ struct rexp_node * exp;
+ int bound;
+#endif
+{
+ int current_pos;
+ unsigned const char * burst;
+ int burst_addr;
+ int burst_len;
+ int burst_end_addr;
+ int rel_pos_in_burst;
+ int best_guess;
+ enum rx_answers vmstat;
+
+#if 0
+ unparse_print_rexp (256, solns->exp);
+ printf ("\n");
+ unparse_print_rexp (256, exp);
+ printf ("\nbound %d \n", bound);
+#endif
+
+ if (rx_yes != rx_start_superstate (&solns->left_match_engine))
+ {
+ return bound - 1;
+ }
+ best_guess = current_pos = solns->start;
+
+ next_burst:
+#if 0
+ printf (" best_guess %d\n", best_guess);
+#endif
+ vmstat = solns->vmfn (solns->closure,
+ &burst, &burst_len, &burst_addr,
+ current_pos, bound,
+ current_pos);
+#if 0
+ printf (" str>%s\n", burst);
+#endif
+ if (vmstat != rx_yes)
+ {
+ return bound - 1;
+ }
+
+ rel_pos_in_burst = current_pos - burst_addr;
+ burst_end_addr = burst_addr + burst_len;
+
+ if (burst_end_addr > bound)
+ {
+ burst_end_addr = bound;
+ burst_len = bound - burst_addr;
+ }
+
+ {
+ int amt_advanced;
+
+#if 0
+ printf (" rel_pos_in_burst %d burst_len %d\n", rel_pos_in_burst, burst_len);
+#endif
+ while (rel_pos_in_burst < burst_len)
+ {
+ amt_advanced= rx_advance_to_final (&solns->left_match_engine,
+ burst + rel_pos_in_burst,
+ burst_len - rel_pos_in_burst);
+#if 0
+ printf (" amt_advanced %d", amt_advanced);
+#endif
+ if (amt_advanced < 0)
+ {
+ return bound - 1;
+ }
+ current_pos += amt_advanced;
+ rel_pos_in_burst += amt_advanced;
+ if (solns->left_match_engine.final_tag)
+ best_guess = current_pos;
+#if 0
+ printf (" best_guess %d\n", best_guess);
+ printf (" current_pos %d\n", current_pos);
+#endif
+ if (amt_advanced == 0)
+ {
+ return best_guess;
+ }
+ }
+ if (current_pos == bound)
+ {
+ return best_guess;
+ }
+ goto next_burst;
+ }
+}
+#endif
+
+
+\f
+#ifdef __STDC__
+enum rx_answers
+rx_next_solution (struct rx_solutions * solns)
+#else
+enum rx_answers
+rx_next_solution (solns)
+ struct rx_solutions * solns;
+#endif
+{
+ if (!solns)
+ return rx_bogus;
+
+ if (solns == &rx_no_solutions)
+ {
+ return rx_no;
+ }
+
+ if (!solns->exp)
+ {
+ if (solns->step != 0)
+ {
+ return rx_no;
+ }
+ else
+ {
+ solns->step = 1;
+ solns->final_tag = 1;
+ return (solns->start == solns->end
+ ? rx_yes
+ : rx_no);
+ }
+ }
+ else if ( (solns->exp->len >= 0)
+ && (solns->exp->len != (solns->end - solns->start)))
+ {
+ return rx_no;
+ }
+ else if (!solns->exp->observed)
+ {
+ if (solns->step != 0)
+ {
+ return rx_no;
+ }
+ else if (solns->exp->type == r_string)
+ {
+ enum rx_answers ans;
+ ans = rx_solution_fit_str_p (solns);
+ solns->final_tag = 1;
+ solns->step = -1;
+ return ans;
+ }
+ else
+ {
+ enum rx_answers ans;
+ ans = rx_solution_fit_p (solns);
+ solns->final_tag = solns->match_engine.final_tag;
+ solns->step = -1;
+ return ans;
+ }
+ }
+ else if (solns->exp->observed)
+ {
+ enum rx_answers fit_p;
+ switch (solns->step)
+ {
+ case -2:
+ if (solns->exp->params.intval)
+ {
+ solns->regs[solns->exp->params.intval].rm_so = solns->saved_rm_so;
+ solns->regs[solns->exp->params.intval].rm_eo = solns->saved_rm_eo;
+ }
+ return rx_no;
+
+ case -1:
+ return rx_no;
+
+ case 0:
+ fit_p = rx_solution_fit_p (solns);
+ /* Set final_tag here because this rough fit test
+ * may be all the matching that gets done.
+ * For example, consider a paren node containing
+ * a true regular expression ending with a cut
+ * operator.
+ */
+ solns->final_tag = solns->match_engine.final_tag;
+ switch (fit_p)
+ {
+ case rx_no:
+ solns->step = -1;
+ return rx_no;
+ case rx_yes:
+ solns->step = 1;
+ goto resolve_fit;
+ case rx_bogus:
+ default:
+ solns->step = -1;
+ return fit_p;
+ }
+
+ default:
+ resolve_fit:
+ switch (solns->exp->type)
+ {
+ case r_cset:
+ case r_string:
+ case r_cut:
+ solns->step = -1;
+ return rx_bogus;
+
+ case r_parens:
+ {
+ enum rx_answers paren_stat;
+ switch (solns->step)
+ {
+ case 1:
+ if (solns->exp->params.intval)
+ {
+ solns->saved_rm_so = solns->regs[solns->exp->params.intval].rm_so;
+ solns->saved_rm_eo = solns->regs[solns->exp->params.intval].rm_eo;
+ }
+
+ if ( !solns->exp->params.pair.left
+ || !solns->exp->params.pair.left->observed)
+ {
+ if (solns->exp->params.intval)
+ {
+ solns->regs[solns->exp->params.intval].rm_so = solns->start;
+ solns->regs[solns->exp->params.intval].rm_eo = solns->end;
+ }
+ solns->step = -2;
+ /* Keep the final_tag from the fit_p test. */
+ return rx_yes;
+ }
+ else
+ {
+ solns->left = rx_make_solutions (solns->regs,
+ solns->verse,
+ solns->exp->params.pair.left,
+ solns->subexps,
+ solns->cset_size,
+ solns->start,
+ solns->end,
+ solns->vmfn,
+ solns->contextfn,
+ solns->closure);
+ if (!solns->left)
+ {
+ solns->step = -1;
+ return rx_bogus;
+ }
+ }
+ solns->step = 2;
+ /* fall through */
+
+ case 2:
+ if (solns->exp->params.intval)
+ {
+ solns->regs[solns->exp->params.intval].rm_so = solns->saved_rm_so;
+ solns->regs[solns->exp->params.intval].rm_eo = solns->saved_rm_eo;
+ }
+
+ paren_stat = rx_next_solution (solns->left);
+ if (paren_stat == rx_yes)
+ {
+ if (solns->exp->params.intval)
+ {
+ solns->regs[solns->exp->params.intval].rm_so = solns->start;
+ solns->regs[solns->exp->params.intval].rm_eo = solns->end;
+ }
+ solns->final_tag = solns->left->final_tag;
+ return rx_yes;
+ }
+ else
+ {
+ solns->step = -1;
+ rx_free_solutions (solns->left);
+ solns->left = 0;
+ if (solns->exp->params.intval)
+ {
+ solns->regs[solns->exp->params.intval].rm_so = solns->saved_rm_so;
+ solns->regs[solns->exp->params.intval].rm_eo = solns->saved_rm_eo;
+ }
+ return paren_stat;
+ }
+ }
+ }
+
+
+ case r_opt:
+ {
+ enum rx_answers opt_stat;
+ switch (solns->step)
+ {
+ case 1:
+ solns->left = rx_make_solutions (solns->regs,
+ solns->verse,
+ solns->exp->params.pair.left,
+ solns->subexps,
+ solns->cset_size,
+ solns->start,
+ solns->end,
+ solns->vmfn,
+ solns->contextfn,
+ solns->closure);
+ if (!solns->left)
+ {
+ solns->step = -1;
+ return rx_bogus;
+ }
+ solns->step = 2;
+ /* fall through */
+
+ case 2:
+ opt_stat = rx_next_solution (solns->left);
+ if (opt_stat == rx_yes)
+ {
+ solns->final_tag = solns->left->final_tag;
+ return rx_yes;
+ }
+ else
+ {
+ solns->step = -1;
+ rx_free_solutions (solns->left);
+ solns->left = 0;
+ return ((solns->start == solns->end)
+ ? rx_yes
+ : rx_no);
+ }
+
+ }
+ }
+
+ case r_alternate:
+ {
+ enum rx_answers alt_stat;
+ switch (solns->step)
+ {
+ case 1:
+ solns->left = rx_make_solutions (solns->regs,
+ solns->verse,
+ solns->exp->params.pair.left,
+ solns->subexps,
+ solns->cset_size,
+ solns->start,
+ solns->end,
+ solns->vmfn,
+ solns->contextfn,
+ solns->closure);
+ if (!solns->left)
+ {
+ solns->step = -1;
+ return rx_bogus;
+ }
+ solns->step = 2;
+ /* fall through */
+
+ case 2:
+ alt_stat = rx_next_solution (solns->left);
+
+ if (alt_stat == rx_yes)
+ {
+ solns->final_tag = solns->left->final_tag;
+ return alt_stat;
+ }
+ else
+ {
+ solns->step = 3;
+ rx_free_solutions (solns->left);
+ solns->left = 0;
+ /* fall through */
+ }
+
+ case 3:
+ solns->right = rx_make_solutions (solns->regs,
+ solns->verse,
+ solns->exp->params.pair.right,
+ solns->subexps,
+ solns->cset_size,
+ solns->start,
+ solns->end,
+ solns->vmfn,
+ solns->contextfn,
+ solns->closure);
+ if (!solns->right)
+ {
+ solns->step = -1;
+ return rx_bogus;
+ }
+ solns->step = 4;
+ /* fall through */
+
+ case 4:
+ alt_stat = rx_next_solution (solns->right);
+
+ if (alt_stat == rx_yes)
+ {
+ solns->final_tag = solns->right->final_tag;
+ return alt_stat;
+ }
+ else
+ {
+ solns->step = -1;
+ rx_free_solutions (solns->right);
+ solns->right = 0;
+ return alt_stat;
+ }
+ }
+ }
+
+ case r_concat:
+ {
+ switch (solns->step)
+ {
+ enum rx_answers concat_stat;
+ case 1:
+ solns->split_guess = solns->end;
+#if 0
+ solns->split_guess = ((solns->end - solns->start) > RX_MANY_CASES
+ ? rx_best_end_guess (solns,
+ solns->exp->params.pair.left, solns->end)
+ : solns->end);
+#endif
+
+ concat_split_guess_loop:
+ solns->left = rx_make_solutions (solns->regs,
+ solns->verse,
+ solns->exp->params.pair.left,
+ solns->subexps,
+ solns->cset_size,
+ solns->start,
+ solns->split_guess,
+ solns->vmfn,
+ solns->contextfn,
+ solns->closure);
+ if (!solns->left)
+ {
+ solns->step = -1;
+ return rx_bogus;
+ }
+ solns->step = 2;
+
+ case 2:
+ concat_try_next_left_match:
+
+ concat_stat = rx_next_solution (solns->left);
+ if (concat_stat != rx_yes)
+ {
+ rx_free_solutions (solns->left);
+ rx_free_solutions (solns->right);
+ solns->left = solns->right = 0;
+ solns->split_guess = solns->split_guess - 1;
+#if 0
+ solns->split_guess = ((solns->split_guess - solns->start) > RX_MANY_CASES
+ ? rx_best_end_guess (solns,
+ solns->exp->params.pair.left,
+ solns->split_guess - 1)
+ : solns->split_guess - 1);
+#endif
+ if (solns->split_guess >= solns->start)
+ goto concat_split_guess_loop;
+ else
+ {
+ solns->step = -1;
+ return concat_stat;
+ }
+ }
+ else
+ {
+ solns->step = 3;
+ /* fall through */
+ }
+
+ case 3:
+ solns->right = rx_make_solutions (solns->regs,
+ solns->verse,
+ solns->exp->params.pair.right,
+ solns->subexps,
+ solns->cset_size,
+ solns->split_guess,
+ solns->end,
+ solns->vmfn,
+ solns->contextfn,
+ solns->closure);
+ if (!solns->right)
+ {
+ rx_free_solutions (solns->left);
+ solns->left = 0;
+ solns->step = -1;
+ return rx_bogus;
+ }
+
+ solns->step = 4;
+ /* fall through */
+
+ case 4:
+ /* concat_try_next_right_match: */
+
+ concat_stat = rx_next_solution (solns->right);
+ if (concat_stat == rx_yes)
+ {
+ solns->final_tag = solns->right->final_tag;
+ return concat_stat;
+ }
+ else if (concat_stat == rx_no)
+ {
+ rx_free_solutions (solns->right);
+ solns->right = 0;
+ solns->step = 2;
+ goto concat_try_next_left_match;
+ }
+ else /* concat_stat == rx_bogus */
+ {
+ rx_free_solutions (solns->left);
+ solns->left = 0;
+ rx_free_solutions (solns->right);
+ solns->right = 0;
+ solns->step = -1;
+ return concat_stat;
+ }
+ }
+ }
+
+
+ case r_plus:
+ case r_star:
+ {
+ switch (solns->step)
+ {
+ enum rx_answers star_stat;
+ case 1:
+ solns->split_guess = solns->end;
+#if 0
+ solns->split_guess = ((solns->end - solns->start) > RX_MANY_CASES
+ ? rx_best_end_guess (solns,
+ solns->exp->params.pair.left, solns->end)
+ : solns->end);
+#endif
+
+ star_split_guess_loop:
+ solns->left = rx_make_solutions (solns->regs,
+ solns->verse,
+ solns->exp->params.pair.left,
+ solns->subexps,
+ solns->cset_size,
+ solns->start,
+ solns->split_guess,
+ solns->vmfn,
+ solns->contextfn,
+ solns->closure);
+ if (!solns->left)
+ {
+ solns->step = -1;
+ return rx_bogus;
+ }
+ solns->step = 2;
+
+ case 2:
+ star_try_next_left_match:
+
+ star_stat = rx_next_solution (solns->left);
+ if (star_stat != rx_yes)
+ {
+ rx_free_solutions (solns->left);
+ rx_free_solutions (solns->right);
+ solns->left = solns->right = 0;
+ solns->split_guess = solns->split_guess - 1;
+#if 0
+ solns->split_guess = ((solns->split_guess - solns->start) > RX_MANY_CASES
+ ? rx_best_end_guess (solns,
+ solns->exp->params.pair.left,
+ solns->split_guess - 1)
+ : solns->split_guess - 1);
+#endif
+ if (solns->split_guess >= solns->start)
+ goto star_split_guess_loop;
+ else
+ {
+ solns->step = -1;
+
+ if ( (solns->exp->type == r_star)
+ && (solns->start == solns->end)
+ && (star_stat == rx_no))
+ {
+ solns->final_tag = 1;
+ return rx_yes;
+ }
+ else
+ return star_stat;
+ }
+ }
+ else
+ {
+ solns->step = 3;
+ /* fall through */
+ }
+
+
+ if (solns->split_guess == solns->end)
+ {
+ solns->final_tag = solns->left->final_tag;
+ return rx_yes;
+ }
+
+ case 3:
+ solns->right = rx_make_solutions (solns->regs,
+ solns->verse,
+ solns->exp,
+ solns->subexps,
+ solns->cset_size,
+ solns->split_guess,
+ solns->end,
+ solns->vmfn,
+ solns->contextfn,
+ solns->closure);
+ if (!solns->right)
+ {
+ rx_free_solutions (solns->left);
+ solns->left = 0;
+ solns->step = -1;
+ return rx_bogus;
+ }
+
+ solns->step = 4;
+ /* fall through */
+
+ case 4:
+ /* star_try_next_right_match: */
+
+ star_stat = rx_next_solution (solns->right);
+ if (star_stat == rx_yes)
+ {
+ solns->final_tag = solns->right->final_tag;
+ return star_stat;
+ }
+ else if (star_stat == rx_no)
+ {
+ rx_free_solutions (solns->right);
+ solns->right = 0;
+ solns->step = 2;
+ goto star_try_next_left_match;
+ }
+ else /* star_stat == rx_bogus */
+ {
+ rx_free_solutions (solns->left);
+ solns->left = 0;
+ rx_free_solutions (solns->right);
+ solns->right = 0;
+ solns->step = -1;
+ return star_stat;
+ }
+ }
+ }
+
+ case r_interval:
+ {
+ switch (solns->step)
+ {
+ enum rx_answers interval_stat;
+
+ case 1:
+ /* If the interval permits nothing,
+ * return immediately.
+ */
+ if (solns->exp->params.intval2 < solns->interval_x)
+ {
+ solns->step = -1;
+ return rx_no;
+ }
+
+ /* If the interval permits only 0 iterations,
+ * return immediately. Success depends on the
+ * emptiness of the match.
+ */
+ if ( (solns->exp->params.intval2 == solns->interval_x)
+ && (solns->exp->params.intval <= solns->interval_x))
+ {
+ solns->step = -1;
+ solns->final_tag = 1;
+ return ((solns->start == solns->end)
+ ? rx_yes
+ : rx_no);
+ }
+
+ /* The interval permits at most 0 iterations,
+ * but also requires more. A bug.
+ */
+ if (solns->exp->params.intval2 == solns->interval_x)
+ {
+ /* indicates a regexp compilation error, actually */
+ solns->step = -1;
+ return rx_bogus;
+ }
+
+ solns->split_guess = solns->end;
+#if 0
+ solns->split_guess = ((solns->end - solns->start) > RX_MANY_CASES
+ ? rx_best_end_guess (solns,
+ solns->exp->params.pair.left, solns->end)
+ : solns->end);
+#endif
+
+ /* The interval permits more than 0 iterations.
+ * If it permits 0 and the match is to be empty,
+ * the trivial match is the most preferred answer.
+ */
+ if (solns->exp->params.intval <= solns->interval_x)
+ {
+ solns->step = 2;
+ if (solns->start == solns->end)
+ {
+ solns->final_tag = 1;
+ return rx_yes;
+ }
+ /* If this isn't a trivial match, or if the trivial match
+ * is rejected, look harder.
+ */
+ }
+
+ case 2:
+ interval_split_guess_loop:
+ /* The match requires at least one iteration, either because
+ * there are characters to match, or because the interval starts
+ * above 0.
+ *
+ * Look for the first iteration:
+ */
+ solns->left = rx_make_solutions (solns->regs,
+ solns->verse,
+ solns->exp->params.pair.left,
+ solns->subexps,
+ solns->cset_size,
+ solns->start,
+ solns->split_guess,
+ solns->vmfn,
+ solns->contextfn,
+ solns->closure);
+ if (!solns->left)
+ {
+ solns->step = -1;
+ return rx_bogus;
+ }
+ solns->step = 3;
+
+ case 3:
+ interval_try_next_left_match:
+
+ interval_stat = rx_next_solution (solns->left);
+ if (interval_stat != rx_yes)
+ {
+ rx_free_solutions (solns->left);
+ rx_free_solutions (solns->right);
+ solns->left = solns->right = 0;
+ solns->split_guess = solns->split_guess - 1;
+#if 0
+ solns->split_guess = ((solns->split_guess - solns->start) > RX_MANY_CASES
+ ? rx_best_end_guess (solns,
+ solns->exp->params.pair.left,
+ solns->split_guess - 1)
+ : solns->split_guess - 1);
+#endif
+ if (solns->split_guess >= solns->start)
+ goto interval_split_guess_loop;
+ else
+ {
+ solns->step = -1;
+ return interval_stat;
+ }
+ }
+ else
+ {
+ solns->step = 4;
+ /* fall through */
+ }
+
+ case 4:
+ {
+ /* After matching one required iteration, construct a smaller
+ * interval and try to match that against the rest.
+ *
+ * To avoid thwarting unfa caching, instead of building a new
+ * rexp node with different interval extents, we keep interval_x
+ * in each solns structure to keep track of the number of
+ * iterations matched so far.
+ */
+ solns->right = rx_make_solutions (solns->regs,
+ solns->verse,
+ solns->exp,
+ solns->subexps,
+ solns->cset_size,
+ solns->split_guess,
+ solns->end,
+ solns->vmfn,
+ solns->contextfn,
+ solns->closure);
+ solns->right->interval_x = solns->interval_x + 1;
+ }
+ if (!solns->right)
+ {
+ rx_free_solutions (solns->left);
+ solns->left = 0;
+ solns->step = -1;
+ return rx_bogus;
+ }
+
+ solns->step = 5;
+ /* fall through */
+
+ case 5:
+ /* interval_try_next_right_match: */
+
+ interval_stat = rx_next_solution (solns->right);
+ if (interval_stat == rx_yes)
+ {
+ solns->final_tag = solns->right->final_tag;
+ return interval_stat;
+ }
+ else if (interval_stat == rx_no)
+ {
+ rx_free_solutions (solns->right);
+ solns->right = 0;
+ solns->step = 2;
+ goto interval_try_next_left_match;
+ }
+ else /* interval_stat == rx_bogus */
+ {
+ rx_free_solutions (solns->left);
+ solns->left = 0;
+ rx_free_solutions (solns->right);
+ solns->right = 0;
+ solns->step = -1;
+ return interval_stat;
+ }
+ }
+ }
+
+ case r_context:
+ {
+ solns->step = -1;
+ solns->final_tag = 1;
+ return solns->contextfn (solns->closure,
+ solns->exp,
+ solns->start, solns->end,
+ solns->regs);
+ }
+
+
+ }
+ }
+ return rx_bogus;
+ }
+}
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXSPENCERH
+#define RXSPENCERH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+#include "rxproto.h"
+#include "rxnode.h"
+#include "rxunfa.h"
+#include "rxanal.h"
+#include "inst-rxposix.h"
+
+\f
+
+#define RX_MANY_CASES 30
+
+
+typedef enum rx_answers (*rx_vmfn)
+ P((void * closure,
+ unsigned const char ** burst, int * len, int * offset,
+ int start, int end, int need));
+
+typedef enum rx_answers (*rx_contextfn)
+ P((void * closure,
+ struct rexp_node * node,
+ int start, int end,
+ struct rx_registers * regs));
+
+
+struct rx_solutions
+{
+ int step;
+
+ int cset_size;
+ struct rexp_node * exp;
+ struct rexp_node ** subexps;
+ struct rx_registers * regs;
+
+ int start;
+ int end;
+
+ rx_vmfn vmfn;
+ rx_contextfn contextfn;
+ void * closure;
+
+ struct rx_unfaniverse * verse;
+ struct rx_unfa * dfa;
+ struct rx_classical_system match_engine;
+ struct rx_unfa * left_dfa;
+ struct rx_classical_system left_match_engine;
+
+ int split_guess;
+ struct rx_solutions * left;
+ struct rx_solutions * right;
+
+ int interval_x;
+
+ int saved_rm_so;
+ int saved_rm_eo;
+
+ int final_tag;
+};
+
+extern struct rx_solutions rx_no_solutions;
+
+\f
+#ifdef __STDC__
+extern struct rx_solutions * rx_make_solutions (struct rx_registers * regs, struct rx_unfaniverse * verse, struct rexp_node * expression, struct rexp_node ** subexps, int cset_size, int start, int end, rx_vmfn vmfn, rx_contextfn contextfn, void * closure);
+extern void rx_free_solutions (struct rx_solutions * solns);
+extern int rx_best_end_guess (struct rx_solutions * solns, struct rexp_node * exp, int bound);
+extern enum rx_answers rx_next_solution (struct rx_solutions * solns);
+
+#else /* STDC */
+extern struct rx_solutions * rx_make_solutions ();
+extern void rx_free_solutions ();
+extern int rx_best_end_guess ();
+extern enum rx_answers rx_next_solution ();
+
+#endif /* STDC */
+
+#endif /* RXSPENCERH */
--- /dev/null
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+#include "rxall.h"
+#include "rxstr.h"
+
+\f
+
+#ifdef __STDC__
+enum rx_answers
+rx_str_vmfn (void * closure, unsigned const char ** burstp, int * lenp, int * offsetp, int start, int end, int need)
+#else
+enum rx_answers
+rx_str_vmfn (closure, burstp, lenp, offsetp, start, end, need)
+ void * closure;
+ unsigned const char ** burstp;
+ int * lenp;
+ int * offsetp;
+ int start;
+ int end;
+ int need;
+#endif
+{
+ struct rx_str_closure * strc;
+ strc = (struct rx_str_closure *)closure;
+
+ if ( (need < 0)
+ || (need > strc->len))
+ return rx_no;
+
+ *burstp = strc->str;
+ *lenp = strc->len;
+ *offsetp = 0;
+ return rx_yes;
+}
+
+#ifdef __STDC__
+enum rx_answers
+rx_str_contextfn (void * closure, struct rexp_node * node, int start, int end, struct rx_registers * regs)
+#else
+enum rx_answers
+rx_str_contextfn (closure, node, start, end, regs)
+ void * closure;
+ struct rexp_node * node;
+ int start;
+ int end;
+ struct rx_registers * regs;
+#endif
+{
+ struct rx_str_closure * strc;
+
+ strc = (struct rx_str_closure *)closure;
+ switch (node->params.intval)
+ {
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ {
+ int cmp;
+ int regn;
+ regn = node->params.intval - '0';
+ if ( (regs[regn].rm_so == -1)
+ || ((end - start) != (regs[regn].rm_eo - regs[regn].rm_so)))
+ return rx_no;
+ else
+ {
+ if (strc->rules.case_indep)
+ cmp = strncasecmp (strc->str + start,
+ strc->str + regs[regn].rm_so,
+ end - start);
+ else
+ cmp = strncmp (strc->str + start,
+ strc->str + regs[regn].rm_so,
+ end - start);
+
+ return (!cmp
+ ? rx_yes
+ : rx_no);
+ }
+ }
+
+ case '^':
+ {
+ return (( (start == end)
+ && ( ((start == 0) && !strc->rules.not_bol)
+ || ( (start > 0)
+ && strc->rules.newline_anchor
+ && (strc->str[start - 1] == '\n'))))
+ ? rx_yes
+ : rx_no);
+ }
+
+ case '$':
+ {
+ return (( (start == end)
+ && ( ((start == strc->len) && !strc->rules.not_eol)
+ || ( (start < strc->len)
+ && strc->rules.newline_anchor
+ && (strc->str[start] == '\n'))))
+ ? rx_yes
+ : rx_no);
+ }
+
+ case '<':
+ case '>':
+
+ case 'B':
+ case 'b':
+
+
+ default:
+ return rx_bogus;
+ }
+}
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXSTRH
+#define RXSTRH
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+
+#include "rxspencer.h"
+#include "rxcontext.h"
+
+\f
+
+
+
+struct rx_str_closure
+{
+ struct rx_context_rules rules;
+ const unsigned char * str;
+ int len;
+};
+
+
+\f
+#ifdef __STDC__
+extern enum rx_answers rx_str_vmfn (void * closure, unsigned const char ** burstp, int * lenp, int * offsetp, int start, int end, int need);
+extern enum rx_answers rx_str_contextfn (void * closure, struct rexp_node * node, int start, int end, struct rx_registers * regs);
+
+#else /* STDC */
+extern enum rx_answers rx_str_vmfn ();
+extern enum rx_answers rx_str_contextfn ();
+
+#endif /* STDC */
+
+
+
+
+
+
+#endif /* RXSTRH */
--- /dev/null
+
+
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+\f
+
+#include "rxall.h"
+#include "rxsuper.h"
+
+\f
+/* The functions in the next several pages define the lazy-NFA-conversion used
+ * by matchers. The input to this construction is an NFA such as
+ * is built by compactify_nfa (rx.c). The output is the superNFA.
+ */
+
+/* Match engines can use arbitrary values for opcodes. So, the parse tree
+ * is built using instructions names (enum rx_opcode), but the superstate
+ * nfa is populated with mystery opcodes (void *).
+ *
+ * For convenience, here is an id table. The opcodes are == to their inxs
+ *
+ * The lables in re_search_2 would make good values for instructions.
+ */
+
+void * rx_id_instruction_table[rx_num_instructions] =
+{
+ (void *) rx_backtrack_point,
+ (void *) rx_do_side_effects,
+ (void *) rx_cache_miss,
+ (void *) rx_next_char,
+ (void *) rx_backtrack,
+ (void *) rx_error_inx
+};
+
+\f
+
+
+/* The partially instantiated superstate graph has a transition
+ * table at every node. There is one entry for every character.
+ * This fills in the transition for a set.
+ */
+#ifdef __STDC__
+static void
+install_transition (struct rx_superstate *super,
+ struct rx_inx *answer, rx_Bitset trcset)
+#else
+static void
+install_transition (super, answer, trcset)
+ struct rx_superstate *super;
+ struct rx_inx *answer;
+ rx_Bitset trcset;
+#endif
+{
+ struct rx_inx * transitions = super->transitions;
+ int chr;
+ for (chr = 0; chr < 256; )
+ if (!*trcset)
+ {
+ ++trcset;
+ chr += 32;
+ }
+ else
+ {
+ RX_subset sub = *trcset;
+ RX_subset mask = 1;
+ int bound = chr + 32;
+ while (chr < bound)
+ {
+ if (sub & mask)
+ transitions [chr] = *answer;
+ ++chr;
+ mask <<= 1;
+ }
+ ++trcset;
+ }
+}
+
+
+#ifdef __STDC__
+static int
+qlen (struct rx_superstate * q)
+#else
+static int
+qlen (q)
+ struct rx_superstate * q;
+#endif
+{
+ int count = 1;
+ struct rx_superstate * it;
+ if (!q)
+ return 0;
+ for (it = q->next_recyclable; it != q; it = it->next_recyclable)
+ ++count;
+ return count;
+}
+
+#ifdef __STDC__
+static void
+check_cache (struct rx_cache * cache)
+#else
+static void
+check_cache (cache)
+ struct rx_cache * cache;
+#endif
+{
+ struct rx_cache * you_fucked_up = 0;
+ int total = cache->superstates;
+ int semi = cache->semifree_superstates;
+ if (semi != qlen (cache->semifree_superstate))
+ check_cache (you_fucked_up);
+ if ((total - semi) != qlen (cache->lru_superstate))
+ check_cache (you_fucked_up);
+}
+#ifdef __STDC__
+char *
+rx_cache_malloc (struct rx_cache * cache, int size)
+#else
+char *
+rx_cache_malloc (cache, size)
+ struct rx_cache * cache;
+ int size;
+#endif
+{
+ char * answer;
+ answer = (char *)malloc (size);
+ if (answer)
+ cache->bytes_used += size;
+ return answer;
+}
+
+
+#ifdef __STDC__
+void
+rx_cache_free (struct rx_cache * cache, int size, char * mem)
+#else
+void
+rx_cache_free (cache, size, mem)
+ struct rx_cache * cache;
+ int size;
+ char * mem;
+#endif
+{
+ free (mem);
+ cache->bytes_used -= size;
+}
+
+
+
+/* When a superstate is old and neglected, it can enter a
+ * semi-free state. A semi-free state is slated to die.
+ * Incoming transitions to a semi-free state are re-written
+ * to cause an (interpreted) fault when they are taken.
+ * The fault handler revives the semi-free state, patches
+ * incoming transitions back to normal, and continues.
+ *
+ * The idea is basicly to free in two stages, aborting
+ * between the two if the state turns out to be useful again.
+ * When a free is aborted, the rescued superstate is placed
+ * in the most-favored slot to maximize the time until it
+ * is next semi-freed.
+ *
+ * Overall, this approximates truly freeing superstates in
+ * lru order, but does not involve the cost of updating perfectly
+ * accurate timestamps every time a superstate is touched.
+ */
+
+#ifdef __STDC__
+static void
+semifree_superstate (struct rx_cache * cache)
+#else
+static void
+semifree_superstate (cache)
+ struct rx_cache * cache;
+#endif
+{
+ int disqualified = cache->semifree_superstates;
+ if (disqualified == cache->superstates)
+ return;
+ while (cache->lru_superstate->locks)
+ {
+ cache->lru_superstate = cache->lru_superstate->next_recyclable;
+ ++disqualified;
+ if (disqualified == cache->superstates)
+ return;
+ }
+ {
+ struct rx_superstate * it = cache->lru_superstate;
+ it->next_recyclable->prev_recyclable = it->prev_recyclable;
+ it->prev_recyclable->next_recyclable = it->next_recyclable;
+ cache->lru_superstate = (it == it->next_recyclable
+ ? 0
+ : it->next_recyclable);
+ if (!cache->semifree_superstate)
+ {
+ cache->semifree_superstate = it;
+ it->next_recyclable = it;
+ it->prev_recyclable = it;
+ }
+ else
+ {
+ it->prev_recyclable = cache->semifree_superstate->prev_recyclable;
+ it->next_recyclable = cache->semifree_superstate;
+ it->prev_recyclable->next_recyclable = it;
+ it->next_recyclable->prev_recyclable = it;
+ }
+ {
+ struct rx_distinct_future *df;
+ it->is_semifree = 1;
+ ++cache->semifree_superstates;
+ df = it->transition_refs;
+ if (df)
+ {
+ df->prev_same_dest->next_same_dest = 0;
+ for (df = it->transition_refs; df; df = df->next_same_dest)
+ {
+ df->future_frame.inx = cache->instruction_table[rx_cache_miss];
+ df->future_frame.data = 0;
+ df->future_frame.data_2 = (void *) df;
+ /* If there are any NEXT-CHAR instruction frames that
+ * refer to this state, we convert them to CACHE-MISS frames.
+ */
+ if (!df->effects
+ && (df->edge->options->next_same_super_edge[0]
+ == df->edge->options))
+ install_transition (df->present, &df->future_frame,
+ df->edge->cset);
+ }
+ df = it->transition_refs;
+ df->prev_same_dest->next_same_dest = df;
+ }
+ }
+ }
+}
+
+
+#ifdef __STDC__
+static void
+refresh_semifree_superstate (struct rx_cache * cache,
+ struct rx_superstate * super)
+#else
+static void
+refresh_semifree_superstate (cache, super)
+ struct rx_cache * cache;
+ struct rx_superstate * super;
+#endif
+{
+ struct rx_distinct_future *df;
+
+ if (super->transition_refs)
+ {
+ super->transition_refs->prev_same_dest->next_same_dest = 0;
+ for (df = super->transition_refs; df; df = df->next_same_dest)
+ {
+ df->future_frame.inx = cache->instruction_table[rx_next_char];
+ df->future_frame.data = (void *) super->transitions;
+ df->future_frame.data_2 = (void *)(super->contents->is_final);
+ /* CACHE-MISS instruction frames that refer to this state,
+ * must be converted to NEXT-CHAR frames.
+ */
+ if (!df->effects
+ && (df->edge->options->next_same_super_edge[0]
+ == df->edge->options))
+ install_transition (df->present, &df->future_frame,
+ df->edge->cset);
+ }
+ super->transition_refs->prev_same_dest->next_same_dest
+ = super->transition_refs;
+ }
+ if (cache->semifree_superstate == super)
+ cache->semifree_superstate = (super->prev_recyclable == super
+ ? 0
+ : super->prev_recyclable);
+ super->next_recyclable->prev_recyclable = super->prev_recyclable;
+ super->prev_recyclable->next_recyclable = super->next_recyclable;
+
+ if (!cache->lru_superstate)
+ (cache->lru_superstate
+ = super->next_recyclable
+ = super->prev_recyclable
+ = super);
+ else
+ {
+ super->next_recyclable = cache->lru_superstate;
+ super->prev_recyclable = cache->lru_superstate->prev_recyclable;
+ super->next_recyclable->prev_recyclable = super;
+ super->prev_recyclable->next_recyclable = super;
+ }
+ super->is_semifree = 0;
+ --cache->semifree_superstates;
+}
+
+#ifdef __STDC__
+void
+rx_refresh_this_superstate (struct rx_cache * cache,
+ struct rx_superstate * superstate)
+#else
+void
+rx_refresh_this_superstate (cache, superstate)
+ struct rx_cache * cache;
+ struct rx_superstate * superstate;
+#endif
+{
+ if (superstate->is_semifree)
+ refresh_semifree_superstate (cache, superstate);
+ else if (cache->lru_superstate == superstate)
+ cache->lru_superstate = superstate->next_recyclable;
+ else if (superstate != cache->lru_superstate->prev_recyclable)
+ {
+ superstate->next_recyclable->prev_recyclable
+ = superstate->prev_recyclable;
+ superstate->prev_recyclable->next_recyclable
+ = superstate->next_recyclable;
+ superstate->next_recyclable = cache->lru_superstate;
+ superstate->prev_recyclable = cache->lru_superstate->prev_recyclable;
+ superstate->next_recyclable->prev_recyclable = superstate;
+ superstate->prev_recyclable->next_recyclable = superstate;
+ }
+}
+
+#ifdef __STDC__
+static void
+release_superset_low (struct rx_cache * cache,
+ struct rx_superset *set)
+#else
+static void
+release_superset_low (cache, set)
+ struct rx_cache * cache;
+ struct rx_superset *set;
+#endif
+{
+ if (!--set->refs)
+ {
+ if (set->starts_for)
+ set->starts_for->start_set = 0;
+
+ if (set->cdr)
+ release_superset_low (cache, set->cdr);
+
+ rx_hash_free (&set->hash_item, &cache->superset_hash_rules);
+ rx_cache_free (cache,
+ sizeof (struct rx_superset),
+ (char *)set);
+ }
+}
+
+#ifdef __STDC__
+void
+rx_release_superset (struct rx *rx,
+ struct rx_superset *set)
+#else
+void
+rx_release_superset (rx, set)
+ struct rx *rx;
+ struct rx_superset *set;
+#endif
+{
+ release_superset_low (rx->cache, set);
+}
+
+/* This tries to add a new superstate to the superstate freelist.
+ * It might, as a result, free some edge pieces or hash tables.
+ * If nothing can be freed because too many locks are being held, fail.
+ */
+
+#ifdef __STDC__
+static int
+rx_really_free_superstate (struct rx_cache * cache)
+#else
+static int
+rx_really_free_superstate (cache)
+ struct rx_cache * cache;
+#endif
+{
+ int locked_superstates = 0;
+ struct rx_superstate * it;
+
+ if (!cache->superstates)
+ return 0;
+
+ /* scale these */
+ while ((cache->hits + cache->misses) > cache->superstates)
+ {
+ cache->hits >>= 1;
+ cache->misses >>= 1;
+ }
+
+ /* semifree superstates faster than they are freed
+ * so popular states can be rescued.
+ */
+ semifree_superstate (cache);
+ semifree_superstate (cache);
+ semifree_superstate (cache);
+
+#if 0
+ Redundant because semifree_superstate already
+ makes this check;
+
+
+ while (cache->semifree_superstate && cache->semifree_superstate->locks)
+ {
+ refresh_semifree_superstate (cache, cache->semifree_superstate);
+ ++locked_superstates;
+ if (locked_superstates == cache->superstates)
+ return 0;
+ }
+
+
+ if (cache->semifree_superstate)
+ insert the "it =" block which follows this "if 0" code;
+ else
+ {
+ while (cache->lru_superstate->locks)
+ {
+ cache->lru_superstate = cache->lru_superstate->next_recyclable;
+ ++locked_superstates;
+ if (locked_superstates == cache->superstates)
+ return 0;
+ }
+ it = cache->lru_superstate;
+ it->next_recyclable->prev_recyclable = it->prev_recyclable;
+ it->prev_recyclable->next_recyclable = it->next_recyclable;
+ cache->lru_superstate = ((it == it->next_recyclable)
+ ? 0
+ : it->next_recyclable);
+ }
+#endif
+
+ if (!cache->semifree_superstate)
+ return 0;
+
+ {
+ it = cache->semifree_superstate;
+ it->next_recyclable->prev_recyclable = it->prev_recyclable;
+ it->prev_recyclable->next_recyclable = it->next_recyclable;
+ cache->semifree_superstate = ((it == it->next_recyclable)
+ ? 0
+ : it->next_recyclable);
+ --cache->semifree_superstates;
+ }
+
+
+ if (it->transition_refs)
+ {
+ struct rx_distinct_future *df;
+ for (df = it->transition_refs,
+ df->prev_same_dest->next_same_dest = 0;
+ df;
+ df = df->next_same_dest)
+ {
+ df->future_frame.inx = cache->instruction_table[rx_cache_miss];
+ df->future_frame.data = 0;
+ df->future_frame.data_2 = (void *) df;
+ df->future = 0;
+ }
+ it->transition_refs->prev_same_dest->next_same_dest =
+ it->transition_refs;
+ }
+ {
+ struct rx_super_edge *tc = it->edges;
+ while (tc)
+ {
+ struct rx_distinct_future * df;
+ struct rx_super_edge *tct = tc->next;
+ df = tc->options;
+ df->next_same_super_edge[1]->next_same_super_edge[0] = 0;
+ while (df)
+ {
+ struct rx_distinct_future *dft = df;
+ df = df->next_same_super_edge[0];
+
+
+ if (dft->future && dft->future->transition_refs == dft)
+ {
+ dft->future->transition_refs = dft->next_same_dest;
+ if (dft->future->transition_refs == dft)
+ dft->future->transition_refs = 0;
+ }
+ dft->next_same_dest->prev_same_dest = dft->prev_same_dest;
+ dft->prev_same_dest->next_same_dest = dft->next_same_dest;
+ rx_cache_free (cache,
+ sizeof (struct rx_distinct_future),
+ (char *)dft);
+ }
+ rx_cache_free (cache,
+ sizeof (struct rx_super_edge),
+ (char *)tc);
+ tc = tct;
+ }
+ }
+
+ if (it->contents->superstate == it)
+ it->contents->superstate = 0;
+ release_superset_low (cache, it->contents);
+ rx_cache_free (cache,
+ (sizeof (struct rx_superstate)
+ + cache->local_cset_size * sizeof (struct rx_inx)),
+ (char *)it);
+ --cache->superstates;
+ return 1;
+}
+
+
+#ifdef __STDC__
+static char *
+rx_cache_malloc_or_get (struct rx_cache * cache, int size)
+#else
+static char *
+rx_cache_malloc_or_get (cache, size)
+ struct rx_cache * cache;
+ int size;
+#endif
+{
+ while ( (cache->bytes_used + size > cache->bytes_allowed)
+ && rx_really_free_superstate (cache))
+ ;
+
+ return rx_cache_malloc (cache, size);
+}
+
+\f
+
+#ifdef __STDC__
+static int
+supersetcmp (void * va, void * vb)
+#else
+static int
+supersetcmp (va, vb)
+ void * va;
+ void * vb;
+#endif
+{
+ struct rx_superset * a = (struct rx_superset *)va;
+ struct rx_superset * b = (struct rx_superset *)vb;
+ return ( (a == b)
+ || (a && b && (a->id == b->id) && (a->car == b->car) && (a->cdr == b->cdr)));
+}
+
+#define rx_abs(A) (((A) > 0) ? (A) : -(A))
+
+
+#ifdef __STDC__
+static struct rx_hash_item *
+superset_allocator (struct rx_hash_rules * rules, void * val)
+#else
+static struct rx_hash_item *
+superset_allocator (rules, val)
+ struct rx_hash_rules * rules;
+ void * val;
+#endif
+{
+ struct rx_cache * cache;
+ struct rx_superset * template;
+ struct rx_superset * newset;
+
+ cache = ((struct rx_cache *)
+ ((char *)rules
+ - (unsigned long)(&((struct rx_cache *)0)->superset_hash_rules)));
+ template = (struct rx_superset *)val;
+ newset = ((struct rx_superset *)
+ rx_cache_malloc (cache, sizeof (*template)));
+ if (!newset)
+ return 0;
+ {
+ int cdrfinal;
+ int cdredges;
+
+ cdrfinal = (template->cdr
+ ? template->cdr->is_final
+ : 0);
+ cdredges = (template->cdr
+ ? template->cdr->has_cset_edges
+ : 0);
+
+ newset->is_final = (rx_abs (template->car->is_final) > rx_abs(cdrfinal)
+ ? template->car->is_final
+ : template->cdr->is_final);
+ newset->has_cset_edges = (template->car->has_cset_edges || cdredges);
+ }
+ newset->refs = 0;
+ newset->id = template->id;
+ newset->car = template->car;
+ newset->cdr = template->cdr;
+ rx_protect_superset (rx, template->cdr);
+ newset->superstate = 0;
+ newset->starts_for = 0;
+ newset->hash_item.data = (void *)newset;
+ newset->hash_item.binding = 0;
+ return &newset->hash_item;
+}
+
+#ifdef __STDC__
+static struct rx_hash *
+super_hash_allocator (struct rx_hash_rules * rules)
+#else
+static struct rx_hash *
+super_hash_allocator (rules)
+ struct rx_hash_rules * rules;
+#endif
+{
+ struct rx_cache * cache;
+ cache = ((struct rx_cache *)
+ ((char *)rules
+ - (unsigned long)(&((struct rx_cache *)0)->superset_hash_rules)));
+ return ((struct rx_hash *)
+ rx_cache_malloc (cache, sizeof (struct rx_hash)));
+}
+
+
+#ifdef __STDC__
+static void
+super_hash_liberator (struct rx_hash * hash, struct rx_hash_rules * rules)
+#else
+static void
+super_hash_liberator (hash, rules)
+ struct rx_hash * hash;
+ struct rx_hash_rules * rules;
+#endif
+{
+ struct rx_cache * cache
+ = ((struct rx_cache *)
+ (char *)rules - (long)(&((struct rx_cache *)0)->superset_hash_rules));
+ rx_cache_free (cache, sizeof (struct rx_hash), (char *)hash);
+}
+
+#ifdef __STDC__
+static void
+superset_hash_item_liberator (struct rx_hash_item * it,
+ struct rx_hash_rules * rules)
+#else
+static void
+superset_hash_item_liberator (it, rules)
+ struct rx_hash_item * it;
+ struct rx_hash_rules * rules;
+#endif
+{
+}
+
+int rx_cache_bound = 3;
+static int rx_default_cache_got = 0;
+
+static struct rx_cache default_cache =
+{
+ {
+ supersetcmp,
+ super_hash_allocator,
+ super_hash_liberator,
+ superset_allocator,
+ superset_hash_item_liberator,
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ RX_DEFAULT_DFA_CACHE_SIZE,
+ 0,
+ 256,
+ rx_id_instruction_table,
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }
+};
+struct rx_cache * rx_default_cache = &default_cache;
+
+/* This adds an element to a superstate set. These sets are lists, such
+ * that lists with == elements are ==. The empty set is returned by
+ * superset_cons (rx, 0, 0) and is NOT equivelent to
+ * (struct rx_superset)0.
+ */
+
+#ifdef __STDC__
+struct rx_superset *
+rx_superset_cons (struct rx * rx,
+ struct rx_nfa_state *car, struct rx_superset *cdr)
+#else
+struct rx_superset *
+rx_superset_cons (rx, car, cdr)
+ struct rx * rx;
+ struct rx_nfa_state *car;
+ struct rx_superset *cdr;
+#endif
+{
+ struct rx_cache * cache = rx->cache;
+ if (!car && !cdr)
+ {
+ if (!cache->empty_superset)
+ {
+ cache->empty_superset
+ = ((struct rx_superset *)
+ rx_cache_malloc (cache, sizeof (struct rx_superset)));
+ if (!cache->empty_superset)
+ return 0;
+ rx_bzero ((char *)cache->empty_superset, sizeof (struct rx_superset));
+ cache->empty_superset->refs = 1000;
+ }
+ return cache->empty_superset;
+ }
+ {
+ struct rx_superset template;
+ struct rx_hash_item * hit;
+ template.car = car;
+ template.cdr = cdr;
+ template.id = rx->rx_id;
+ rx_protect_superset (rx, template.cdr);
+ hit = rx_hash_store (&cache->superset_table,
+ (unsigned long)car ^ car->id ^ (unsigned long)cdr,
+ (void *)&template,
+ &cache->superset_hash_rules);
+ rx_protect_superset (rx, template.cdr);
+ return (hit
+ ? (struct rx_superset *)hit->data
+ : 0);
+ }
+}
+
+/* This computes a union of two NFA state sets. The sets do not have the
+ * same representation though. One is a RX_SUPERSET structure (part
+ * of the superstate NFA) and the other is an NFA_STATE_SET (part of the NFA).
+ */
+
+#ifdef __STDC__
+struct rx_superset *
+rx_superstate_eclosure_union (struct rx * rx, struct rx_superset *set, struct rx_nfa_state_set *ecl)
+#else
+struct rx_superset *
+rx_superstate_eclosure_union (rx, set, ecl)
+ struct rx * rx;
+ struct rx_superset *set;
+ struct rx_nfa_state_set *ecl;
+#endif
+{
+ if (!ecl)
+ return set;
+
+ if (!set->car)
+ return rx_superset_cons (rx, ecl->car,
+ rx_superstate_eclosure_union (rx, set, ecl->cdr));
+ if (set->car == ecl->car)
+ return rx_superstate_eclosure_union (rx, set, ecl->cdr);
+
+ {
+ struct rx_superset * tail;
+ struct rx_nfa_state * first;
+
+ if (set->car->id < ecl->car->id)
+ {
+ tail = rx_superstate_eclosure_union (rx, set->cdr, ecl);
+ first = set->car;
+ }
+ else
+ {
+ tail = rx_superstate_eclosure_union (rx, set, ecl->cdr);
+ first = ecl->car;
+ }
+ if (!tail)
+ return 0;
+ else
+ {
+ struct rx_superset * answer;
+ answer = rx_superset_cons (rx, first, tail);
+ if (!answer)
+ {
+ rx_protect_superset (rx, tail);
+ rx_release_superset (rx, tail);
+ return 0;
+ }
+ else
+ return answer;
+ }
+ }
+}
+
+
+\f
+
+/*
+ * This makes sure that a list of rx_distinct_futures contains
+ * a future for each possible set of side effects in the eclosure
+ * of a given state. This is some of the work of filling in a
+ * superstate transition.
+ */
+
+#ifdef __STDC__
+static struct rx_distinct_future *
+include_futures (struct rx *rx,
+ struct rx_distinct_future *df, struct rx_nfa_state
+ *state, struct rx_superstate *superstate)
+#else
+static struct rx_distinct_future *
+include_futures (rx, df, state, superstate)
+ struct rx *rx;
+ struct rx_distinct_future *df;
+ struct rx_nfa_state *state;
+ struct rx_superstate *superstate;
+#endif
+{
+ struct rx_possible_future *future;
+ struct rx_cache * cache = rx->cache;
+ for (future = rx_state_possible_futures (rx, state); future; future = future->next)
+ {
+ struct rx_distinct_future *dfp;
+ struct rx_distinct_future *insert_before = 0;
+ if (df)
+ df->next_same_super_edge[1]->next_same_super_edge[0] = 0;
+ for (dfp = df; dfp; dfp = dfp->next_same_super_edge[0])
+ if (dfp->effects == future->effects)
+ break;
+ else
+ {
+ int order = rx->se_list_cmp (rx, dfp->effects, future->effects);
+ if (order > 0)
+ {
+ insert_before = dfp;
+ dfp = 0;
+ break;
+ }
+ }
+ if (df)
+ df->next_same_super_edge[1]->next_same_super_edge[0] = df;
+ if (!dfp)
+ {
+ dfp
+ = ((struct rx_distinct_future *)
+ rx_cache_malloc (cache,
+ sizeof (struct rx_distinct_future)));
+ if (!dfp)
+ return 0;
+ if (!df)
+ {
+ df = insert_before = dfp;
+ df->next_same_super_edge[0] = df->next_same_super_edge[1] = df;
+ }
+ else if (!insert_before)
+ insert_before = df;
+ else if (insert_before == df)
+ df = dfp;
+
+ dfp->next_same_super_edge[0] = insert_before;
+ dfp->next_same_super_edge[1]
+ = insert_before->next_same_super_edge[1];
+ dfp->next_same_super_edge[1]->next_same_super_edge[0] = dfp;
+ dfp->next_same_super_edge[0]->next_same_super_edge[1] = dfp;
+ dfp->next_same_dest = dfp->prev_same_dest = dfp;
+ dfp->future = 0;
+ dfp->present = superstate;
+ dfp->future_frame.inx = rx->instruction_table[rx_cache_miss];
+ dfp->future_frame.data = 0;
+ dfp->future_frame.data_2 = (void *) dfp;
+ dfp->side_effects_frame.inx
+ = rx->instruction_table[rx_do_side_effects];
+ dfp->side_effects_frame.data = 0;
+ dfp->side_effects_frame.data_2 = (void *) dfp;
+ dfp->effects = future->effects;
+ }
+ }
+ return df;
+}
+\f
+
+
+/* This constructs a new superstate from its state set. The only
+ * complexity here is memory management.
+ */
+#ifdef __STDC__
+struct rx_superstate *
+rx_superstate (struct rx *rx,
+ struct rx_superset *set)
+#else
+struct rx_superstate *
+rx_superstate (rx, set)
+ struct rx *rx;
+ struct rx_superset *set;
+#endif
+{
+ struct rx_cache * cache = rx->cache;
+ struct rx_superstate * superstate = 0;
+
+ /* Does the superstate already exist in the cache? */
+ if (set->superstate)
+ {
+ if (set->superstate->rx_id != rx->rx_id)
+ {
+ /* Aha. It is in the cache, but belongs to a superstate
+ * that refers to an NFA that no longer exists.
+ * (We know it no longer exists because it was evidently
+ * stored in the same region of memory as the current nfa
+ * yet it has a different id.)
+ */
+ superstate = set->superstate;
+ if (!superstate->is_semifree)
+ {
+ if (cache->lru_superstate == superstate)
+ {
+ cache->lru_superstate = superstate->next_recyclable;
+ if (cache->lru_superstate == superstate)
+ cache->lru_superstate = 0;
+ }
+ {
+ superstate->next_recyclable->prev_recyclable
+ = superstate->prev_recyclable;
+ superstate->prev_recyclable->next_recyclable
+ = superstate->next_recyclable;
+ if (!cache->semifree_superstate)
+ {
+ (cache->semifree_superstate
+ = superstate->next_recyclable
+ = superstate->prev_recyclable
+ = superstate);
+ }
+ else
+ {
+ superstate->next_recyclable = cache->semifree_superstate;
+ superstate->prev_recyclable
+ = cache->semifree_superstate->prev_recyclable;
+ superstate->next_recyclable->prev_recyclable
+ = superstate;
+ superstate->prev_recyclable->next_recyclable
+ = superstate;
+ cache->semifree_superstate = superstate;
+ }
+ ++cache->semifree_superstates;
+ }
+ }
+ set->superstate = 0;
+ goto handle_cache_miss;
+ }
+ ++cache->hits;
+ superstate = set->superstate;
+
+ rx_refresh_this_superstate (cache, superstate);
+ return superstate;
+ }
+
+ handle_cache_miss:
+
+ /* This point reached only for cache misses. */
+ ++cache->misses;
+#if RX_DEBUG
+ if (rx_debug_trace > 1)
+ {
+ struct rx_superset * setp = set;
+ fprintf (stderr, "Building a superstet %d(%d): ", rx->rx_id, set);
+ while (setp)
+ {
+ fprintf (stderr, "%d ", setp->id);
+ setp = setp->cdr;
+ }
+ fprintf (stderr, "(%d)\n", set);
+ }
+#endif
+
+ {
+ int superstate_size;
+ superstate_size = ( sizeof (*superstate)
+ + ( sizeof (struct rx_inx)
+ * rx->local_cset_size));
+ superstate = ((struct rx_superstate *)
+ rx_cache_malloc_or_get (cache, superstate_size));
+ ++cache->superstates;
+ }
+
+ if (!superstate)
+ return 0;
+
+ if (!cache->lru_superstate)
+ (cache->lru_superstate
+ = superstate->next_recyclable
+ = superstate->prev_recyclable
+ = superstate);
+ else
+ {
+ superstate->next_recyclable = cache->lru_superstate;
+ superstate->prev_recyclable = cache->lru_superstate->prev_recyclable;
+ ( superstate->prev_recyclable->next_recyclable
+ = superstate->next_recyclable->prev_recyclable
+ = superstate);
+ }
+ superstate->rx_id = rx->rx_id;
+ superstate->transition_refs = 0;
+ superstate->locks = 0;
+ superstate->is_semifree = 0;
+ set->superstate = superstate;
+ superstate->contents = set;
+ rx_protect_superset (rx, set);
+ superstate->edges = 0;
+ {
+ int x;
+ /* None of the transitions from this superstate are known yet. */
+ for (x = 0; x < rx->local_cset_size; ++x)
+ {
+ struct rx_inx * ifr = &superstate->transitions[x];
+ ifr->inx = rx->instruction_table [rx_cache_miss];
+ ifr->data = ifr->data_2 = 0;
+ }
+ }
+ return superstate;
+}
+\f
+
+/* This computes the destination set of one edge of the superstate NFA.
+ * Note that a RX_DISTINCT_FUTURE is a superstate edge.
+ * Returns 0 on an allocation failure.
+ */
+
+#ifdef __STDC__
+static int
+solve_destination (struct rx *rx, struct rx_distinct_future *df)
+#else
+static int
+solve_destination (rx, df)
+ struct rx *rx;
+ struct rx_distinct_future *df;
+#endif
+{
+ struct rx_super_edge *tc = df->edge;
+ struct rx_superset *nfa_state;
+ struct rx_superset *nil_set = rx_superset_cons (rx, 0, 0);
+ struct rx_superset *solution = nil_set;
+ struct rx_superstate *dest;
+
+ rx_protect_superset (rx, solution);
+ /* Iterate over all NFA states in the state set of this superstate. */
+ for (nfa_state = df->present->contents;
+ nfa_state->car;
+ nfa_state = nfa_state->cdr)
+ {
+ struct rx_nfa_edge *e;
+ /* Iterate over all edges of each NFA state. */
+ for (e = nfa_state->car->edges; e; e = e->next)
+ /* If we find an edge that is labeled with
+ * the characters we are solving for.....
+ */
+ if ((e->type == ne_cset)
+ && rx_bitset_is_subset (rx->local_cset_size,
+ tc->cset,
+ e->params.cset))
+ {
+ struct rx_nfa_state *n = e->dest;
+ struct rx_possible_future *pf;
+ /* ....search the partial epsilon closures of the destination
+ * of that edge for a path that involves the same set of
+ * side effects we are solving for.
+ * If we find such a RX_POSSIBLE_FUTURE, we add members to the
+ * stateset we are computing.
+ */
+ for (pf = rx_state_possible_futures (rx, n); pf; pf = pf->next)
+ if (pf->effects == df->effects)
+ {
+ struct rx_superset * old_sol;
+ old_sol = solution;
+ solution = rx_superstate_eclosure_union (rx, solution,
+ pf->destset);
+ if (!solution)
+ return 0;
+ rx_protect_superset (rx, solution);
+ rx_release_superset (rx, old_sol);
+ }
+ }
+ }
+ /* It is possible that the RX_DISTINCT_FUTURE we are working on has
+ * the empty set of NFA states as its definition. In that case, this
+ * is a failure point.
+ */
+ if (solution == nil_set)
+ {
+ df->future_frame.inx = (void *) rx_backtrack;
+ df->future_frame.data = 0;
+ df->future_frame.data_2 = 0;
+ return 1;
+ }
+ dest = rx_superstate (rx, solution);
+ rx_release_superset (rx, solution);
+ if (!dest)
+ return 0;
+
+ {
+ struct rx_distinct_future *dft;
+ dft = df;
+ df->prev_same_dest->next_same_dest = 0;
+ while (dft)
+ {
+ dft->future = dest;
+ dft->future_frame.inx = rx->instruction_table[rx_next_char];
+ dft->future_frame.data = (void *) dest->transitions;
+ dft->future_frame.data_2 = (void *) dest->contents->is_final;
+ dft = dft->next_same_dest;
+ }
+ df->prev_same_dest->next_same_dest = df;
+ }
+ if (!dest->transition_refs)
+ dest->transition_refs = df;
+ else
+ {
+ struct rx_distinct_future *dft;
+ dft = dest->transition_refs->next_same_dest;
+ dest->transition_refs->next_same_dest = df->next_same_dest;
+ df->next_same_dest->prev_same_dest = dest->transition_refs;
+ df->next_same_dest = dft;
+ dft->prev_same_dest = df;
+ }
+ return 1;
+}
+
+
+/* This takes a superstate and a character, and computes some edges
+ * from the superstate NFA. In particular, this computes all edges
+ * that lead from SUPERSTATE given CHR. This function also
+ * computes the set of characters that share this edge set.
+ * This returns 0 on allocation error.
+ * The character set and list of edges are returned through
+ * the paramters CSETOUT and DFOUT.
+ */
+
+#ifdef __STDC__
+static int
+compute_super_edge (struct rx *rx, struct rx_distinct_future **dfout,
+ rx_Bitset csetout, struct rx_superstate *superstate,
+ unsigned char chr)
+#else
+static int
+compute_super_edge (rx, dfout, csetout, superstate, chr)
+ struct rx *rx;
+ struct rx_distinct_future **dfout;
+ rx_Bitset csetout;
+ struct rx_superstate *superstate;
+ unsigned char chr;
+#endif
+{
+ struct rx_superset *stateset = superstate->contents;
+
+ /* To compute the set of characters that share edges with CHR,
+ * we start with the full character set, and subtract.
+ */
+ rx_bitset_universe (rx->local_cset_size, csetout);
+ *dfout = 0;
+
+ /* Iterate over the NFA states in the superstate state-set. */
+ while (stateset->car)
+ {
+ struct rx_nfa_edge *e;
+ for (e = stateset->car->edges; e; e = e->next)
+ if (e->type == ne_cset)
+ {
+ if (!RX_bitset_member (e->params.cset, chr))
+ /* An edge that doesn't apply at least tells us some characters
+ * that don't share the same edge set as CHR.
+ */
+ rx_bitset_difference (rx->local_cset_size, csetout, e->params.cset);
+ else
+ {
+ /* If we find an NFA edge that applies, we make sure there
+ * are corresponding edges in the superstate NFA.
+ */
+ {
+ struct rx_distinct_future * saved;
+ saved = *dfout;
+ *dfout = include_futures (rx, *dfout, e->dest, superstate);
+ if (!*dfout)
+ {
+ struct rx_distinct_future * df;
+ df = saved;
+ if (df)
+ df->next_same_super_edge[1]->next_same_super_edge[0] = 0;
+ while (df)
+ {
+ struct rx_distinct_future *dft;
+ dft = df;
+ df = df->next_same_super_edge[0];
+
+ if (dft->future && dft->future->transition_refs == dft)
+ {
+ dft->future->transition_refs = dft->next_same_dest;
+ if (dft->future->transition_refs == dft)
+ dft->future->transition_refs = 0;
+ }
+ dft->next_same_dest->prev_same_dest = dft->prev_same_dest;
+ dft->prev_same_dest->next_same_dest = dft->next_same_dest;
+ rx_cache_free (rx->cache, sizeof (*dft), (char *)dft);
+ }
+ return 0;
+ }
+ }
+ /* We also trim the character set a bit. */
+ rx_bitset_intersection (rx->local_cset_size,
+ csetout, e->params.cset);
+ }
+ }
+ stateset = stateset->cdr;
+ }
+ return 1;
+}
+\f
+
+/* This is a constructor for RX_SUPER_EDGE structures. These are
+ * wrappers for lists of superstate NFA edges that share character sets labels.
+ * If a transition class contains more than one rx_distinct_future (superstate
+ * edge), then it represents a non-determinism in the superstate NFA.
+ */
+
+#ifdef __STDC__
+static struct rx_super_edge *
+rx_super_edge (struct rx *rx,
+ struct rx_superstate *super, rx_Bitset cset,
+ struct rx_distinct_future *df)
+#else
+static struct rx_super_edge *
+rx_super_edge (rx, super, cset, df)
+ struct rx *rx;
+ struct rx_superstate *super;
+ rx_Bitset cset;
+ struct rx_distinct_future *df;
+#endif
+{
+ struct rx_super_edge *tc;
+ int tc_size;
+
+ tc_size = ( sizeof (struct rx_super_edge)
+ + rx_sizeof_bitset (rx->local_cset_size));
+
+ tc = ((struct rx_super_edge *)
+ rx_cache_malloc (rx->cache, tc_size));
+
+ if (!tc)
+ return 0;
+
+ tc->next = super->edges;
+ super->edges = tc;
+ tc->rx_backtrack_frame.inx = rx->instruction_table[rx_backtrack_point];
+ tc->rx_backtrack_frame.data = 0;
+ tc->rx_backtrack_frame.data_2 = (void *) tc;
+ tc->options = df;
+ tc->cset = (rx_Bitset) ((char *) tc + sizeof (*tc));
+ rx_bitset_assign (rx->local_cset_size, tc->cset, cset);
+ if (df)
+ {
+ struct rx_distinct_future * dfp = df;
+ df->next_same_super_edge[1]->next_same_super_edge[0] = 0;
+ while (dfp)
+ {
+ dfp->edge = tc;
+ dfp = dfp->next_same_super_edge[0];
+ }
+ df->next_same_super_edge[1]->next_same_super_edge[0] = df;
+ }
+ return tc;
+}
+
+
+/* There are three kinds of cache miss. The first occurs when a
+ * transition is taken that has never been computed during the
+ * lifetime of the source superstate. That cache miss is handled by
+ * calling COMPUTE_SUPER_EDGE. The second kind of cache miss
+ * occurs when the destination superstate of a transition doesn't
+ * exist. SOLVE_DESTINATION is used to construct the destination superstate.
+ * Finally, the third kind of cache miss occurs when the destination
+ * superstate of a transition is in a `semi-free state'. That case is
+ * handled by UNFREE_SUPERSTATE.
+ *
+ * The function of HANDLE_CACHE_MISS is to figure out which of these
+ * cases applies.
+ */
+
+#ifdef __STDC__
+static void
+install_partial_transition (struct rx_superstate *super,
+ struct rx_inx *answer,
+ RX_subset set, int offset)
+#else
+static void
+install_partial_transition (super, answer, set, offset)
+ struct rx_superstate *super;
+ struct rx_inx *answer;
+ RX_subset set;
+ int offset;
+#endif
+{
+ int start = offset;
+ int end = start + 32;
+ RX_subset pos = 1;
+ struct rx_inx * transitions = super->transitions;
+
+ while (start < end)
+ {
+ if (set & pos)
+ transitions[start] = *answer;
+ pos <<= 1;
+ ++start;
+ }
+}
+
+
+#ifdef __STDC__
+struct rx_inx *
+rx_handle_cache_miss (struct rx *rx, struct rx_superstate *super, unsigned char chr, void *data)
+#else
+struct rx_inx *
+rx_handle_cache_miss (rx, super, chr, data)
+ struct rx *rx;
+ struct rx_superstate *super;
+ unsigned char chr;
+ void *data;
+#endif
+{
+ int offset = chr / RX_subset_bits;
+ struct rx_distinct_future *df = data;
+
+ if (!df) /* must be the shared_cache_miss_frame */
+ {
+ /* Perhaps this is just a transition waiting to be filled. */
+ struct rx_super_edge *tc;
+ RX_subset mask = rx_subset_singletons [chr % RX_subset_bits];
+
+ for (tc = super->edges; tc; tc = tc->next)
+ if (tc->cset[offset] & mask)
+ {
+ struct rx_inx * answer;
+ df = tc->options;
+ answer = ((tc->options->next_same_super_edge[0] != tc->options)
+ ? &tc->rx_backtrack_frame
+ : (df->effects
+ ? &df->side_effects_frame
+ : &df->future_frame));
+ install_partial_transition (super, answer,
+ tc->cset [offset], offset * 32);
+ return answer;
+ }
+ /* Otherwise, it's a flushed or newly encountered edge. */
+ {
+ char cset_space[1024]; /* this limit is far from unreasonable */
+ rx_Bitset trcset;
+ struct rx_inx *answer;
+
+ if (rx_sizeof_bitset (rx->local_cset_size) > sizeof (cset_space))
+ return 0; /* If the arbitrary limit is hit, always fail */
+ /* cleanly. */
+ trcset = (rx_Bitset)cset_space;
+ rx_lock_superstate (rx, super);
+ if (!compute_super_edge (rx, &df, trcset, super, chr))
+ {
+ rx_unlock_superstate (rx, super);
+ return 0;
+ }
+ if (!df) /* We just computed the fail transition. */
+ {
+ static struct rx_inx
+ shared_fail_frame = { 0, 0, (void *)rx_backtrack, 0 };
+ answer = &shared_fail_frame;
+ }
+ else
+ {
+ tc = rx_super_edge (rx, super, trcset, df);
+ if (!tc)
+ {
+ rx_unlock_superstate (rx, super);
+ return 0;
+ }
+ answer = ((tc->options->next_same_super_edge[0] != tc->options)
+ ? &tc->rx_backtrack_frame
+ : (df->effects
+ ? &df->side_effects_frame
+ : &df->future_frame));
+ }
+ install_partial_transition (super, answer,
+ trcset[offset], offset * 32);
+ rx_unlock_superstate (rx, super);
+ return answer;
+ }
+ }
+ else if (df->future) /* A cache miss on an edge with a future? Must be
+ * a semi-free destination. */
+ {
+ if (df->future->is_semifree)
+ refresh_semifree_superstate (rx->cache, df->future);
+ return &df->future_frame;
+ }
+ else
+ /* no future superstate on an existing edge */
+ {
+ rx_lock_superstate (rx, super);
+ if (!solve_destination (rx, df))
+ {
+ rx_unlock_superstate (rx, super);
+ return 0;
+ }
+ if (!df->effects
+ && (df->edge->options->next_same_super_edge[0] == df->edge->options))
+ install_partial_transition (super, &df->future_frame,
+ df->edge->cset[offset], offset * 32);
+ rx_unlock_superstate (rx, super);
+ return &df->future_frame;
+ }
+}
+
+
--- /dev/null
+/* classes: h_files */
+
+#ifndef RXSUPERH
+#define RXSUPERH
+
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* lord Sun May 7 12:40:17 1995 */
+
+\f
+
+#include "rxnfa.h"
+
+\f
+
+/* This begins the description of the superstate NFA.
+ *
+ * The superstate NFA corresponds to the NFA in these ways:
+ *
+ * Superstate states correspond to sets of NFA states (nfa_states(SUPER)),
+ *
+ * Superstate edges correspond to NFA paths.
+ *
+ * The superstate has no epsilon transitions;
+ * every edge has a character label, and a (possibly empty) side
+ * effect label. The side effect label corresponds to a list of
+ * side effects that occur in the NFA. These parts are referred
+ * to as: superedge_character(EDGE) and superedge_sides(EDGE).
+ *
+ * For a superstate edge EDGE starting in some superstate SUPER,
+ * the following is true (in pseudo-notation :-):
+ *
+ * exists DEST in nfa_states s.t.
+ * exists nfaEDGE in nfa_edges s.t.
+ * origin (nfaEDGE) == DEST
+ * && origin (nfaEDGE) is a member of nfa_states(SUPER)
+ * && exists PF in possible_futures(dest(nfaEDGE)) s.t.
+ * sides_of_possible_future (PF) == superedge_sides (EDGE)
+ *
+ * also:
+ *
+ * let SUPER2 := superedge_destination(EDGE)
+ * nfa_states(SUPER2)
+ * == union of all nfa state sets S s.t.
+ * exists PF in possible_futures(dest(nfaEDGE)) s.t.
+ * sides_of_possible_future (PF) == superedge_sides (EDGE)
+ * && S == dests_of_possible_future (PF) }
+ *
+ * Or in english, every superstate is a set of nfa states. A given
+ * character and a superstate implies many transitions in the NFA --
+ * those that begin with an edge labeled with that character from a
+ * state in the set corresponding to the superstate.
+ *
+ * The destinations of those transitions each have a set of possible
+ * futures. A possible future is a list of side effects and a set of
+ * destination NFA states. Two sets of possible futures can be
+ * `merged' by combining all pairs of possible futures that have the
+ * same side effects. A pair is combined by creating a new future
+ * with the same side effect but the union of the two destination sets.
+ * In this way, all the possible futures suggested by a superstate
+ * and a character can be merged into a set of possible futures where
+ * no two elements of the set have the same set of side effects.
+ *
+ * The destination of a possible future, being a set of NFA states,
+ * corresponds to a supernfa state. So, the merged set of possible
+ * futures we just created can serve as a set of edges in the
+ * supernfa.
+ *
+ * The representation of the superstate nfa and the nfa is critical.
+ * The nfa has to be compact, but has to facilitate the rapid
+ * computation of missing superstates. The superstate nfa has to
+ * be fast to interpret, lazilly constructed, and bounded in space.
+ *
+ * To facilitate interpretation, the superstate data structures are
+ * peppered with `instruction frames'. There is an instruction set
+ * defined below which matchers using the supernfa must be able to
+ * interpret.
+ *
+ * We'd like to make it possible but not mandatory to use code
+ * addresses to represent instructions (c.f. gcc's computed goto).
+ * Therefore, we define an enumerated type of opcodes, and when
+ * writing one of these instructions into a data structure, use
+ * the opcode as an index into a table of instruction values.
+ *
+ * Below are the opcodes that occur in the superstate nfa.
+ *
+ * The descriptions of the opcodes refer to data structures that are
+ * described further below.
+ */
+
+enum rx_opcode
+{
+ /*
+ * BACKTRACK_POINT is invoked when a character transition in
+ * a superstate leads to more than one edge. In that case,
+ * the edges have to be explored independently using a backtracking
+ * strategy.
+ *
+ * A BACKTRACK_POINT instruction is stored in a superstate's
+ * transition table for some character when it is known that that
+ * character crosses more than one edge. On encountering this
+ * instruction, the matcher saves enough state to backtrack to this
+ * point later in the match.
+ */
+ rx_backtrack_point = 0, /* data is (struct transition_class *) */
+
+ /*
+ * RX_DO_SIDE_EFFECTS evaluates the side effects of an epsilon path.
+ * There is one occurence of this instruction per rx_distinct_future.
+ * This instruction is skipped if a rx_distinct_future has no side effects.
+ */
+ rx_do_side_effects = rx_backtrack_point + 1,
+
+ /* data is (struct rx_distinct_future *) */
+
+ /*
+ * RX_CACHE_MISS instructions are stored in rx_distinct_futures whose
+ * destination superstate has been reclaimed (or was never built).
+ * It recomputes the destination superstate.
+ * RX_CACHE_MISS is also stored in a superstate transition table before
+ * any of its edges have been built.
+ */
+ rx_cache_miss = rx_do_side_effects + 1,
+ /* data is (struct rx_distinct_future *) */
+
+ /*
+ * RX_NEXT_CHAR is called to consume the next character and take the
+ * corresponding transition. This is the only instruction that uses
+ * the DATA field of the instruction frame instead of DATA_2.
+ * The comments about rx_inx explain this further.
+ */
+ rx_next_char = rx_cache_miss + 1, /* data is (struct superstate *) */
+
+ /* RX_BACKTRACK indicates that a transition fails. Don't
+ * confuse this with rx_backtrack_point.
+ */
+ rx_backtrack = rx_next_char + 1, /* no data */
+
+ /*
+ * RX_ERROR_INX is stored only in places that should never be executed.
+ */
+ rx_error_inx = rx_backtrack + 1, /* Not supposed to occur. */
+
+ rx_num_instructions = rx_error_inx + 1
+};
+
+/* The heart of the matcher is a `word-code-interpreter'
+ * (like a byte-code interpreter, except that instructions
+ * are a full word wide).
+ *
+ * Instructions are not stored in a vector of code, instead,
+ * they are scattered throughout the data structures built
+ * by the regexp compiler and the matcher. One word-code instruction,
+ * together with the arguments to that instruction, constitute
+ * an instruction frame (struct rx_inx).
+ *
+ * This structure type is padded by hand to a power of 2 because
+ * in one of the dominant cases, we dispatch by indexing a table
+ * of instruction frames. If that indexing can be accomplished
+ * by just a shift of the index, we're happy.
+ *
+ * Instructions take at most one argument, but there are two
+ * slots in an instruction frame that might hold that argument.
+ * These are called data and data_2. The data slot is only
+ * used for one instruction (RX_NEXT_CHAR). For all other
+ * instructions, data should be set to 0.
+ *
+ * RX_NEXT_CHAR is the most important instruction by far.
+ * By reserving the data field for its exclusive use,
+ * instruction dispatch is sped up in that case. There is
+ * no need to fetch both the instruction and the data,
+ * only the data is needed. In other words, a `cycle' begins
+ * by fetching the field data. If that is non-0, then it must
+ * be the destination state of a next_char transition, so
+ * make that value the current state, advance the match position
+ * by one character, and start a new cycle. On the other hand,
+ * if data is 0, fetch the instruction and do a more complicated
+ * dispatch on that.
+ */
+
+struct rx_inx
+{
+ void * data;
+ void * data_2;
+ void * inx;
+ void * fnord;
+};
+
+#ifndef RX_TAIL_ARRAY
+#define RX_TAIL_ARRAY 1
+#endif
+
+/* A superstate corresponds to a set of nfa states. Those sets are
+ * represented by STRUCT RX_SUPERSET. The constructors
+ * guarantee that only one (shared) structure is created for a given set.
+ */
+struct rx_superset
+{
+ int refs; /* This is a reference counted structure. */
+
+ /* We keep these sets in a cache because (in an unpredictable way),
+ * the same set is often created again and again.
+ *
+ * When an NFA is destroyed, some of the supersets for that NFA
+ * may still exist. This can lead to false cache hits -- an apparent cache
+ * hit on a superset that properly belongs to an already free NFA.
+ *
+ * When a cache hit appears to occur, we will have in hand the
+ * nfa for which it may have happened. Every nfa is given
+ * its own sequence number. The cache is validated
+ * by comparing the nfa sequence number to this field:
+ */
+ int id;
+
+ struct rx_nfa_state * car; /* May or may not be a valid addr. */
+ struct rx_superset * cdr;
+
+ /* If the corresponding superstate exists: */
+ struct rx_superstate * superstate;
+
+ /* That is_final field of the constiuent nfa states which has the greatest magnitude. */
+ int is_final;
+
+ /* The OR of the corresponding fields of the constiuent nfa states. */
+ int has_cset_edges;
+
+
+ /* There is another bookkeeping problem. It is expensive to
+ * compute the starting nfa state set for an nfa. So, once computed,
+ * it is cached in the `struct rx'.
+ *
+ * But, the state set can be flushed from the superstate cache.
+ * When that happens, the cached value in the `struct rx' has
+ * to be flushed.
+ */
+ struct rx * starts_for;
+
+ /* This is used to link into a hash bucket so these objects can
+ * be `hash-consed'.
+ */
+ struct rx_hash_item hash_item;
+};
+
+#define rx_protect_superset(RX,CON) (++(CON)->refs)
+
+/* The terminology may be confusing (rename this structure?).
+ * Every character occurs in at most one rx_super_edge per super-state.
+ * But, that structure might have more than one option, indicating a point
+ * of non-determinism.
+ *
+ * In other words, this structure holds a list of superstate edges
+ * sharing a common starting state and character label. The edges
+ * are in the field OPTIONS. All superstate edges sharing the same
+ * starting state and character are in this list.
+ */
+struct rx_super_edge
+{
+ struct rx_super_edge *next;
+ struct rx_inx rx_backtrack_frame;
+ int cset_size;
+ rx_Bitset cset;
+ struct rx_distinct_future *options;
+};
+
+/* A superstate is a set of nfa states (RX_SUPERSET) along
+ * with a transition table. Superstates are built on demand and reclaimed
+ * without warning. To protect a superstate from this ghastly fate,
+ * use LOCK_SUPERSTATE.
+ */
+struct rx_superstate
+{
+ int rx_id; /* c.f. the id field of rx_superset */
+ int locks; /* protection from reclamation */
+
+ /* Within a superstate cache, all the superstates are kept in a big
+ * queue. The tail of the queue is the state most likely to be
+ * reclaimed. The *recyclable fields hold the queue position of
+ * this state.
+ */
+ struct rx_superstate * next_recyclable;
+ struct rx_superstate * prev_recyclable;
+
+ /* The supernfa edges that exist in the cache and that have
+ * this state as their destination are kept in this list:
+ */
+ struct rx_distinct_future * transition_refs;
+
+ /* The list of nfa states corresponding to this superstate: */
+ struct rx_superset * contents;
+
+ /* The list of edges in the cache beginning from this state. */
+ struct rx_super_edge * edges;
+
+ /* A tail of the recyclable queue is marked as semifree. A semifree
+ * state has no incoming next_char transitions -- any transition
+ * into a semifree state causes a complex dispatch with the side
+ * effect of rescuing the state from its semifree state into a
+ * fully free state at the head of the queue.
+ *
+ * An alternative to this might be to make next_char more expensive,
+ * and to move a state to the head of the recyclable queue whenever
+ * it is entered. That way, popular states would never be recycled.
+ *
+ * But unilaterally making next_char more expensive actually loses.
+ * So, incoming transitions are only made expensive for states near
+ * the tail of the recyclable queue. The more cache contention
+ * there is, the more frequently a state will have to prove itself
+ * and be moved back to the front of the queue. If there is less
+ * contention, then popular states just aggregate in the front of
+ * the queue and stay there.
+ */
+ int is_semifree;
+
+
+ /* This keeps track of the size of the transition table for this
+ * state. There is a half-hearted attempt to support variable sized
+ * superstates.
+ */
+ int trans_size;
+
+ /* Indexed by characters... */
+ struct rx_inx transitions[RX_TAIL_ARRAY];
+};
+
+
+/* A list of distinct futures define the edges that leave from a
+ * given superstate on a given character. c.f. rx_super_edge.
+ */
+struct rx_distinct_future
+{
+ struct rx_distinct_future * next_same_super_edge[2];
+ struct rx_distinct_future * next_same_dest;
+ struct rx_distinct_future * prev_same_dest;
+ struct rx_superstate * present; /* source state */
+ struct rx_superstate * future; /* destination state */
+ struct rx_super_edge * edge;
+
+
+ /* The future_frame holds the instruction that should be executed
+ * after all the side effects are done, when it is time to complete
+ * the transition to the next state.
+ *
+ * Normally this is a next_char instruction, but it may be a
+ * cache_miss instruction as well, depending on whether or not
+ * the superstate is in the cache and semifree.
+ *
+ * If this is the only future for a given superstate/char, and
+ * if there are no side effects to be performed, this frame is
+ * not used (directly) at all. Instead, its contents are copied
+ * into the transition table of the starting state of this dist. future
+ * (a sort of goto elimination).
+ */
+ struct rx_inx future_frame;
+
+ struct rx_inx side_effects_frame;
+ struct rx_se_list * effects;
+};
+
+#define rx_lock_superstate(R,S) ((S)->locks++)
+#define rx_unlock_superstate(R,S) (--(S)->locks)
+\f
+struct rx_cache;
+
+#ifdef __STDC__
+typedef void (*rx_morecore_fn)(struct rx_cache *);
+#else
+typedef void (*rx_morecore_fn)();
+#endif
+
+struct rx_cache
+{
+ struct rx_hash_rules superset_hash_rules;
+
+ struct rx_superstate * lru_superstate;
+ struct rx_superstate * semifree_superstate;
+
+ struct rx_superset * empty_superset;
+
+ int superstates;
+ int semifree_superstates;
+ int hits;
+ int misses;
+
+ int bytes_allowed;
+ int bytes_used;
+
+ int local_cset_size;
+ void ** instruction_table;
+
+ struct rx_hash superset_table;
+};
+
+#ifndef RX_DEFAULT_DFA_CACHE_SIZE
+/* This is an upper bound on the number of bytes that may (normally)
+ * be allocated for DFA states. If this threshold would be exceeded,
+ * Rx tries to flush some DFA states from the cache.
+ *
+ * This value is used whenever the rx_default_cache is used (for example,
+ * with the Posix entry points).
+ */
+#define RX_DEFAULT_DFA_CACHE_SIZE (1 << 19)
+#endif
+
+extern struct rx_cache * rx_default_cache;
+
+\f
+#ifdef __STDC__
+extern char * rx_cache_malloc (struct rx_cache * cache, int size);
+extern void rx_cache_free (struct rx_cache * cache, int size, char * mem);
+extern void rx_release_superset (struct rx *rx,
+ struct rx_superset *set);
+extern struct rx_superset * rx_superset_cons (struct rx * rx,
+ struct rx_nfa_state *car, struct rx_superset *cdr);
+extern struct rx_superset * rx_superstate_eclosure_union (struct rx * rx, struct rx_superset *set, struct rx_nfa_state_set *ecl) ;
+extern struct rx_superstate * rx_superstate (struct rx *rx,
+ struct rx_superset *set);
+extern struct rx_inx * rx_handle_cache_miss (struct rx *rx, struct rx_superstate *super, unsigned char chr, void *data) ;
+
+#else /* STDC */
+extern char * rx_cache_malloc ();
+extern void rx_cache_free ();
+extern void rx_release_superset ();
+extern struct rx_superset * rx_superset_cons ();
+extern struct rx_superset * rx_superstate_eclosure_union ();
+extern struct rx_superstate * rx_superstate ();
+extern struct rx_inx * rx_handle_cache_miss ();
+
+#endif /* STDC */
+
+#endif /* RXSUPERH */
--- /dev/null
+/* classes: src_files */
+
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+#include "rxall.h"
+#include "rx.h"
+#include "rxunfa.h"
+#include "rxnfa.h"
+\f
+
+#ifdef __STDC__
+static int
+unfa_equal (void * va, void * vb)
+#else
+static int
+unfa_equal (va, vb)
+ void * va;
+ void * vb;
+#endif
+{
+ return rx_rexp_equal ((struct rexp_node *)va, (struct rexp_node *)vb);
+}
+
+
+struct rx_hash_rules unfa_rules = { unfa_equal, 0, 0, 0, 0 };
+
+
+#ifdef __STDC__
+static struct rx_cached_rexp *
+canonical_unfa (struct rx_hash * table, struct rexp_node * rexp, int cset_size)
+#else
+static struct rx_cached_rexp *
+canonical_unfa (table, rexp, cset_size)
+ struct rx_hash * table;
+ struct rexp_node * rexp;
+ int cset_size;
+#endif
+{
+ struct rx_hash_item * it;
+ it = rx_hash_store (table, rx_rexp_hash (rexp, 0), rexp, &unfa_rules);
+
+ if (it->binding == 0)
+ {
+ struct rx_cached_rexp * cr;
+
+ if (it->data == (void *)rexp)
+ rx_save_rexp (rexp);
+
+ cr = (struct rx_cached_rexp *)malloc (sizeof (*cr));
+ rx_bzero ((char *)cr, sizeof (*cr));
+ if (!cr)
+ return 0;
+ it->binding = (void *)cr;
+ cr->unfa.nfa = 0;
+ cr->unfa.exp = rexp;
+ cr->hash_item = it;
+ rx_save_rexp (rexp);
+ }
+ return (struct rx_cached_rexp *)it->binding;
+}
+
+
+#ifdef __STDC__
+static struct rx *
+rx_unfa_rx (struct rx_cached_rexp * cr, struct rexp_node * exp, int cset_size)
+#else
+static struct rx *
+rx_unfa_rx (cr, exp, cset_size)
+ struct rx_cached_rexp * cr;
+ struct rexp_node * exp;
+ int cset_size;
+#endif
+{
+ struct rx * new_rx;
+
+ if (cr->unfa.nfa)
+ return cr->unfa.nfa;
+
+ new_rx = rx_make_rx (cset_size);
+ if (!new_rx)
+ return 0;
+ {
+ struct rx_nfa_state * start;
+ struct rx_nfa_state * end;
+ start = end = 0;
+ if (!rx_build_nfa (new_rx, exp, &start, &end))
+ {
+ free (new_rx);
+ return 0;
+ }
+ new_rx->start_nfa_states = start;
+ end->is_final = 1;
+ start->is_start = 1;
+ {
+ struct rx_nfa_state * s;
+ int x;
+ x = 0;
+ for (s = new_rx->nfa_states; s; s = s->next)
+ s->id = x++;
+ }
+ }
+ cr->unfa.nfa = new_rx;
+ return new_rx;
+}
+
+
+\f
+
+#ifdef __STDC__
+struct rx_unfaniverse *
+rx_make_unfaniverse (int delay)
+#else
+struct rx_unfaniverse *
+rx_make_unfaniverse (delay)
+ int delay;
+#endif
+{
+ struct rx_unfaniverse * it;
+ it = (struct rx_unfaniverse *)malloc (sizeof (*it));
+ if (!it) return 0;
+ rx_bzero ((char *)it, sizeof (*it));
+ it->delay = delay;
+ return it;
+}
+
+
+#ifdef __STDC__
+void
+rx_free_unfaniverse (struct rx_unfaniverse * it)
+#else
+void
+rx_free_unfaniverse (it)
+ struct rx_unfaniverse * it;
+#endif
+{
+}
+
+
+
+\f
+
+#ifdef __STDC__
+struct rx_unfa *
+rx_unfa (struct rx_unfaniverse * unfaniverse, struct rexp_node * exp, int cset_size)
+#else
+struct rx_unfa *
+rx_unfa (unfaniverse, exp, cset_size)
+ struct rx_unfaniverse * unfaniverse;
+ struct rexp_node * exp;
+ int cset_size;
+#endif
+{
+ struct rx_cached_rexp * cr;
+ if (exp && exp->cr)
+ cr = exp->cr;
+ else
+ {
+ cr = canonical_unfa (&unfaniverse->table, exp, cset_size);
+ if (exp)
+ exp->cr = cr;
+ }
+ if (!cr)
+ return 0;
+ if (cr->next)
+ {
+ if (unfaniverse->free_queue == cr)
+ {
+ unfaniverse->free_queue = cr->next;
+ if (unfaniverse->free_queue == cr)
+ unfaniverse->free_queue = 0;
+ }
+ cr->next->prev = cr->prev;
+ cr->prev->next = cr->next;
+ cr->next = 0;
+ cr->prev = 0;
+ --unfaniverse->delayed;
+ }
+ ++cr->unfa.refs;
+ cr->unfa.cset_size = cset_size;
+ cr->unfa.verse = unfaniverse;
+ rx_unfa_rx (cr, exp, cset_size);
+ return &cr->unfa;
+}
+
+
+#ifdef __STDC__
+void
+rx_free_unfa (struct rx_unfa * unfa)
+#else
+void
+rx_free_unfa (unfa)
+ struct rx_unfa * unfa;
+#endif
+{
+ struct rx_cached_rexp * cr;
+ cr = (struct rx_cached_rexp *)unfa;
+ if (!cr)
+ return;
+ if (!--cr->unfa.refs)
+ {
+ if (!unfa->verse->free_queue)
+ {
+ unfa->verse->free_queue = cr;
+ cr->next = cr->prev = cr;
+ }
+ else
+ {
+ cr->next = unfa->verse->free_queue;
+ cr->prev = unfa->verse->free_queue->prev;
+ cr->next->prev = cr;
+ cr->prev->next = cr;
+ }
+
+ ++unfa->verse->delayed;
+ while (unfa->verse->delayed > unfa->verse->delay)
+ {
+ struct rx_cached_rexp * it;
+ it = unfa->verse->free_queue;
+ unfa->verse->free_queue = it->next;
+ if (!--unfa->verse->delayed)
+ unfa->verse->free_queue = 0;
+ it->prev->next = it->next;
+ it->next->prev = it->prev;
+ if (it->unfa.exp)
+ it->unfa.exp->cr = 0;
+ rx_free_rexp ((struct rexp_node *)it->hash_item->data);
+ rx_hash_free (it->hash_item, &unfa_rules);
+ rx_free_rx (it->unfa.nfa);
+ rx_free_rexp (it->unfa.exp);
+ free (it);
+ if (it == cr)
+ break;
+ }
+ }
+ else
+ return;
+}
+
+
+#ifdef __STDC__
+void
+rx_save_unfa (struct rx_unfa * unfa)
+#else
+void
+rx_save_unfa (unfa)
+ struct rx_unfa * unfa;
+#endif
+{
+ ++(unfa->refs);
+}
+
--- /dev/null
+#ifndef RXUNFAH
+#define RXUNFAH
+
+/* Copyright (C) 1995, 1996 Tom Lord
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+\f
+#include "_rx.h"
+
+\f
+struct rx_unfaniverse
+{
+ int delay;
+ int delayed;
+ struct rx_hash table;
+ struct rx_cached_rexp * free_queue;
+};
+
+
+struct rx_unfa
+{
+ int refs;
+ struct rexp_node * exp;
+ struct rx * nfa;
+ int cset_size;
+ struct rx_unfaniverse * verse;
+};
+
+struct rx_cached_rexp
+{
+ struct rx_unfa unfa;
+ struct rx_cached_rexp * next;
+ struct rx_cached_rexp * prev;
+ struct rx_hash_item * hash_item;
+};
+
+
+\f
+#ifdef __STDC__
+extern struct rx_unfaniverse * rx_make_unfaniverse (int delay);
+extern void rx_free_unfaniverse (struct rx_unfaniverse * it);
+extern struct rx_unfa * rx_unfa (struct rx_unfaniverse * unfaniverse, struct rexp_node * exp, int cset_size);
+extern void rx_free_unfa (struct rx_unfa * unfa);
+extern void rx_save_unfa (struct rx_unfa * unfa);
+
+#else /* STDC */
+extern struct rx_unfaniverse * rx_make_unfaniverse ();
+extern void rx_free_unfaniverse ();
+extern struct rx_unfa * rx_unfa ();
+extern void rx_free_unfa ();
+extern void rx_save_unfa ();
+
+#endif /* STDC */
+#endif /* RXUNFAH */
--- /dev/null
+text/html; netscape -remote 'openURL(%s)'
+image/gif; xv %s
+image/jpg; xv %s
+application/pgp-keys; pgp -f < %s ; copiousoutput
--- /dev/null
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# ME's personal .muttrc (Mutt 0.92.5)
+#
+# The format of this file is one command per line. Everything after a pound
+# sign (#) is a comment, unless a backward slash (\) precedes it
+#
+
+# Note: $folder should be set _before_ any other path vars where `+' or `='
+# is used because paths are expanded when parsed
+#
+#set folder=~/Mail # where i keep my mailboxes
+
+#set abort_unmodified=yes # automatically abort replies if I don't
+ # change the message
+set alias_file=~/.mail_aliases # where I keep my aliases
+#set allow_8bit # never do Q-P encoding on legal 8-bit chars
+set arrow_cursor # use -> instead of hiliting the whole line
+#set ascii_chars # use ASCII instead of ACS chars for threads
+#set askbcc
+#set askcc
+#set attribution="On %d, %n wrote:" # how to attribute replies
+set autoedit # go to the editor right away when composing
+#set auto_tag # always operate on tagged messages
+#set charset="iso-8859-1" # character set for your terminal
+set noconfirmappend # don't ask me if i want to append to mailboxes
+#set confirmcreate # prompt when creating new files
+set copy=yes # always save a copy of outgoing messages
+set delete=yes # purge deleted messages without asking
+set edit_headers # let me edit the message header when composing
+#set editor="emacs -nw" # editor to use when composing messages
+#set fast_reply # skip initial prompts when replying
+#set fcc_attach # keep attachments in copies of sent messages?
+#set force_name # fcc by recipient, create if mailbox doesn't exist
+#set forward_decode # weed and MIME decode forwaded messages
+#set forward_format="[%a: %s]" # subject to use when forwarding messages
+#set forward_quote # quote the header and body of forward msgs
+#set hdr_format="%4C %Z %{%m/%d} [%2N] %-15.15F (%4c) %s"
+set hdr_format="%4C %Z %{%m/%d} %-15.15F (%4c) %s" # format of the index
+#set hdrs # include `my_hdr' lines in outgoing messages
+#set header # include message header when replying
+set help # show the help lines
+#set history=20 # number of lines of history to remember
+#set hostname="cs.hmc.edu" # my DNS domain
+set include # always include messages when replying
+#set indent_string="> " # how to quote replied text
+#set locale="C" # locale to use for printing time
+#set mailcap_path="~/.mailcap:/usr/local/share/mailcap"
+set nomark_old # i don't care about whether a message is old
+set mail_check=10 # how often to poll for new mail
+set mbox=+mbox # where to store read messages
+#set menu_scroll # no implicit next-page/prev-page
+#set metoo # remove my address when replying
+set mime_forward # use message/rfc822 type to forward messages
+set move=yes # don't ask about moving messages, just do it
+#set pager=less # some people prefer an external pager
+#set pager_context=3 # no. of lines of context to give when scrolling
+#set pager_format="-%S- %-20.20f %s" # format of the pager status bar
+set pager_index_lines=6 # how many index lines to show in the pager
+#set pager_stop # don't move to the next message on next-page
+#set pgp_strict_enc # use Q-P encoding when needed for PGP
+set postponed=+postponed # mailbox to store postponed messages in
+#set post_indent_string='---end quoted text---'
+#set print=ask-yes # ask me if I really want to print messages
+set print_command=/bin/false # how to print things (I like to save trees)
+set noprompt_after # ask me for a command after the external pager exits
+#set quote_regexp="^ *[a-zA-Z]*[>:#}]" # how to catch quoted text
+set read_inc=25 # show progress when reading a mailbox
+#set recall # prompt to recall postponed messages
+set record=+outbox # default location to save outgoing mail
+set reply_to # always use reply-to if present
+#set reply_regexp="^(re:[ \t]*)+"# how to identify replies in the subject:
+#set resolve # move to the next message when an action is performed
+#set reverse_alias # attempt to look up my names for people
+set reverse_name # use my address as it appears in the message
+ # i am replying to
+set nosave_empty # remove files when no messages are left
+#set save_name # save outgoing messages by recipient, if the
+#set sendmail="/usr/lib/sendmail -oi -oem" # how to deliver mail
+#set shell="/bin/zsh" # program to use for shell escapes
+#set signature="~/.signature" # file which contains my signature
+
+# I subscribe to a lot of mailing lists, so this is _very_ useful. This
+# groups messages on the same subject to make it easier to follow a
+# discussion. Mutt will draw a nice tree showing how the discussion flows.
+set sort=threads # primary sorting method
+
+#set sort_aux=reverse-date-received # how to sort subthreads
+#set sort_aux=last-date # date of the last message in thread
+set sort_browser=reverse-date # how to sort files in the dir browser
+set spoolfile='~/mailbox' # where my new mail is located
+#set status_format="-%r-Mutt: %f [Msgs:%?M?%M/?%m%?n? New:%n?%?d? Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b? %l]---(%s)-%>-(%P)---"
+#set status_on_top # some people prefer the status bar on top
+#set strict_threads # don't thread by subject
+set tilde # virtual lines to pad blank lines in the pager
+#set timeout=0 # timeout for prompt in the index menu
+#set tmpdir=~/tmp # where to store temp files
+#set to_chars=" +TCF"
+#set use_8bitmime # enable the -B8BITMIME sendmail flag
+set nouse_domain # don't qualify local addresses with $domain
+#set use_from # always generate the `From:' header field
+set use_mailcap=yes # always use a mailcap entry when found
+set pgp_verify_sig=no # don't automatically verify message signatures
+#set visual=vim # editor invoked by ~v in the builtin editor
+#set nowait_key # prompt when a pipe returns normal status
+set write_inc=25 # show progress while writing mailboxes
+
+# only enable the following IFF you have sendmail 8.8.x or you will not
+# be able to send mail!!!
+#set dsn_notify='failure,delay' # when to return an error message
+#set dsn_return=hdrs # what to return in the error message
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Header fields I don't normally want to see
+#
+ignore * # this means "ignore all lines by default"
+
+# I do want to see these fields, though!
+unignore from: subject to cc mail-followup-to \
+ date x-mailer x-url # this shows how nicely wrap long lines
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Color definitions
+#
+
+#color normal white default
+color hdrdefault red default
+color quoted brightblue default
+color signature red default
+color indicator brightyellow red
+color error brightred default
+color status yellow blue
+color tree magenta default # the thread tree in the index menu
+color tilde magenta default
+color message brightcyan default
+color markers brightcyan default
+color attachment brightmagenta default
+color search default green # how to hilite search patterns in the pager
+
+color header brightred default ^(From|Subject):
+color body magenta default "(ftp|http)://[^ ]+" # point out URLs
+color body magenta default [-a-z_0-9.]+@[-a-z_0-9.]+ # e-mail addresses
+color underline brightgreen default
+
+# attributes when using a mono terminal
+#mono header underline ^(From|Subject):
+mono quoted bold
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Key bindings
+#
+# maps:
+# alias alias menu
+# attach attachment menu
+# browser directory browser
+# compose compose menu
+# index message index
+# pgp pgp menu
+# postpone postponed message recall menu
+# generic generic keymap for all of the above
+# editor line editor
+# pager text viewer
+#
+
+bind generic "\e<" first-entry # emacs-like bindings for moving to top/bottom
+bind generic \e> last-entry
+bind generic { top-page
+bind generic } bottom-page
+bind generic \177 last-entry
+
+macro index \cb |urlview\n # simulate the old browse-url function
+
+macro index S s+spam\n
+macro pager S s+spam\n
+
+#macro index \# /bug^M # search for bugs
+#macro index "\"" ":set realname=\"real hairy macro\"^M:set ?realname^M" # and a comment to boot!
+#macro index f1 :woohoo!
+
+bind pager G bottom # just like vi and less
+#macro pager \Ck "|pgp -kaf^M" # a comment is valid here
+#macro pager X "|morepgp^M" # pipe PGP message to a script
+
+#bind editor \cy eol # make ^Y jump to the end of the line
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# User Defined Headers
+#
+
+#my_hdr X-Useless-Header: Look ma, it's a \# sign! # real comment
+#my_hdr X-Operating-System: `uname -a`
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Specify default filename when saving messages
+#
+# save-hook [!]<pattern> <mailbox>
+#
+# <mailbox> is provided as default when saving messages from <pattern>
+
+#save-hook mutt- =mutt-mail
+#save-hook aol\.com +spam
+save-hook ^judge +diplomacy
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Multiple spool mailboxes
+#
+# mbox-hook [!]<pattern> <mbox-mailbox>
+#
+# Read mail in <pattern> is moved to <mbox-mailbox> when <pattern> is
+# closed.
+
+#mbox-hook =mutt-users.in =mutt-users
+#mbox-hook +TEST +inbox
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Change settings based upon message recipient
+#
+# send-hook [!]<pattern> <command>
+#
+# <command> is executed when sending mail to an address matching <pattern>
+
+#send-hook mutt- 'set signature=~/.sigmutt; my_hdr From: Mutt User <user@example.com>'
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Specify where to save composed messages
+#
+# fcc-hook [!]<pattern> <mailbox>
+#
+# <pattern> is recipient(s), <mailbox> is where to save a copy
+
+#fcc-hook joe +joe
+#fcc-hook bob +bob
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Change settings based on mailbox
+#
+# folder-hook [!]<pattern> <command>
+#
+# <command> is executed when opening a mailbox matching <pattern>
+
+#folder-hook . 'set sort=date-sent'
+#folder-hook mutt 'set hdr_format="%4C %Z %02m/%02N %-20.20F (%4l) %s"'
+#folder-hook =mutt my_hdr Revolution: \#9 # real comment
+
+#folder-hook . 'set reply_regexp="^re:[ \t]*"'
+
+# this mailing list prepends "[WM]" to all non reply subjects, so set
+# $reply_regexp to ignore it
+#folder-hook +wmaker 'set reply_regexp="^(re:[ \t]*)?\[WM\][ \t]*"'
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Aliases
+#
+# alias <name> <address> [ , <address> ... ]
+
+#alias exam "\# to annoy michael" <user@host>
+#alias me Michael Elkins <michael> # me!
+alias mutt-dev Mutt Development List <mutt-dev@cs.hmc.edu> # power users
+alias mutt-users Mutt User List <mutt-users@cs.hmc.edu>
+alias mutt-announce Mutt Announcement List <mutt-announce@cs.hmc.edu>
+alias wmaker WindowMaker Mailing List <wmaker@eosys.com>
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Mailboxes to watch for new mail
+#
+# mailboxes <path1> [ <path2> ... ]
+#
+
+mailboxes ! +mutt-dev +mutt-users +open-pgp +wmaker +hurricane +vim +ietf \
+ +drums
+#mailboxes `echo $HOME/Mail/*`
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Specify the order of the headers to appear when displaying a message
+#
+# hdr_order <hdr1> [ <hdr2> ... ]
+#
+
+hdr_order date from subject to cc
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Identify mailing lists I subscribe to
+#
+# lists <list-name> [ <list-name> ... ]
+
+lists mutt-dev mutt-users
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Automatically use entries from ~/.mailcap to view these MIME types
+#
+# auto_view <type> [ <type> ... ]
+
+auto_view application/x-gunzip
+auto_view application/x-gzip
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# Scoring
+#
+# score <pattern> <value>
+#
+# 9999 and -9999 are special values which cause processing of hooks to stop
+# at that entry. If you prefix the score with an equal sign (=), the score
+# is assigned to the message and processing stops.
+
+#score '~f ^me@cs\.hmc\.edu$' 1000
+#score '~t mutt | ~c mutt' =500
+#score '~f aol\.com$' -9999
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# I use Mutt on several different machines, so I put local config commands
+# in a separate file so I can have the rest of the settings the same on all
+# machines.
+#
+
+source ~/.muttrc-local # config commands local to this site
+
+# EOF
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "sort.h"
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct score_t
+{
+ char *str;
+ pattern_t *pat;
+ int val;
+ int exact; /* if this rule matches, don't evaluate any more */
+ struct score_t *next;
+} SCORE;
+
+SCORE *Score = NULL;
+
+void mutt_check_rescore (CONTEXT *ctx)
+{
+ int i;
+
+ if (option (OPTNEEDRESCORE))
+ {
+ if ((Sort & SORT_MASK) == SORT_SCORE ||
+ (SortAux & SORT_MASK) == SORT_SCORE)
+ {
+ set_option (OPTNEEDRESORT);
+ if ((Sort & SORT_MASK) == SORT_THREADS)
+ set_option (OPTSORTSUBTHREADS);
+ }
+
+ /* must redraw the index since the user might have %N in it */
+ set_option (OPTFORCEREDRAWINDEX);
+ set_option (OPTFORCEREDRAWPAGER);
+
+ for (i = 0; i < ctx->msgcount; i++)
+ mutt_score_message (ctx->hdrs[i]);
+
+ mutt_cache_index_colors (ctx);
+
+ unset_option (OPTNEEDRESCORE);
+ }
+}
+
+int mutt_parse_score (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ SCORE *ptr, *last;
+ char *pattern, *pc;
+ struct pattern_t *pat;
+
+ mutt_extract_token (buf, s, 0);
+ if (!MoreArgs (s))
+ {
+ strfcpy (err->data, "score: too few arguments", err->dsize);
+ return (-1);
+ }
+ pattern = buf->data;
+ memset (buf, 0, sizeof (BUFFER));
+ mutt_extract_token (buf, s, 0);
+ if (MoreArgs (s))
+ {
+ FREE (&pattern);
+ strfcpy (err->data, "score: too many arguments", err->dsize);
+ return (-1);
+ }
+
+ /* look for an existing entry and update the value, else add it to the end
+ of the list */
+ for (ptr = Score, last = NULL; ptr; last = ptr, ptr = ptr->next)
+ if (strcmp (pattern, ptr->str) == 0)
+ break;
+ if (!ptr)
+ {
+ if ((pat = mutt_pattern_comp (pattern, 0, err)) == NULL)
+ {
+ FREE (&pattern);
+ return (-1);
+ }
+ ptr = safe_calloc (1, sizeof (SCORE));
+ if (last)
+ last->next = ptr;
+ else
+ Score = ptr;
+ ptr->pat = pat;
+ ptr->str = pattern;
+ }
+ pc = buf->data;
+ if (*pc == '=')
+ {
+ ptr->exact = 1;
+ pc++;
+ }
+ ptr->val = atoi (pc);
+ set_option (OPTNEEDRESCORE);
+ return 0;
+}
+
+void mutt_score_message (HEADER *hdr)
+{
+ SCORE *tmp;
+
+ hdr->score = 0; /* in case of re-scoring */
+ for (tmp = Score; tmp; tmp = tmp->next)
+ {
+ if (mutt_pattern_exec (tmp->pat, 0, NULL, hdr) > 0)
+ {
+ if (tmp->exact || tmp->val == 9999 || tmp->val == -9999)
+ {
+ hdr->score = tmp->val;
+ break;
+ }
+ hdr->score += tmp->val;
+ }
+ }
+ if (hdr->score < 0)
+ hdr->score = 0;
+}
+
+int mutt_parse_unscore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ SCORE *tmp, *last = NULL;
+
+ while (MoreArgs (s))
+ {
+ mutt_extract_token (buf, s, 0);
+ if (!strcmp ("*", buf->data))
+ {
+ for (tmp = Score; tmp; )
+ {
+ last = tmp;
+ tmp = tmp->next;
+ mutt_pattern_free (&last->pat);
+ safe_free ((void **) &last);
+ }
+ Score = NULL;
+ }
+ else
+ {
+ for (tmp = Score; tmp; last = tmp, tmp = tmp->next)
+ {
+ if (!strcmp (buf->data, tmp->str))
+ {
+ if (last)
+ last->next = tmp->next;
+ else
+ Score = tmp->next;
+ mutt_pattern_free (&tmp->pat);
+ safe_free ((void **) &tmp);
+ /* there should only be one score per pattern, so we can stop here */
+ break;
+ }
+ }
+ }
+ }
+ set_option (OPTNEEDRESCORE);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "keymap.h"
+#include "mime.h"
+#include "mailbox.h"
+#include "copy.h"
+#include "mx.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <dirent.h>
+
+extern char RFC822Specials[];
+
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+
+static void append_signature (ENVELOPE *env, FILE *f)
+{
+ FILE *tmpfp;
+ pid_t thepid;
+
+ if (Signature && (tmpfp = mutt_open_read (Signature, &thepid)))
+ {
+ if (option (OPTSIGDASHES))
+ fputs ("\n-- \n", f);
+ mutt_copy_stream (tmpfp, f);
+ fclose (tmpfp);
+ if (thepid != -1)
+ mutt_wait_filter (thepid);
+ }
+}
+
+/* compare two e-mail addresses and return 1 if they are equivalent */
+static int mutt_addrcmp (ADDRESS *a, ADDRESS *b)
+{
+ if (!a->mailbox || !b->mailbox)
+ return 0;
+ if (strcasecmp (a->mailbox, b->mailbox))
+ return 0;
+ return 1;
+}
+
+/* search an e-mail address in a list */
+static int mutt_addrsrc (ADDRESS *a, ADDRESS *lst)
+{
+ for (; lst; lst = lst->next)
+ {
+ if (mutt_addrcmp (a, lst))
+ return (1);
+ }
+ return (0);
+}
+
+/* removes addresses from "b" which are contained in "a" */
+static ADDRESS *mutt_remove_xrefs (ADDRESS *a, ADDRESS *b)
+{
+ ADDRESS *top, *p, *prev = NULL;
+
+ top = b;
+ while (b)
+ {
+ for (p = a; p; p = p->next)
+ {
+ if (mutt_addrcmp (p, b))
+ break;
+ }
+ if (p)
+ {
+ if (prev)
+ {
+ prev->next = b->next;
+ b->next = NULL;
+ rfc822_free_address (&b);
+ b = prev;
+ }
+ else
+ {
+ top = top->next;
+ b->next = NULL;
+ rfc822_free_address (&b);
+ b = top;
+ }
+ }
+ else
+ {
+ prev = b;
+ b = b->next;
+ }
+ }
+ return top;
+}
+
+/* remove any address which matches the current user. if `leave_only' is
+ * nonzero, don't remove the user's address if it is the only one in the list
+ */
+static ADDRESS *remove_user (ADDRESS *a, int leave_only)
+{
+ ADDRESS *top = NULL, *last = NULL;
+
+ while (a)
+ {
+ if (!mutt_addr_is_user (a))
+ {
+ if (top)
+ {
+ last->next = a;
+ last = last->next;
+ }
+ else
+ last = top = a;
+ a = a->next;
+ last->next = NULL;
+ }
+ else
+ {
+ ADDRESS *tmp = a;
+
+ a = a->next;
+ if (!leave_only || a || last)
+ {
+ tmp->next = NULL;
+ rfc822_free_address (&tmp);
+ }
+ else
+ last = top = tmp;
+ }
+ }
+ return top;
+}
+
+static ADDRESS *find_mailing_lists (ADDRESS *t, ADDRESS *c)
+{
+ ADDRESS *top = NULL, *ptr = NULL;
+
+ for (; t || c; t = c, c = NULL)
+ {
+ for (; t; t = t->next)
+ {
+ if (mutt_is_mail_list (t))
+ {
+ if (top)
+ {
+ ptr->next = rfc822_cpy_adr_real (t);
+ ptr = ptr->next;
+ }
+ else
+ ptr = top = rfc822_cpy_adr_real (t);
+ }
+ }
+ }
+ return top;
+}
+
+static int edit_address (ADDRESS **a, /* const */ char *field)
+{
+ char buf[HUGE_STRING];
+
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), *a);
+ if (mutt_get_field (field, buf, sizeof (buf), M_ALIAS) != 0)
+ return (-1);
+ rfc822_free_address (a);
+ *a = mutt_expand_aliases (mutt_parse_adrlist (NULL, buf));
+ return 0;
+}
+
+static int edit_envelope (ENVELOPE *en)
+{
+ char buf[HUGE_STRING];
+ LIST *uh = UserHeader;
+
+ if (edit_address (&en->to, "To: ") == -1 || en->to == NULL)
+ return (-1);
+ if (option (OPTASKCC) && edit_address (&en->cc, "Cc: ") == -1)
+ return (-1);
+ if (option (OPTASKBCC) && edit_address (&en->bcc, "Bcc: ") == -1)
+ return (-1);
+
+ if (en->subject)
+ {
+ if (option (OPTFASTREPLY))
+ return (0);
+ else
+ strfcpy (buf, en->subject, sizeof (buf));
+ }
+ else
+ {
+ char *p;
+
+ buf[0] = 0;
+ for (; uh; uh = uh->next)
+ {
+ if (strncasecmp ("subject:", uh->data, 8) == 0)
+ {
+ p = uh->data + 8;
+ SKIPWS (p);
+ strncpy (buf, p, sizeof (buf));
+ }
+ }
+ }
+
+ if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) != 0 ||
+ (!buf[0] && query_quadoption (OPT_SUBJECT, "No subject, abort?") != 0))
+ {
+ mutt_message ("No subject, aborting.");
+ return (-1);
+ }
+ safe_free ((void **) &en->subject);
+ en->subject = safe_strdup (buf);
+
+ return 0;
+}
+
+static void process_user_recips (ENVELOPE *env)
+{
+ LIST *uh = UserHeader;
+
+ for (; uh; uh = uh->next)
+ {
+ if (strncasecmp ("to:", uh->data, 3) == 0)
+ env->to = rfc822_parse_adrlist (env->to, uh->data + 3);
+ else if (strncasecmp ("cc:", uh->data, 3) == 0)
+ env->cc = rfc822_parse_adrlist (env->cc, uh->data + 3);
+ else if (strncasecmp ("bcc:", uh->data, 4) == 0)
+ env->bcc = rfc822_parse_adrlist (env->bcc, uh->data + 4);
+ }
+}
+
+static void process_user_header (ENVELOPE *env)
+{
+ LIST *uh = UserHeader;
+ LIST *last = env->userhdrs;
+
+ if (last)
+ while (last->next)
+ last = last->next;
+
+ for (; uh; uh = uh->next)
+ {
+ if (strncasecmp ("from:", uh->data, 5) == 0)
+ {
+ /* User has specified a default From: address. Remove default address */
+ rfc822_free_address (&env->from);
+ env->from = rfc822_parse_adrlist (env->from, uh->data + 5);
+ }
+ else if (strncasecmp ("reply-to:", uh->data, 9) == 0)
+ {
+ rfc822_free_address (&env->reply_to);
+ env->reply_to = rfc822_parse_adrlist (env->reply_to, uh->data + 9);
+ }
+ else if (strncasecmp ("to:", uh->data, 3) != 0 &&
+ strncasecmp ("cc:", uh->data, 3) != 0 &&
+ strncasecmp ("bcc:", uh->data, 4) != 0 &&
+ strncasecmp ("subject:", uh->data, 8) != 0)
+ {
+ if (last)
+ {
+ last->next = mutt_new_list ();
+ last = last->next;
+ }
+ else
+ last = env->userhdrs = mutt_new_list ();
+ last->data = safe_strdup (uh->data);
+ }
+ }
+}
+
+LIST *mutt_copy_list (LIST *p)
+{
+ LIST *t, *r=NULL, *l=NULL;
+
+ for (; p; p = p->next)
+ {
+ t = (LIST *) safe_malloc (sizeof (LIST));
+ t->data = safe_strdup (p->data);
+ t->next = NULL;
+ if (l)
+ {
+ r->next = t;
+ r = r->next;
+ }
+ else
+ l = r = t;
+ }
+ return (l);
+}
+
+static int include_forward (CONTEXT *ctx, HEADER *cur, FILE *out)
+{
+ char buffer[STRING];
+ int chflags = CH_DECODE, cmflags = 0;
+
+
+
+#ifdef _PGPPATH
+ if (cur->pgp)
+ {
+ if (cur->pgp & PGPENCRYPT)
+ {
+ /* make sure we have the user's passphrase before proceeding... */
+ pgp_valid_passphrase ();
+ }
+ }
+#endif /* _PGPPATH */
+
+
+
+ fputs ("----- Forwarded message from ", out);
+ buffer[0] = 0;
+ rfc822_write_address (buffer, sizeof (buffer), cur->env->from);
+ fputs (buffer, out);
+ fputs (" -----\n\n", out);
+ if (option (OPTFORWDECODE))
+ {
+ cmflags |= M_CM_DECODE;
+ chflags |= CH_WEED;
+ }
+ if (option (OPTFORWQUOTE))
+ cmflags |= M_CM_PREFIX;
+ mutt_parse_mime_message (ctx, cur);
+ mutt_copy_message (out, ctx, cur, cmflags, chflags);
+ fputs ("\n----- End forwarded message -----\n", out);
+ return 0;
+}
+
+static int include_reply (CONTEXT *ctx, HEADER *cur, FILE *out)
+{
+ char buffer[STRING];
+ int flags = M_CM_PREFIX | M_CM_DECODE;
+
+
+
+#ifdef _PGPPATH
+ if (cur->pgp)
+ {
+ if (cur->pgp & PGPENCRYPT)
+ {
+ /* make sure we have the user's passphrase before proceeding... */
+ pgp_valid_passphrase ();
+ }
+ }
+#endif /* _PGPPATH */
+
+
+
+ if (Attribution)
+ {
+ mutt_make_string (buffer, sizeof (buffer), Attribution, cur);
+ fputs (buffer, out);
+ fputc ('\n', out);
+ }
+ if (!option (OPTHEADER))
+ flags |= M_CM_NOHEADER;
+ mutt_parse_mime_message (ctx, cur);
+ mutt_copy_message (out, ctx, cur, flags, CH_DECODE);
+ if (PostIndentString)
+ {
+ mutt_make_string (buffer, sizeof (buffer), PostIndentString, cur);
+ fputs (buffer, out);
+ fputc ('\n', out);
+ }
+ return 0;
+}
+
+static BODY *make_forward (CONTEXT *ctx, HEADER *hdr)
+{
+ char buffer[LONG_STRING];
+ BODY *body;
+ FILE *fpout;
+
+ mutt_mktemp (buffer);
+ if ((fpout = safe_fopen (buffer, "w")) == NULL)
+ return NULL;
+
+ body = mutt_new_body ();
+ body->type = TYPEMESSAGE;
+ body->subtype = safe_strdup ("rfc822");
+ body->filename = safe_strdup (buffer);
+ body->unlink = 1;
+ body->use_disp = 0;
+
+ /* this MUST come after setting ->filename because we reuse buffer[] */
+ strfcpy (buffer, "Forwarded message from ", sizeof (buffer));
+ rfc822_write_address (buffer + 23, sizeof (buffer) - 23, hdr->env->from);
+ body->description = safe_strdup (buffer);
+
+ mutt_parse_mime_message (ctx, hdr);
+ mutt_copy_message (fpout, ctx, hdr,
+ option (OPTMIMEFORWDECODE) ? M_CM_DECODE : 0,
+ CH_XMIT | (option (OPTMIMEFORWDECODE) ? (CH_MIME | CH_TXTPLAIN ) : 0));
+
+ fclose (fpout);
+ mutt_update_encoding (body);
+ return (body);
+}
+
+static int default_to (ADDRESS **to, ENVELOPE *env, int group)
+{
+ char prompt[STRING];
+ int i = 0;
+
+ if (group)
+ {
+ if (env->mail_followup_to)
+ {
+ rfc822_append (to, env->mail_followup_to);
+ return 0;
+ }
+ if (mutt_addr_is_user (env->from))
+ return 0;
+ }
+
+ if (!group && mutt_addr_is_user (env->from))
+ {
+ /* mail is from the user, assume replying to recipients */
+ rfc822_append (to, env->to);
+ }
+ else if (env->reply_to)
+ {
+ if (option (OPTIGNORELISTREPLYTO) &&
+ mutt_is_mail_list (env->reply_to) &&
+ (mutt_addrsrc (env->reply_to, env->to) ||
+ mutt_addrsrc (env->reply_to, env->cc)))
+ {
+ /* If the Reply-To: address is a mailing list, assume that it was
+ * put there by the mailing list, and use the From: address
+ */
+ rfc822_append (to, env->from);
+ }
+ else if (!mutt_addrcmp (env->from, env->reply_to) &&
+ quadoption (OPT_REPLYTO) != M_YES)
+ {
+ /* There are quite a few mailing lists which set the Reply-To:
+ * header field to the list address, which makes it quite impossible
+ * to send a message to only the sender of the message. This
+ * provides a way to do that.
+ */
+ snprintf (prompt, sizeof (prompt), "Reply to %s?", env->reply_to->mailbox);
+ if ((i = query_quadoption (OPT_REPLYTO, prompt)) == M_YES)
+ rfc822_append (to, env->reply_to);
+ else if (i == M_NO)
+ rfc822_append (to, env->from);
+ else
+ return (-1); /* abort */
+ }
+ else
+ rfc822_append (to, env->reply_to);
+ }
+ else
+ rfc822_append (to, env->from);
+
+ return (0);
+}
+
+static int fetch_recips (ENVELOPE *out, ENVELOPE *in, int flags)
+{
+ ADDRESS *tmp;
+ if (flags & SENDLISTREPLY)
+ {
+ tmp = find_mailing_lists (in->to, in->cc);
+ rfc822_append (&out->to, tmp);
+ rfc822_free_address (&tmp);
+ }
+ else
+ {
+ if (default_to (&out->to, in, flags & SENDGROUPREPLY) == -1)
+ return (-1); /* abort */
+
+ if ((flags & SENDGROUPREPLY) && !in->mail_followup_to)
+ {
+ rfc822_append (&out->to, in->to);
+ rfc822_append (&out->cc, in->cc);
+ }
+ }
+ return 0;
+}
+
+static int
+envelope_defaults (ENVELOPE *env, CONTEXT *ctx, HEADER *cur, int flags)
+{
+ ENVELOPE *curenv = NULL;
+ LIST *tmp;
+ char buffer[STRING];
+ int i = 0, tag = 0;
+
+ if (!cur)
+ {
+ tag = 1;
+ for (i = 0; i < ctx->vcount; i++)
+ if (ctx->hdrs[ctx->v2r[i]]->tagged)
+ {
+ cur = ctx->hdrs[ctx->v2r[i]];
+ curenv = cur->env;
+ break;
+ }
+
+ if (!cur)
+ {
+ /* This could happen if the user tagged some messages and then did
+ * a limit such that none of the tagged message are visible.
+ */
+ mutt_error ("No tagged messages are visible!");
+ return (-1);
+ }
+ }
+ else
+ curenv = cur->env;
+
+ if (flags & SENDREPLY)
+ {
+ if (tag)
+ {
+ HEADER *h;
+
+ for (i = 0; i < ctx->vcount; i++)
+ {
+ h = ctx->hdrs[ctx->v2r[i]];
+ if (h->tagged && fetch_recips (env, h->env, flags) == -1)
+ return -1;
+ }
+ }
+ else if (fetch_recips (env, curenv, flags) == -1)
+ return -1;
+
+ if ((flags & SENDLISTREPLY) && !env->to)
+ {
+ mutt_error ("No mailing lists found!");
+ return (-1);
+ }
+
+ if (! option (OPTMETOO))
+ {
+ /* the order is important here. do the CC: first so that if the
+ * the user is the only recipient, it ends up on the TO: field
+ */
+ env->cc = remove_user (env->cc, (env->to == NULL));
+ env->to = remove_user (env->to, (env->cc == NULL));
+ }
+
+ /* the CC field can get cluttered, especially with lists */
+ env->to = mutt_remove_duplicates (env->to);
+ env->cc = mutt_remove_duplicates (env->cc);
+ env->cc = mutt_remove_xrefs (env->to, env->cc);
+
+ if (curenv->real_subj)
+ {
+ env->subject = safe_malloc (strlen (curenv->real_subj) + 5);
+ sprintf (env->subject, "Re: %s", curenv->real_subj);
+ }
+ else
+ env->subject = safe_strdup ("Re: your mail");
+
+ /* add the In-Reply-To field */
+ if (InReplyTo)
+ {
+ strfcpy (buffer, "In-Reply-To: ", sizeof (buffer));
+ mutt_make_string (buffer + 13, sizeof (buffer) - 13, InReplyTo, cur);
+ tmp = env->userhdrs;
+ while (tmp && tmp->next)
+ tmp = tmp->next;
+ if (tmp)
+ {
+ tmp->next = mutt_new_list ();
+ tmp = tmp->next;
+ }
+ else
+ tmp = env->userhdrs = mutt_new_list ();
+ tmp->data = safe_strdup (buffer);
+ }
+
+ env->references = mutt_copy_list (curenv->references);
+
+ if (curenv->message_id)
+ {
+ LIST *t;
+
+ t = mutt_new_list ();
+ t->data = safe_strdup (curenv->message_id);
+ t->next = env->references;
+ env->references = t;
+ }
+ }
+ else if (flags & SENDFORWARD)
+ {
+ /* set the default subject for the message. */
+ mutt_make_string (buffer, sizeof (buffer), ForwFmt, cur);
+ env->subject = safe_strdup (buffer);
+ }
+
+ return (0);
+}
+
+static int
+generate_body (FILE *tempfp, /* stream for outgoing message */
+ HEADER *msg, /* header for outgoing message */
+ int flags, /* compose mode */
+ CONTEXT *ctx, /* current mailbox */
+ HEADER *cur) /* current message */
+{
+ int i;
+ HEADER *h;
+ BODY *tmp;
+
+ if (flags & SENDREPLY)
+ {
+ if ((i = query_quadoption (OPT_INCLUDE, "Include message in reply?")) == -1)
+ return (-1);
+
+ if (i == M_YES)
+ {
+ if (!cur)
+ {
+ for (i = 0; i < ctx->vcount; i++)
+ {
+ h = ctx->hdrs[ctx->v2r[i]];
+ if (h->tagged)
+ {
+ if (include_reply (ctx, h, tempfp) == -1)
+ {
+ mutt_error ("Could not include all requested messages!");
+ return (-1);
+ }
+ fputc ('\n', tempfp);
+ }
+ }
+ }
+ else
+ include_reply (ctx, cur, tempfp);
+ }
+ }
+ else if (flags & SENDFORWARD)
+ {
+ if (query_quadoption (OPT_MIMEFWD, "Forward MIME encapsulated?"))
+ {
+ BODY *last = msg->content;
+
+ while (last && last->next)
+ last = last->next;
+
+ if (cur)
+ {
+ tmp = make_forward (ctx, cur);
+ if (last)
+ last->next = tmp;
+ else
+ msg->content = tmp;
+ }
+ else
+ {
+ for (i = 0; i < ctx->vcount; i++)
+ {
+ if (ctx->hdrs[ctx->v2r[i]]->tagged)
+ {
+ tmp = make_forward (ctx, ctx->hdrs[ctx->v2r[i]]);
+ if (last)
+ {
+ last->next = tmp;
+ last = tmp;
+ }
+ else
+ last = msg->content = tmp;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (cur)
+ include_forward (ctx, cur, tempfp);
+ else
+ for (i=0; i < ctx->vcount; i++)
+ if (ctx->hdrs[ctx->v2r[i]]->tagged)
+ include_forward (ctx, ctx->hdrs[ctx->v2r[i]], tempfp);
+ }
+ }
+
+
+
+#ifdef _PGPPATH
+ else if (flags & SENDKEY)
+ {
+ BODY *tmp;
+ if ((tmp = pgp_make_key_attachment (NULL)) == NULL)
+ return -1;
+
+ tmp->next = msg->content;
+ msg->content = tmp;
+ }
+#endif
+
+
+
+ return (0);
+}
+
+static void mutt_set_followup_to (ENVELOPE *e)
+{
+ ADDRESS *t = NULL;
+
+ /* only generate the Mail-Followup-To if the user has requested it, and
+ it hasn't already been set */
+ if (option (OPTFOLLOWUPTO) && !e->mail_followup_to)
+ {
+ if (mutt_is_list_recipient (e->to) || mutt_is_list_recipient (e->cc))
+ {
+ /* i am a list recipient, so set the Mail-Followup-To: field so that
+ * i don't end up getting multiple copies of responses to my mail
+ */
+ t = rfc822_append (&e->mail_followup_to, e->to);
+ rfc822_append (&t, e->cc);
+ /* the following is needed if $metoo is set, because the ->to and ->cc
+ may contain the user's private address(es) */
+ e->mail_followup_to = remove_user (e->mail_followup_to, 0);
+ }
+ }
+}
+
+/* remove the multipart body if it exists */
+static BODY *remove_multipart (BODY *b)
+{
+ BODY *t;
+
+ if (b->parts)
+ {
+ t = b;
+ b = b->parts;
+ t->parts = NULL;
+ mutt_free_body (&t);
+ }
+ return b;
+}
+
+/* look through the recipients of the message we are replying to, and if
+ we find an address that matches $alternates, we use that as the default
+ from field */
+static ADDRESS *set_reverse_name (ENVELOPE *env)
+{
+ ADDRESS *tmp;
+
+ for (tmp = env->to; tmp; tmp = tmp->next)
+ {
+ if (mutt_addr_is_user (tmp))
+ break;
+ }
+ if (!tmp)
+ {
+ for (tmp = env->cc; tmp; tmp = tmp->next)
+ {
+ if (mutt_addr_is_user (tmp))
+ break;
+ }
+ }
+ if (!tmp && mutt_addr_is_user (env->from))
+ tmp = env->from;
+ if (tmp)
+ {
+ tmp = rfc822_cpy_adr_real (tmp);
+ if (!tmp->personal)
+ tmp->personal = safe_strdup (Realname);
+ }
+ return (tmp);
+}
+
+static ADDRESS *mutt_default_from (void)
+{
+ ADDRESS *adr = rfc822_new_address ();
+
+ /* don't set realname here, it will be set later */
+
+ if (option (OPTUSEDOMAIN))
+ {
+ adr->mailbox = safe_malloc (strlen (Username) + strlen (Fqdn) + 2);
+ sprintf (adr->mailbox, "%s@%s", Username, Fqdn);
+ }
+ else
+ adr->mailbox = safe_strdup (Username);
+ return (adr);
+}
+
+void
+ci_send_message (int flags, /* send mode */
+ HEADER *msg, /* template to use for new message */
+ char *tempfile, /* file specified by -i or -H */
+ CONTEXT *ctx, /* current mailbox */
+ HEADER *cur) /* current message */
+{
+ char buffer[LONG_STRING];
+ char fcc[_POSIX_PATH_MAX] = ""; /* where to copy this message */
+ FILE *tempfp = NULL;
+ BODY *pbody;
+ int i, killfrom = 0;
+
+
+
+#ifdef _PGPPATH
+ /* save current value of "pgp_sign_as" */
+ char *signas = NULL;
+ char *signmic = NULL;
+ if (flags == SENDPOSTPONED)
+ {
+ signas = safe_strdup(PgpSignAs);
+ signmic = safe_strdup(PgpSignMicalg);
+ }
+#endif /* _PGPPATH */
+
+
+
+ if (msg)
+ {
+ msg->env->to = mutt_expand_aliases (msg->env->to);
+ msg->env->cc = mutt_expand_aliases (msg->env->cc);
+ msg->env->bcc = mutt_expand_aliases (msg->env->bcc);
+ }
+ else
+ {
+ msg = mutt_new_header ();
+
+ if (flags == SENDPOSTPONED)
+ {
+ if ((flags = mutt_get_postponed (ctx, msg, &cur)) < 0)
+ goto cleanup;
+ }
+ else if (!flags && quadoption (OPT_RECALL) != M_NO && mutt_num_postponed ())
+ {
+ /* If the user is composing a new message, check to see if there
+ * are any postponed messages first.
+ */
+ if ((i = query_quadoption (OPT_RECALL, "Recall postponed message?")) == -1)
+ goto cleanup;
+
+ if (i == M_YES)
+ {
+ if ((flags = mutt_get_postponed (ctx, msg, &cur)) < 0)
+ flags = 0;
+ }
+ }
+
+ if (flags & SENDPOSTPONED)
+ {
+ if ((tempfp = safe_fopen (msg->content->filename, "a+")) == NULL)
+ {
+ mutt_perror (msg->content->filename);
+ goto cleanup;
+ }
+ }
+
+ if (!msg->env)
+ msg->env = mutt_new_envelope ();
+ }
+
+ if (! (flags & (SENDKEY | SENDPOSTPONED)))
+ {
+ pbody = mutt_new_body ();
+ pbody->next = msg->content; /* don't kill command-line attachments */
+ msg->content = pbody;
+
+ msg->content->type = TYPETEXT;
+ msg->content->subtype = safe_strdup ("plain");
+ msg->content->unlink = 1;
+ msg->content->use_disp = 0;
+
+ if (!tempfile)
+ {
+ mutt_mktemp (buffer);
+ tempfp = safe_fopen (buffer, "w+");
+ msg->content->filename = safe_strdup (buffer);
+ }
+ else
+ {
+ tempfp = safe_fopen (tempfile, "a+");
+ msg->content->filename = safe_strdup (tempfile);
+ }
+
+ if (!tempfp)
+ {
+ dprint(1,(debugfile, "newsend_message: can't create tempfile %s (errno=%d)\n", tempfile, errno));
+ mutt_perror (tempfile);
+ goto cleanup;
+ }
+ }
+
+ /* this is handled here so that the user can match ~f in send-hook */
+ if (cur && option (OPTREVNAME))
+ {
+ /* we shouldn't have to worry about freeing `msg->env->from' before
+ setting it here since this code will only execute when doing some
+ sort of reply. the pointer will only be set when using the -H command
+ line option */
+ msg->env->from = set_reverse_name (cur->env);
+ }
+
+ if (!msg->env->from && option (OPTUSEFROM))
+ msg->env->from = mutt_default_from ();
+
+ if (flags & SENDBATCH)
+ {
+ mutt_copy_stream (stdin, tempfp);
+ if (option (OPTHDRS))
+ {
+ process_user_recips (msg->env);
+ process_user_header (msg->env);
+ }
+ }
+ else if (! (flags & SENDPOSTPONED))
+ {
+ if ((flags & (SENDREPLY | SENDFORWARD)) &&
+ envelope_defaults (msg->env, ctx, cur, flags) == -1)
+ goto cleanup;
+
+ if (option (OPTHDRS))
+ process_user_recips (msg->env);
+
+ if (! (flags & SENDMAILX) &&
+ ! (option (OPTAUTOEDIT) && option (OPTEDITHDRS)) &&
+ ! ((flags & SENDREPLY) && option (OPTFASTREPLY)))
+ {
+ if (edit_envelope (msg->env) == -1)
+ goto cleanup;
+ }
+
+ /* the from address must be set here regardless of whether or not
+ $use_from is set so that the `~P' (from you) operator in send-hook
+ patterns will work. if $use_from is unset, the from address is killed
+ after send-hooks are evaulated */
+ if (!msg->env->from)
+ {
+ msg->env->from = mutt_default_from ();
+ killfrom = 1;
+ }
+
+ /* change settings based upon recipients */
+ mutt_send_hook (msg);
+
+ if (killfrom)
+ {
+ rfc822_free_address (&msg->env->from);
+ killfrom = 0;
+ }
+
+ if (option (OPTHDRS))
+ process_user_header (msg->env);
+
+
+
+#ifdef _PGPPATH
+ if (! (flags & SENDMAILX))
+ {
+ if (option (OPTPGPAUTOSIGN))
+ msg->pgp |= PGPSIGN;
+ if (option (OPTPGPAUTOENCRYPT))
+ msg->pgp |= PGPENCRYPT;
+ if (option (OPTPGPREPLYENCRYPT) && cur && cur->pgp & PGPENCRYPT)
+ msg->pgp |= PGPENCRYPT;
+ if (option (OPTPGPREPLYSIGN) && cur && cur->pgp & PGPSIGN)
+ msg->pgp |= PGPSIGN;
+ }
+#endif /* _PGPPATH */
+
+
+
+ /* include replies/forwarded messages */
+ if (generate_body (tempfp, msg, flags, ctx, cur) == -1)
+ goto cleanup;
+
+ if (! (flags & (SENDMAILX | SENDKEY)) && strcmp (Editor, "builtin") != 0)
+ append_signature (msg->env, tempfp);
+ }
+ /* wait until now to set the real name portion of our return address so
+ that $realname can be set in a send-hook */
+ if (msg->env->from && !msg->env->from->personal)
+ msg->env->from->personal = safe_strdup (Realname);
+
+
+
+#ifdef _PGPPATH
+
+ if (! (flags & SENDKEY))
+ {
+
+#endif
+
+
+
+ fclose (tempfp);
+ tempfp = NULL;
+
+
+
+#ifdef _PGPPATH
+
+ }
+
+#endif
+
+
+
+ if (flags & SENDMAILX)
+ {
+ if (mutt_builtin_editor (msg->content->filename, msg, cur) == -1)
+ goto cleanup;
+ }
+ else if (! (flags & SENDBATCH))
+ {
+ struct stat st;
+ time_t mtime;
+
+ stat (msg->content->filename, &st);
+ mtime = st.st_mtime;
+
+ mutt_update_encoding (msg->content);
+
+ /* If the this isn't a text message, look for a mailcap edit command */
+ if(! (flags & SENDKEY))
+ {
+ if (mutt_needs_mailcap (msg->content))
+ mutt_edit_attachment (msg->content, 0);
+ else if (strcmp ("builtin", Editor) == 0)
+ mutt_builtin_editor (msg->content->filename, msg, cur);
+ else if (option (OPTEDITHDRS))
+ mutt_edit_headers (Editor, msg->content->filename, msg, fcc, sizeof (fcc));
+ else
+ mutt_edit_file (Editor, msg->content->filename);
+ }
+
+ if (! (flags & (SENDPOSTPONED | SENDFORWARD | SENDKEY)))
+ {
+ if (stat (msg->content->filename, &st) == 0)
+ {
+ /* if the file was not modified, bail out now */
+ if (mtime == st.st_mtime &&
+ query_quadoption (OPT_ABORT, "Abort unmodified message?") == M_YES)
+ {
+ mutt_message ("Aborted unmodified message.");
+ goto cleanup;
+ }
+ }
+ else
+ mutt_perror (msg->content->filename);
+ }
+ }
+
+ /* specify a default fcc. if we are in batchmode, only save a copy of
+ the message if the value of $copy is yes or ask-yes */
+ if (!fcc[0] && (!(flags & SENDBATCH) || (quadoption (OPT_COPY) & 0x1)))
+ {
+ /* set the default FCC */
+ if (!msg->env->from)
+ {
+ msg->env->from = mutt_default_from ();
+ killfrom = 1; /* no need to check $use_from because if the user specified
+ a from address it would have already been set by now */
+ }
+ mutt_select_fcc (fcc, sizeof (fcc), msg);
+ if (killfrom)
+ {
+ rfc822_free_address (&msg->env->from);
+ killfrom = 0;
+ }
+ }
+
+ mutt_update_encoding (msg->content);
+
+ if (! (flags & (SENDMAILX | SENDBATCH)))
+ {
+main_loop:
+
+ if ((i = mutt_send_menu (msg, fcc, sizeof (fcc), cur)) == -1)
+ {
+ /* abort */
+ mutt_message ("Mail not sent.");
+ goto cleanup;
+ }
+ else if (i == 1)
+ {
+ /* postpone the message until later. */
+ if (msg->content->next)
+ msg->content = mutt_make_multipart (msg->content);
+ if (mutt_write_fcc (NONULL (Postponed), msg, (cur && (flags & SENDREPLY)) ? cur->env->message_id : NULL, 1) < 0)
+ {
+ msg->content = remove_multipart (msg->content);
+ goto main_loop;
+ }
+ mutt_message ("Message postponed.");
+ goto cleanup;
+ }
+ }
+
+ if (!msg->env->to && !msg->env->cc && !msg->env->bcc)
+ {
+ if (! (flags & SENDBATCH))
+ {
+ mutt_error ("No recipients are specified!");
+ goto main_loop;
+ }
+ else
+ {
+ puts ("No recipients were specified.");
+ goto cleanup;
+ }
+ }
+
+ if (!msg->env->subject && ! (flags & SENDBATCH) &&
+ (i = query_quadoption (OPT_SUBJECT, "No subject, abort sending?")) != M_NO)
+ {
+ /* if the abort is automatic, print an error message */
+ if (quadoption (OPT_SUBJECT) == M_YES)
+ mutt_error ("No subject specified.");
+ goto main_loop;
+ }
+
+ if (msg->content->next)
+ msg->content = mutt_make_multipart (msg->content);
+
+
+
+#ifdef _PGPPATH
+ if (msg->pgp)
+ {
+ if (pgp_protect (msg) != 0)
+ {
+ if (msg->content->parts)
+ {
+ /* remove the toplevel multipart structure */
+ pbody = msg->content;
+ msg->content = msg->content->parts;
+ pbody->parts = NULL;
+ mutt_free_body (&pbody);
+ }
+ goto main_loop;
+ }
+ }
+#endif /* _PGPPATH */
+
+
+
+ mutt_expand_path (fcc, sizeof (fcc));
+
+ if (!option (OPTNOCURSES) && ! (flags & SENDMAILX))
+ mutt_message ("Sending message...");
+
+ if (msg->env->bcc && ! (msg->env->to || msg->env->cc))
+ {
+ /* some MTA's will put an Apparently-To: header field showing the Bcc:
+ * recipients if there is no To: or Cc: field, so attempt to suppress
+ * it by using an empty To: field.
+ */
+ msg->env->to = rfc822_new_address ();
+ msg->env->to->mailbox = safe_strdup (EmptyTo);
+ msg->env->to->group = 1;
+ msg->env->to->next = rfc822_new_address ();
+
+ buffer[0] = 0;
+ rfc822_cat (buffer, sizeof (buffer), msg->env->to->mailbox, RFC822Specials);
+ msg->env->to->mailbox = safe_strdup (buffer);
+ }
+
+ mutt_set_followup_to (msg->env);
+
+ if (mutt_send_message (msg, fcc) == -1)
+ {
+ msg->content = remove_multipart (msg->content);
+ goto main_loop;
+ }
+
+ if (!option (OPTNOCURSES) && ! (flags & SENDMAILX))
+ mutt_message ("Mail sent.");
+
+ /* now free up the memory used to generate this message. */
+ mutt_free_header (&msg);
+
+ if (flags & SENDREPLY)
+ {
+ if (cur)
+ mutt_set_flag (ctx, cur, M_REPLIED, 1);
+ else if (!(flags & SENDPOSTPONED) && ctx && ctx->tagged)
+ {
+ for (i = 0; i < ctx->vcount; i++)
+ if (ctx->hdrs[ctx->v2r[i]]->tagged)
+ mutt_set_flag (ctx, ctx->hdrs[ctx->v2r[i]], M_REPLIED, 1);
+ }
+ }
+
+
+
+#ifdef _PGPPATH
+ if (flags == SENDPOSTPONED)
+ {
+ safe_free((void **) &PgpSignAs);
+ safe_free((void **) &PgpSignMicalg);
+
+ PgpSignAs = signas;
+ PgpSignMicalg = signmic;
+ }
+#endif /* _PGPPATH */
+
+
+
+ return; /* all done */
+
+cleanup:
+
+
+
+#ifdef _PGPPATH
+ if (flags == SENDPOSTPONED)
+ {
+ safe_free((void **) &PgpSignAs);
+ safe_free((void **) &PgpSignMicalg);
+
+ PgpSignAs = signas;
+ PgpSignMicalg = signmic;
+ }
+#endif /* _PGPPATH */
+
+
+
+ if (tempfp)
+ fclose (tempfp);
+ mutt_free_header (&msg);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "rfc2047.h"
+#include "mx.h"
+#include "mime.h"
+#include "mailbox.h"
+#include "copy.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <sysexits.h>
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif /* _PGPPATH */
+
+
+
+#define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
+
+const char MimeSpecials[] = "@.,;<>[]\\\"()?/=";
+
+char B64Chars[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '+', '/'
+};
+
+static char MsgIdPfx = 'A';
+
+static void transform_to_7bit (BODY *a, FILE *fpin);
+
+static void encode_quoted (FILE * fin, FILE *fout, int istext)
+{
+ int c, linelen = 0;
+ char line[77], savechar;
+
+ while ((c = fgetc (fin)) != EOF)
+ {
+ /* Escape lines that begin with "the message separator". */
+ if (linelen == 5 && !strncmp ("From ", line, 5))
+ {
+ strfcpy (line, "=46rom ", sizeof (line));
+ linelen = 7;
+ }
+ else if (linelen == 1 && line[0] == '.')
+ {
+ strfcpy (line, "=2E", sizeof (line));
+ linelen = 3;
+ }
+
+ /* Wrap the line if needed. */
+ if (linelen == 76 && ((istext && c != '\n') || !istext))
+ {
+ /* If the last character is "quoted", then be sure to move all three
+ * characters to the next line. Otherwise, just move the last
+ * character...
+ */
+ if (line[linelen-3] == '=')
+ {
+ line[linelen-3] = 0;
+ fputs (line, fout);
+ fputs ("=\n", fout);
+ line[linelen] = 0;
+ line[0] = '=';
+ line[1] = line[linelen-2];
+ line[2] = line[linelen-1];
+ linelen = 3;
+ }
+ else
+ {
+ savechar = line[linelen-1];
+ line[linelen-1] = '=';
+ line[linelen] = 0;
+ fputs (line, fout);
+ fputc ('\n', fout);
+ line[0] = savechar;
+ linelen = 1;
+ }
+ }
+
+ if (c == '\n' && istext)
+ {
+ /* Check to make sure there is no trailing space on this line. */
+ if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
+ {
+ if (linelen < 74)
+ {
+ sprintf (line+linelen-1, "=%2.2X", line[linelen-1]);
+ fputs (line, fout);
+ }
+ else
+ {
+ int savechar = line[linelen-1];
+
+ line[linelen-1] = '=';
+ line[linelen] = 0;
+ fputs (line, fout);
+ fprintf (fout, "\n=%2.2X", savechar);
+ }
+ }
+ else
+ {
+ line[linelen] = 0;
+ fputs (line, fout);
+ }
+ fputc ('\n', fout);
+ linelen = 0;
+ }
+ else if (c != 9 && (c < 32 || c > 126 || c == '='))
+ {
+ /* Check to make sure there is enough room for the quoted character.
+ * If not, wrap to the next line.
+ */
+ if (linelen > 73)
+ {
+ line[linelen++] = '=';
+ line[linelen] = 0;
+ fputs (line, fout);
+ fputc ('\n', fout);
+ linelen = 0;
+ }
+ sprintf (line+linelen,"=%2.2X", c);
+ linelen += 3;
+ }
+ else
+ {
+ /* Don't worry about wrapping the line here. That will happen during
+ * the next iteration when I'll also know what the next character is.
+ */
+ line[linelen++] = c;
+ }
+ }
+
+ /* Take care of anything left in the buffer */
+ if (linelen > 0)
+ {
+ if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
+ {
+ /* take care of trailing whitespace */
+ if (linelen < 74)
+ sprintf (line+linelen-1, "=%2.2X", line[linelen-1]);
+ else
+ {
+ savechar = line[linelen-1];
+ line[linelen-1] = '=';
+ line[linelen] = 0;
+ fputs (line, fout);
+ fputc ('\n', fout);
+ sprintf (line, "=%2.2X", savechar);
+ }
+ }
+ else
+ line[linelen] = 0;
+ fputs (line, fout);
+ }
+}
+
+static void encode_base64 (FILE * fin, FILE *fout, int istext)
+{
+ int c1, c2, c3, ch;
+ int insert_newline = 0;
+ int linelen = 0;
+
+ FOREVER
+ {
+ if (istext)
+ {
+ if (insert_newline)
+ {
+ c1 = '\n';
+ insert_newline = 0;
+
+ c2 = fgetc(fin);
+ if (c2 == '\n')
+ {
+ c2 = '\r';
+ c3 = '\n';
+ }
+ else
+ {
+ c3 = fgetc(fin);
+ if (c3 == '\n')
+ {
+ c3 = '\r';
+ insert_newline = 1;
+ }
+ }
+ }
+ else
+ {
+ c1 = fgetc(fin);
+ if (c1 == '\n')
+ {
+ c1 = '\r';
+ c2 = '\n';
+ c3 = fgetc(fin);
+ if (c3 == '\n')
+ {
+ c3 = '\r';
+ insert_newline = 1;
+ }
+ }
+ else
+ {
+ c2 = fgetc(fin);
+ if (c2 == '\n')
+ {
+ c2 = '\r';
+ c3 = '\n';
+ }
+ else
+ {
+ c3 = fgetc(fin);
+ if (c3 == '\n')
+ {
+ c3 = '\r';
+ insert_newline = 1;
+ }
+ }
+ }
+ }
+ }
+ else /* !istext */
+ {
+ if ((c1 = fgetc(fin)) == EOF)
+ break;
+ c2 = fgetc(fin);
+ c3 = fgetc(fin);
+ }
+
+ if (linelen + 4 >= 76)
+ {
+ fputc('\n', fout);
+ linelen = 0;
+ }
+
+ ch = c1 >> 2;
+ fputc (B64Chars[ch], fout);
+
+ if (c2 != EOF)
+ {
+ ch = ((c1 & 0x3) << 4) | (c2 >> 4);
+ fputc (B64Chars[ch], fout);
+ }
+ else
+ {
+ ch = (c1 & 0x3) << 4;
+ fputc (B64Chars[ch], fout);
+ fputs("==", fout);
+ break;
+ }
+
+ if (c3 != EOF)
+ {
+ ch = ((c2 & 0xf) << 2) | (c3 >> 6);
+ fputc (B64Chars[ch], fout);
+ }
+ else
+ {
+ ch = (c2 & 0xf) << 2;
+ fputc(B64Chars[ch], fout);
+ fputc('=', fout);
+ break;
+ }
+
+ ch = c3 & 0x3f;
+ fputc(B64Chars[ch], fout);
+
+ linelen += 4;
+ }
+
+ fputc('\n', fout);
+}
+
+int mutt_write_mime_header (BODY *a, FILE *f)
+{
+ PARAMETER *p;
+ char buffer[STRING];
+ char *t;
+ char *fn;
+ int len;
+ int tmplen;
+
+ fprintf (f, "Content-Type: %s/%s", TYPE (a->type), a->subtype);
+
+ if (a->parameter)
+ {
+ len = 25 + strlen (a->subtype); /* approximate len. of content-type */
+
+ p = a->parameter;
+ while (p)
+ {
+ fputc (';', f);
+
+ buffer[0] = 0;
+ rfc822_cat (buffer, sizeof (buffer), p->value, MimeSpecials);
+
+ tmplen = strlen (buffer) + strlen (p->attribute) + 1;
+
+ if (len + tmplen + 2 > 76)
+ {
+ fputs ("\n\t", f);
+ len = tmplen + 8;
+ }
+ else
+ {
+ fputc (' ', f);
+ len += tmplen + 1;
+ }
+
+ fprintf (f, "%s=%s", p->attribute, buffer);
+
+ p = p->next;
+ }
+ }
+
+ fputc ('\n', f);
+
+ if (a->encoding != ENC7BIT)
+ fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
+
+ if (a->description)
+ fprintf(f, "Content-Description: %s\n", a->description);
+
+ if (a->use_disp && (a->disposition == DISPATTACH || a->filename || a->d_filename))
+ {
+ fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
+
+ if(!(fn = a->d_filename))
+ fn = a->filename;
+
+ if (fn)
+ {
+ /* Strip off the leading path... */
+ if ((t = strrchr (fn, '/')))
+ t++;
+ else
+ t = fn;
+
+ buffer[0] = 0;
+ rfc822_cat (buffer, sizeof (buffer), t, MimeSpecials);
+ fprintf (f, "; filename=%s", buffer);
+ }
+
+ fputc ('\n', f);
+ }
+
+ /* Do NOT add the terminator here!!! */
+ return (ferror (f) ? -1 : 0);
+}
+
+int mutt_write_mime_body (BODY *a, FILE *f)
+{
+ char *p, boundary[SHORT_STRING];
+ FILE *fpin;
+ BODY *t;
+
+ if (a->type == TYPEMULTIPART)
+ {
+ /* First, find the boundary to use */
+ if (!(p = mutt_get_parameter ("boundary", a->parameter)))
+ {
+ dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
+ return (-1);
+ }
+ strfcpy (boundary, p, sizeof (boundary));
+
+ for (t = a->parts; t ; t = t->next)
+ {
+ fprintf (f, "\n--%s\n", boundary);
+ if (mutt_write_mime_header (t, f) == -1)
+ return -1;
+ fputc ('\n', f);
+ if (mutt_write_mime_body (t, f) == -1)
+ return -1;
+ }
+ fprintf (f, "\n--%s--\n", boundary);
+ return (ferror (f) ? -1 : 0);
+ }
+
+
+
+#ifdef _PGPPATH
+ /* This is pretty gross, but it's the best solution for now... */
+ if (a->type == TYPEAPPLICATION && strcmp (a->subtype, "pgp-encrypted") == 0)
+ {
+ fputs ("Version: 1\n", f);
+ return 0;
+ }
+#endif /* _PGPPATH */
+
+
+
+ if ((fpin = fopen (a->filename, "r")) == NULL)
+ {
+ dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
+ return -1;
+ }
+
+ if (a->encoding == ENCQUOTEDPRINTABLE)
+ encode_quoted (fpin, f, mutt_is_text_type (a->type, a->subtype));
+ else if (a->encoding == ENCBASE64)
+ encode_base64 (fpin, f, mutt_is_text_type (a->type, a->subtype));
+ else
+ mutt_copy_stream (fpin, f);
+ fclose (fpin);
+
+ return (ferror (f) ? -1 : 0);
+}
+
+#define BOUNDARYLEN 16
+char *mutt_generate_boundary (void)
+{
+ char *rs = (char *)safe_malloc (BOUNDARYLEN + 1);
+ char *p = rs;
+ int i;
+
+ rs[BOUNDARYLEN] = 0;
+ for (i=0;i<BOUNDARYLEN;i++) *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
+ *p = 0;
+ return (rs);
+}
+
+/* analyze the contents of a file to determine which MIME encoding to use */
+static CONTENT *mutt_get_content_info (const char *fname)
+{
+ CONTENT *info;
+ FILE *fp;
+ int ch, from=0, whitespace=0, dot=0, linelen=0;
+
+ if ((fp = fopen (fname, "r")) == NULL)
+ {
+ dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
+ fname, strerror (errno), errno));
+ return (NULL);
+ }
+
+ info = safe_calloc (1, sizeof (CONTENT));
+ while ((ch = fgetc (fp)) != EOF)
+ {
+ linelen++;
+ if (ch == '\n')
+ {
+ if (whitespace) info->space = 1;
+ if (dot) info->dot = 1;
+ if (linelen > info->linemax) info->linemax = linelen;
+ whitespace = 0;
+ linelen = 0;
+ dot = 0;
+ }
+ else if (ch == '\r')
+ {
+ if ((ch = fgetc (fp)) == EOF)
+ {
+ info->binary = 1;
+ break;
+ }
+ else if (ch != '\n')
+ {
+ info->binary = 1;
+ ungetc (ch, fp);
+ continue;
+ }
+ else
+ {
+ if (whitespace) info->space = 1;
+ if (dot) info->dot = 1;
+ if (linelen > info->linemax) info->linemax = linelen;
+ whitespace = 0;
+ dot = 0;
+ linelen = 0;
+ }
+ }
+ else if (ch & 0x80)
+ info->hibin++;
+ else if (ch == '\t' || ch == '\f')
+ {
+ info->ascii++;
+ whitespace++;
+ }
+ else if (ch < 32 || ch == 127)
+ info->lobin++;
+ else
+ {
+ if (linelen == 1)
+ {
+ if (ch == 'F')
+ from = 1;
+ else
+ from = 0;
+ if (ch == '.')
+ dot = 1;
+ else
+ dot = 0;
+ }
+ else if (from)
+ {
+ if (linelen == 2 && ch != 'r') from = 0;
+ else if (linelen == 3 && ch != 'o') from = 0;
+ else if (linelen == 4 && ch != 'm') from = 0;
+ else if (linelen == 5)
+ {
+ if (ch == ' ') info->from = 1;
+ from = 0;
+ }
+ }
+ if (ch == ' ') whitespace++;
+ info->ascii++;
+ }
+ if (linelen > 1) dot = 0;
+ if (ch != ' ' && ch != '\t') whitespace = 0;
+ }
+ fclose (fp);
+ return (info);
+}
+
+/* Given a file with path ``s'', see if there is a registered MIME type.
+ * returns the major MIME type, and copies the subtype to ``d''. First look
+ * for ~/.mime.types, then look in a system mime.types if we can find one.
+ * The longest match is used so that we can match `ps.gz' when `gz' also
+ * exists.
+ */
+
+static int lookup_mime_type (char *d, const char *s)
+{
+ FILE *f;
+ char *p, *ct,
+ buf[LONG_STRING];
+ int count;
+ int szf, sze, cur_n, cur_sze;
+
+ *d = 0;
+ cur_n = TYPEOTHER;
+ cur_sze = 0;
+ szf = strlen (s);
+
+ for (count = 0 ; count < 2 ; count++)
+ {
+ /*
+ * can't use strtok() because we use it in an inner loop below, so use
+ * a switch statement here instead.
+ */
+ switch (count)
+ {
+ case 0:
+ snprintf (buf, sizeof (buf), "%s/.mime.types", Homedir);
+ break;
+ case 1:
+ strfcpy (buf, SHAREDIR"/mime.types", sizeof (buf));
+ break;
+ default:
+ return (cur_n);
+ }
+
+ if ((f = fopen (buf, "r")) != NULL)
+ {
+ while (fgets (buf, sizeof (buf) - 1, f) != NULL)
+ {
+ /* weed out any comments */
+ if ((p = strchr (buf, '#')))
+ *p = 0;
+
+ /* remove any leading space. */
+ ct = buf;
+ SKIPWS (ct);
+
+ /* position on the next field in this line */
+ if ((p = strpbrk (ct, " \t")) == NULL)
+ continue;
+ *p++ = 0;
+ SKIPWS (p);
+
+ /* cycle through the file extensions */
+ while ((p = strtok (p, " \t\n")))
+ {
+ sze = strlen (p);
+ if ((sze > cur_sze) && (szf >= sze) &&
+ strcasecmp (s + szf - sze, p) == 0 &&
+ (szf == sze || s[szf - sze - 1] == '.'))
+ {
+ char *dc;
+
+ /* get the content-type */
+
+ if ((p = strchr (ct, '/')) == NULL)
+ {
+ /* malformed line, just skip it. */
+ break;
+ }
+ *p++ = 0;
+
+ dc = d;
+ while (*p && !ISSPACE (*p))
+ *dc++ = *p++;
+ *dc = 0;
+
+ cur_n = mutt_check_mime_type (ct);
+ cur_sze = sze;
+ }
+ p = NULL;
+ }
+ }
+ fclose (f);
+ }
+ }
+ return (cur_n);
+}
+
+static char *set_text_charset (CONTENT *info)
+{
+ if (strcasecmp (NONULL (Charset), "us-ascii") == 0)
+ {
+ if (info->hibin != 0)
+ return "unknown-8bit";
+ }
+ else if (info->hibin == 0)
+ return "us-ascii";
+
+ /* if no charset is given, provide a reasonable default */
+ return (Charset ? Charset : "us-ascii");
+}
+
+void mutt_message_to_7bit (BODY *a, FILE *fp)
+{
+ char temp[_POSIX_PATH_MAX];
+ size_t linelen = 0;
+ char *line = NULL;
+ FILE *fpin = NULL;
+ FILE *fpout = NULL;
+ struct stat sb;
+
+ if (!a->filename && fp)
+ fpin = fp;
+ else if (!a->filename || !(fpin = fopen (a->filename, "r")))
+ {
+ mutt_error ("Could not open %s", a->filename ? a->filename : "(null)");
+ return;
+ }
+ else
+ {
+ a->offset = 0;
+ if (stat (a->filename, &sb) == -1)
+ {
+ mutt_perror ("stat");
+ fclose (fpin);
+ }
+ a->length = sb.st_size;
+ }
+
+ mutt_mktemp (temp);
+ if (!(fpout = safe_fopen (temp, "w+")))
+ {
+ mutt_perror ("fopen");
+ goto cleanup;
+ }
+
+ fseek (fpin, a->offset, 0);
+ a->parts = mutt_parse_messageRFC822 (fpin, a);
+
+ transform_to_7bit (a->parts, fpin);
+
+ mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
+ CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
+
+ fputs ("Mime-Version: 1.0\n", fpout);
+ mutt_write_mime_header (a->parts, fpout);
+ fputc ('\n', fpout);
+ mutt_write_mime_body (a->parts, fpout);
+
+ cleanup:
+ safe_free ((void **) &line);
+ linelen = 0;
+
+ if (fpin && !fp)
+ fclose (fpin);
+ if (fpout)
+ fclose (fpout);
+ else
+ return;
+
+ a->encoding = ENC7BIT;
+ a->d_filename = a->filename;
+ if (a->filename && a->unlink)
+ unlink (a->filename);
+ a->filename = safe_strdup (temp);
+ a->unlink = 1;
+ if(stat (a->filename, &sb) == -1)
+ {
+ mutt_perror ("stat");
+ return;
+ }
+ a->length = sb.st_size;
+ mutt_free_body (&a->parts);
+}
+
+static void transform_to_7bit (BODY *a, FILE *fpin)
+{
+ char buff[_POSIX_PATH_MAX];
+ STATE s;
+ struct stat sb;
+
+ memset (&s, 0, sizeof (s));
+ for (; a; a = a->next)
+ {
+ if (a->type == TYPEMULTIPART)
+ {
+ if (a->encoding != ENC7BIT)
+ a->encoding = ENC7BIT;
+
+ transform_to_7bit (a->parts, fpin);
+ }
+ else if (a->type == TYPEMESSAGE && strcasecmp (a->subtype, "delivery-status"))
+ {
+ mutt_message_to_7bit (a, fpin);
+ }
+ else
+ {
+ mutt_mktemp (buff);
+ if ((s.fpout = safe_fopen (buff, "w")) == NULL)
+ {
+ mutt_perror ("fopen");
+ return;
+ }
+ s.fpin = fpin;
+ mutt_decode_attachment (a, &s);
+ fclose (s.fpout);
+ a->d_filename = a->filename;
+ a->filename = safe_strdup (buff);
+ a->unlink = 1;
+ if (stat (a->filename, &sb) == -1)
+ {
+ mutt_perror ("stat");
+ return;
+ }
+ a->length = sb.st_size;
+
+ mutt_update_encoding (a);
+ if (a->encoding == ENC8BIT)
+ a->encoding = ENCQUOTEDPRINTABLE;
+ else if(a->encoding == ENCBINARY)
+ a->encoding = ENCBASE64;
+ }
+ }
+}
+
+/* determine which Content-Transfer-Encoding to use */
+static void mutt_set_encoding (BODY *b, CONTENT *info)
+{
+ if (b->type == TYPETEXT)
+ {
+ if (info->lobin)
+ b->encoding = ENCQUOTEDPRINTABLE;
+ else if (info->hibin)
+ b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
+ else
+ b->encoding = ENC7BIT;
+ }
+ else if (b->type == TYPEMESSAGE)
+ {
+ if (info->lobin || info->hibin)
+ {
+ if (option (OPTALLOW8BIT) && !info->lobin)
+ b->encoding = ENC8BIT;
+ else
+ mutt_message_to_7bit (b, NULL);
+ }
+ else
+ b->encoding = ENC7BIT;
+ }
+ else if (info->lobin || info->hibin || info->binary || info->linemax > 990)
+ {
+ /* Determine which encoding is smaller */
+ if (1.33 * (float)(info->lobin+info->hibin+info->ascii) < 3.0 * (float) (info->lobin + info->hibin) + (float)info->ascii)
+ b->encoding = ENCBASE64;
+ else
+ b->encoding = ENCQUOTEDPRINTABLE;
+ }
+ else
+ b->encoding = ENC7BIT;
+}
+
+/* Assumes called from send mode where BODY->filename points to actual file */
+void mutt_update_encoding (BODY *a)
+{
+ CONTENT *info;
+
+ if ((info = mutt_get_content_info (a->filename)) == NULL)
+ return;
+
+ mutt_set_encoding (a, info);
+
+ if (a->type == TYPETEXT)
+ {
+ /* make sure the charset is valid */
+ if (a->parameter)
+ safe_free ((void **) &a->parameter->value);
+ else
+ {
+ a->parameter = mutt_new_parameter ();
+ a->parameter->attribute = safe_strdup ("charset");
+ }
+ a->parameter->value = safe_strdup (set_text_charset (info));
+ }
+
+
+
+#ifdef _PGPPATH
+ /* save the info in case this message is signed. we will want to do Q-P
+ * encoding if any lines begin with "From " so the signature won't be munged,
+ * for example.
+ */
+ safe_free ((void **) &a->content);
+ a->content = info;
+ info = NULL;
+#endif
+
+
+
+ safe_free ((void **) &info);
+}
+
+BODY *mutt_make_attach (const char *path)
+{
+ BODY *att;
+ CONTENT *info;
+ char buf[SHORT_STRING];
+ int n;
+
+ if ((info = mutt_get_content_info (path)) == NULL)
+ return NULL;
+
+ att = mutt_new_body ();
+ att->filename = safe_strdup (path);
+
+ /* Attempt to determine the appropriate content-type based on the filename
+ * suffix.
+ */
+ if ((n = lookup_mime_type (buf, path)) != TYPEOTHER)
+ {
+ att->type = n;
+ att->subtype = safe_strdup (buf);
+ }
+
+ if (!att->subtype)
+ {
+ if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
+ {
+ /*
+ * Statistically speaking, there should be more than 10% "lobin"
+ * chars if this is really a binary file...
+ */
+ att->type = TYPETEXT;
+ att->subtype = safe_strdup ("plain");
+ att->parameter = mutt_new_parameter ();
+ att->parameter->attribute = safe_strdup ("charset");
+ att->parameter->value = safe_strdup (set_text_charset (info));
+ }
+ else
+ {
+ att->type = TYPEAPPLICATION;
+ att->subtype = safe_strdup ("octet-stream");
+ }
+ }
+
+ mutt_set_encoding (att, info);
+
+
+
+#ifdef _PGPPATH
+ /*
+ * save the info in case this message is signed. we will want to do Q-P
+ * encoding if any lines begin with "From " so the signature won't be munged,
+ * for example.
+ */
+ att->content = info;
+ info = NULL;
+#endif
+
+
+
+ safe_free ((void **) &info);
+
+ return (att);
+}
+
+static int get_toplevel_encoding (BODY *a)
+{
+ int e = ENC7BIT;
+
+ for (; a; a = a->next)
+ {
+ if (a->encoding == ENCBINARY)
+ return (ENCBINARY);
+ else if (a->encoding == ENC8BIT)
+ e = ENC8BIT;
+ }
+
+ return (e);
+}
+
+BODY *mutt_make_multipart (BODY *b)
+{
+ BODY *new;
+
+ new = mutt_new_body ();
+ new->type = TYPEMULTIPART;
+ new->subtype = safe_strdup ("mixed");
+ new->encoding = get_toplevel_encoding (b);
+ new->parameter = mutt_new_parameter ();
+ new->parameter->attribute = safe_strdup ("boundary");
+ new->parameter->value = mutt_generate_boundary ();
+ new->use_disp = 0;
+ new->parts = b;
+
+ return new;
+}
+
+static char *mutt_make_date (char *s)
+{
+ time_t t = time (NULL);
+ struct tm *l = gmtime(&t);
+ int yday = l->tm_yday;
+ int tz = l->tm_hour * 60 + l->tm_min;
+
+ l = localtime(&t);
+ tz = l->tm_hour * 60 + l->tm_min - tz;
+ yday = l->tm_yday - yday;
+
+ if (yday != 0)
+ tz += yday * 24 * 60; /* GMT is next or previous day! */
+
+ sprintf (s, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
+ Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon], l->tm_year+1900,
+ l->tm_hour, l->tm_min, l->tm_sec, tz/60, abs(tz) % 60);
+ return (s);
+}
+
+/* wrapper around mutt_write_address() so we can handle very large
+ recipient lists without needing a huge temporary buffer in memory */
+void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen)
+{
+ ADDRESS *tmp;
+ char buf[LONG_STRING];
+ int count = 0;
+ int len;
+
+ while (adr)
+ {
+ tmp = adr->next;
+ adr->next = NULL;
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), adr);
+ len = strlen (buf);
+ if (count && linelen + len > 74)
+ {
+ if (count)
+ {
+ fputs ("\n\t", fp);
+ linelen = len + 8; /* tab is usually about 8 spaces... */
+ }
+ }
+ else
+ {
+ if (count && adr->mailbox)
+ {
+ fputc (' ', fp);
+ linelen++;
+ }
+ linelen += len;
+ }
+ fputs (buf, fp);
+ adr->next = tmp;
+ if (!adr->group && adr->next && adr->next->mailbox)
+ {
+ linelen++;
+ fputc (',', fp);
+ }
+ adr = adr->next;
+ count++;
+ }
+ fputc ('\n', fp);
+}
+
+/* arbitrary number of elements to grow the array by */
+#define REF_INC 16
+
+#define TrimRef 10
+
+/* need to write the list in reverse because they are stored in reverse order
+ * when parsed to speed up threading
+ */
+static void write_references (LIST *r, FILE *f)
+{
+ LIST **ref = NULL;
+ int refcnt = 0, refmax = 0;
+
+ for ( ; (TrimRef == 0 || refcnt < TrimRef) && r ; r = r->next)
+ {
+ if (refcnt == refmax)
+ safe_realloc ((void **) &ref, (refmax += REF_INC) * sizeof (LIST *));
+ ref[refcnt++] = r;
+ }
+
+ while (refcnt-- > 0)
+ {
+ fputc (' ', f);
+ fputs (ref[refcnt]->data, f);
+ }
+
+ safe_free ((void **) &ref);
+}
+
+/* Note: all RFC2047 encoding should be done outside of this routine, except
+ * for the "real name." This will allow this routine to be used more than
+ * once, if necessary.
+ *
+ * mode == 1 => "lite" mode (used for edit_hdrs)
+ * mode == 0 => normal mode. write full header + MIME headers
+ * mode == -1 => write just the envelope info (used for postponing messages)
+ */
+
+int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach, int mode)
+{
+ char buffer[LONG_STRING];
+ LIST *tmp = env->userhdrs;
+
+ if (mode == 0)
+ {
+ if (env->message_id)
+ fprintf (fp, "Message-ID: %s\n", env->message_id);
+ fputs (mutt_make_date (buffer), fp);
+ }
+
+ /* OPTUSEFROM is not consulted here so that we can still write a From:
+ * field if the user sets it with the `my_hdr' command
+ */
+ if (env->from)
+ {
+ buffer[0] = 0;
+ rfc822_write_address (buffer, sizeof (buffer), env->from);
+ fprintf (fp, "From: %s\n", buffer);
+ }
+
+ if (env->to)
+ {
+ fputs ("To: ", fp);
+ mutt_write_address_list (env->to, fp, 4);
+ }
+ else if (mode > 0)
+ fputs ("To: \n", fp);
+
+ if (env->cc)
+ {
+ fputs ("Cc: ", fp);
+ mutt_write_address_list (env->cc, fp, 4);
+ }
+ else if (mode > 0)
+ fputs ("Cc: \n", fp);
+
+ if (env->bcc)
+ {
+ fputs ("Bcc: ", fp);
+ mutt_write_address_list (env->bcc, fp, 5);
+ }
+ else if (mode > 0)
+ fputs ("Bcc: \n", fp);
+
+ if (env->subject)
+ fprintf (fp, "Subject: %s\n", env->subject);
+ else if (mode == 1)
+ fputs ("Subject: \n", fp);
+
+ if (env->reply_to)
+ {
+ fputs ("Reply-To: ", fp);
+ mutt_write_address_list (env->reply_to, fp, 10);
+ }
+ else if (mode > 0)
+ fputs ("Reply-To: \n", fp);
+
+ if (env->mail_followup_to)
+ {
+ fputs ("Mail-Followup-To: ", fp);
+ mutt_write_address_list (env->mail_followup_to, fp, 18);
+ }
+
+ if (mode <= 0)
+ {
+ if (env->references)
+ {
+ fputs ("References:", fp);
+ write_references (env->references, fp);
+ fputc('\n', fp);
+ }
+
+ /* Add the MIME headers */
+ fputs ("Mime-Version: 1.0\n", fp);
+ mutt_write_mime_header (attach, fp);
+ }
+
+#ifndef NO_XMAILER
+ if (mode == 0)
+ {
+ /* Add a vanity header */
+ fprintf (fp, "X-Mailer: Mutt %s\n", VERSION);
+ }
+#endif
+
+ /* Add any user defined headers */
+ for (; tmp; tmp = tmp->next)
+ {
+ fputs (tmp->data, fp);
+ fputc ('\n', fp);
+ }
+
+ return (ferror (fp) == 0 ? 0 : -1);
+}
+
+static void encode_headers (LIST *h)
+{
+ char tmp[LONG_STRING];
+ char *p;
+ size_t len;
+
+ for (; h; h = h->next)
+ {
+ if ((p = strchr (h->data, ':')))
+ {
+ *p++ = 0;
+ SKIPWS (p);
+ snprintf (tmp, sizeof (tmp), "%s: ", h->data);
+ len = strlen (tmp);
+ rfc2047_encode_string (tmp + len, sizeof (tmp) - len, (unsigned char *) p);
+ safe_free ((void **) &h->data);
+ h->data = safe_strdup (tmp);
+ }
+ }
+}
+
+/* rfc2047 encode the content-descriptions */
+static void encode_descriptions (BODY *b)
+{
+ BODY *t;
+ char tmp[LONG_STRING];
+
+ for (t = b; t; t = t->next)
+ {
+ if (t->description)
+ {
+ rfc2047_encode_string (tmp, sizeof (tmp), (unsigned char *) t->description);
+ safe_free ((void **) &t->description);
+ t->description = safe_strdup (tmp);
+ }
+ if (t->parts)
+ encode_descriptions (t->parts);
+ }
+}
+
+char *mutt_gen_msgid (void)
+{
+ char buf[SHORT_STRING];
+ time_t now;
+ struct tm *tm;
+
+ now = time (NULL);
+ tm = localtime (&now);
+ snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.%c%d@%s>",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
+ tm->tm_min, tm->tm_sec, MsgIdPfx, getpid (),
+ Fqdn[0] != '@' ? Fqdn : Hostname);
+ MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
+ return (safe_strdup (buf));
+}
+
+static RETSIGTYPE alarm_handler (int sig)
+{
+ Signals |= S_ALARM;
+}
+
+/* invoke sendmail in a subshell
+ path (in) path to program to execute
+ args (in) arguments to pass to program
+ msg (in) temp file containing message to send
+ tempfile (out) if sendmail is put in the background, this points
+ to the temporary file containing the stdout of the
+ child process */
+static int
+send_msg (const char *path, char **args, const char *msg, char **tempfile)
+{
+ int fd, st, w = 0, err = 0;
+ pid_t pid;
+ struct sigaction act, oldint, oldquit, oldalrm;
+
+ memset (&act, 0, sizeof (struct sigaction));
+ sigemptyset (&(act.sa_mask));
+ act.sa_handler = SIG_IGN;
+ sigaction (SIGINT, &act, &oldint);
+ sigaction (SIGQUIT, &act, &oldquit);
+
+ if (SendmailWait)
+ {
+ char tmp[_POSIX_PATH_MAX];
+
+ mutt_mktemp (tmp);
+ *tempfile = safe_strdup (tmp);
+ }
+
+ if ((pid = fork ()) == 0)
+ {
+ /* reset signals for the child */
+ act.sa_handler = SIG_DFL;
+ /* we need SA_RESTART for the open() below */
+#ifdef SA_RESTART
+ act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+#else
+ act.sa_flags = SA_NOCLDSTOP;
+#endif
+ sigaction (SIGCHLD, &act, NULL);
+ act.sa_flags = 0;
+ sigaction (SIGINT, &act, NULL);
+ sigaction (SIGQUIT, &act, NULL);
+
+ /* if it is possible that we will deliver in the background, then we have
+ to detach the child from this process group or it will die when the
+ parent process exists, causing the mail to not get delivered. The
+ problem here is that any error messages will get lost... */
+ if (SendmailWait)
+ setsid ();
+ if ((pid = fork ()) == 0)
+ {
+ fd = open (msg, O_RDONLY, 0);
+ if (fd < 0)
+ _exit (127);
+ dup2 (fd, 0);
+ close (fd);
+ if (SendmailWait)
+ {
+ /* write stdout to a tempfile */
+ w = open (*tempfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (w < 0)
+ _exit (errno);
+ dup2 (w, 1);
+ close (w);
+ }
+ sigaction (SIGCHLD, &act, NULL);
+ execv (path, args);
+ unlink (msg);
+ _exit (127);
+ }
+ else if (pid == -1)
+ {
+ unlink (msg);
+ _exit (errno);
+ }
+
+ if (waitpid (pid, &st, 0) > 0)
+ {
+ st = WIFEXITED (st) ? WEXITSTATUS (st) : 127;
+ if (st == (EX_OK & 0xff) && SendmailWait)
+ unlink (*tempfile); /* no longer needed */
+ }
+ else
+ {
+ st = 127;
+ if (SendmailWait)
+ unlink (*tempfile);
+ }
+ unlink (msg);
+ _exit (st);
+ }
+ /* SendmailWait > 0: SIGALRM will interrupt wait() after alrm seconds
+ SendmailWait = 0: wait forever
+ SendmailWait < 0: don't wait */
+ if (SendmailWait > 0)
+ {
+ Signals &= ~S_ALARM;
+ act.sa_handler = alarm_handler;
+#ifdef SA_INTERRUPT
+ /* need to make sure the waitpid() is interrupted on SIGALRM */
+ act.sa_flags = SA_INTERRUPT;
+#else
+ act.sa_flags = 0;
+#endif
+ sigaction (SIGALRM, &act, &oldalrm);
+ alarm (SendmailWait);
+ }
+ if (SendmailWait >= 0)
+ {
+ w = waitpid (pid, &st, 0);
+ err = errno; /* save error since it might be clobbered by another
+ system call before we check the value */
+ dprint (1, (debugfile, "send_msg(): waitpid returned %d (%s)\n", w,
+ (w < 0 ? strerror (errno) : "OK")));
+ }
+ if (SendmailWait > 0)
+ {
+ alarm (0);
+ sigaction (SIGALRM, &oldalrm, NULL);
+ }
+ if (SendmailWait < 0 || ((Signals & S_ALARM) && w < 0 && err == EINTR))
+ {
+ /* add to list of children pids to reap */
+ mutt_add_child_pid (pid);
+ /* since there is no way for the user to be notified of error in this case,
+ remove the temp file now */
+ unlink (*tempfile);
+ FREE (tempfile);
+#ifdef DEBUG
+ if (Signals & S_ALARM)
+ dprint (1, (debugfile, "send_msg(): received SIGALRM\n"));
+ dprint (1, (debugfile, "send_msg(): putting sendmail in the background\n"));
+#endif
+ st = EX_OK & 0xff;
+ }
+ else if (w < 0)
+ {
+ /* if err==EINTR, alarm interrupt, child status unknown,
+ otherwise, there was an error invoking the child */
+ st = (err == EINTR) ? (EX_OK & 0xff) : 127;
+ }
+ else
+ {
+#ifdef DEBUG
+ if (WIFEXITED (st))
+ dprint (1, (debugfile, "send_msg(): child exited %d\n", WEXITSTATUS (st)));
+ else
+ dprint (1, (debugfile, "send_msg(): child did not exit\n"));
+#endif /* DEBUG */
+ st = WIFEXITED (st) ? WEXITSTATUS (st) : -1; /* return child status */
+ }
+ sigaction (SIGINT, &oldint, NULL);
+ sigaction (SIGQUIT, &oldquit, NULL);
+ /* restore errno so that the caller can build an error message */
+ errno = err;
+ return (st);
+}
+
+static char **
+add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
+{
+ for (; addr; addr = addr->next)
+ {
+ /* weed out group mailboxes, since those are for display only */
+ if (addr->mailbox && !addr->group)
+ {
+ if (*argslen == *argsmax)
+ safe_realloc ((void **) &args, (*argsmax += 5) * sizeof (char *));
+ args[(*argslen)++] = addr->mailbox;
+ }
+ }
+ return (args);
+}
+
+static char **
+add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
+{
+ if (*argslen == *argsmax)
+ safe_realloc ((void **) &args, (*argsmax += 5) * sizeof (char *));
+ args[(*argslen)++] = s;
+ return (args);
+}
+
+static int
+invoke_sendmail (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
+ const char *msg, /* file containing message */
+ int eightbit) /* message contains 8bit chars */
+{
+ char *ps = NULL, *path = NULL, *s = safe_strdup (Sendmail), *childout = NULL;
+ char **args = NULL;
+ size_t argslen = 0, argsmax = 0;
+ int i;
+
+ ps = s;
+ i = 0;
+ while ((ps = strtok (ps, " ")))
+ {
+ if (argslen == argsmax)
+ safe_realloc ((void **) &args, sizeof (char *) * (argsmax += 5));
+
+ if (i)
+ args[argslen++] = ps;
+ else
+ {
+ path = safe_strdup (ps);
+ ps = strrchr (ps, '/');
+ if (ps)
+ ps++;
+ else
+ ps = path;
+ args[argslen++] = ps;
+ }
+ ps = NULL;
+ i++;
+ }
+ if (eightbit && option (OPTUSE8BITMIME))
+ args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
+ if (DsnNotify)
+ {
+ args = add_option (args, &argslen, &argsmax, "-N");
+ args = add_option (args, &argslen, &argsmax, DsnNotify);
+ }
+ if (DsnReturn)
+ {
+ args = add_option (args, &argslen, &argsmax, "-R");
+ args = add_option (args, &argslen, &argsmax, DsnReturn);
+ }
+ args = add_args (args, &argslen, &argsmax, to);
+ args = add_args (args, &argslen, &argsmax, cc);
+ args = add_args (args, &argslen, &argsmax, bcc);
+
+ if (argslen == argsmax)
+ safe_realloc ((void **) &args, sizeof (char *) * (++argsmax));
+
+ args[argslen++] = NULL;
+
+ if (!option (OPTNOCURSES))
+ endwin ();
+ if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff))
+ {
+ char *e = strerror (errno);
+
+ fprintf (stderr, "Error sending message, child exited %d (%s).\n", i, NONULL (e));
+ if (childout)
+ fprintf (stderr, "Saved output of child process to %s.\n", childout);
+ if (!option (OPTNOCURSES))
+ {
+ mutt_any_key_to_continue (NULL);
+ mutt_error ("Error sending message.");
+ }
+ }
+ FREE (&childout);
+ FREE (&path);
+ FREE (&s);
+ FREE (&args);
+
+ return (i);
+}
+
+/* appends string 'b' to string 'a', and returns the pointer to the new
+ string. */
+char *mutt_append_string (char *a, const char *b)
+{
+ size_t la = strlen (a);
+ safe_realloc ((void **) &a, la + strlen (b) + 1);
+ strcpy (a + la, b);
+ return (a);
+}
+
+/* returns 1 if char `c' needs to be quoted to protect from shell
+ interpretation when executing commands in a subshell */
+#define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
+
+/* returns 1 if string `s' contains characters which could cause problems
+ when used on a command line to execute a command */
+int mutt_needs_quote (const char *s)
+{
+ while (*s)
+ {
+ if (INVALID_CHAR (*s))
+ return 1;
+ s++;
+ }
+ return 0;
+}
+
+/* Quote a string to prevent shell escapes when this string is used on the
+ command line to send mail. */
+char *mutt_quote_string (const char *s)
+{
+ char *r, *pr;
+ size_t rlen;
+
+ rlen = strlen (s) + 3;
+ pr = r = malloc (rlen);
+ *pr++ = '"';
+ while (*s)
+ {
+ if (INVALID_CHAR (*s))
+ {
+ size_t o = pr - r;
+ safe_realloc ((void **) &r, ++rlen);
+ pr = r + o;
+ *pr++ = '\\';
+ }
+ *pr++ = *s++;
+ }
+ *pr++ = '"';
+ *pr = 0;
+ return (r);
+}
+
+int mutt_send_message (HEADER *msg, const char *fcc)
+{
+ char tempfile[_POSIX_PATH_MAX], buffer[STRING];
+ FILE *tempfp;
+ int i;
+
+ /* Take care of 8-bit => 7-bit conversion. */
+ rfc2047_encode_adrlist (msg->env->to);
+ rfc2047_encode_adrlist (msg->env->cc);
+ rfc2047_encode_adrlist (msg->env->from);
+ rfc2047_encode_adrlist (msg->env->mail_followup_to);
+ if (msg->env->subject)
+ {
+ rfc2047_encode_string (buffer, sizeof (buffer) - 1,
+ (unsigned char *) msg->env->subject);
+ safe_free ((void **) &msg->env->subject);
+ msg->env->subject = safe_strdup (buffer);
+ }
+ encode_headers (msg->env->userhdrs);
+ encode_descriptions (msg->content);
+
+ if (!msg->env->message_id)
+ msg->env->message_id = mutt_gen_msgid ();
+
+ /* Write out the message in MIME form. */
+ mutt_mktemp (tempfile);
+ if ((tempfp = safe_fopen (tempfile, "w")) == NULL)
+ return (-1);
+
+ mutt_write_rfc822_header (tempfp, msg->env, msg->content, 0);
+ fputc ('\n', tempfp); /* tie off the header. */
+
+ mutt_write_mime_body (msg->content, tempfp);
+ if (fclose (tempfp) != 0)
+ {
+ mutt_perror (tempfile);
+ unlink (tempfile);
+ return (-1);
+ }
+
+ /* save a copy of the message, if necessary. */
+ if (*fcc && strcmp ("/dev/null", fcc) != 0)
+ {
+ BODY *tmpbody = msg->content;
+
+ /* check to see if the user wants copies of all attachments */
+ if (msg->content->type == TYPEMULTIPART &&
+ strcmp (msg->content->subtype, "encrypted") &&
+ strcmp (msg->content->subtype, "signed") &&
+ !option (OPTFCCATTACH))
+ msg->content = msg->content->parts;
+
+ mutt_write_fcc (fcc, msg, NULL, 0);
+ msg->content = tmpbody;
+ }
+
+ i = invoke_sendmail (msg->env->to, msg->env->cc, msg->env->bcc,
+ tempfile,
+ (msg->content->encoding == ENC8BIT));
+ return (i ? -1 : 0);
+}
+
+void mutt_bounce_message (HEADER *h, ADDRESS *to)
+{
+ int i;
+ FILE *f;
+ char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
+ MESSAGE *msg;
+
+ if (!h)
+ {
+ for (i=0; i<Context->msgcount; i++)
+ if (Context->hdrs[i]->tagged)
+ mutt_bounce_message (Context->hdrs[i], to);
+ return;
+ }
+
+ if ((msg = mx_open_message (Context, h->msgno)) != NULL)
+ {
+ mutt_mktemp (tempfile);
+ if ((f = safe_fopen (tempfile, "w")) != NULL)
+ {
+ fseek (msg->fp, h->offset, 0);
+ mutt_copy_header (msg->fp, h, f, CH_XMIT | CH_NONEWLINE, NULL);
+ fprintf (f, "Resent-From: %s", Username);
+ if (Fqdn[0] != '@')
+ fprintf (f, "@%s", Fqdn);
+ fprintf (f, "\nResent-%s", mutt_make_date (date));
+ fputs ("Resent-To: ", f);
+ mutt_write_address_list (to, f, 11);
+ fputc ('\n', f);
+ mutt_copy_bytes (msg->fp, f, h->content->length);
+ fclose (f);
+
+ invoke_sendmail (to, NULL, NULL, tempfile, h->content->encoding == ENC8BIT);
+ }
+ mx_close_message (&msg);
+ }
+}
+
+/* given a list of addresses, return a list of unique addresses */
+ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
+{
+ ADDRESS *top = NULL;
+ ADDRESS *tmp;
+
+ if ((top = addr) == NULL)
+ return (NULL);
+ addr = addr->next;
+ top->next = NULL;
+ while (addr)
+ {
+ tmp = top;
+ do {
+ if (addr->mailbox && tmp->mailbox &&
+ !strcasecmp (addr->mailbox, tmp->mailbox))
+ {
+ /* duplicate address, just ignore it */
+ tmp = addr;
+ addr = addr->next;
+ tmp->next = NULL;
+ rfc822_free_address (&tmp);
+ }
+ else if (!tmp->next)
+ {
+ /* unique address. add it to the list */
+ tmp->next = addr;
+ addr = addr->next;
+ tmp = tmp->next;
+ tmp->next = NULL;
+ tmp = NULL; /* so we exit the loop */
+ }
+ else
+ tmp = tmp->next;
+ } while (tmp);
+ }
+
+ return (top);
+}
+
+int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post)
+{
+ CONTEXT f;
+ MESSAGE *msg;
+ char tempfile[_POSIX_PATH_MAX];
+ FILE *tempfp = NULL;
+ int r;
+
+ if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
+ {
+ dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
+ path));
+ return (-1);
+ }
+
+ /* We need to add a Content-Length field to avoid problems where a line in
+ * the message body begins with "From "
+ */
+ if (f.magic == M_MMDF || f.magic == M_MBOX)
+ {
+ mutt_mktemp (tempfile);
+ if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
+ {
+ mutt_perror (tempfile);
+ mx_close_mailbox (&f);
+ return (-1);
+ }
+ }
+
+ hdr->read = 1; /* make sure to put it in the `cur' directory (maildir) */
+ if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
+ {
+ mx_close_mailbox (&f);
+ return (-1);
+ }
+
+ mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, (post ? -1 : 0));
+
+ /* (postponment) if this was a reply of some sort, <msgid> contians the
+ * Message-ID: of message replied to. Save it using a special X-Mutt-
+ * header so it can be picked up if the message is recalled at a later
+ * point in time. This will allow the message to be marked as replied if
+ * the same mailbox is still open.
+ */
+ if (post && msgid)
+ fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
+ fprintf (msg->fp, "Status: RO\n");
+
+
+
+#ifdef _PGPPATH
+ /* (postponment) if the mail is to be signed or encrypted, save this info */
+ if (post && (hdr->pgp & (PGPENCRYPT | PGPSIGN)))
+ {
+ fputs ("Pgp: ", msg->fp);
+ if (hdr->pgp & PGPENCRYPT)
+ fputc ('E', msg->fp);
+ if (hdr->pgp & PGPSIGN)
+ {
+ fputc ('S', msg->fp);
+ if (PgpSignAs && *PgpSignAs)
+ fprintf (msg->fp, "<%s>", PgpSignAs);
+ if (*PgpSignMicalg)
+ fprintf (msg->fp, "M<%s>", PgpSignMicalg);
+ }
+ fputc ('\n', msg->fp);
+ }
+#endif /* _PGPPATH */
+
+
+
+ if (tempfp)
+ {
+ char sasha[LONG_STRING];
+ int lines = 0;
+
+ mutt_write_mime_body (hdr->content, tempfp);
+
+ /* make sure the last line ends with a newline. Emacs doesn't ensure
+ * this will happen, and it can cause problems parsing the mailbox
+ * later.
+ */
+ fseek (tempfp, -1, 2);
+ if (fgetc (tempfp) != '\n')
+ {
+ fseek (tempfp, 0, 2);
+ fputc ('\n', tempfp);
+ }
+
+ fflush (tempfp);
+ if (ferror (tempfp))
+ {
+ dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
+ fclose (tempfp);
+ unlink (tempfile);
+ mx_close_message (&msg);
+ mx_close_mailbox (&f);
+ return -1;
+ }
+
+ /* count the number of lines */
+ rewind (tempfp);
+ while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
+ lines++;
+ fprintf (msg->fp, "Content-Length: %ld\n", (long) ftell (tempfp));
+ fprintf (msg->fp, "Lines: %d\n\n", lines);
+
+ /* copy the body and clean up */
+ rewind (tempfp);
+ r = mutt_copy_stream (tempfp, msg->fp);
+ if (fclose (tempfp) != 0)
+ r = -1;
+ /* if there was an error, leave the temp version */
+ if (!r)
+ unlink (tempfile);
+ }
+ else
+ {
+ fputc ('\n', msg->fp); /* finish off the header */
+ r = mutt_write_mime_body (hdr->content, msg->fp);
+ }
+
+ mx_close_message (&msg);
+ mx_close_mailbox (&f);
+
+ return r;
+}
--- /dev/null
+/* crypto/sha/sha.h */
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#ifndef HEADER_SHA_H
+#define HEADER_SHA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SHA_CBLOCK 64
+#define SHA_LBLOCK 16
+#define SHA_BLOCK 16
+#define SHA_LAST_BLOCK 56
+#define SHA_LENGTH_BLOCK 8
+#define SHA_DIGEST_LENGTH 20
+
+typedef struct SHAstate_st
+ {
+ unsigned long h0,h1,h2,h3,h4;
+ unsigned long Nl,Nh;
+ unsigned long data[SHA_LBLOCK];
+ int num;
+ } SHA_CTX;
+
+#ifndef NOPROTO
+void SHA_Init(SHA_CTX *c);
+void SHA_Update(SHA_CTX *c, unsigned char *data, unsigned long len);
+void SHA_Final(unsigned char *md, SHA_CTX *c);
+unsigned char *SHA(unsigned char *d, unsigned long n,unsigned char *md);
+void SHA1_Init(SHA_CTX *c);
+void SHA1_Update(SHA_CTX *c, unsigned char *data, unsigned long len);
+void SHA1_Final(unsigned char *md, SHA_CTX *c);
+unsigned char *SHA1(unsigned char *d, unsigned long n,unsigned char *md);
+#else
+void SHA_Init();
+void SHA_Update();
+void SHA_Final();
+unsigned char *SHA();
+void SHA1_Init();
+void SHA1_Update();
+void SHA1_Final();
+unsigned char *SHA1();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/* crypto/sha/sha1dgst.c */
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#include <stdio.h>
+#undef SHA_0
+#define SHA_1
+#include "sha.h"
+#include "sha_locl.h"
+
+char *SHA1_version="SHA1 part of SSLeay 0.8.1 19-Jul-1997";
+
+/* Implemented from SHA-1 document - The Secure Hash Algorithm
+ */
+
+#define INIT_DATA_h0 (unsigned long)0x67452301L
+#define INIT_DATA_h1 (unsigned long)0xefcdab89L
+#define INIT_DATA_h2 (unsigned long)0x98badcfeL
+#define INIT_DATA_h3 (unsigned long)0x10325476L
+#define INIT_DATA_h4 (unsigned long)0xc3d2e1f0L
+
+#define K_00_19 0x5a827999L
+#define K_20_39 0x6ed9eba1L
+#define K_40_59 0x8f1bbcdcL
+#define K_60_79 0xca62c1d6L
+
+#ifndef NOPROTO
+static void sha1_block(SHA_CTX *c, register unsigned long *p);
+#else
+static void sha1_block();
+#endif
+
+void SHA1_Init(c)
+SHA_CTX *c;
+ {
+ c->h0=INIT_DATA_h0;
+ c->h1=INIT_DATA_h1;
+ c->h2=INIT_DATA_h2;
+ c->h3=INIT_DATA_h3;
+ c->h4=INIT_DATA_h4;
+ c->Nl=0;
+ c->Nh=0;
+ c->num=0;
+ }
+
+void SHA1_Update(c, data, len)
+SHA_CTX *c;
+register unsigned char *data;
+unsigned long len;
+ {
+ register ULONG *p;
+ int ew,ec,sw,sc;
+ ULONG l;
+
+ if (len == 0) return;
+
+ l=(c->Nl+(len<<3))&0xffffffff;
+ if (l < c->Nl) /* overflow */
+ c->Nh++;
+ c->Nh+=(len>>29);
+ c->Nl=l;
+
+ if (c->num != 0)
+ {
+ p=c->data;
+ sw=c->num>>2;
+ sc=c->num&0x03;
+
+ if ((c->num+len) >= SHA_CBLOCK)
+ {
+ l= p[sw];
+ p_c2nl(data,l,sc);
+ p[sw++]=l;
+ for (; sw<SHA_LBLOCK; sw++)
+ {
+ c2nl(data,l);
+ p[sw]=l;
+ }
+ len-=(SHA_CBLOCK-c->num);
+
+ sha1_block(c,p);
+ c->num=0;
+ /* drop through and do the rest */
+ }
+ else
+ {
+ c->num+=(int)len;
+ if ((sc+len) < 4) /* ugly, add char's to a word */
+ {
+ l= p[sw];
+ p_c2nl_p(data,l,sc,len);
+ p[sw]=l;
+ }
+ else
+ {
+ ew=(c->num>>2);
+ ec=(c->num&0x03);
+ l= p[sw];
+ p_c2nl(data,l,sc);
+ p[sw++]=l;
+ for (; sw < ew; sw++)
+ { c2nl(data,l); p[sw]=l; }
+ if (ec)
+ {
+ c2nl_p(data,l,ec);
+ p[sw]=l;
+ }
+ }
+ return;
+ }
+ }
+ /* we now can process the input data in blocks of SHA_CBLOCK
+ * chars and save the leftovers to c->data. */
+ p=c->data;
+ while (len >= SHA_CBLOCK)
+ {
+#if defined(B_ENDIAN) || defined(L_ENDIAN)
+ memcpy(p,data,SHA_CBLOCK);
+ data+=SHA_CBLOCK;
+#ifdef L_ENDIAN
+ for (sw=(SHA_LBLOCK/4); sw; sw--)
+ {
+ Endian_Reverse32(p[0]);
+ Endian_Reverse32(p[1]);
+ Endian_Reverse32(p[2]);
+ Endian_Reverse32(p[3]);
+ p+=4;
+ }
+#endif
+#else
+ for (sw=(SHA_BLOCK/4); sw; sw--)
+ {
+ c2nl(data,l); *(p++)=l;
+ c2nl(data,l); *(p++)=l;
+ c2nl(data,l); *(p++)=l;
+ c2nl(data,l); *(p++)=l;
+ }
+#endif
+ p=c->data;
+ sha1_block(c,p);
+ len-=SHA_CBLOCK;
+ }
+ ec=(int)len;
+ c->num=ec;
+ ew=(ec>>2);
+ ec&=0x03;
+
+ for (sw=0; sw < ew; sw++)
+ { c2nl(data,l); p[sw]=l; }
+ c2nl_p(data,l,ec);
+ p[sw]=l;
+ }
+
+static void sha1_block(c, X)
+SHA_CTX *c;
+register unsigned long *X;
+ {
+ register ULONG A,B,C,D,E,T;
+
+ A=c->h0;
+ B=c->h1;
+ C=c->h2;
+ D=c->h3;
+ E=c->h4;
+
+ BODY_00_15( 0,A,B,C,D,E,T);
+ BODY_00_15( 1,T,A,B,C,D,E);
+ BODY_00_15( 2,E,T,A,B,C,D);
+ BODY_00_15( 3,D,E,T,A,B,C);
+ BODY_00_15( 4,C,D,E,T,A,B);
+ BODY_00_15( 5,B,C,D,E,T,A);
+ BODY_00_15( 6,A,B,C,D,E,T);
+ BODY_00_15( 7,T,A,B,C,D,E);
+ BODY_00_15( 8,E,T,A,B,C,D);
+ BODY_00_15( 9,D,E,T,A,B,C);
+ BODY_00_15(10,C,D,E,T,A,B);
+ BODY_00_15(11,B,C,D,E,T,A);
+ BODY_00_15(12,A,B,C,D,E,T);
+ BODY_00_15(13,T,A,B,C,D,E);
+ BODY_00_15(14,E,T,A,B,C,D);
+ BODY_00_15(15,D,E,T,A,B,C);
+ BODY_16_19(16,C,D,E,T,A,B);
+ BODY_16_19(17,B,C,D,E,T,A);
+ BODY_16_19(18,A,B,C,D,E,T);
+ BODY_16_19(19,T,A,B,C,D,E);
+
+ BODY_20_39(20,E,T,A,B,C,D);
+ BODY_20_39(21,D,E,T,A,B,C);
+ BODY_20_39(22,C,D,E,T,A,B);
+ BODY_20_39(23,B,C,D,E,T,A);
+ BODY_20_39(24,A,B,C,D,E,T);
+ BODY_20_39(25,T,A,B,C,D,E);
+ BODY_20_39(26,E,T,A,B,C,D);
+ BODY_20_39(27,D,E,T,A,B,C);
+ BODY_20_39(28,C,D,E,T,A,B);
+ BODY_20_39(29,B,C,D,E,T,A);
+ BODY_20_39(30,A,B,C,D,E,T);
+ BODY_20_39(31,T,A,B,C,D,E);
+ BODY_20_39(32,E,T,A,B,C,D);
+ BODY_20_39(33,D,E,T,A,B,C);
+ BODY_20_39(34,C,D,E,T,A,B);
+ BODY_20_39(35,B,C,D,E,T,A);
+ BODY_20_39(36,A,B,C,D,E,T);
+ BODY_20_39(37,T,A,B,C,D,E);
+ BODY_20_39(38,E,T,A,B,C,D);
+ BODY_20_39(39,D,E,T,A,B,C);
+
+ BODY_40_59(40,C,D,E,T,A,B);
+ BODY_40_59(41,B,C,D,E,T,A);
+ BODY_40_59(42,A,B,C,D,E,T);
+ BODY_40_59(43,T,A,B,C,D,E);
+ BODY_40_59(44,E,T,A,B,C,D);
+ BODY_40_59(45,D,E,T,A,B,C);
+ BODY_40_59(46,C,D,E,T,A,B);
+ BODY_40_59(47,B,C,D,E,T,A);
+ BODY_40_59(48,A,B,C,D,E,T);
+ BODY_40_59(49,T,A,B,C,D,E);
+ BODY_40_59(50,E,T,A,B,C,D);
+ BODY_40_59(51,D,E,T,A,B,C);
+ BODY_40_59(52,C,D,E,T,A,B);
+ BODY_40_59(53,B,C,D,E,T,A);
+ BODY_40_59(54,A,B,C,D,E,T);
+ BODY_40_59(55,T,A,B,C,D,E);
+ BODY_40_59(56,E,T,A,B,C,D);
+ BODY_40_59(57,D,E,T,A,B,C);
+ BODY_40_59(58,C,D,E,T,A,B);
+ BODY_40_59(59,B,C,D,E,T,A);
+
+ BODY_60_79(60,A,B,C,D,E,T);
+ BODY_60_79(61,T,A,B,C,D,E);
+ BODY_60_79(62,E,T,A,B,C,D);
+ BODY_60_79(63,D,E,T,A,B,C);
+ BODY_60_79(64,C,D,E,T,A,B);
+ BODY_60_79(65,B,C,D,E,T,A);
+ BODY_60_79(66,A,B,C,D,E,T);
+ BODY_60_79(67,T,A,B,C,D,E);
+ BODY_60_79(68,E,T,A,B,C,D);
+ BODY_60_79(69,D,E,T,A,B,C);
+ BODY_60_79(70,C,D,E,T,A,B);
+ BODY_60_79(71,B,C,D,E,T,A);
+ BODY_60_79(72,A,B,C,D,E,T);
+ BODY_60_79(73,T,A,B,C,D,E);
+ BODY_60_79(74,E,T,A,B,C,D);
+ BODY_60_79(75,D,E,T,A,B,C);
+ BODY_60_79(76,C,D,E,T,A,B);
+ BODY_60_79(77,B,C,D,E,T,A);
+ BODY_60_79(78,A,B,C,D,E,T);
+ BODY_60_79(79,T,A,B,C,D,E);
+
+ c->h0=(c->h0+E)&0xffffffff;
+ c->h1=(c->h1+T)&0xffffffff;
+ c->h2=(c->h2+A)&0xffffffff;
+ c->h3=(c->h3+B)&0xffffffff;
+ c->h4=(c->h4+C)&0xffffffff;
+ }
+
+void SHA1_Final(md, c)
+unsigned char *md;
+SHA_CTX *c;
+ {
+ register int i,j;
+ register ULONG l;
+ register ULONG *p;
+ static unsigned char end[4]={0x80,0x00,0x00,0x00};
+ unsigned char *cp=end;
+
+ /* c->num should definitly have room for at least one more byte. */
+ p=c->data;
+ j=c->num;
+ i=j>>2;
+#ifdef PURIFY
+ if ((j&0x03) == 0) p[i]=0;
+#endif
+ l=p[i];
+ p_c2nl(cp,l,j&0x03);
+ p[i]=l;
+ i++;
+ /* i is the next 'undefined word' */
+ if (c->num >= SHA_LAST_BLOCK)
+ {
+ for (; i<SHA_LBLOCK; i++)
+ p[i]=0;
+ sha1_block(c,p);
+ i=0;
+ }
+ for (; i<(SHA_LBLOCK-2); i++)
+ p[i]=0;
+ p[SHA_LBLOCK-2]=c->Nh;
+ p[SHA_LBLOCK-1]=c->Nl;
+ sha1_block(c,p);
+ cp=md;
+ l=c->h0; nl2c(l,cp);
+ l=c->h1; nl2c(l,cp);
+ l=c->h2; nl2c(l,cp);
+ l=c->h3; nl2c(l,cp);
+ l=c->h4; nl2c(l,cp);
+
+ /* clear stuff, sha1_block may be leaving some stuff on the stack
+ * but I'm not worried :-) */
+ c->num=0;
+/* memset((char *)&c,0,sizeof(c));*/
+ }
+
+#ifdef undef
+int printit(l)
+unsigned long *l;
+ {
+ int i,ii;
+
+ for (i=0; i<2; i++)
+ {
+ for (ii=0; ii<8; ii++)
+ {
+ fprintf(stderr,"%08lx ",l[i*8+ii]);
+ }
+ fprintf(stderr,"\n");
+ }
+ }
+#endif
--- /dev/null
+/* crypto/sha/sha_locl.h */
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef undef
+/* one or the other needs to be defined */
+#ifndef SHA_1 /* FIPE 180-1 */
+#define SHA_0 /* FIPS 180 */
+#endif
+#endif
+
+#define ULONG unsigned long
+#define UCHAR unsigned char
+#define UINT unsigned int
+
+#ifdef NOCONST
+#define const
+#endif
+
+#undef c2nl
+#define c2nl(c,l) (l =(((unsigned long)(*((c)++)))<<24), \
+ l|=(((unsigned long)(*((c)++)))<<16), \
+ l|=(((unsigned long)(*((c)++)))<< 8), \
+ l|=(((unsigned long)(*((c)++))) ))
+
+#undef p_c2nl
+#define p_c2nl(c,l,n) { \
+ switch (n) { \
+ case 0: l =((unsigned long)(*((c)++)))<<24; \
+ case 1: l|=((unsigned long)(*((c)++)))<<16; \
+ case 2: l|=((unsigned long)(*((c)++)))<< 8; \
+ case 3: l|=((unsigned long)(*((c)++))); \
+ } \
+ }
+
+#undef c2nl_p
+/* NOTE the pointer is not incremented at the end of this */
+#define c2nl_p(c,l,n) { \
+ l=0; \
+ (c)+=n; \
+ switch (n) { \
+ case 3: l =((unsigned long)(*(--(c))))<< 8; \
+ case 2: l|=((unsigned long)(*(--(c))))<<16; \
+ case 1: l|=((unsigned long)(*(--(c))))<<24; \
+ } \
+ }
+
+#undef p_c2nl_p
+#define p_c2nl_p(c,l,sc,len) { \
+ switch (sc) \
+ { \
+ case 0: l =((unsigned long)(*((c)++)))<<24; \
+ if (--len == 0) break; \
+ case 1: l|=((unsigned long)(*((c)++)))<<16; \
+ if (--len == 0) break; \
+ case 2: l|=((unsigned long)(*((c)++)))<< 8; \
+ } \
+ }
+
+#undef nl2c
+#define nl2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \
+ *((c)++)=(unsigned char)(((l)>>16)&0xff), \
+ *((c)++)=(unsigned char)(((l)>> 8)&0xff), \
+ *((c)++)=(unsigned char)(((l) )&0xff))
+
+/* I have taken some of this code from my MD5 implementation */
+
+#undef ROTATE
+#if defined(WIN32)
+#define ROTATE(a,n) _lrotl(a,n)
+#else
+#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
+#endif
+
+/* A nice byte order reversal from Wei Dai <weidai@eskimo.com> */
+#if defined(WIN32)
+/* 5 instructions with rotate instruction, else 9 */
+#define Endian_Reverse32(a) \
+ { \
+ unsigned long l=(a); \
+ (a)=((ROTATE(l,8)&0x00FF00FF)|(ROTATE(l,24)&0xFF00FF00)); \
+ }
+#else
+/* 6 instructions with rotate instruction, else 8 */
+#define Endian_Reverse32(a) \
+ { \
+ unsigned long l=(a); \
+ l=(((l&0xFF00FF00)>>8L)|((l&0x00FF00FF)<<8L)); \
+ (a)=ROTATE(l,16L); \
+ }
+#endif
+
+/* As pointed out by Wei Dai <weidai@eskimo.com>, F() below can be
+ * simplified to the code in F_00_19. Wei attributes these optimisations
+ * to Peter Gutmann's SHS code, and he attributes it to Rich Schroeppel.
+ * #define F(x,y,z) (((x) & (y)) | ((~(x)) & (z)))
+ * I've just become aware of another tweak to be made, again from Wei Dai,
+ * in F_40_59, (x&a)|(y&a) -> (x|y)&a
+ */
+#define F_00_19(b,c,d) ((((c) ^ (d)) & (b)) ^ (d))
+#define F_20_39(b,c,d) ((b) ^ (c) ^ (d))
+#define F_40_59(b,c,d) (((b) & (c)) | (((b)|(c)) & (d)))
+#define F_60_79(b,c,d) F_20_39(b,c,d)
+
+#ifdef SHA_0
+#undef Xupdate
+#define Xupdate(a,i) \
+ X[(i)&0x0f]=(a)=\
+ (X[(i)&0x0f]^X[((i)+2)&0x0f]^X[((i)+8)&0x0f]^X[((i)+13)&0x0f]);
+#endif
+#ifdef SHA_1
+#undef Xupdate
+#define Xupdate(a,i) \
+ (a)=(X[(i)&0x0f]^X[((i)+2)&0x0f]^X[((i)+8)&0x0f]^X[((i)+13)&0x0f]); \
+ X[(i)&0x0f]=(a)=ROTATE((a),1);
+#endif
+
+#define BODY_00_15(i,a,b,c,d,e,f) \
+ (f)=X[i]+(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \
+ (b)=ROTATE((b),30);
+
+#define BODY_16_19(i,a,b,c,d,e,f) \
+ Xupdate(f,i); \
+ (f)+=(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \
+ (b)=ROTATE((b),30);
+
+#define BODY_20_39(i,a,b,c,d,e,f) \
+ Xupdate(f,i); \
+ (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \
+ (b)=ROTATE((b),30);
+
+#define BODY_40_59(i,a,b,c,d,e,f) \
+ Xupdate(f,i); \
+ (f)+=(e)+K_40_59+ROTATE((a),5)+F_40_59((b),(c),(d)); \
+ (b)=ROTATE((b),30);
+
+#define BODY_60_79(i,a,b,c,d,e,f) \
+ Xupdate(f,i); \
+ (f)=X[(i)&0x0f]+(e)+K_60_79+ROTATE((a),5)+F_60_79((b),(c),(d)); \
+ (b)=ROTATE((b),30);
+
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+
+static sigset_t Sigset;
+static int IsEndwin = 0;
+
+static pid_t *PidList = NULL;
+static int PidListLen = 0;
+
+/* Attempt to catch "ordinary" signals and shut down gracefully. */
+RETSIGTYPE mutt_exit_handler (int sig)
+{
+ curs_set (1);
+ endwin (); /* just to be safe */
+#if SYS_SIGLIST_DECLARED
+ printf("Caught %s... Exiting.\n", sys_siglist[sig]);
+#else
+#if (__sun__ && __svr4__)
+ printf("Caught %s... Exiting.\n", _sys_siglist[sig]);
+#else
+ printf("Caught signal %d... Exiting.\n", sig);
+#endif
+#endif
+ exit (0);
+}
+
+static void reap_children (void)
+{
+ int i, flag;
+
+ /* at this point we don't know which child died */
+ for (i = 0; i < PidListLen; i++)
+ {
+ if (PidList[i])
+ {
+ /* try to reap child "i" */
+ flag = 0;
+ while (waitpid (PidList[i], NULL, WNOHANG) > 0)
+ flag = 1;
+ /* remove child from list if reaped */
+ if (flag)
+ PidList[i] = 0;
+ /* don't break here */
+ }
+ }
+}
+
+void mutt_add_child_pid (pid_t pid)
+{
+ int i;
+
+ for (i = 0; i < PidListLen; i++)
+ {
+ if (PidList[i] == 0)
+ {
+ PidList[i] = pid;
+ break;
+ }
+ }
+ if (i >= PidListLen)
+ {
+ /* quite a few children around... */
+ safe_realloc ((void **) &PidList, (PidListLen += 2) * sizeof (pid_t));
+ PidList[i++] = pid;
+ for (; i < PidListLen; i++)
+ PidList[i] = 0;
+ }
+}
+
+RETSIGTYPE sighandler (int sig)
+{
+ switch (sig)
+ {
+ case SIGTSTP: /* user requested a suspend */
+ if(!option(OPTSUSPEND))
+ break;
+ IsEndwin = isendwin ();
+ curs_set (1);
+ if (!IsEndwin)
+ endwin ();
+ kill (0, SIGSTOP);
+
+ case SIGCONT:
+ if (!IsEndwin)
+ refresh ();
+ mutt_curs_set (-1);
+ break;
+
+#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
+ case SIGWINCH:
+ Signals |= S_SIGWINCH;
+ break;
+#endif
+ case SIGINT:
+ Signals |= S_INTERRUPT;
+ break;
+
+ case SIGCHLD:
+ reap_children ();
+ break;
+ }
+}
+
+#ifdef USE_SLANG_CURSES
+int mutt_intr_hook (void)
+{
+ return (-1);
+}
+#endif /* USE_SLANG_CURSES */
+
+void mutt_signal_init (void)
+{
+ struct sigaction act;
+
+ memset (&act, 0, sizeof (struct sigaction));
+
+ act.sa_handler = SIG_IGN;
+ sigaction (SIGPIPE, &act, NULL);
+
+ act.sa_handler = mutt_exit_handler;
+ sigaction (SIGTERM, &act, NULL);
+ sigaction (SIGHUP, &act, NULL);
+
+ act.sa_handler = sighandler;
+#ifdef SA_INTERRUPT
+ /* POSIX.1 uses SA_RESTART, but SunOS 4.x uses this instead */
+ act.sa_flags = SA_INTERRUPT;
+#endif
+ sigaction (SIGCONT, &act, NULL);
+ sigaction (SIGINT, &act, NULL);
+
+ /* SIGTSTP is the one signal in which we want to restart a system call if it
+ * was interrupted in progress. This is especially important if we are in
+ * the middle of a system() call, like if the user is editing a message.
+ * Otherwise, the system() will exit when SIGCONT is received and Mutt will
+ * resume even though the subprocess may not be finished.
+ */
+#ifdef SA_RESTART
+ act.sa_flags = SA_RESTART;
+#else
+ act.sa_flags = 0;
+#endif
+ sigaction (SIGTSTP, &act, NULL);
+
+#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
+ sigaction (SIGWINCH, &act, NULL);
+#endif
+
+ /* we don't want to mess with stopped children */
+ act.sa_flags |= SA_NOCLDSTOP;
+ sigaction (SIGCHLD, &act, NULL);
+
+#ifdef USE_SLANG_CURSES
+ /* This bit of code is required because of the implementation of
+ * SLcurses_wgetch(). If a signal is received (like SIGWINCH) when we
+ * are in blocking mode, SLsys_getkey() will not return an error unless
+ * a handler function is defined and it returns -1. This is needed so
+ * that if the user resizes the screen while at a prompt, it will just
+ * abort and go back to the main-menu.
+ */
+ SLang_getkey_intr_hook = mutt_intr_hook;
+#endif
+}
+
+/* signals which are important to block while doing critical ops */
+void mutt_block_signals (void)
+{
+ if (!option (OPTSIGNALSBLOCKED))
+ {
+ sigemptyset (&Sigset);
+ sigaddset (&Sigset, SIGINT);
+ sigaddset (&Sigset, SIGWINCH);
+ sigaddset (&Sigset, SIGHUP);
+ sigaddset (&Sigset, SIGTERM);
+ sigaddset (&Sigset, SIGTSTP);
+ sigprocmask (SIG_BLOCK, &Sigset, 0);
+ set_option (OPTSIGNALSBLOCKED);
+ }
+}
+
+/* restore the previous signal mask */
+void mutt_unblock_signals (void)
+{
+ if (option (OPTSIGNALSBLOCKED))
+ {
+ sigprocmask (SIG_UNBLOCK, &Sigset, 0);
+ unset_option (OPTSIGNALSBLOCKED);
+ }
+}
+
+void mutt_block_signals_system (void)
+{
+ struct sigaction sa;
+
+ if (! option (OPTSIGNALSBLOCKED))
+ {
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ sigaction (SIGINT, &sa, NULL);
+ sigaction (SIGQUIT, &sa, NULL);
+ sigemptyset (&Sigset);
+ sigaddset (&Sigset, SIGCHLD);
+ sigprocmask (SIG_BLOCK, &Sigset, 0);
+ set_option (OPTSIGNALSBLOCKED);
+ }
+}
+
+void mutt_unblock_signals_system (int catch)
+{
+ struct sigaction sa;
+
+ if (option (OPTSIGNALSBLOCKED))
+ {
+ sa.sa_flags = 0;
+ sa.sa_handler = mutt_exit_handler;
+ sigaction (SIGQUIT, &sa, NULL);
+ if (catch)
+ sa.sa_handler = sighandler;
+ sigaction (SIGINT, &sa, NULL);
+ sigprocmask (SIG_UNBLOCK, &Sigset, NULL);
+ unset_option (OPTSIGNALSBLOCKED);
+ }
+}
--- /dev/null
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything
+ * from the normal C string format, at least as far as I can tell from
+ * the Solaris 2.5 printf(3S) man page.
+ *
+ * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ * Ok, added some minimal floating point support, which means this
+ * probably requires libm on most operating systems. Don't yet
+ * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
+ * was pretty badly broken, it just wasn't being exercised in ways
+ * which showed it, so that's been fixed. Also, formated the code
+ * to mutt conventions, and removed dead code left over from the
+ * original. Also, there is now a builtin-test, just compile with:
+ * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ * and run snprintf for results.
+ *
+ * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ * The PGP code was using unsigned hexadecimal formats.
+ * Unfortunately, unsigned formats simply didn't work.
+ *
+ * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ * The original code assumed that both snprintf() and vsnprintf() were
+ * missing. Some systems only have snprintf() but not vsnprintf(), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ **************************************************************/
+
+#include "config.h"
+
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
+
+#include <ctype.h>
+#include <sys/types.h>
+
+/* Define this as a fall through, HAVE_STDARG_H is probably already set */
+
+#define HAVE_VARARGS_H
+
+/* varargs declarations: */
+
+#if defined(HAVE_STDARG_H)
+# include <stdarg.h>
+# define HAVE_STDARGS /* let's hope that works everywhere (mj) */
+# define VA_LOCAL_DECL va_list ap
+# define VA_START(f) va_start(ap, f)
+# define VA_SHIFT(v,t) ; /* no-op for ANSI */
+# define VA_END va_end(ap)
+#else
+# if defined(HAVE_VARARGS_H)
+# include <varargs.h>
+# undef HAVE_STDARGS
+# define VA_LOCAL_DECL va_list ap
+# define VA_START(f) va_start(ap) /* f is ignored! */
+# define VA_SHIFT(v,t) v = va_arg(ap,t)
+# define VA_END va_end(ap)
+# else
+/*XX ** NO VARARGS ** XX*/
+# endif
+#endif
+
+/*int snprintf (char *str, size_t count, const char *fmt, ...);*/
+/*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
+
+static void dopr (char *buffer, size_t maxlen, const char *format,
+ va_list args);
+static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max);
+static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags);
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ long double fvalue, int min, int max, int flags);
+static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS 1
+#define DP_S_MIN 2
+#define DP_S_DOT 3
+#define DP_S_MAX 4
+#define DP_S_MOD 5
+#define DP_S_CONV 6
+#define DP_S_DONE 7
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UP (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+
+/* Conversion Flags */
+#define DP_C_SHORT 1
+#define DP_C_LONG 2
+#define DP_C_LDOUBLE 3
+
+#define char_to_int(p) (p - '0')
+#define MAX(p,q) ((p >= q) ? p : q)
+
+static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
+{
+ char ch;
+ long value;
+ long double fvalue;
+ char *strvalue;
+ int min;
+ int max;
+ int state;
+ int flags;
+ int cflags;
+ size_t currlen;
+
+ state = DP_S_DEFAULT;
+ currlen = flags = cflags = min = 0;
+ max = -1;
+ ch = *format++;
+
+ while (state != DP_S_DONE)
+ {
+ if ((ch == '\0') || (currlen >= maxlen))
+ state = DP_S_DONE;
+
+ switch(state)
+ {
+ case DP_S_DEFAULT:
+ if (ch == '%')
+ state = DP_S_FLAGS;
+ else
+ dopr_outch (buffer, &currlen, maxlen, ch);
+ ch = *format++;
+ break;
+ case DP_S_FLAGS:
+ switch (ch)
+ {
+ case '-':
+ flags |= DP_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ flags |= DP_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ flags |= DP_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ flags |= DP_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ flags |= DP_F_ZERO;
+ ch = *format++;
+ break;
+ default:
+ state = DP_S_MIN;
+ break;
+ }
+ break;
+ case DP_S_MIN:
+ if (isdigit(ch))
+ {
+ min = 10*min + char_to_int (ch);
+ ch = *format++;
+ }
+ else if (ch == '*')
+ {
+ min = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_DOT;
+ }
+ else
+ state = DP_S_DOT;
+ break;
+ case DP_S_DOT:
+ if (ch == '.')
+ {
+ state = DP_S_MAX;
+ ch = *format++;
+ }
+ else
+ state = DP_S_MOD;
+ break;
+ case DP_S_MAX:
+ if (isdigit(ch))
+ {
+ if (max < 0)
+ max = 0;
+ max = 10*max + char_to_int (ch);
+ ch = *format++;
+ }
+ else if (ch == '*')
+ {
+ max = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_MOD;
+ }
+ else
+ state = DP_S_MOD;
+ break;
+ case DP_S_MOD:
+ /* Currently, we don't support Long Long, bummer */
+ switch (ch)
+ {
+ case 'h':
+ cflags = DP_C_SHORT;
+ ch = *format++;
+ break;
+ case 'l':
+ cflags = DP_C_LONG;
+ ch = *format++;
+ break;
+ case 'L':
+ cflags = DP_C_LDOUBLE;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ state = DP_S_CONV;
+ break;
+ case DP_S_CONV:
+ switch (ch)
+ {
+ case 'd':
+ case 'i':
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, short int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, long int);
+ else
+ value = va_arg (args, int);
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'o':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned short int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else
+ value = va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+ break;
+ case 'u':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned short int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else
+ value = va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'X':
+ flags |= DP_F_UP;
+ case 'x':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned short int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, unsigned long int);
+ else
+ value = va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+ break;
+ case 'f':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, long double);
+ else
+ fvalue = va_arg (args, double);
+ /* um, floating point? */
+ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'E':
+ flags |= DP_F_UP;
+ case 'e':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, long double);
+ else
+ fvalue = va_arg (args, double);
+ break;
+ case 'G':
+ flags |= DP_F_UP;
+ case 'g':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, long double);
+ else
+ fvalue = va_arg (args, double);
+ break;
+ case 'c':
+ dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+ break;
+ case 's':
+ strvalue = va_arg (args, char *);
+ if (max < 0)
+ max = maxlen; /* ie, no max */
+ fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+ break;
+ case 'p':
+ strvalue = va_arg (args, void *);
+ fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
+ break;
+ case 'n':
+ if (cflags == DP_C_SHORT)
+ {
+ short int *num;
+ num = va_arg (args, short int *);
+ *num = currlen;
+ }
+ else if (cflags == DP_C_LONG)
+ {
+ long int *num;
+ num = va_arg (args, long int *);
+ *num = currlen;
+ }
+ else
+ {
+ int *num;
+ num = va_arg (args, int *);
+ *num = currlen;
+ }
+ break;
+ case '%':
+ dopr_outch (buffer, &currlen, maxlen, ch);
+ break;
+ case 'w':
+ /* not supported yet, treat as next char */
+ ch = *format++;
+ break;
+ default:
+ /* Unknown, skip */
+ break;
+ }
+ ch = *format++;
+ state = DP_S_DEFAULT;
+ flags = cflags = min = 0;
+ max = -1;
+ break;
+ case DP_S_DONE:
+ break;
+ default:
+ /* hmm? */
+ break; /* some picky compilers need this */
+ }
+ }
+ if (currlen < maxlen - 1)
+ buffer[currlen] = '\0';
+ else
+ buffer[maxlen - 1] = '\0';
+}
+
+static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max)
+{
+ int padlen, strln; /* amount to pad */
+ int cnt = 0;
+
+ if (value == 0)
+ {
+ value = "<NULL>";
+ }
+
+ for (strln = 0; value[strln]; ++strln); /* strlen */
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ while ((padlen > 0) && (cnt < max))
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ ++cnt;
+ }
+ while (*value && (cnt < max))
+ {
+ dopr_outch (buffer, currlen, maxlen, *value++);
+ ++cnt;
+ }
+ while ((padlen < 0) && (cnt < max))
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ ++cnt;
+ }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags)
+{
+ int signvalue = 0;
+ unsigned long uvalue;
+ char convert[20];
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ int caps = 0;
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if(!(flags & DP_F_UNSIGNED))
+ {
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ }
+ else
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+ do {
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")
+ [uvalue % (unsigned)base ];
+ uvalue = (uvalue / (unsigned)base );
+ } while(uvalue && (place < 20));
+ if (place == 20) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO)
+ {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place));
+#endif
+
+ /* Spaces */
+ while (spadlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0)
+ {
+ while (zpadlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0)
+ dopr_outch (buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+}
+
+static long double abs_val (long double value)
+{
+ long double result = value;
+
+ if (value < 0)
+ result = -value;
+
+ return result;
+}
+
+static long double pow10 (int exp)
+{
+ long double result = 1;
+
+ while (exp)
+ {
+ result *= 10;
+ exp--;
+ }
+
+ return result;
+}
+
+static long round (long double value)
+{
+ long intpart;
+
+ intpart = value;
+ value = value - intpart;
+ if (value >= 0.5)
+ intpart++;
+
+ return intpart;
+}
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ long double fvalue, int min, int max, int flags)
+{
+ int signvalue = 0;
+ long double ufvalue;
+ char iconvert[20];
+ char fconvert[20];
+ int iplace = 0;
+ int fplace = 0;
+ int padlen = 0; /* amount to pad */
+ int zpadlen = 0;
+ int caps = 0;
+ long intpart;
+ long fracpart;
+
+ /*
+ * AIX manpage says the default is 0, but Solaris says the default
+ * is 6, and sprintf on AIX defaults to 6
+ */
+ if (max < 0)
+ max = 6;
+
+ ufvalue = abs_val (fvalue);
+
+ if (fvalue < 0)
+ signvalue = '-';
+ else
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+
+#if 0
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+ intpart = ufvalue;
+
+ /*
+ * Sorry, we only support 9 digits past the decimal because of our
+ * conversion method
+ */
+ if (max > 9)
+ max = 9;
+
+ /* We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of 10
+ */
+ fracpart = round ((pow10 (max)) * (ufvalue - intpart));
+
+ if (fracpart >= pow10 (max))
+ {
+ intpart++;
+ fracpart -= pow10 (max);
+ }
+
+#ifdef DEBUG_SNPRINTF
+ dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
+#endif
+
+ /* Convert integer part */
+ do {
+ iconvert[iplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
+ intpart = (intpart / 10);
+ } while(intpart && (iplace < 20));
+ if (iplace == 20) iplace--;
+ iconvert[iplace] = 0;
+
+ /* Convert fractional part */
+ do {
+ fconvert[fplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
+ fracpart = (fracpart / 10);
+ } while(fracpart && (fplace < 20));
+ if (fplace == 20) fplace--;
+ fconvert[fplace] = 0;
+
+ /* -1 for decimal point, another -1 if we are printing a sign */
+ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
+ zpadlen = max - fplace;
+ if (zpadlen < 0)
+ zpadlen = 0;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justifty */
+
+ if ((flags & DP_F_ZERO) && (padlen > 0))
+ {
+ if (signvalue)
+ {
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --padlen;
+ }
+ }
+ while (padlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ while (iplace > 0)
+ dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+ /*
+ * Decimal point. This should probably use locale to find the correct
+ * char to print out.
+ */
+ dopr_outch (buffer, currlen, maxlen, '.');
+
+ while (fplace > 0)
+ dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+
+ while (zpadlen > 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+
+ while (padlen < 0)
+ {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+ if (*currlen < maxlen)
+ buffer[(*currlen)++] = c;
+}
+#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
+
+#ifndef HAVE_VSNPRINTF
+int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+ str[0] = 0;
+ dopr(str, count, fmt, args);
+ return(strlen(str));
+}
+#endif /* !HAVE_VSNPRINTF */
+
+#ifndef HAVE_SNPRINTF
+/* VARARGS3 */
+#ifdef HAVE_STDARGS
+int snprintf (char *str,size_t count,const char *fmt,...)
+#else
+int snprintf (va_alist) va_dcl
+#endif
+{
+#ifndef HAVE_STDARGS
+ char *str;
+ size_t count;
+ char *fmt;
+#endif
+ VA_LOCAL_DECL;
+
+ VA_START (fmt);
+ VA_SHIFT (str, char *);
+ VA_SHIFT (count, size_t );
+ VA_SHIFT (fmt, char *);
+ (void) vsnprintf(str, count, fmt, ap);
+ VA_END;
+ return(strlen(str));
+}
+
+#ifdef TEST_SNPRINTF
+#ifndef LONG_STRING
+#define LONG_STRING 1024
+#endif
+int main (void)
+{
+ char buf1[LONG_STRING];
+ char buf2[LONG_STRING];
+ char *fp_fmt[] = {
+ "%-1.5f",
+ "%1.5f",
+ "%123.9f",
+ "%10.5f",
+ "% 10.5f",
+ "%+22.9f",
+ "%+4.9f",
+ "%01.3f",
+ "%4f",
+ "%3.1f",
+ "%3.2f",
+ NULL
+ };
+ double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
+ 0.9996, 1.996, 4.136, 0};
+ char *int_fmt[] = {
+ "%-1.5d",
+ "%1.5d",
+ "%123.9d",
+ "%5.5d",
+ "%10.5d",
+ "% 10.5d",
+ "%+22.33d",
+ "%01.3d",
+ "%4d",
+ NULL
+ };
+ long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
+ int x, y;
+ int fail = 0;
+ int num = 0;
+
+ printf ("Testing snprintf format codes against system sprintf...\n");
+
+ for (x = 0; fp_fmt[x] != NULL ; x++)
+ for (y = 0; fp_nums[y] != 0 ; y++)
+ {
+ snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
+ sprintf (buf2, fp_fmt[x], fp_nums[y]);
+ if (strcmp (buf1, buf2))
+ {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
+ fp_fmt[x], buf1, buf2);
+ fail++;
+ }
+ num++;
+ }
+
+ for (x = 0; int_fmt[x] != NULL ; x++)
+ for (y = 0; int_nums[y] != 0 ; y++)
+ {
+ snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
+ sprintf (buf2, int_fmt[x], int_nums[y]);
+ if (strcmp (buf1, buf2))
+ {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
+ int_fmt[x], buf1, buf2);
+ fail++;
+ }
+ num++;
+ }
+ printf ("%d tests failed out of %d.\n", fail, num);
+}
+#endif /* SNPRINTF_TEST */
+
+#endif /* !HAVE_SNPRINTF */
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "sort.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#define SORTCODE(x) (Sort & SORT_REVERSE) ? -(x) : x
+
+/* function to use as discriminator when normal sort method is equal */
+static sort_t *AuxSort = NULL;
+
+#define AUXSORT(code,a,b) if (!code && AuxSort && !option(OPTAUXSORT)) { \
+ set_option(OPTAUXSORT); \
+ code = AuxSort(a,b); \
+ unset_option(OPTAUXSORT); \
+}
+
+int compare_score (const void *a, const void *b)
+{
+ HEADER **pa = (HEADER **) a;
+ HEADER **pb = (HEADER **) b;
+ int result = (*pb)->score - (*pa)->score; /* note that this is reverse */
+ AUXSORT(result,a,b);
+ return (SORTCODE (result));
+}
+
+int compare_size (const void *a, const void *b)
+{
+ HEADER **pa = (HEADER **) a;
+ HEADER **pb = (HEADER **) b;
+ int result = (*pa)->content->length - (*pb)->content->length;
+ AUXSORT(result,a,b);
+ return (SORTCODE (result));
+}
+
+int compare_date_sent (const void *a, const void *b)
+{
+ HEADER **pa = (HEADER **) a;
+ HEADER **pb = (HEADER **) b;
+ int result = (*pa)->date_sent - (*pb)->date_sent;
+ AUXSORT(result,a,b);
+ return (SORTCODE (result));
+}
+
+int compare_subject (const void *a, const void *b)
+{
+ HEADER **pa = (HEADER **) a;
+ HEADER **pb = (HEADER **) b;
+ int rc;
+
+ if (!(*pa)->env->real_subj)
+ {
+ if (!(*pb)->env->real_subj)
+ rc = compare_date_sent (pa, pb);
+ else
+ rc = -1;
+ }
+ else if (!(*pb)->env->real_subj)
+ rc = 1;
+ else
+ rc = strcasecmp ((*pa)->env->real_subj, (*pb)->env->real_subj);
+ AUXSORT(rc,a,b);
+ return (SORTCODE (rc));
+}
+
+char *mutt_get_name (ADDRESS *a)
+{
+ ADDRESS *ali;
+
+ if (a)
+ {
+ if (option (OPTREVALIAS) && (ali = alias_reverse_lookup (a)) && ali->personal)
+ return ali->personal;
+ else if (a->personal)
+ return a->personal;
+ else if (a->mailbox)
+ return (a->mailbox);
+ }
+ /* don't return NULL to avoid segfault when printing/comparing */
+ return ("");
+}
+
+int compare_to (const void *a, const void *b)
+{
+ HEADER **ppa = (HEADER **) a;
+ HEADER **ppb = (HEADER **) b;
+ char *fa, *fb;
+ int result;
+
+ fa = mutt_get_name ((*ppa)->env->to);
+ fb = mutt_get_name ((*ppb)->env->to);
+ result = strcasecmp (fa, fb);
+ AUXSORT(result,a,b);
+ return (SORTCODE (result));
+}
+
+int compare_from (const void *a, const void *b)
+{
+ HEADER **ppa = (HEADER **) a;
+ HEADER **ppb = (HEADER **) b;
+ char *fa, *fb;
+ int result;
+
+ fa = mutt_get_name ((*ppa)->env->from);
+ fb = mutt_get_name ((*ppb)->env->from);
+ result = strcasecmp (fa, fb);
+ AUXSORT(result,a,b);
+ return (SORTCODE (result));
+}
+
+int compare_date_received (const void *a, const void *b)
+{
+ HEADER **pa = (HEADER **) a;
+ HEADER **pb = (HEADER **) b;
+ int result = (*pa)->received - (*pb)->received;
+ AUXSORT(result,a,b);
+ return (SORTCODE (result));
+}
+
+int compare_order (const void *a, const void *b)
+{
+ HEADER **ha = (HEADER **) a;
+ HEADER **hb = (HEADER **) b;
+
+ /* no need to auxsort because you will never have equality here */
+ return (SORTCODE ((*ha)->index - (*hb)->index));
+}
+
+sort_t *mutt_get_sort_func (int method)
+{
+ switch (method & SORT_MASK)
+ {
+ case SORT_RECEIVED:
+ return (compare_date_received);
+ case SORT_ORDER:
+ return (compare_order);
+ case SORT_DATE:
+ return (compare_date_sent);
+ case SORT_SUBJECT:
+ return (compare_subject);
+ case SORT_FROM:
+ return (compare_from);
+ case SORT_SIZE:
+ return (compare_size);
+ case SORT_TO:
+ return (compare_to);
+ case SORT_SCORE:
+ return (compare_score);
+ default:
+ return (NULL);
+ }
+ /* not reached */
+}
+
+void mutt_sort_headers (CONTEXT *ctx, int init)
+{
+ int i;
+ sort_t *sortfunc;
+
+ unset_option (OPTNEEDRESORT);
+
+ if (!ctx)
+ return;
+
+ if (!ctx->msgcount)
+ {
+ /* this function gets called by mutt_sync_mailbox(), which may have just
+ * deleted all the messages. the virtual message numbers are not updated
+ * in that routine, so we must make sure to zero the vcount member.
+ */
+ ctx->vcount = 0;
+ ctx->tree = 0;
+ return; /* nothing to do! */
+ }
+
+ if (!ctx->quiet)
+ mutt_message ("Sorting mailbox...");
+
+ /* threads may be bogus, so clear the links */
+ if (init)
+ mutt_clear_threads (ctx);
+
+ if (option (OPTNEEDRESCORE))
+ {
+ for (i = 0; i < ctx->msgcount; i++)
+ mutt_score_message (ctx->hdrs[i]);
+ unset_option (OPTNEEDRESCORE);
+ }
+
+ if ((Sort & SORT_MASK) == SORT_THREADS)
+ {
+ AuxSort = NULL;
+ /* if $sort_aux changed after the mailbox is sorted, then all the
+ subthreads need to be resorted */
+ if (option (OPTSORTSUBTHREADS))
+ {
+ ctx->tree = mutt_sort_subthreads (ctx->tree, mutt_get_sort_func (SortAux));
+ unset_option (OPTSORTSUBTHREADS);
+ }
+ mutt_sort_threads (ctx, init);
+ }
+ else if ((sortfunc = mutt_get_sort_func (Sort)) == NULL ||
+ (AuxSort = mutt_get_sort_func (SortAux)) == NULL)
+ {
+ mutt_error ("Could not find sorting function! [report this bug]");
+ sleep (1);
+ return;
+ }
+ else
+ qsort ((void *) ctx->hdrs, ctx->msgcount, sizeof (HEADER *), sortfunc);
+
+ /* the threading function find_reference() needs to know how the mailbox
+ * is currently sorted in memory in order to speed things up a bit
+ */
+ ctx->revsort = (Sort & SORT_REVERSE) ? 1 : 0;
+
+ /* adjust the virtual message numbers */
+ ctx->vcount = 0;
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ if (ctx->hdrs[i]->virtual != -1)
+ {
+ ctx->hdrs[i]->virtual = ctx->vcount;
+ ctx->v2r[ctx->vcount] = i;
+ ctx->vcount++;
+ }
+ ctx->hdrs[i]->msgno = i;
+ }
+
+ mutt_cache_index_colors(ctx);
+
+ if (!ctx->quiet)
+ mutt_clear_error ();
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mapping.h"
+
+#define SORT_DATE 1 /* the date the mail was sent. */
+#define SORT_SIZE 2
+#define SORT_SUBJECT 3
+#define SORT_FROM 4
+#define SORT_ORDER 5 /* the order the messages appear in the mailbox. */
+#define SORT_THREADS 6
+#define SORT_RECEIVED 7 /* when the message were delivered locally */
+#define SORT_TO 8
+#define SORT_SCORE 9
+#define SORT_ALIAS 10
+#define SORT_ADDRESS 11
+#define SORT_MASK 0xf
+#define SORT_REVERSE (1<<4)
+#define SORT_LAST (1<<5)
+
+typedef int sort_t (const void *, const void *);
+sort_t *mutt_get_sort_func (int);
+
+void mutt_clear_threads (CONTEXT *);
+void mutt_sort_headers (CONTEXT *, int);
+void mutt_sort_threads (CONTEXT *, int);
+int mutt_select_sort (int);
+HEADER *mutt_sort_subthreads (HEADER *, sort_t *);
+
+WHERE short BrowserSort INITVAL (SORT_SUBJECT);
+WHERE short Sort INITVAL (SORT_DATE);
+WHERE short SortAux INITVAL (SORT_DATE); /* auxiallary sorting method */
+WHERE short SortAlias INITVAL (SORT_ALIAS);
+
+extern const struct mapping_t SortMethods[];
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_menu.h"
+#include "mutt_curses.h"
+#include "sort.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+static char *get_sort_str (char *buf, size_t buflen, int method)
+{
+ snprintf (buf, buflen, "%s%s%s",
+ (method & SORT_REVERSE) ? "reverse-" : "",
+ (method & SORT_LAST) ? "last-" : "",
+ mutt_getnamebyvalue (method & SORT_MASK, SortMethods));
+ return buf;
+}
+
+/* %b = number of incoming folders with unread messages [option]
+ * %d = number of deleted messages [option]
+ * %f = full mailbox path
+ * %F = number of flagged messages [option]
+ * %h = hostname
+ * %l = length of mailbox (in bytes) [option]
+ * %m = total number of messages [option]
+ * %M = number of messages shown (virutal message count when limiting) [option]
+ * %n = number of new messages [option]
+ * %p = number of postponed messages [option]
+ * %P = percent of way through index
+ * %r = readonly/wontwrite/changed flag
+ * %s = current sorting method ($sort)
+ * %S = current aux sorting method ($sort_aux)
+ * %t = # of tagged messages [option]
+ * %v = Mutt version
+ * %V = currently active limit pattern [option] */
+static const char *
+status_format_str (char *buf, size_t buflen, char op, const char *src,
+ const char *prefix, const char *ifstring,
+ const char *elsestring,
+ unsigned long data, format_flag flags)
+{
+ char fmt[SHORT_STRING], tmp[SHORT_STRING], *cp;
+ int count, optional = (flags & M_FORMAT_OPTIONAL);
+ MUTTMENU *menu = (MUTTMENU *) data;
+
+ *buf = 0;
+ switch (op)
+ {
+ case 'b':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (buf, buflen, fmt, mutt_buffy_check (0));
+ }
+ else if (!mutt_buffy_check (0))
+ optional = 0;
+ break;
+
+ case 'd':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (buf, buflen, fmt, Context ? Context->deleted : 0);
+ }
+ else if (!Context || !Context->deleted)
+ optional = 0;
+ break;
+
+ case 'h':
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (buf, buflen, fmt, Hostname);
+ break;
+
+ case 'f':
+ snprintf (fmt, sizeof(fmt), "%%%ss", prefix);
+ if (Context && Context->path)
+ {
+ strfcpy (tmp, Context->path, sizeof (tmp));
+ mutt_pretty_mailbox (tmp);
+ }
+ else
+ strfcpy (tmp, "(no mailbox)", sizeof (tmp));
+ snprintf (buf, buflen, fmt, tmp);
+ break;
+
+ case 'F':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (buf, buflen, fmt, Context ? Context->flagged : 0);
+ }
+ else if (!Context || !Context->flagged)
+ optional = 0;
+ break;
+
+ case 'l':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ mutt_pretty_size (tmp, sizeof (tmp), Context ? Context->size : 0);
+ snprintf (buf, buflen, fmt, tmp);
+ }
+ else if (!Context || !Context->size)
+ optional = 0;
+ break;
+
+ case 'L':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ mutt_pretty_size (tmp, sizeof (tmp), Context ? Context->vsize: 0);
+ snprintf (buf, buflen, fmt, tmp);
+ }
+ else if (!Context || !Context->pattern)
+ optional = 0;
+ break;
+
+ case 'm':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (buf, buflen, fmt, Context ? Context->msgcount : 0);
+ }
+ else if (!Context || !Context->msgcount)
+ optional = 0;
+ break;
+
+ case 'M':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof(fmt), "%%%sd", prefix);
+ snprintf (buf, buflen, fmt, Context ? Context->vcount : 0);
+ }
+ else if (!Context || !Context->pattern)
+ optional = 0;
+ break;
+
+ case 'n':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (buf, buflen, fmt, Context ? Context->new : 0);
+ }
+ else if (!Context || !Context->new)
+ optional = 0;
+ break;
+
+ case 'o':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (buf, buflen, fmt, Context ? Context->unread - Context->new : 0);
+ }
+ else if (!Context || !(Context->unread - Context->new))
+ optional = 0;
+ break;
+
+ case 'p':
+ count = mutt_num_postponed ();
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (buf, buflen, fmt, count);
+ }
+ else if (!count)
+ optional = 0;
+ break;
+
+ case 'P':
+ if (menu->top + menu->pagelen >= menu->max)
+ cp = menu->top ? "end" : "all";
+ else
+ {
+ count = (100 * (menu->top + menu->pagelen)) / menu->max;
+ snprintf (tmp, sizeof (tmp), "%d%%", count);
+ cp = tmp;
+ }
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (buf, buflen, fmt, cp);
+ break;
+
+ case 'r':
+ if (Context)
+ buf[0] = (Context->readonly || Context->dontwrite) ? StChars[2] :
+ (Context->changed || Context->deleted) ? StChars[1] : StChars[0];
+ else
+ buf[0] = StChars[0];
+ buf[1] = 0;
+ break;
+
+ case 's':
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (buf, buflen, fmt,
+ get_sort_str (tmp, sizeof (tmp), Sort));
+ break;
+
+ case 'S':
+ snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+ snprintf (buf, buflen, fmt,
+ get_sort_str (tmp, sizeof (tmp), SortAux));
+ break;
+
+ case 't':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (buf, buflen, fmt, Context ? Context->tagged : 0);
+ }
+ else if (!Context || !Context->tagged)
+ optional = 0;
+ break;
+
+ case 'u':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (buf, buflen, fmt, Context ? Context->unread : 0);
+ }
+ else if (!Context || !Context->unread)
+ optional = 0;
+ break;
+
+ case 'v':
+ snprintf (fmt, sizeof (fmt), "Mutt %%s");
+ snprintf (buf, buflen, fmt, VERSION);
+ break;
+
+ case 'V':
+ if (!optional)
+ {
+ snprintf (fmt, sizeof(fmt), "%%%ss", prefix);
+ snprintf (buf, buflen, fmt, Context ? Context->pattern : 0);
+ }
+ else if (!Context || !Context->pattern)
+ optional = 0;
+ break;
+
+ case 0:
+ *buf = 0;
+ return (src);
+
+ default:
+ snprintf (buf, buflen, "%%%s%c", prefix, op);
+ break;
+ }
+
+ if (optional)
+ menu_status_line (buf, buflen, menu, ifstring);
+ else if (flags & M_FORMAT_OPTIONAL)
+ menu_status_line (buf, buflen, menu, elsestring);
+
+ return (src);
+}
+
+void menu_status_line (char *buf, size_t buflen, MUTTMENU *menu, const char *p)
+{
+ mutt_FormatString (buf, buflen, p, status_format_str, (unsigned long) menu, 0);
+}
--- /dev/null
+#include <ctype.h>
+#include <sys/types.h>
+
+/* UnixWare doesn't have these functions in its standard C library */
+
+int strncasecmp (char *s1, char *s2, size_t n)
+{
+ register int c1, c2, l = 0;
+
+ while (*s1 && *s2 && l < n)
+ {
+ c1 = tolower (*s1);
+ c2 = tolower (*s2);
+ if (c1 != c2)
+ return (c1 - c2);
+ s1++;
+ s2++;
+ l++;
+ }
+ return (int) (0);
+}
+
+int strcasecmp (char *s1, char *s2)
+{
+ register int c1, c2;
+
+ while (*s1 && *s2)
+ {
+ c1 = tolower (*s1);
+ c2 = tolower (*s2);
+ if (c1 != c2)
+ return (c1 - c2);
+ s1++;
+ s2++;
+ }
+ return (int) (*s1 - *s2);
+}
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+int _mutt_system (const char *cmd, int flags)
+{
+ int rc = -1;
+ struct sigaction act;
+ struct sigaction oldcont;
+ struct sigaction oldtstp;
+ struct sigaction oldint;
+ struct sigaction oldquit;
+ struct sigaction oldchld;
+ sigset_t set;
+ pid_t thepid;
+
+ /* must block SIGCHLD and ignore SIGINT and SIGQUIT */
+
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = 0;
+ sigemptyset (&(act.sa_mask));
+ sigaction (SIGINT, &act, &oldint);
+ sigaction (SIGQUIT, &act, &oldquit);
+ if (flags & M_DETACH_PROCESS)
+ {
+ act.sa_flags = SA_NOCLDSTOP;
+ sigaction (SIGCHLD, &act, &oldchld);
+ act.sa_flags = 0;
+ }
+ else
+ {
+ sigemptyset (&set);
+ sigaddset (&set, SIGCHLD);
+ sigprocmask (SIG_BLOCK, &set, NULL);
+ }
+
+ act.sa_handler = SIG_DFL;
+ sigaction (SIGTSTP, &act, &oldtstp);
+ sigaction (SIGCONT, &act, &oldcont);
+
+ if ((thepid = fork ()) == 0)
+ {
+ /* reset signals for the child */
+ sigaction (SIGINT, &act, NULL);
+ sigaction (SIGQUIT, &act, NULL);
+
+ if (flags & M_DETACH_PROCESS)
+ {
+ setsid ();
+ switch (fork ())
+ {
+ case 0:
+ sigaction (SIGCHLD, &act, NULL);
+ break;
+
+ case -1:
+ _exit (127);
+
+ default:
+ _exit (0);
+ }
+ }
+ else
+ sigprocmask (SIG_UNBLOCK, &set, NULL);
+
+ execl (EXECSHELL, "sh", "-c", cmd, NULL);
+ _exit (127); /* execl error */
+ }
+ else if (thepid != -1)
+ {
+ /* wait for the child process to finish */
+ waitpid (thepid, &rc, 0);
+ }
+
+ /* reset SIGINT, SIGQUIT and SIGCHLD */
+ sigaction (SIGINT, &oldint, NULL);
+ sigaction (SIGQUIT, &oldquit, NULL);
+ if (flags & M_DETACH_PROCESS)
+ sigaction (SIGCHLD, &oldchld, NULL);
+ else
+ sigprocmask (SIG_UNBLOCK, &set, NULL);
+
+ sigaction (SIGCONT, &oldcont, NULL);
+ sigaction (SIGTSTP, &oldtstp, NULL);
+
+ rc = WIFEXITED (rc) ? WEXITSTATUS (rc) : -1;
+
+ return (rc);
+}
--- /dev/null
+From michael Thu Apr 2 17:51:50 PST 1998
+From: michael
+To: michael
+Subject: complex multipart
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary=foo
+
+--foo
+Content-Type text/plain
+
+This is part one.
+
+--foo
+Content-Type text/plain
+
+This is part two.
+
+--foo
+Content-Type text/plain
+
+This is part three.
+
+--foo
+Content-Type: message/rfc822
+
+From: bob
+To: joe
+Subject: nested message
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary=bar
+
+--bar
+Content-Type text/plain
+
+This is subpart one.
+
+--bar
+Content-Type text/plain
+
+This is subpart two.
+
+--bar--
+
+--foo--
+
--- /dev/null
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "sort.h"
+
+#include <string.h>
+#include <ctype.h>
+
+/* This function makes use of the fact that Mutt stores message references in
+ reverse order (i.e., last to first). This is optiminal since we would like
+ to find the most recent message to which "cur" refers itself. */
+static HEADER *find_reference (HEADER *cur, CONTEXT *ctx)
+{
+ LIST *refs = cur->env->references;
+ void *ptr;
+
+ for (; refs; refs = refs->next)
+ if ((ptr = hash_find (ctx->id_hash, refs->data)))
+ return ptr;
+
+ return NULL;
+}
+
+/* Determines whether to display a message's subject. */
+static int need_display_subject (HEADER *tree)
+{
+ HEADER *tmp;
+
+ if (tree->subject_changed)
+ return (1);
+ for (tmp = tree->prev; tmp; tmp = tmp->prev)
+ {
+ if (tmp->virtual >= 0)
+ {
+ if (!tmp->subject_changed)
+ return (0);
+ else
+ break;
+ }
+ }
+ for (tmp = tree->parent; tmp; tmp = tmp->parent)
+ {
+ if (tmp->virtual >= 0)
+ return (0);
+ else if (tmp->subject_changed)
+ return (1);
+ }
+ return (0);
+}
+
+/* determines whether a later sibling or the child of a later
+ sibling is displayed. */
+int is_next_displayed (HEADER *tree)
+{
+ int depth = 0;
+
+ if ((tree = tree->next) == NULL)
+ return (0);
+
+ FOREVER
+ {
+ if (tree->virtual >= 0)
+ return (1);
+
+ if (tree->child)
+ {
+ tree = tree->child;
+ depth++;
+ }
+ else
+ {
+ while (!tree->next && depth > 0)
+ {
+ tree = tree->parent;
+ depth--;
+ }
+ if ((tree = tree->next) == NULL)
+ break;
+ }
+ }
+ return (0);
+}
+
+
+/* Since the graphics characters have a value >255, I have to resort to
+ * using escape sequences to pass the information to print_enriched_string().
+ * These are the macros M_TREE_* defined in mutt.h.
+ *
+ * ncurses should automatically use the default ASCII characters instead of
+ * graphics chars on terminals which don't support them (see the man page
+ * for curs_addch).
+ */
+void mutt_linearize_tree (CONTEXT *ctx, int linearize)
+{
+ char *pfx = NULL, *mypfx = NULL, *arrow = NULL, *myarrow = NULL;
+ char corner = Sort & SORT_REVERSE ? M_TREE_ULCORNER : M_TREE_LLCORNER;
+ int depth = 0, start_depth = 0, max_depth = 0, max_width = 0;
+ int nextdisp = 0;
+ HEADER *tree = ctx->tree;
+ HEADER **array = ctx->hdrs + (Sort & SORT_REVERSE ? ctx->msgcount - 1 : 0);
+
+ /* A NULL tree should never be passed here, but may occur if there is
+ a cycle. */
+ if (!tree)
+ return;
+
+ FOREVER
+ {
+ if (tree->virtual >= 0)
+ tree->display_subject = need_display_subject (tree);
+
+ if (depth >= max_depth)
+ safe_realloc ((void **) &pfx,
+ (max_depth += 32) * 2 * sizeof (char));
+
+ if (depth - start_depth >= max_width)
+ safe_realloc ((void **) &arrow,
+ (max_width += 16) * 2 * sizeof (char));
+
+ safe_free ((void **) &tree->tree);
+ if (!depth)
+ {
+ if (tree->virtual >= 0)
+ tree->tree = safe_strdup ("");
+ }
+ else
+ {
+ myarrow = arrow + (depth - start_depth - (start_depth ? 0 : 1)) * 2;
+ nextdisp = is_next_displayed (tree);
+
+ if (depth && start_depth == depth)
+ myarrow[0] = nextdisp ? M_TREE_LTEE : corner;
+ else
+ myarrow[0] = M_TREE_HIDDEN;
+ myarrow[1] = tree->fake_thread ? M_TREE_STAR : M_TREE_HLINE;
+ if (tree->virtual >= 0)
+ {
+ myarrow[2] = M_TREE_RARROW;
+ myarrow[3] = 0;
+ }
+
+ if (tree->virtual >= 0)
+ {
+ tree->tree = safe_malloc ((2 + depth * 2) * sizeof (char));
+ if (start_depth > 1)
+ {
+ strncpy (tree->tree, pfx, (start_depth - 1) * 2);
+ strfcpy (tree->tree + (start_depth - 1) * 2,
+ arrow, (2 + depth - start_depth) * 2);
+ }
+ else
+ strfcpy (tree->tree, arrow, 2 + depth * 2);
+ }
+ }
+
+ if (linearize)
+ {
+ *array = tree;
+ array += Sort & SORT_REVERSE ? -1 : 1;
+ }
+
+ if (tree->child)
+ {
+ if (depth)
+ {
+ mypfx = pfx + (depth - 1) * 2;
+ mypfx[0] = nextdisp ? M_TREE_VLINE : M_TREE_SPACE;
+ mypfx[1] = M_TREE_SPACE;
+ }
+ depth++;
+ if (tree->virtual >= 0)
+ start_depth = depth;
+ tree = tree->child;
+ }
+ else
+ {
+ while (!tree->next && tree->parent)
+ {
+ if (tree->virtual >= 0)
+ start_depth = depth;
+ tree = tree->parent;
+ if (start_depth == depth)
+ start_depth--;
+ depth--;
+ }
+ if (tree->virtual >= 0)
+ start_depth = depth;
+ if ((tree = tree->next) == NULL)
+ break;
+ }
+ }
+
+ safe_free ((void **) &pfx);
+ safe_free ((void **) &arrow);
+}
+
+/* inserts `msg' into the list `tree' using an insertion sort. this function
+ * assumes that `tree' is the first element in the list, and not some
+ * element in the middle of the list.
+ */
+static void insert_message (HEADER **tree, HEADER *msg, sort_t *sortFunc)
+{
+ HEADER *tmp;
+
+ /* NOTE: we do NOT clear the `msg->child' link here because when we do
+ * the pseudo-threading, we want to preserve any sub-threads. So we clear
+ * the `msg->child' in the main routine where we know it is safe to do.
+ */
+
+ /* if there are no elements in the list, just add it and return */
+ if (!*tree)
+ {
+ msg->prev = msg->next = NULL;
+ *tree = msg;
+ return;
+ }
+
+ /* check to see if this message belongs at the beginning of the list */
+ if (!sortFunc || sortFunc ((void *) &msg, (void *) tree) < 0)
+ {
+ (*tree)->prev = msg;
+ msg->next = *tree;
+ msg->prev = NULL;
+ *tree = msg;
+ return;
+ }
+
+ /* search for the correct spot in the list to insert */
+ for (tmp = *tree; tmp->next; tmp = tmp->next)
+ if (sortFunc ((void *) &msg, (void *) &tmp->next) < 0)
+ {
+ msg->prev = tmp;
+ msg->next = tmp->next;
+ tmp->next->prev = msg;
+ tmp->next = msg;
+ return;
+ }
+
+ /* did not insert yet, so add this message to the end of the list */
+ tmp->next = msg;
+ msg->prev = tmp;
+ msg->next = NULL;
+}
+
+/* returns 1 if `a' is a descendant (child) of thread `b' */
+static int is_descendant (HEADER *a, HEADER *b)
+{
+ /* find the top parent of the thread */
+ while (a->parent)
+ a = a->parent;
+ return (a == b);
+}
+
+/* find the best possible match for a parent mesage based upon subject.
+ if there are multiple matches, the one which was sent the latest, but
+ before the current message, is used. */
+static HEADER *find_subject (CONTEXT *ctx, HEADER *cur)
+{
+ struct hash_elem *ptr;
+ HEADER *tmp, *last = NULL;
+ ENVELOPE *env = cur->env;
+ int hash;
+
+ if (env->real_subj &&
+ ((env->real_subj != env->subject) || (!option (OPTSORTRE))))
+ {
+ hash = hash_string ((unsigned char *) env->real_subj, ctx->subj_hash->nelem);
+ for (ptr = ctx->subj_hash->table[hash]; ptr; ptr = ptr->next)
+ {
+ tmp = ptr->data;
+ if (tmp != cur && /* don't match the same message */
+ !tmp->fake_thread && /* don't match pseudo threads */
+ tmp->subject_changed && /* only match interesting replies */
+ !is_descendant (tmp, cur) && /* don't match in the same thread */
+ cur->date_sent >= tmp->date_sent &&
+ (!last || (last->date_sent <= tmp->date_sent)) &&
+ tmp->env->real_subj &&
+ strcmp (env->real_subj, tmp->env->real_subj) == 0)
+ {
+ last = tmp; /* best match so far */
+ }
+ }
+ }
+ return last;
+}
+
+static void unlink_message (HEADER **top, HEADER *cur)
+{
+ if (cur->prev)
+ {
+ cur->prev->next = cur->next;
+
+ if (cur->next)
+ cur->next->prev = cur->prev;
+ }
+ else
+ {
+ if (cur->next)
+ cur->next->prev = NULL;
+ *top = cur->next;
+ }
+}
+
+static void pseudo_threads (CONTEXT *ctx, sort_t *sortFunc)
+{
+ HEADER *tree = ctx->tree;
+ HEADER *top = tree, *cur, *tmp, *curchild, *nextchild;
+
+ while (tree)
+ {
+ cur = tree;
+ tree = tree->next;
+ if ((tmp = find_subject (ctx, cur)) != NULL)
+ {
+ /* detach this message from it's current location */
+ unlink_message (&top, cur);
+
+ cur->subject_changed = 0;
+ cur->fake_thread = 1;
+ cur->parent = tmp;
+ insert_message (&tmp->child, cur, sortFunc);
+
+ /* if the message we're attaching has pseudo-children, they
+ need to be attached to its parent, so move them up a level. */
+ for (curchild = cur->child; curchild; )
+ {
+ nextchild = curchild->next;
+ if (curchild->fake_thread)
+ {
+ /* detach this message from its current location */
+ unlink_message (&cur->child, curchild);
+ curchild->parent = tmp;
+ insert_message (&tmp->child, curchild, sortFunc);
+ }
+ curchild = nextchild;
+ }
+ }
+ }
+ ctx->tree = top;
+}
+
+static HEADER *sort_last (HEADER *top)
+{
+ HEADER *tree;
+ HEADER *tmp;
+ HEADER *first;
+ HEADER *last;
+ HEADER *nextsearch;
+ sort_t *usefunc;
+
+ usefunc = mutt_get_sort_func (Sort);
+
+ tree = top;
+ FOREVER
+ {
+ if (tree->child)
+ tree = tree->child;
+ else
+ {
+ while (!tree->next)
+ {
+ first = last = tree;
+ nextsearch = tree->prev;
+ first->prev = NULL;
+ last->next = NULL;
+ while ((tree = nextsearch) != NULL)
+ {
+ tmp = last;
+ nextsearch = nextsearch->prev;
+ while (tmp && (*usefunc) ((void *) &tree->last_sort,
+ (void *) &tmp->last_sort) < 0)
+ tmp = tmp->prev;
+ if (tmp)
+ {
+ if ((tree->next = tmp->next) != NULL)
+ tmp->next->prev = tree;
+ else
+ last = tree;
+ tmp->next = tree;
+ tree->prev = tmp;
+ }
+ else
+ {
+ tree->next = first;
+ first->prev = tree;
+ first = tree;
+ tree->prev = NULL;
+ }
+ }
+ if (first->parent)
+ {
+ first->parent->child = first;
+ tree = first->parent;
+ if (Sort & SORT_REVERSE)
+ {
+ if ((*usefunc) ((void *) &tree->last_sort,
+ (void *) &first->last_sort) > 0)
+ tree->last_sort = first->last_sort;
+ }
+ else
+ {
+ if ((*usefunc) ((void *) &tree->last_sort,
+ (void *) &last->last_sort) < 0)
+ tree->last_sort = last->last_sort;
+ }
+ }
+ else
+ {
+ top = first;
+ tree = last;
+ break;
+ }
+ }
+ if ((tree = tree->next) == NULL)
+ break;
+ }
+ }
+ return top;
+}
+
+static void move_descendants (HEADER **tree, HEADER *cur, sort_t *usefunc)
+{
+ HEADER *ptr, *tmp = *tree;
+
+ while (tmp)
+ {
+ /* only need to look at the last reference */
+ if (tmp->env->references &&
+ strcmp (tmp->env->references->data, cur->env->message_id) == 0)
+ {
+ /* remove message from current location */
+ unlink_message (tree, tmp);
+
+ tmp->parent = cur;
+ if (cur->env->real_subj && tmp->env->real_subj)
+ tmp->subject_changed = strcmp (tmp->env->real_subj, cur->env->real_subj) ? 1 : 0;
+ else
+ tmp->subject_changed = (cur->env->real_subj || tmp->env->real_subj) ? 1 : 0;
+ tmp->fake_thread = 0; /* real reference */
+
+ ptr = tmp;
+ tmp = tmp->next;
+ insert_message (&cur->child, ptr, usefunc);
+ }
+ else
+ tmp = tmp->next;
+ }
+}
+
+void mutt_clear_threads (CONTEXT *ctx)
+{
+ int i;
+
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ ctx->hdrs[i]->parent = NULL;
+ ctx->hdrs[i]->next = NULL;
+ ctx->hdrs[i]->prev = NULL;
+ ctx->hdrs[i]->child = NULL;
+ ctx->hdrs[i]->threaded = 0;
+ ctx->hdrs[i]->fake_thread = 0;
+ }
+ ctx->tree = NULL;
+}
+
+HEADER *mutt_sort_subthreads (HEADER *hdr, sort_t *func)
+{
+ HEADER *top = NULL;
+ HEADER *t;
+
+ while (hdr)
+ {
+ t = hdr;
+ hdr = hdr->next;
+ insert_message (&top, t, func);
+ if (t->child)
+ t->child = mutt_sort_subthreads (t->child, func);
+ }
+ return top;
+}
+
+void mutt_sort_threads (CONTEXT *ctx, int init)
+{
+ sort_t *usefunc = NULL;
+ HEADER *tmp, *CUR;
+ int i, oldsort;
+
+ /* set Sort to the secondary method to support the set sort_aux=reverse-*
+ * settings. The sorting functions just look at the value of
+ * SORT_REVERSE
+ */
+ oldsort = Sort;
+ Sort = SortAux;
+
+ /* get secondary sorting method. we can't have threads, so use the date
+ * if the user specified it
+ */
+ if ((Sort & SORT_MASK) == SORT_THREADS)
+ Sort = (Sort & ~SORT_MASK) | SORT_DATE;
+
+ /* if the SORT_LAST bit is set, we save sorting for later */
+ if (!(Sort & SORT_LAST))
+ usefunc = mutt_get_sort_func (Sort);
+
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ CUR = ctx->hdrs[i];
+
+ if (CUR->fake_thread)
+ {
+ /* Move pseudo threads back to the top level thread so that they can
+ * can be moved later if they are descendants of messages that were
+ * just delivered.
+ */
+ CUR->fake_thread = 0;
+ CUR->subject_changed = 1;
+ unlink_message (&CUR->parent->child, CUR);
+ CUR->parent = NULL;
+ insert_message (&ctx->tree, CUR, usefunc);
+ }
+ else if (!CUR->threaded)
+ {
+ if ((tmp = find_reference (CUR, ctx)) != NULL)
+ {
+ CUR->parent = tmp;
+ if (CUR->env->real_subj && tmp->env->real_subj)
+ CUR->subject_changed = strcmp (tmp->env->real_subj, CUR->env->real_subj) ? 1 : 0;
+ else
+ CUR->subject_changed = (CUR->env->real_subj || tmp->env->real_subj) ? 1 : 0;
+ }
+ else
+ CUR->subject_changed = 1;
+
+ if (!init)
+ {
+ /* Search the children of `tmp' for decendants of `cur'. This is only
+ * done when the mailbox has already been threaded since we don't have
+ * to worry about the tree being threaded wrong (because of a missing
+ * parent) during the initial threading.
+ */
+ if (CUR->env->message_id)
+ move_descendants (tmp ? &tmp->child : &ctx->tree, CUR, usefunc);
+ }
+ insert_message (tmp ? &tmp->child : &ctx->tree, CUR, usefunc);
+ CUR->threaded = 1;
+ }
+ }
+
+ if (!option (OPTSTRICTTHREADS))
+ pseudo_threads (ctx, usefunc);
+
+ /* now that the whole tree is put together, we can sort by last-* */
+ if (Sort & SORT_LAST)
+ {
+ for (i = 0; i < ctx->msgcount; i++)
+ ctx->hdrs[i]->last_sort = ctx->hdrs[i];
+ ctx->tree = sort_last (ctx->tree);
+ }
+
+ /* restore the oldsort order. */
+ Sort = oldsort;
+
+ /* Put the list into an array. If we are reverse sorting, give the
+ * offset of the last message, and work backwards (tested for and
+ * done inside the function), so that the threads go backwards.
+ * This, of course, means the auxillary sort has to go forwards
+ * because we map it backwards here.
+ */
+ mutt_linearize_tree (ctx, 1);
+}
+
+int _mutt_aside_thread (HEADER *hdr, short dir, short subthreads)
+{
+ if ((Sort & SORT_MASK) != SORT_THREADS)
+ {
+ mutt_error ("Threading is not enabled.");
+ return (hdr->virtual);
+ }
+
+ if (!subthreads)
+ {
+ while (hdr->parent)
+ hdr = hdr->parent;
+ }
+ else
+ {
+ if (dir)
+ {
+ while (!hdr->next && hdr->parent)
+ hdr = hdr->parent;
+ }
+ else
+ {
+ while (!hdr->prev && hdr->parent)
+ hdr = hdr->parent;
+ }
+ }
+
+ hdr = (dir != 0) ^ ((Sort & SORT_REVERSE) != 0) ? hdr->next : hdr->prev;
+ if (hdr)
+ {
+ if (Sort & SORT_REVERSE)
+ return (hdr->next ? hdr->next->virtual + 1 : 0);
+ else
+ return (hdr->virtual);
+ }
+ else
+ return (-1);
+}