]> granicus.if.org Git - mutt/commitdiff
Initial revision
authorThomas Roessler <roessler@does-not-exist.org>
Mon, 8 Jun 1998 09:16:03 +0000 (09:16 +0000)
committerThomas Roessler <roessler@does-not-exist.org>
Mon, 8 Jun 1998 09:16:03 +0000 (09:16 +0000)
163 files changed:
COPYING [new file with mode: 0644]
COPYRIGHT [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Changes [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
Mush.rc [new file with mode: 0644]
Muttrc [new file with mode: 0644]
NEWS [new file with mode: 0644]
OPS [new file with mode: 0644]
OPS.PGP [new file with mode: 0644]
Pine.rc [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
acconfig.h [new file with mode: 0644]
addrbook.c [new file with mode: 0644]
alias.c [new file with mode: 0644]
attach.c [new file with mode: 0644]
attach.h [new file with mode: 0644]
bind.c [new file with mode: 0644]
browser.c [new file with mode: 0644]
buffy.c [new file with mode: 0644]
buffy.h [new file with mode: 0644]
color.c [new file with mode: 0644]
commands.c [new file with mode: 0644]
complete.c [new file with mode: 0644]
compose.c [new file with mode: 0644]
config.h.in [new file with mode: 0644]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
copy.c [new file with mode: 0644]
copy.h [new file with mode: 0644]
curs_lib.c [new file with mode: 0644]
curs_main.c [new file with mode: 0644]
date.c [new file with mode: 0644]
depend.awk [new file with mode: 0644]
doc/language.txt [new file with mode: 0644]
doc/language50.txt [new file with mode: 0644]
doc/makefile [new file with mode: 0644]
doc/manual.sgml [new file with mode: 0644]
doc/manual.txt [new file with mode: 0644]
doc/mutt.man [new file with mode: 0644]
doc/mutt.sgml [new file with mode: 0644]
doc/pgp-Notes.txt [new file with mode: 0644]
doc/style-guide [new file with mode: 0644]
edit.c [new file with mode: 0644]
enter.c [new file with mode: 0644]
filter.c [new file with mode: 0644]
flags.c [new file with mode: 0644]
from.c [new file with mode: 0644]
functions.h [new file with mode: 0644]
gen_defs [new file with mode: 0755]
getdomain.c [new file with mode: 0644]
globals.h [new file with mode: 0644]
handler.c [new file with mode: 0644]
hash.c [new file with mode: 0644]
hash.h [new file with mode: 0644]
hdrline.c [new file with mode: 0644]
headers.c [new file with mode: 0644]
help.c [new file with mode: 0644]
hook.c [new file with mode: 0644]
imap.c [new file with mode: 0644]
init.c [new file with mode: 0644]
init.h [new file with mode: 0644]
install-sh [new file with mode: 0755]
keymap.c [new file with mode: 0644]
keymap.h [new file with mode: 0644]
lib.c [new file with mode: 0644]
mailbox.h [new file with mode: 0644]
main.c [new file with mode: 0644]
mapping.h [new file with mode: 0644]
mbox.c [new file with mode: 0644]
menu.c [new file with mode: 0644]
mh.c [new file with mode: 0644]
mime.h [new file with mode: 0644]
mime.types [new file with mode: 0644]
mkinstalldirs [new file with mode: 0755]
mutt.h [new file with mode: 0644]
mutt_curses.h [new file with mode: 0644]
mutt_menu.h [new file with mode: 0644]
mutt_regex.h [new file with mode: 0644]
mx.c [new file with mode: 0644]
mx.h [new file with mode: 0644]
pager.c [new file with mode: 0644]
pager.h [new file with mode: 0644]
parse.c [new file with mode: 0644]
parse.h [new file with mode: 0644]
pattern.c [new file with mode: 0644]
pgp.c [new file with mode: 0644]
pgp.h [new file with mode: 0644]
pgpinvoke.c [new file with mode: 0644]
pgpkey.c [new file with mode: 0644]
pgppubring.c [new file with mode: 0644]
pop.c [new file with mode: 0644]
postpone.c [new file with mode: 0644]
protos.h [new file with mode: 0644]
query.c [new file with mode: 0644]
reap.pl [new file with mode: 0755]
recvattach.c [new file with mode: 0644]
reldate.h [new file with mode: 0644]
resize.c [new file with mode: 0644]
rfc1524.c [new file with mode: 0644]
rfc1524.h [new file with mode: 0644]
rfc2047.c [new file with mode: 0644]
rfc2047.h [new file with mode: 0644]
rfc822.c [new file with mode: 0644]
rfc822.h [new file with mode: 0644]
rx/COPYING.LIB [new file with mode: 0644]
rx/Makefile [new file with mode: 0644]
rx/Makefile.in [new file with mode: 0644]
rx/_rx.h [new file with mode: 0644]
rx/hashrexp.c [new file with mode: 0644]
rx/inst-rxposix.h [new file with mode: 0644]
rx/rx.c [new file with mode: 0644]
rx/rx.h [new file with mode: 0644]
rx/rxall.h [new file with mode: 0644]
rx/rxanal.c [new file with mode: 0644]
rx/rxanal.h [new file with mode: 0644]
rx/rxbasic.c [new file with mode: 0644]
rx/rxbasic.h [new file with mode: 0644]
rx/rxbitset.c [new file with mode: 0644]
rx/rxbitset.h [new file with mode: 0644]
rx/rxcontext.h [new file with mode: 0644]
rx/rxcset.c [new file with mode: 0644]
rx/rxcset.h [new file with mode: 0644]
rx/rxgnucomp.c [new file with mode: 0644]
rx/rxgnucomp.h [new file with mode: 0644]
rx/rxhash.c [new file with mode: 0644]
rx/rxhash.h [new file with mode: 0644]
rx/rxnfa.c [new file with mode: 0644]
rx/rxnfa.h [new file with mode: 0644]
rx/rxnode.c [new file with mode: 0644]
rx/rxnode.h [new file with mode: 0644]
rx/rxposix.c [new file with mode: 0644]
rx/rxposix.h [new file with mode: 0644]
rx/rxproto.h [new file with mode: 0644]
rx/rxsimp.c [new file with mode: 0644]
rx/rxsimp.h [new file with mode: 0644]
rx/rxspencer.c [new file with mode: 0644]
rx/rxspencer.h [new file with mode: 0644]
rx/rxstr.c [new file with mode: 0644]
rx/rxstr.h [new file with mode: 0644]
rx/rxsuper.c [new file with mode: 0644]
rx/rxsuper.h [new file with mode: 0644]
rx/rxunfa.c [new file with mode: 0644]
rx/rxunfa.h [new file with mode: 0644]
sample.mailcap [new file with mode: 0644]
sample.muttrc [new file with mode: 0644]
score.c [new file with mode: 0644]
send.c [new file with mode: 0644]
sendlib.c [new file with mode: 0644]
sha.h [new file with mode: 0644]
sha1dgst.c [new file with mode: 0644]
sha_locl.h [new file with mode: 0644]
signal.c [new file with mode: 0644]
snprintf.c [new file with mode: 0644]
sort.c [new file with mode: 0644]
sort.h [new file with mode: 0644]
status.c [new file with mode: 0644]
strcasecmp.c [new file with mode: 0644]
system.c [new file with mode: 0644]
testmsg [new file with mode: 0644]
thread.c [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..3142536
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,410 @@
+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.]
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..d45c12f
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,16 @@
+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.
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..822fafc
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1709 @@
+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 &gt; in the description of
+  $status_format
+
+- [patch-0.90.11i.ds.man_tilde_nit.1] fixed use of ~ where &tilde; 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)
+
diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..7b012d8
--- /dev/null
+++ b/Changes
@@ -0,0 +1,38 @@
+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.
+
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..edef9e8
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,144 @@
+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.
+
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..0fe9f8a
--- /dev/null
@@ -0,0 +1,143 @@
+#
+# 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)")
+
diff --git a/Mush.rc b/Mush.rc
new file mode 100644 (file)
index 0000000..4100892
--- /dev/null
+++ b/Mush.rc
@@ -0,0 +1,18 @@
+#
+# 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
diff --git a/Muttrc b/Muttrc
new file mode 100644 (file)
index 0000000..9eed4f1
--- /dev/null
+++ b/Muttrc
@@ -0,0 +1,24 @@
+#
+# 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
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..bab7ea6
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,79 @@
+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
diff --git a/OPS b/OPS
new file mode 100644 (file)
index 0000000..049e892
--- /dev/null
+++ b/OPS
@@ -0,0 +1,142 @@
+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"
diff --git a/OPS.PGP b/OPS.PGP
new file mode 100644 (file)
index 0000000..ff1a7a2
--- /dev/null
+++ b/OPS.PGP
@@ -0,0 +1,7 @@
+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"
diff --git a/Pine.rc b/Pine.rc
new file mode 100644 (file)
index 0000000..5eb0f86
--- /dev/null
+++ b/Pine.rc
@@ -0,0 +1,40 @@
+#
+# 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"
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..0b566b0
--- /dev/null
+++ b/README
@@ -0,0 +1,17 @@
+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
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..8b4c410
--- /dev/null
+++ b/TODO
@@ -0,0 +1,49 @@
+- 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
+
diff --git a/acconfig.h b/acconfig.h
new file mode 100644 (file)
index 0000000..acd02bb
--- /dev/null
@@ -0,0 +1,104 @@
+
+/* 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
diff --git a/addrbook.c b/addrbook.c
new file mode 100644 (file)
index 0000000..36038e0
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * 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);
+}
diff --git a/alias.c b/alias.c
new file mode 100644 (file)
index 0000000..b4cd7c0
--- /dev/null
+++ b/alias.c
@@ -0,0 +1,401 @@
+/*
+ * 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;
+}
diff --git a/attach.c b/attach.c
new file mode 100644 (file)
index 0000000..6ee4631
--- /dev/null
+++ b/attach.c
@@ -0,0 +1,769 @@
+/*
+ * 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;
+  }
+}
diff --git a/attach.h b/attach.h
new file mode 100644 (file)
index 0000000..46e374d
--- /dev/null
+++ b/attach.h
@@ -0,0 +1,26 @@
+/*
+ * 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);
diff --git a/bind.c b/bind.c
new file mode 100644 (file)
index 0000000..a327bf7
--- /dev/null
+++ b/bind.c
@@ -0,0 +1,200 @@
+/*
+ * 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;
+}
diff --git a/browser.c b/browser.c
new file mode 100644 (file)
index 0000000..b66982c
--- /dev/null
+++ b/browser.c
@@ -0,0 +1,714 @@
+/*
+ * 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 */
+}
diff --git a/buffy.c b/buffy.c
new file mode 100644 (file)
index 0000000..1fa1d7c
--- /dev/null
+++ b/buffy.c
@@ -0,0 +1,420 @@
+/* 
+ * 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;
+  }
+}
diff --git a/buffy.h b/buffy.h
new file mode 100644 (file)
index 0000000..c736a86
--- /dev/null
+++ b/buffy.h
@@ -0,0 +1,41 @@
+/* 
+ * 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
diff --git a/color.c b/color.c
new file mode 100644 (file)
index 0000000..6468b2e
--- /dev/null
+++ b/color.c
@@ -0,0 +1,731 @@
+/*
+ * 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);
+}
diff --git a/commands.c b/commands.c
new file mode 100644 (file)
index 0000000..4de052a
--- /dev/null
@@ -0,0 +1,751 @@
+/*
+ * 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);
+}
diff --git a/complete.c b/complete.c
new file mode 100644 (file)
index 0000000..360eebf
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * 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);
+}
diff --git a/compose.c b/compose.c
new file mode 100644 (file)
index 0000000..494cd2c
--- /dev/null
+++ b/compose.c
@@ -0,0 +1,898 @@
+/*
+ * 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);
+}
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..8b88813
--- /dev/null
@@ -0,0 +1,176 @@
+/* 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
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..4fd0e17
--- /dev/null
+++ b/configure
@@ -0,0 +1,3093 @@
+#! /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
+
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..2af4a27
--- /dev/null
@@ -0,0 +1,374 @@
+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)
diff --git a/copy.c b/copy.c
new file mode 100644 (file)
index 0000000..c05e2eb
--- /dev/null
+++ b/copy.c
@@ -0,0 +1,630 @@
+/*
+ * 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;
+}
diff --git a/copy.h b/copy.h
new file mode 100644 (file)
index 0000000..ff78a3c
--- /dev/null
+++ b/copy.h
@@ -0,0 +1,63 @@
+/*
+ * 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);
diff --git a/curs_lib.c b/curs_lib.c
new file mode 100644 (file)
index 0000000..c2177fc
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * 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
diff --git a/curs_main.c b/curs_main.c
new file mode 100644 (file)
index 0000000..7dbadca
--- /dev/null
@@ -0,0 +1,1539 @@
+/*
+ * 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]);
+}
diff --git a/date.c b/date.c
new file mode 100644 (file)
index 0000000..9cd2089
--- /dev/null
+++ b/date.c
@@ -0,0 +1,146 @@
+/*
+ * 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];
+  }
+}
diff --git a/depend.awk b/depend.awk
new file mode 100644 (file)
index 0000000..8938521
--- /dev/null
@@ -0,0 +1,2 @@
+$0 ~ /^# DO NOT REMOVE THIS LINE/ { exit }
+{ print }
diff --git a/doc/language.txt b/doc/language.txt
new file mode 100644 (file)
index 0000000..4e9032f
--- /dev/null
@@ -0,0 +1,3258 @@
+# 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"
diff --git a/doc/language50.txt b/doc/language50.txt
new file mode 100644 (file)
index 0000000..a573c10
--- /dev/null
@@ -0,0 +1,1339 @@
+#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"
diff --git a/doc/makefile b/doc/makefile
new file mode 100644 (file)
index 0000000..7d85b31
--- /dev/null
@@ -0,0 +1,13 @@
+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
diff --git a/doc/manual.sgml b/doc/manual.sgml
new file mode 100644 (file)
index 0000000..ef54e69
--- /dev/null
@@ -0,0 +1,4293 @@
+<!doctype linuxdoc system>
+
+<article>
+
+<title>The Mutt E-Mail Client
+<author>by Michael Elkins <htmlurl url="mailto:me@cs.hmc.edu" name="&lt;me@cs.hmc.edu&gt;">
+<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 &lt;me@cs.hmc.edu&gt;
+
+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="&dollar;to&lowbar;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,
+``&lowbar;'' 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="&dollar;strict&lowbar;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="&dollar;alias&lowbar;file"> variable for future use. <bf/Note:/
+Specifying an <ref id="alias_file" name="&dollar;alias&lowbar;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="&dollar;pgp&lowbar;v2&lowbar;pubring"> or <ref
+id="pgp_v5_pubring" name="&dollar;pgp&lowbar;v5&lowbar;pubring">
+depending on <ref id="pgp_key_version"
+name="&dollar;pgp&lowbar;key&lowbar;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="&dollar;pipe&lowbar;decode">, <ref id="pipe_split"
+name="&dollar;pipe&lowbar;split">, <ref id="pipe_sep"
+name="&dollar;pipe&lowbar;sep"> and <ref id="wait_key"
+name="&dollar;wait&lowbar;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="&dollar;wait&lowbar;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="&dollar;quote&lowbar;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="&dollar;mime&lowbar;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="&dollar;askcc">, <ref id="askbcc" name="&dollar;askbcc">, <ref
+id="autoedit" name="&dollar;autoedit">, and <ref id="fast_reply"
+name="&dollar;fast&lowbar;reply"> for changing how Mutt asks these
+questions.
+
+Mutt will then automatically start your <ref id="editor"
+name="&dollar;editor"> on the message body.  If the <ref id="edit_headers"
+name="&dollar;edit&lowbar;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="&dollar;attribution">, <ref id="indent_string"
+name="&dollar;indent&lowbar;string"> and <ref id="post_indent_string"
+name="&dollar;post&lowbar;indent&lowbar;string">.  When forwarding a
+message, if the <ref id="mime_forward" name="&dollar;mime&lowbar;forward">
+variable is unset, a copy of the forwarded message will be included.  If
+you have specified a <ref id="signature" name="&dollar;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/  &lsqb; <em/description/ &rsqb;<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:/ &lsqb; <tt/E/ | <tt/S/ | <tt/S&lt;id/&gt; &rsqb; <newline>
+
+``E'' encrypts, ``S'' signs and
+``S&lt;id&gt;'' signs with the given key, setting <ref
+id="pgp_sign_as" name="&dollar;pgp&lowbar;sign&lowbar;as"> permanently.
+
+Also see <ref id="edit_headers" name="edit&lowbar;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="&dollar;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="&dollar;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
+(``&num;''), 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? &num; 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 &lsqb;see
+next paragraph&rsqb;), 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.
+
+&bsol; 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
+``&bsol;'' to force the next character to be a literal instead of interpreted
+character.
+<tscreen><verb>
+set realname="Michael \"MuttDude\" Elkins"
+</verb></tscreen>
+
+``&bsol;&bsol;'' means to insert a literal ``&bsol;'' into the line.
+``&bsol;n'' and ``&bsol;r'' have their usual C meanings of linefeed and
+carriage-return, respectively.
+
+A &bsol; 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/ &lsqb; , <em/address/, ... &rsqb;
+
+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/ &lsqb; <em/addr/ <em/.../ &rsqb;
+
+<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="&dollar;alias&lowbar;file"> variable (which is
+<tt>&tilde;/.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="&dollar;edit&lowbar;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/&bsol;Cx/, where <em/x/ is the
+letter of the control character (for example, to specify control-A use
+``&bsol;Ca'').  Note that the case of <em/x/ as well as <em/&bsol;C/ is
+ignored, so that <em/&bsol;CA, &bsol;Ca, &bsol;cA/ and <em/&bsol;ca/ are all
+equivalent.  An alternative form is to specify the key as a three digit
+octal number prefixed with a ``&bsol;'' (for example <em/&bsol;177/ is
+equivalent to <em/&bsol;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/ &lsqb;!&rsqb;<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="&dollar;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/&circ;x/.  In
+order to get a caret (``&circ;'') you need to use <em/&circ;&circ;/.
+
+<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/ &lsqb; <em/regexp/ &rsqb;<newline>
+Usage: <tt/color/ index <em/foreground/ <em/background/ &lsqb; <em/pattern/ &rsqb;<newline>
+Usage: <tt/uncolor/ index <em/pattern/ &lsqb; <em/pattern/ ...  &rsqb;<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="&dollar;quote&lowbar;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 ``&tilde;'' 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/, &hellip;,
+<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/&lt;object&gt; &lt;attribute&gt;/ &lsqb; <em/regexp/ &rsqb;
+
+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/&lsqb;un&rsqb;ignore/ <em/pattern/ &lsqb; <em/pattern/ ... &rsqb;
+
+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/&lsqb;un&rsqb;lists/ <em/address/ &lsqb; <em/address/ ... &rsqb; 
+
+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/ &lsqb;!&rsqb;<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/ &lsqb;!&rsqb;<em/filename/ &lsqb; <em/filename/ ... &rsqb;
+
+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="&dollar;folder"> and <ref id="spoolfile" name="&dollar;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/ &lsqb; <em/field/ ... &rsqb;
+
+The ``my&lowbar;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&lowbar;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&lowbar;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&lowbar;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&lowbar;order From Date: From: To: Cc: Subject:
+</verb></tscreen>
+
+<sect1>Specify default save filename<label id="save-hook">
+<p>
+Usage: <tt/save-hook/ &lsqb;!&rsqb;<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/ &lsqb;!&rsqb;<em/regexp/ <em/mailbox/
+
+This command is used to save outgoing mail in a mailbox other than
+<ref id="record" name="&dollar;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="&dollar;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&dollar; +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/ &lsqb;!&rsqb;<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/ &lsqb;!&rsqb;<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&lowbar;forward signature=''&dquot;/
+
+Another typical use for this command is to change the values of the
+<ref id="attribution" name="&dollar;attribution">, <ref id="signature"
+name="&dollar;signature"> and <ref id="locale" name="&dollar;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/ &lsqb; <em/pattern/ ... &rsqb;
+
+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/ &lsqb;no|inv&rsqb;<em/variable/&lsqb;=<em/value/&rsqb; &lsqb; <em/variable/ ... &rsqb;<newline>
+Usage: <tt/toggle/ <em/variable/ &lsqb;<em/variable/ ... &rsqb;<newline>
+Usage: <tt/unset/ <em/variable/ &lsqb;<em/variable/ ... &rsqb;<newline>
+Usage: <tt/reset/ <em/variable/ &lsqb;<em/variable/ ... &rsqb;
+
+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/&bsol;n/ and <bf/&bsol;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&lowbar;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 ``&amp;'' 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>&tilde;/.mail&lowbar;aliases</tt> so that I can make my
+<tt>&tilde;/.muttrc</tt> readable and keep my aliases private.
+
+If the filename begins with a tilde (``&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 ``&bsol;''
+must be quoted if used for a regular expression in an initialization
+command: ``&bsol;&bsol;''.  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 ``&circ;'' 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 ``&lsqb;'' and ``&rsqb'' matches any
+single character in that list; if the first character of the list
+is a caret ``&circ;'' then it matches any character <bf/not/ in the
+list.  For example, the regular expression <bf/&lsqb;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
+``&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 ``&circ;'' place it anywhere but first.
+Finally, to include a literal hyphen ``&hyphen;'' place it last.
+
+Certain named classes of characters are predefined.  Character classes
+consist of ``&lsqb;:'', a keyword denoting the class, and ``:&rsqb;''.
+The following classes are defined by the POSIX standard:
+
+<descrip>
+<tag/&lsqb;:alnum:&rsqb;/
+Alphanumeric characters.
+<tag/&lsqb;:alpha:&rsqb;/
+Alphabetic characters.
+<tag/&lsqb;:blank:&rsqb;/
+Space or tab characters.
+<tag/&lsqb;:cntrl:&rsqb;/
+Control characters.
+<tag/&lsqb;:digit:&rsqb;/
+Numeric characters.
+<tag/&lsqb;:graph:&rsqb;/
+Characters that are both printable and visible.  (A space is printable,
+but not visible, while an ``a'' is both.)
+<tag/&lsqb;:lower:&rsqb;/
+Lower-case alphabetic characters.
+<tag/&lsqb;:print:&rsqb;/
+Printable characters (characters that are not control characters.)
+<tag/&lsqb;:punct:&rsqb;/
+Punctuation characters (characters that are not letter, digits, control
+characters, or space characters).
+<tag/&lsqb;:space:&rsqb;/
+Space characters (such as space, tab and formfeed, to name a few).
+<tag/&lsqb;:upper:&rsqb;/
+Upper-case alphabetic characters.
+<tag/&lsqb;:xdigit:&rsqb;/
+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/&lsqb;&lsqb;:digit:&rsqb;&rsqb;/ is equivalent to
+<bf/&lsqb;0-9&rsqb;/.
+
+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
+``&lsqb;.'' and ``.&rsqb;''.  For example, if ``ch'' is a collating
+element, then <bf/&lsqb;&lsqb;.ch.&rsqb;&rsqb;/ is a regexp that matches
+this collating element, while <bf/&lsqb;ch&rsqb;/ 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 ``&lsqb;=''
+and ``=&rsqb;''.  For example, the name ``e'' might be used to
+represent all of ``&egrave;'' ``&eacute;'' and ``e''.  In this case,
+<bf/&lsqb;&lsqb;=e=&rsqb;&rsqb;/ is a regexp that matches any of
+``&egrave;'', ``&eacute;'' 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/&ast;/
+The preceding item will be matched zero or more times.
+<tag/+/
+The preceding item will be matched one or more times.
+<tag/&lcub;n&rcub;/
+The preceding item is matched exactly <em/n/ times.
+<tag/&lcub;n,&rcub;/
+The preceding item is matched <em/n/ or more times.
+<tag/&lcub;,m&rcub;/
+The preceding item is matched at most <em/m/ times.
+<tag/&lcub;n,m&rcub;/
+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/&bsol;&bsol;y/
+Matches the empty string at either the beginning or the end of a word.
+<tag/&bsol;&bsol;B/
+Matches the empty string within a word.
+<tag/&bsol;&bsol;&lt;/
+Matches the empty string at the beginning of a word.
+<tag/&bsol;&bsol;&gt;/
+Matches the empty string at the end of a word.
+<tag/&bsol;&bsol;w/
+Matches any word-constituent character (letter, digit, or underscore).
+<tag/&bsol;&bsol;W/
+Matches any character that is not word-constituent.
+<tag/&bsol;&bsol;`/
+Matches the empty string at the beginning of a buffer (string).
+<tag/&bsol;&bsol;'/
+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>&gt;<em/offset/ (messages older than <em/offset/ units)
+<item>&lt;<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="&dollar;index&lowbar;format"> to include a
+<tt/&percnt;&lsqb;...&rsqb;/ 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="&dollar;auto&lowbar;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="&dollar;dfault&lowbar;hook"> variable.  The pattern
+is translated at the time the hook is declared, so the value of 
+<ref id="default_hook" name="&dollar;dfault&lowbar;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="&dollar;query&lowbar;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="&dollar;mbox&lowbar;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&lowbar;'' 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&lowbar;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="&dollar;spool"> (incoming) mailbox
+<item>&gt; -- refers to your <ref id="mbox" name="&dollar;mbox"> file
+<item>&lt; -- refers to your <ref id="record" name="&dollar;record"> file
+<item>- -- refers to the file you've last visited
+<item>&tilde; -- refers to your home directory
+<item>= or + -- refers to your <ref id="folder" name="&dollar;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="&dollar;index&lowbar;format"> variable, the escape ``&percnt;L''
+will return the string ``To &lt;list&gt;'' when ``list'' appears in the
+``To'' field, and ``Cc &lt;list&gt;'' 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="&dollar;reply&lowbar;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="&dollar;dsn&lowbar;notify"> is used to request receipts for
+different results (such as failed message, message delivered, etc.).
+<ref id="dsn_return" name="&dollar;dsn&lowbar;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&lowbar;host"> and authenticate by logging in
+as <ref id="pop_user" name="pop&lowbar;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>&dollar;{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/&dollar;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 &num; 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 &percnt;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 &percnt;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 &percnt;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="&dollar;wait&lowbar;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="&dollar;wait&lowbar;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=&lt;command&gt;</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=&lt;command&gt;</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=&lt;command&gt;</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=&lt;command&gt;</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=&lt;template&gt;</tag>
+This field specifies the format for the file denoted by &percnt;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=&lt;command&gt;</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/&percnt;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/&percnt;t/
+Mutt will expand &percnt;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>&percnt;{&lt;parameter&gt;}</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 &percnt;{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>&setmn;&percnt;</tag>
+This will be replaced by a &percnt;
+</descrip>
+Mutt does not currently support the &percnt;F and &percnt;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&lowbar;view/ muttrc command to list the
+content-types that you wish to view automatically.
+
+For instance, if you set auto&lowbar;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&lowbar;order list to determine if one of the available types
+is preferred.  The alternative&lowbar;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&lowbar;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/ &lsqb; -nz &rsqb; &lsqb; -F <em/muttrc/ &rsqb; &lsqb; -m <em/type/ &rsqb; &lsqb; -f <em/mailbox/ &rsqb; 
+
+To compose a new message
+
+<tt/mutt/ &lsqb; -n &rsqb; &lsqb; -F <em/muttrc/ &rsqb; &lsqb; -a <em/file/ &rsqb; &lsqb; -c <em/address/ &rsqb; &lsqb; -i <em/filename/ &rsqb; &lsqb; -s <em/subject/ &rsqb; <em/address/ &lsqb; <em/address/ ... &rsqb; 
+
+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 &num;2&dquot; professor@bigschool.edu
+&lt; &tilde;/run2.dat</tt>
+
+This command will send a message to ``professor@bigschool.edu'' with a subject
+of ``data set for run &num;2''.  In the body of the message will be the contents
+of the file ``&tilde;/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/ &lsqb; , <em/address/, ... &rsqb;
+<item>
+<tt><ref id="alias" name="unalias"></tt> <em/key/ <em/address/ &lsqb; , <em/address/, ... &rsqb;
+<item>
+<tt><ref id="alternative_order" name="alternative&lowbar;order"></tt> <em/mimetype/ &lsqb; <em/mimetype/ ... &rsqb;
+<item>
+<tt><ref id="auto_view" name="auto&lowbar;view"></tt> <em/mimetype/ &lsqb; <em/mimetype/ ... &rsqb;
+<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/ &lsqb; <em/regexp/ &rsqb;
+<item>
+<tt><ref id="folder-hook" name="folder-hook"></tt> <em/pattern/ <em/command/
+<item>
+<tt><ref id="ignore" name="ignore"></tt> <em/pattern/ &lsqb; <em/pattern/ ... &rsqb;
+<item>
+<tt><ref id="ignore" name="unignore"></tt> <em/pattern/ &lsqb; <em/pattern/ ... &rsqb;
+<item>
+<tt><ref id="hdr_order" name="hdr&lowbar;order"></tt> <em/header/ &lsqb; <em/header/ ... &rsqb;
+<item>
+<tt><ref id="lists" name="lists"></tt> <em/address/ &lsqb; <em/address/ ... &rsqb; 
+<item>
+<tt><ref id="lists" name="unlists"></tt> <em/address/ &lsqb; <em/address/ ... &rsqb; 
+<item>
+<tt><ref id="macro" name="macro"></tt> <em/menu/ <em/key/ <em/sequence/
+<item>
+<tt><ref id="mailboxes" name="mailboxes"></tt> <em/filename/ &lsqb; <em/filename/ ... &rsqb;
+<item>
+<tt><ref id="color" name="mono"></tt> <em/object attribute/ &lsqb; <em/regexp/ &rsqb;
+<item>
+<tt><ref id="mbox-hook" name="mbox-hook"></tt> <em/pattern/ <em/mailbox/
+<item>
+<tt><ref id="my_hdr" name="my&lowbar;hdr"></tt> <em/string/
+<item>
+<tt><ref id="my_hdr" name="unmy&lowbar;hdr"></tt> <em/field/ &lsqb; <em/field/ ... &rsqb;
+<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> &lsqb;no|inv&rsqb;<em/variable/&lsqb;=<em/value/&rsqb; &lsqb; <em/variable/ ... &rsqb;
+<item>
+<tt><ref id="set" name="toggle"></tt> <em/variable/ &lsqb;<em/variable/ ... &rsqb;
+<item>
+<tt><ref id="set" name="unset"></tt> <em/variable/ &lsqb;<em/variable/ ... &rsqb;
+<item>
+<tt><ref id="source" name="source"></tt> <em/filename/
+</itemize>
+
+<sect1>Configuration variables<label id="variables">
+<p>
+
+<sect2>abort&lowbar;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&lowbar;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&lowbar;file<label id="alias_file">
+<p>
+Type: string<newline>
+Default: &tilde;/.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&lowbar;format<label id="alias_format">
+<p>
+Type: string<newline>
+Default: &dquot;&percnt;2n &percnt;t &percnt;-10a   &percnt;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&lowbar;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&lowbar;cursor<label id="arrow_cursor">
+<p>
+Type: boolean<newline>
+Default: unset
+
+When set, an arrow (``-&gt;'') 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&lowbar;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 &percnt;d, &percnt;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="&dollar;index&lowbar;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="&dollar;edit&lowbar;headers"> variable is
+also set, the initial prompts in the send-menu are always skipped, even
+when composing a new message.
+
+<sect2>auto&lowbar;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&lowbar;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&lowbar;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&lowbar;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&lowbar;name">,
+<ref id="force_name" name="force&lowbar;name"> and
+<ref id="fcc-hook" name="fcc-hook">.
+
+<sect2>date&lowbar;format<label id="date_format">
+<p>
+Type: string<newline>
+Default: &dquot;!&percnt;a, &percnt;b &percnt;d, &percnt;Y at &percnt;I:&percnt;M:&percnt;S&percnt;p &percnt;Z&dquot;
+
+This variable controls the format of the date printed
+by the ``&percnt;d'' sequence in <ref id="index_format"
+name="&dollar;index&lowbar;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&lowbar;hook<label id="default_hook">
+<p>
+Type: string<newline>
+Default: "&tilde;f &percnt;s | (&tilde;P (&tilde;c &percnt;s | &tilde;t &percnt;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&lowbar;format<label id="delete_format">
+<p>
+Type: string<newline>
+Default: "&lsqb;-- Attachment from &percnt;u deleted on &percnt;&lt;&percnt;D&gt; --&rsqb;"
+
+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="&dollar;index&lowbar;format">
+variable.
+
+<sect2>dsn&lowbar;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&lowbar;notify=&dquot;failure,delay&dquot;/
+
+<sect2>dsn&lowbar;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&lowbar;return=hdrs/
+
+<sect2>edit&lowbar;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 &dollar;VISUAL, &dollar;EDITOR, or &dquot;vi&dquot;
+
+This variable specifies which editor to use when composing messages.
+
+<sect2>empty&lowbar;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: &tilde;
+
+Escape character to use for functions in the builtin editor.
+
+<sect2>fast&lowbar;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="&dollar;autoedit"> variable is set.
+
+<sect2>fcc&lowbar;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: &tilde;/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&lowbar;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&lowbar;name<label id="force_name">
+<p>
+Type: boolean<newline>
+Default: unset
+
+This variable is similar to <ref id="save_name"
+name="&dollar;save&lowbar;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="&dollar;record"> variable.
+
+<sect2>forward&lowbar;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&lowbar;forward"> is <em/unset/, otherwise <ref
+id="mime_forward_decode" name="mime&lowbar;forward&lowbar;decode"> is
+used instead.
+
+<sect2>forward&lowbar;format<label id="forward_format">
+<p>
+Type: format string<newline>
+Default: "&lsqb;&percnt;a: &percnt;s&rsqb;"
+
+This variable controls the default subject when forwarding a message.  It
+uses the same format sequences as the <ref id="index_format"
+name="&dollar;index&lowbar;format"> variable.
+
+<sect2>forward&lowbar;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&lowbar;forward"> is <em/unset/) will be
+quoted using <ref id="indent_string" name="indent&lowbar;string">.
+
+<sect2>index&lowbar;format<label id="index_format">
+<p>
+Type: format string<newline>
+Default: &dquot;&percnt;4C &percnt;Z &percnt;{&percnt;b &percnt;d} &percnt;-15.15L (&percnt;4l) &percnt;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="&dollar;to&lowbar;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&lowbar;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&lowbar;list&lowbar;reply&lowbar;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&lowbar;reply&lowbar;to
+<p>
+Type: format string<newline>
+Default: &dquot;&percnt;i; from &bsol;&dquot;&percnt;n&bsol;&dquot; on &percnt;{!&percnt;a, &percnt;b &percnt;d, &percnt;Y at &percnt;I:&percnt;M:&percnt;S&percnt;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="&dollar;index&lowbar;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&lowbar;string<label id="indent_string">
+<p>
+Type: format string<newline>
+Default: "&gt; "
+
+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&lowbar;TIME/.
+
+<sect2>mailcap&lowbar;path<label id="mailcap_path">
+<p>
+Type: string<newline>
+Default: &dollar;MAILCAPS or &tilde;/.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&lowbar;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="&dollar;smart&lowbar;wrap"> variable.
+
+<sect2>mask<label id="mask">
+<p>
+Type: string<newline>
+Default: "^(&bsol;.&bsol;.&dollar;|&lsqb;^.&rsqb;)"
+
+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&lowbar;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&lowbar;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&lowbar;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&lowbar;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&lowbar;decode"> and
+<ref id="mime_forward_decode"
+name="mime&lowbar;forward&lowbar;decode">.
+
+<sect2>mime&lowbar;forward&lowbar;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&lowbar;forward"> is <em/set/. Otherwise <ref
+id="forward_decode" name="forward&lowbar;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="&dollar;mbox"> mailbox, or as a
+result of a <ref id="mbox-hook" name="mbox-hook"> command.
+
+<sect2>message&lowbar;format<label id="message_format">
+<p>
+Type: string<newline>
+Default: &dquot&percnt;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&lowbar;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&lowbar;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&lowbar;format<label id="pager_format">
+<p>
+Type: format string<newline>
+Default: &dquot;-&percnt;S- &percnt;C/&percnt;m: &percnt;-20.20n   &percnt;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&lowbar;format"> section.
+
+<sect2>pager&lowbar;index&lowbar;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&lowbar;index&lowbar;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&lowbar;index&lowbar;lines/, then the index will only use as
+many lines as it needs.
+
+<sect2>pager&lowbar;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&lowbar;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&lowbar;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&lowbar;default&lowbar;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="&dollar;pgp&lowbar;receive&lowbar;version">, <ref
+id="pgp_send_version" name="&dollar;pgp&lowbar;send&lowbar;version">,
+and <ref id="pgp_key_version"
+name="&dollar;pgp&lowbar;key&lowbar;version">.
+
+<sect2>pgp&lowbar;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&lowbar;key&lowbar;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="&dollar;pgp&lowbar;default&lowbar;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&lowbar;long&lowbar;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&lowbar;receive&lowbar;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="&dollar;pgp&lowbar;default&lowbar;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&lowbar;replyencrypt<label id="pgp_replyencrypt">
+<p>
+Type: boolean<newline>
+Default: unset
+
+If set, automatically PGP encrypt replies to messages which are encrypted.
+
+<sect2>pgp&lowbar;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&lowbar;send&lowbar;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="&dollar;pgp&lowbar;default&lowbar;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&lowbar;sign&lowbar;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&lowbar;sign&lowbar;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&lowbar;strict&lowbar;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&lowbar;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&lowbar;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&lowbar;v2&lowbar;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&lowbar;v2&lowbar;pubring<label id="pgp_v2_pubring">
+<p>
+Type: string<newline>
+Default: &dollar;PGPPATH/pubring.pgp or &tilde;/.pgp/pubring.pgp if
+&dollar;PGPPATH isn't set.
+
+Points to the PGP 2.* public keyring.
+
+<sect2>pgp&lowbar;v2&lowbar;secring<label id="pgp_v2_secring">
+<p>
+Type: string<newline>
+Default: &dollar;PGPPATH/secring.pgp or &tilde;/.pgp/secring.pgp if
+&dollar;PGPPATH isn't set.
+
+Points to the PGP 2.* secret keyring.
+
+<sect2>pgp&lowbar;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&lowbar;v5&lowbar;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&lowbar;v5&lowbar;pubring<label id="pgp_v5_pubring">
+<p>
+Type: string<newline>
+Default: &dollar;PGPPATH/pubring.pkr or &tilde;/.pgp/pubring.pkr if
+&dollar;PGPPATH isn't set.
+
+Points to the PGP 5.* public keyring.
+
+<sect2>pgp&lowbar;v5&lowbar;secring<label id="pgp_v5_secring">
+<p>
+Type: string<newline>
+Default: &dollar;PGPPATH/secring.skr or &tilde;/.pgp/secring.skr if
+&dollar;PGPPATH isn't set.
+
+Points to the PGP 5.* secret keyring.
+
+<sect2>pipe&lowbar;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&lowbar;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&lowbar;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="&dollar;pipe&lowbar;sep"> separator is
+added after each message.
+
+<sect2>pop&lowbar;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&lowbar;host<label id="pop_host">
+<p>
+Type: string<newline>
+Default: none
+
+The name or address of your POP3 server.
+
+<sect2>pop&lowbar;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&lowbar;port<label id="pop_port">
+<p>
+Type: number<newline>
+Default: 110
+
+This variable specifies which port your POP server is listening on.
+
+<sect2>pop&lowbar;user<label id="pop_user">
+<p>
+Type: string<newline>
+Default: login name on local system
+
+Your login name on the POP3 server.
+
+<sect2>post&lowbar;indent&lowbar;string<label id="post_indent_string">
+<p>
+Type: format string<newline>
+Default: none
+
+Similar to the <ref id="attribution" name="&dollar;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="&dollar;postponed"> mailbox when you elect not to send
+immediately.
+
+<sect2>postponed<label id="postponed">
+<p>
+Type: string<newline>
+Default: &tilde;/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="&dollar;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&lowbar;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&lowbar;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&lowbar;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&lowbar;regexp<label id="quote_regexp">
+<p>
+Type: string<newline>
+Default: "^(&lsqb; &bsol;t&rsqb;*&lsqb;&gt;|&num;:}&rsqb;)+"
+
+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&lowbar;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&lowbar;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="&dollar;write&lowbar;inc"> variable.
+
+<sect2>read&lowbar;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&lowbar;hdr"> command to create a <em/Bcc:/ field with your
+email address in it.)
+
+The value of <em/&dollar;record/ is overridden by the
+<ref id="force_name" name="&dollar;force&lowbar;name"> and 
+<ref id="save_name" name="&dollar;save&lowbar;name"> variables, and the
+<ref id="fcc-hook" name="fcc-hook"> command.
+
+<sect2>reply&lowbar;regexp<label id="reply_regexp">
+<p>
+Type: string<newline>
+Default: "^(re|aw):&lsqb; &bsol;t&rsqb;*"
+
+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&lowbar;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&lowbar;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&dollar;erve addresses).
+
+<sect2>reverse&lowbar;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&lowbar;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&lowbar;name"> or <ref id="force_name"
+name="force&lowbar;name"> is set too, the selection of the fcc folder
+will be changed as well.
+
+<sect2>save&lowbar;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&lowbar;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="&dollar;force&lowbar;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&lowbar;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&lowbar;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 ``-- &bsol;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: &tilde;/.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&lowbar;search<label id="simple_search">
+<p>
+Type: string<newline>
+Default: &dquot;&tilde;f &percnt;s | &tilde;s &percnt;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/&tilde;/
+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/&tilde;f joe | &tilde;s joe/
+
+<sect2>smart&lowbar;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="&dollar;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&lowbar;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&lowbar;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&lowbar;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/&dollar;USER or /usr/spool/mail/&dollar;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/&dollar;MAIL/
+if it is not set.
+
+<sect2>sort&lowbar;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&lowbar;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&lowbar;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&lowbar;regexp"> parts of
+both messages are identical.
+
+<sect2>status&lowbar;chars<label id="status_chars">
+<p>
+Type: string<newline>
+Default: &dquot;-*&percnt;&dquot;
+
+Controls the characters used by the &dquot;&percnt;r&dquot; indicator
+in <ref id="status_format" name="status&lowbar;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;&percnt;&dquot;).
+
+<sect2>status&lowbar;format<label id="status_format">
+<p>
+Type: string<newline>
+Default: &dquot;-&percnt;r-Mutt: &percnt;f &lsqb;Msgs:&percnt;?M?&percnt;M/?&percnt;m&percnt;?n? New:&percnt;n?&percnt;?o? Old:&percnt;o?&percnt;?d? Del:%d?&percnt;?F? Flag:%F?&percnt;?t? Tag:%t?&percnt;?p? Post:%p?&percnt;?b? Inc:&percnt;b? &percnt;?l? &percnt;l?&rsqb;---(&percnt;s/&percnt;S)-&percnt;&gt;-(&percnt;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="&dollar;index&lowbar;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&lowbar;char/ is a character from the table above, and
+<em/optional&lowbar;string/ is the string you would like printed if
+<em/status&lowbar;char/ is nonzero.  <em/optional&lowbar;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&lowbar;on&lowbar;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&lowbar;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&lowbar;search<label id="thorough_search">
+<p>
+Type: boolean<newline>
+Default: unset
+
+Affects the <em/&tilde;b/ and <em/&tilde;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 (&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&lowbar;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&lowbar;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="&dollar;sendmail">
+with the <tt/-B8BITMIME/ flag when sending 8-bit messages to enable ESMTP
+negotiation.
+
+<sect2>use&lowbar;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="&dollar;hostname">.  If
+<em/unset/, no addresses will be qualified.
+
+<sect2>use&lowbar;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&lowbar;hdr"> command.
+
+<sect2>use&lowbar;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&lowbar;NOASK/.  Setting this to <bf/1/ is
+equivalent to setting <em/use&lowbar;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&lowbar;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: &dollar;VISUAL
+
+Specifies the visual editor to invoke when the <em/&tilde;v/ command is given in
+the builtin editor.
+
+<sect2>wait&lowbar;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&lowbar;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&lowbar;inc<label id="write_inc">
+<p>
+Type: number<newline>
+Default: 10
+
+When writing a mailbox, a message will be printed every
+<em/write&lowbar;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="&dollar;read&lowbar;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="&lt;kari.hurtta@fmi.fi&gt;">
+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="&lt;Francois.Berjon@aar.alcatel-alsthom.fr&gt;">,<newline>
+Aric Blumer <htmlurl url="mailto:aric@fore.com" name="&lt;aric@fore.com&gt;">,<newline>
+John Capo <htmlurl url="mailto:jc@irbs.com" name="&lt;jc@irbs.com&gt;">,<newline>
+Liviu Daia <htmlurl url="mailto:daia@stoilow.imar.ro" name="&lt;daia@stoilow.imar.ro&gt;">,<newline>
+David DeSimone <htmlurl url="mailto:fox@convex.hp.com" name="&lt;fox@convex.hp.com&gt;">,<newline>
+Nickolay N. Dudorov <htmlurl url="mailto:nnd@wint.itfs.nsk.su" name="&lt;nnd@wint.itfs.nsk.su&gt;">,<newline>
+Michael Finken <htmlurl url="mailto:finken@conware.de" name="&lt;finken@conware.de&gt;">,<newline>
+Sven Guckes <htmlurl url="mailto:guckes@math.fu-berlin.de" name="&lt;guckes@math.fu-berlin.de&gt;">,<newline>
+Mark Holloman <htmlurl url="mailto:holloman@nando.net" name="&lt;holloman@nando.net&gt;">,<newline>
+Andreas Holzmann <htmlurl url="mailto:holzmann@fmi.uni-passau.de" name="&lt;holzmann@fmi.uni-passau.de&gt;">,<newline>
+David Jeske <htmlurl url="mailto:jeske@igcom.net" name="&lt;jeske@igcom.net&gt;">,<newline>
+Christophe Kalt <htmlurl url="mailto:kalt@hugo.int-evry.fr" name="&lt;kalt@hugo.int-evry.fr&gt;">,<newline>
+Felix von Leitner (a.k.a ``Fefe'') <htmlurl url="mailto:leitner@math.fu-berlin.de" name="&lt;leitner@math.fu-berlin.de&gt;">,<newline>
+Brandon Long <htmlurl url="mailto:blong@fiction.net" name="&lt;blong@fiction.net&gt;">,<newline>
+Lars Marowsky-Bree <htmlurl url="mailto:lmb@pointer.in-minden.de" name="&lt;lmb@pointer.in-minden.de&gt;">,<newline>
+Thomas ``Mike'' Michlmayr <htmlurl url="mailto:mike@cosy.sbg.ac.at" name="&lt;mike@cosy.sbg.ac.at&gt;">,<newline>
+David O'Brien <htmlurl url="mailto:obrien@Nuxi.cs.ucdavis.edu" name="&lt;obrien@Nuxi.cs.ucdavis.edu&gt;">,<newline>
+Clint Olsen <htmlurl url="mailto:olsenc@ichips.intel.com" name="&lt;olsenc@ichips.intel.com&gt;">,<newline>
+Park Myeong Seok <htmlurl url="mailto:pms@romance.kaist.ac.kr" name="&lt;pms@romance.kaist.ac.kr&gt;">,<newline>
+Thomas Parmelan <htmlurl url="mailto:tom@ankh.fr.eu.org" name="&lt;tom@ankh.fr.eu.org&gt;">,<newline>
+Ollivier Robert <htmlurl url="mailto:roberto@keltia.freenix.fr" name="&lt;roberto@keltia.freenix.fr&gt;">,<newline>
+Thomas Roessler <htmlurl url="mailto:roessler@guug.de" name="&lt;roessler@guug.de&gt;">,<newline>
+Allain Thivillon <htmlurl url="mailto:Allain.Thivillon@alma.fr" name="&lt;Allain.Thivillon@alma.fr&gt;">,<newline>
+Ken Weinert <htmlurl url="mailto:kenw@ihs.com" name="&lt;kenw@ihs.com&gt;">
+
+<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>
diff --git a/doc/manual.txt b/doc/manual.txt
new file mode 100644 (file)
index 0000000..17cdd29
--- /dev/null
@@ -0,0 +1,4115 @@
+  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.
+
diff --git a/doc/mutt.man b/doc/mutt.man
new file mode 100644 (file)
index 0000000..7fcc3ce
--- /dev/null
@@ -0,0 +1,259 @@
+.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
diff --git a/doc/mutt.sgml b/doc/mutt.sgml
new file mode 100644 (file)
index 0000000..169fe05
--- /dev/null
@@ -0,0 +1,181 @@
+<!doctype linuxdoc system>
+
+<manpage title="Mutt" sectnum="1">
+
+<sect1>NAME
+<p>
+mutt - The Mutt Mail User Agent
+
+<sect1>SYNOPSIS
+<p>
+mutt &lsqb; -hnpRvxyzZ &rsqb;
+&lsqb; -a <em/file/ &rsqb;
+&lsqb; -b <em/address/ &rsqb;
+&lsqb; -c <em/address/ &rsqb;
+&lsqb; -e <em/command/ &rsqb;
+&lsqb; -f <em/mailbox/ &rsqb;
+&lsqb; -F <em/muttrc/ &rsqb;
+&lsqb; -H <em/draftfile/ &rsqb;
+&lsqb; -i <em/include/ &rsqb;
+&lsqb; -m <em/type/ &rsqb;
+&lsqb; -s <em/subject/ &rsqb;
+&lsqb <em/address/ ... &rsqb;
+
+<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 &tilde;/.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 &tilde;v command is given in the builtin editor.
+</descrip>
+
+<sect1>FILES
+<p>
+<descrip>
+<tag>&tilde;/.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>&tilde;/.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>&tilde;/.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/&tilde;me/mutt/
+
+<sect1>AUTHOR
+<p>
+Michael Elkins &lt;me@cs.hmc.edu&gt;
+<p>
+http://www.cs.hmc.edu/&tilde;me
+</manpage>
diff --git a/doc/pgp-Notes.txt b/doc/pgp-Notes.txt
new file mode 100644 (file)
index 0000000..fc064f8
--- /dev/null
@@ -0,0 +1,162 @@
+          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.
+
diff --git a/doc/style-guide b/doc/style-guide
new file mode 100644 (file)
index 0000000..1ac44ab
--- /dev/null
@@ -0,0 +1,18 @@
+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!
diff --git a/edit.c b/edit.c
new file mode 100644 (file)
index 0000000..3f02f0b
--- /dev/null
+++ b/edit.c
@@ -0,0 +1,458 @@
+/*
+ * 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);
+}
diff --git a/enter.c b/enter.c
new file mode 100644 (file)
index 0000000..a7981cc
--- /dev/null
+++ b/enter.c
@@ -0,0 +1,525 @@
+/*
+ * 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 */
+}
diff --git a/filter.c b/filter.c
new file mode 100644 (file)
index 0000000..0685f69
--- /dev/null
+++ b/filter.c
@@ -0,0 +1,179 @@
+/*
+ * 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;
+}
diff --git a/flags.c b/flags.c
new file mode 100644 (file)
index 0000000..20fc650
--- /dev/null
+++ b/flags.c
@@ -0,0 +1,303 @@
+/*
+ * 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;
+}
diff --git a/from.c b/from.c
new file mode 100644 (file)
index 0000000..4279f9a
--- /dev/null
+++ b/from.c
@@ -0,0 +1,187 @@
+/*
+ * 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));
+}
diff --git a/functions.h b/functions.h
new file mode 100644 (file)
index 0000000..f8a1af1
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+ * 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 */
diff --git a/gen_defs b/gen_defs
new file mode 100755 (executable)
index 0000000..926c3da
--- /dev/null
+++ b/gen_defs
@@ -0,0 +1,31 @@
+#!/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
diff --git a/getdomain.c b/getdomain.c
new file mode 100644 (file)
index 0000000..70c8e88
--- /dev/null
@@ -0,0 +1,54 @@
+#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);
+}
diff --git a/globals.h b/globals.h
new file mode 100644 (file)
index 0000000..8413a7a
--- /dev/null
+++ b/globals.h
@@ -0,0 +1,144 @@
+/*
+ * 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 */
diff --git a/handler.c b/handler.c
new file mode 100644 (file)
index 0000000..15a0ca9
--- /dev/null
+++ b/handler.c
@@ -0,0 +1,1289 @@
+/*
+ * 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);
+  }
+}
diff --git a/hash.c b/hash.c
new file mode 100644 (file)
index 0000000..f670eb4
--- /dev/null
+++ b/hash.c
@@ -0,0 +1,141 @@
+/*
+ * 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);
+}
diff --git a/hash.h b/hash.h
new file mode 100644 (file)
index 0000000..a0757c6
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,43 @@
+/*
+ * 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 *));
diff --git a/hdrline.c b/hdrline.c
new file mode 100644 (file)
index 0000000..1d43bc1
--- /dev/null
+++ b/hdrline.c
@@ -0,0 +1,481 @@
+/*
+ * 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);
+}
diff --git a/headers.c b/headers.c
new file mode 100644 (file)
index 0000000..b718eaf
--- /dev/null
+++ b/headers.c
@@ -0,0 +1,269 @@
+/* 
+ * 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);
+}
diff --git a/help.c b/help.c
new file mode 100644 (file)
index 0000000..fa79b4c
--- /dev/null
+++ b/help.c
@@ -0,0 +1,205 @@
+/*
+ * 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);
+}
diff --git a/hook.c b/hook.c
new file mode 100644 (file)
index 0000000..b50af1f
--- /dev/null
+++ b/hook.c
@@ -0,0 +1,308 @@
+/* 
+ * 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);
+}
diff --git a/imap.c b/imap.c
new file mode 100644 (file)
index 0000000..022d72d
--- /dev/null
+++ b/imap.c
@@ -0,0 +1,918 @@
+/*
+ * 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);
+}
diff --git a/init.c b/init.c
new file mode 100644 (file)
index 0000000..ddcb531
--- /dev/null
+++ b/init.c
@@ -0,0 +1,1437 @@
+/*
+ * 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 */
+}
diff --git a/init.h b/init.h
new file mode 100644 (file)
index 0000000..4051571
--- /dev/null
+++ b/init.h
@@ -0,0 +1,342 @@
+/*
+ * 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 }
+};
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..89fc9b0
--- /dev/null
@@ -0,0 +1,238 @@
+#! /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
diff --git a/keymap.c b/keymap.c
new file mode 100644 (file)
index 0000000..0eebfd3
--- /dev/null
+++ b/keymap.c
@@ -0,0 +1,637 @@
+/*
+ * 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);
+}
diff --git a/keymap.h b/keymap.h
new file mode 100644 (file)
index 0000000..580bc2b
--- /dev/null
+++ b/keymap.h
@@ -0,0 +1,105 @@
+/*
+ * 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 */
diff --git a/lib.c b/lib.c
new file mode 100644 (file)
index 0000000..e71027b
--- /dev/null
+++ b/lib.c
@@ -0,0 +1,1037 @@
+/*
+ * 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);
+}
diff --git a/mailbox.h b/mailbox.h
new file mode 100644 (file)
index 0000000..ad9d841
--- /dev/null
+++ b/mailbox.h
@@ -0,0 +1,58 @@
+/*
+ * 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 *);
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..29848c2
--- /dev/null
+++ b/main.c
@@ -0,0 +1,644 @@
+/*
+ * 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);
+}
diff --git a/mapping.h b/mapping.h
new file mode 100644 (file)
index 0000000..1464075
--- /dev/null
+++ b/mapping.h
@@ -0,0 +1,28 @@
+/*
+ * 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 *);
diff --git a/mbox.c b/mbox.c
new file mode 100644 (file)
index 0000000..0e3211f
--- /dev/null
+++ b/mbox.c
@@ -0,0 +1,1008 @@
+/*
+ * 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;
+}
diff --git a/menu.c b/menu.c
new file mode 100644 (file)
index 0000000..f582a1e
--- /dev/null
+++ b/menu.c
@@ -0,0 +1,826 @@
+/*
+ * 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 */
+}
diff --git a/mh.c b/mh.c
new file mode 100644 (file)
index 0000000..b4f088a
--- /dev/null
+++ b/mh.c
@@ -0,0 +1,765 @@
+/*
+ * 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);
+}
diff --git a/mime.h b/mime.h
new file mode 100644 (file)
index 0000000..662bae1
--- /dev/null
+++ b/mime.h
@@ -0,0 +1,68 @@
+/*
+ * 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)]
diff --git a/mime.types b/mime.types
new file mode 100644 (file)
index 0000000..1ce7cab
--- /dev/null
@@ -0,0 +1,80 @@
+#
+# 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
diff --git a/mkinstalldirs b/mkinstalldirs
new file mode 100755 (executable)
index 0000000..d0fd194
--- /dev/null
@@ -0,0 +1,40 @@
+#! /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
diff --git a/mutt.h b/mutt.h
new file mode 100644 (file)
index 0000000..34b8bd5
--- /dev/null
+++ b/mutt.h
@@ -0,0 +1,593 @@
+/*
+ * 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"
diff --git a/mutt_curses.h b/mutt_curses.h
new file mode 100644 (file)
index 0000000..2d5b7ea
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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
diff --git a/mutt_menu.h b/mutt_menu.h
new file mode 100644 (file)
index 0000000..e30da69
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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);
diff --git a/mutt_regex.h b/mutt_regex.h
new file mode 100644 (file)
index 0000000..27133be
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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 */
diff --git a/mx.c b/mx.c
new file mode 100644 (file)
index 0000000..604e7f3
--- /dev/null
+++ b/mx.c
@@ -0,0 +1,1519 @@
+/*
+ * 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, &times);
+#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);
+}
diff --git a/mx.h b/mx.h
new file mode 100644 (file)
index 0000000..f03c590
--- /dev/null
+++ b/mx.h
@@ -0,0 +1,63 @@
+/*
+ * 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);
diff --git a/pager.c b/pager.c
new file mode 100644 (file)
index 0000000..63f9a26
--- /dev/null
+++ b/pager.c
@@ -0,0 +1,2207 @@
+/*
+ * 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);
+}
diff --git a/pager.h b/pager.h
new file mode 100644 (file)
index 0000000..bc3fab2
--- /dev/null
+++ b/pager.h
@@ -0,0 +1,28 @@
+/*
+ * 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 *);
diff --git a/parse.c b/parse.c
new file mode 100644 (file)
index 0000000..3ae7f94
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,1249 @@
+/*
+ * 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;
+}
diff --git a/parse.h b/parse.h
new file mode 100644 (file)
index 0000000..d9cc4c8
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,7 @@
+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);
diff --git a/pattern.c b/pattern.c
new file mode 100644 (file)
index 0000000..0435ac7
--- /dev/null
+++ b/pattern.c
@@ -0,0 +1,1075 @@
+/*
+ * 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);
+}
diff --git a/pgp.c b/pgp.c
new file mode 100644 (file)
index 0000000..881f325
--- /dev/null
+++ b/pgp.c
@@ -0,0 +1,1278 @@
+/*
+ * 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 */
diff --git a/pgp.h b/pgp.h
new file mode 100644 (file)
index 0000000..80af991
--- /dev/null
+++ b/pgp.h
@@ -0,0 +1,155 @@
+/*
+ * 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 */
diff --git a/pgpinvoke.c b/pgpinvoke.c
new file mode 100644 (file)
index 0000000..81823b1
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * 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);
+}
diff --git a/pgpkey.c b/pgpkey.c
new file mode 100644 (file)
index 0000000..23120a2
--- /dev/null
+++ b/pgpkey.c
@@ -0,0 +1,589 @@
+/*
+ * 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 */
diff --git a/pgppubring.c b/pgppubring.c
new file mode 100644 (file)
index 0000000..322b32a
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ * 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);
+  }
+}
diff --git a/pop.c b/pop.c
new file mode 100644 (file)
index 0000000..c3495a0
--- /dev/null
+++ b/pop.c
@@ -0,0 +1,313 @@
+/*
+ * 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);
+}
diff --git a/postpone.c b/postpone.c
new file mode 100644 (file)
index 0000000..805a3c6
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * 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 */
diff --git a/protos.h b/protos.h
new file mode 100644 (file)
index 0000000..678db40
--- /dev/null
+++ b/protos.h
@@ -0,0 +1,384 @@
+/*
+ * 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 *);
diff --git a/query.c b/query.c
new file mode 100644 (file)
index 0000000..f9d75e9
--- /dev/null
+++ b/query.c
@@ -0,0 +1,456 @@
+/*
+ * 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);
+}
diff --git a/reap.pl b/reap.pl
new file mode 100755 (executable)
index 0000000..90e532b
--- /dev/null
+++ b/reap.pl
@@ -0,0 +1,26 @@
+#!/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);
+}
diff --git a/recvattach.c b/recvattach.c
new file mode 100644 (file)
index 0000000..2ee6956
--- /dev/null
@@ -0,0 +1,779 @@
+/*
+ * 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 */
+}
diff --git a/reldate.h b/reldate.h
new file mode 100644 (file)
index 0000000..84aeef0
--- /dev/null
+++ b/reldate.h
@@ -0,0 +1 @@
+const char *ReleaseDate = "1998-05-14";
diff --git a/resize.c b/resize.c
new file mode 100644 (file)
index 0000000..ccbcb9d
--- /dev/null
+++ b/resize.c
@@ -0,0 +1,77 @@
+/*
+ * 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
+}
diff --git a/rfc1524.c b/rfc1524.c
new file mode 100644 (file)
index 0000000..ac16ae8
--- /dev/null
+++ b/rfc1524.c
@@ -0,0 +1,599 @@
+/*
+ * 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;
+}
diff --git a/rfc1524.h b/rfc1524.h
new file mode 100644 (file)
index 0000000..a34e7fe
--- /dev/null
+++ b/rfc1524.h
@@ -0,0 +1,45 @@
+/*
+ * 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 */
diff --git a/rfc2047.c b/rfc2047.c
new file mode 100644 (file)
index 0000000..733a8d2
--- /dev/null
+++ b/rfc2047.c
@@ -0,0 +1,393 @@
+/*
+ * 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;
+  }
+}
diff --git a/rfc2047.h b/rfc2047.h
new file mode 100644 (file)
index 0000000..07bb1b8
--- /dev/null
+++ b/rfc2047.h
@@ -0,0 +1,23 @@
+/*
+ * 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 *);
diff --git a/rfc822.c b/rfc822.c
new file mode 100644 (file)
index 0000000..487bea1
--- /dev/null
+++ b/rfc822.c
@@ -0,0 +1,762 @@
+/*
+ * 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
diff --git a/rfc822.h b/rfc822.h
new file mode 100644 (file)
index 0000000..f9ad5d0
--- /dev/null
+++ b/rfc822.h
@@ -0,0 +1,64 @@
+/*
+ * 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 */
diff --git a/rx/COPYING.LIB b/rx/COPYING.LIB
new file mode 100644 (file)
index 0000000..eb685a5
--- /dev/null
@@ -0,0 +1,481 @@
+                 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!
diff --git a/rx/Makefile b/rx/Makefile
new file mode 100644 (file)
index 0000000..160bc24
--- /dev/null
@@ -0,0 +1,19 @@
+# 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 *~
diff --git a/rx/Makefile.in b/rx/Makefile.in
new file mode 100644 (file)
index 0000000..b52d64c
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# 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 *~
diff --git a/rx/_rx.h b/rx/_rx.h
new file mode 100644 (file)
index 0000000..2c982a2
--- /dev/null
+++ b/rx/_rx.h
@@ -0,0 +1,176 @@
+/* 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 */
diff --git a/rx/hashrexp.c b/rx/hashrexp.c
new file mode 100644 (file)
index 0000000..33c22ba
--- /dev/null
@@ -0,0 +1,50 @@
+/*     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)));
+}
+
+
diff --git a/rx/inst-rxposix.h b/rx/inst-rxposix.h
new file mode 100644 (file)
index 0000000..b6c6746
--- /dev/null
@@ -0,0 +1,155 @@
+/* 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 */
diff --git a/rx/rx.c b/rx/rx.c
new file mode 100644 (file)
index 0000000..afa8485
--- /dev/null
+++ b/rx/rx.c
@@ -0,0 +1,85 @@
+/*     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;
+    }
+}
diff --git a/rx/rx.h b/rx/rx.h
new file mode 100644 (file)
index 0000000..ccca945
--- /dev/null
+++ b/rx/rx.h
@@ -0,0 +1,49 @@
+/* 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 */
diff --git a/rx/rxall.h b/rx/rxall.h
new file mode 100644 (file)
index 0000000..adec72d
--- /dev/null
@@ -0,0 +1,36 @@
+/* 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 */
diff --git a/rx/rxanal.c b/rx/rxanal.c
new file mode 100644 (file)
index 0000000..431be7a
--- /dev/null
@@ -0,0 +1,732 @@
+/*     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
+
diff --git a/rx/rxanal.h b/rx/rxanal.h
new file mode 100644 (file)
index 0000000..53dd576
--- /dev/null
@@ -0,0 +1,79 @@
+/* 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 */
diff --git a/rx/rxbasic.c b/rx/rxbasic.c
new file mode 100644 (file)
index 0000000..e6e0e59
--- /dev/null
@@ -0,0 +1,118 @@
+/*     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);
+}
diff --git a/rx/rxbasic.h b/rx/rxbasic.h
new file mode 100644 (file)
index 0000000..876381e
--- /dev/null
@@ -0,0 +1,60 @@
+/* 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 */
diff --git a/rx/rxbitset.c b/rx/rxbitset.c
new file mode 100644 (file)
index 0000000..a34b8b3
--- /dev/null
@@ -0,0 +1,364 @@
+/*     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;
+}     
diff --git a/rx/rxbitset.h b/rx/rxbitset.h
new file mode 100644 (file)
index 0000000..9ddb0f0
--- /dev/null
@@ -0,0 +1,109 @@
+/* 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 */
diff --git a/rx/rxcontext.h b/rx/rxcontext.h
new file mode 100644 (file)
index 0000000..b72cdfc
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 */
diff --git a/rx/rxcset.c b/rx/rxcset.c
new file mode 100644 (file)
index 0000000..e8f21df
--- /dev/null
@@ -0,0 +1,79 @@
+/*     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);
+}
+
diff --git a/rx/rxcset.h b/rx/rxcset.h
new file mode 100644 (file)
index 0000000..d130189
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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 */
diff --git a/rx/rxgnucomp.c b/rx/rxgnucomp.c
new file mode 100644 (file)
index 0000000..18d4008
--- /dev/null
@@ -0,0 +1,1665 @@
+/*     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;
+}
+
+
diff --git a/rx/rxgnucomp.h b/rx/rxgnucomp.h
new file mode 100644 (file)
index 0000000..d572ece
--- /dev/null
@@ -0,0 +1,210 @@
+/* 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 */
diff --git a/rx/rxhash.c b/rx/rxhash.c
new file mode 100644 (file)
index 0000000..4e95973
--- /dev/null
@@ -0,0 +1,394 @@
+/*     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;
+}
+
diff --git a/rx/rxhash.h b/rx/rxhash.h
new file mode 100644 (file)
index 0000000..9763fdf
--- /dev/null
@@ -0,0 +1,112 @@
+/* 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 */
+
diff --git a/rx/rxnfa.c b/rx/rxnfa.c
new file mode 100644 (file)
index 0000000..d67dd0e
--- /dev/null
@@ -0,0 +1,853 @@
+/*     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);
+}
diff --git a/rx/rxnfa.h b/rx/rxnfa.h
new file mode 100644 (file)
index 0000000..82a135b
--- /dev/null
@@ -0,0 +1,232 @@
+/* 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 */
diff --git a/rx/rxnode.c b/rx/rxnode.c
new file mode 100644 (file)
index 0000000..a4e1cca
--- /dev/null
@@ -0,0 +1,545 @@
+/* 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;
+}
diff --git a/rx/rxnode.h b/rx/rxnode.h
new file mode 100644 (file)
index 0000000..ea3a1fc
--- /dev/null
@@ -0,0 +1,125 @@
+/* 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 */
diff --git a/rx/rxposix.c b/rx/rxposix.c
new file mode 100644 (file)
index 0000000..17a7e10
--- /dev/null
@@ -0,0 +1,484 @@
+/*     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;
+    }
+}
diff --git a/rx/rxposix.h b/rx/rxposix.h
new file mode 100644 (file)
index 0000000..9edcfb5
--- /dev/null
@@ -0,0 +1,51 @@
+/* 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 */
diff --git a/rx/rxproto.h b/rx/rxproto.h
new file mode 100644 (file)
index 0000000..25f5b57
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 */
diff --git a/rx/rxsimp.c b/rx/rxsimp.c
new file mode 100644 (file)
index 0000000..5041c35
--- /dev/null
@@ -0,0 +1,147 @@
+/*     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;
+}  
+
+
diff --git a/rx/rxsimp.h b/rx/rxsimp.h
new file mode 100644 (file)
index 0000000..2b16737
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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 */
diff --git a/rx/rxspencer.c b/rx/rxspencer.c
new file mode 100644 (file)
index 0000000..0e2805c
--- /dev/null
@@ -0,0 +1,1191 @@
+/*     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;
+    }
+}
diff --git a/rx/rxspencer.h b/rx/rxspencer.h
new file mode 100644 (file)
index 0000000..0c4a46b
--- /dev/null
@@ -0,0 +1,99 @@
+/* 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 */
diff --git a/rx/rxstr.c b/rx/rxstr.c
new file mode 100644 (file)
index 0000000..c3ccb03
--- /dev/null
@@ -0,0 +1,130 @@
+/*     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;
+    }
+}
diff --git a/rx/rxstr.h b/rx/rxstr.h
new file mode 100644 (file)
index 0000000..991f53b
--- /dev/null
@@ -0,0 +1,57 @@
+/* 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 */
diff --git a/rx/rxsuper.c b/rx/rxsuper.c
new file mode 100644 (file)
index 0000000..46e745a
--- /dev/null
@@ -0,0 +1,1416 @@
+
+
+/*     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;
+    }
+}
+
+
diff --git a/rx/rxsuper.h b/rx/rxsuper.h
new file mode 100644 (file)
index 0000000..59e1ed9
--- /dev/null
@@ -0,0 +1,446 @@
+/* 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 */
diff --git a/rx/rxunfa.c b/rx/rxunfa.c
new file mode 100644 (file)
index 0000000..f43181d
--- /dev/null
@@ -0,0 +1,269 @@
+/* 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);
+}
+
diff --git a/rx/rxunfa.h b/rx/rxunfa.h
new file mode 100644 (file)
index 0000000..aeb589a
--- /dev/null
@@ -0,0 +1,70 @@
+#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 */
diff --git a/sample.mailcap b/sample.mailcap
new file mode 100644 (file)
index 0000000..6cba2b6
--- /dev/null
@@ -0,0 +1,4 @@
+text/html; netscape -remote 'openURL(%s)'
+image/gif; xv %s
+image/jpg; xv %s
+application/pgp-keys; pgp -f < %s ; copiousoutput
diff --git a/sample.muttrc b/sample.muttrc
new file mode 100644 (file)
index 0000000..f0bff29
--- /dev/null
@@ -0,0 +1,333 @@
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# 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
diff --git a/score.c b/score.c
new file mode 100644 (file)
index 0000000..4fa11c1
--- /dev/null
+++ b/score.c
@@ -0,0 +1,174 @@
+/*
+ * 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;
+}
diff --git a/send.c b/send.c
new file mode 100644 (file)
index 0000000..5cfa847
--- /dev/null
+++ b/send.c
@@ -0,0 +1,1250 @@
+/*
+ * 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);
+}
diff --git a/sendlib.c b/sendlib.c
new file mode 100644 (file)
index 0000000..b6eaac6
--- /dev/null
+++ b/sendlib.c
@@ -0,0 +1,1788 @@
+/*
+ * 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;
+}
diff --git a/sha.h b/sha.h
new file mode 100644 (file)
index 0000000..9e22fa8
--- /dev/null
+++ b/sha.h
@@ -0,0 +1,105 @@
+/* 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
diff --git a/sha1dgst.c b/sha1dgst.c
new file mode 100644 (file)
index 0000000..58230ac
--- /dev/null
@@ -0,0 +1,373 @@
+/* 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
diff --git a/sha_locl.h b/sha_locl.h
new file mode 100644 (file)
index 0000000..0a5cf46
--- /dev/null
@@ -0,0 +1,198 @@
+/* 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);
+
diff --git a/signal.c b/signal.c
new file mode 100644 (file)
index 0000000..b8e63f7
--- /dev/null
+++ b/signal.c
@@ -0,0 +1,245 @@
+/*
+ * 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);
+  }
+}
diff --git a/snprintf.c b/snprintf.c
new file mode 100644 (file)
index 0000000..c03aa87
--- /dev/null
@@ -0,0 +1,787 @@
+/**************************************************************
+ * 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 */
diff --git a/sort.c b/sort.c
new file mode 100644 (file)
index 0000000..3ece2f7
--- /dev/null
+++ b/sort.c
@@ -0,0 +1,254 @@
+/*
+ * 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 ();
+}
diff --git a/sort.h b/sort.h
new file mode 100644 (file)
index 0000000..7550024
--- /dev/null
+++ b/sort.h
@@ -0,0 +1,50 @@
+/*
+ * 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[];
diff --git a/status.c b/status.c
new file mode 100644 (file)
index 0000000..dd61d75
--- /dev/null
+++ b/status.c
@@ -0,0 +1,276 @@
+/*
+ * 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);
+}
diff --git a/strcasecmp.c b/strcasecmp.c
new file mode 100644 (file)
index 0000000..7578d0c
--- /dev/null
@@ -0,0 +1,37 @@
+#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);
+}
diff --git a/system.c b/system.c
new file mode 100644 (file)
index 0000000..eb9a2aa
--- /dev/null
+++ b/system.c
@@ -0,0 +1,111 @@
+/*
+ * 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);
+}
diff --git a/testmsg b/testmsg
new file mode 100644 (file)
index 0000000..87ec8f6
--- /dev/null
+++ b/testmsg
@@ -0,0 +1,45 @@
+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--
+
diff --git a/thread.c b/thread.c
new file mode 100644 (file)
index 0000000..2c2570a
--- /dev/null
+++ b/thread.c
@@ -0,0 +1,625 @@
+/*
+ * 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);
+}