home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / elm / elm2.4 / utils / readmsg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-20  |  19.2 KB  |  674 lines

  1.  
  2. static char rcsid[] = "@(#)$Id: readmsg.c,v 5.9 1993/04/21 01:17:51 syd Exp $";
  3.  
  4. /*******************************************************************************
  5.  *  The Elm Mail System  -  $Revision: 5.9 $   $State: Exp $
  6.  *
  7.  *            Copyright (c) 1988-1992 USENET Community Trust
  8.  *            Copyright (c) 1986,1987 Dave Taylor
  9.  *******************************************************************************
  10.  * Bug reports, patches, comments, suggestions should be sent to:
  11.  *
  12.  *    Syd Weinstein, Elm Coordinator
  13.  *    elm@DSI.COM            dsinc!elm
  14.  *
  15.  *******************************************************************************
  16.  * $Log: readmsg.c,v $
  17.  * Revision 5.9  1993/04/21  01:17:51  syd
  18.  * readmsg treated a line with From_ preceeded by whitespace as a valid
  19.  * message delimiter.
  20.  * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
  21.  *
  22.  * Revision 5.8  1993/04/12  01:54:07  syd
  23.  * Modified to use new safe_malloc() routines.
  24.  * From: chip@chinacat.unicom.com (Chip Rosenthal)
  25.  *
  26.  * Revision 5.7  1993/02/03  16:46:28  syd
  27.  * xrealloc name conflicts with some os having a routine called xrealloc,
  28.  * renamed it elm_xrealloc.
  29.  * From: Syd
  30.  *
  31.  * Revision 5.6  1993/02/03  15:26:13  syd
  32.  * protect atol in ifndef __STDC__ as some make it a macro, and its in stdlib.h
  33.  *
  34.  * Revision 5.5  1993/01/19  05:07:05  syd
  35.  * Trim erroreous extra log entry
  36.  * From: Syd
  37.  *
  38.  * Revision 5.4  1993/01/19  04:47:12  syd
  39.  * Significant changes to provide consistent Date and From_ header
  40.  * cracking.  Overhauled date utilities and moved into library.  Moved
  41.  * real_from() into library.  Modified frm, newmail, and readmsg utilities
  42.  * to use library version of real_from().  Moved get_word() from Elm
  43.  * source into library.  Added new library routines atonum() and strfcpy().
  44.  * Fixed trailing backslash bug in len_next().
  45.  * From: chip@chinacat.unicom.com (Chip Rosenthal)
  46.  *
  47.  * Revision 5.3  1992/12/11  01:45:04  syd
  48.  * remove sys/types.h include, it is now included by defs.h
  49.  * and this routine includes defs.h or indirectly includes defs.h
  50.  * From: Syd
  51.  *
  52.  * Revision 5.2  1992/11/07  19:37:21  syd
  53.  * Enhanced printing support.  Added "-I" to readmsg to
  54.  * suppress spurious diagnostic messages.
  55.  * From: chip@chinacat.unicom.com (Chip Rosenthal)
  56.  *
  57.  * Revision 5.1  1992/10/04  00:46:45  syd
  58.  * Initial checkin as of 2.4 Release at PL0
  59.  *
  60.  *
  61.  ******************************************************************************/
  62.  
  63. /** This program extracts messages from a mail folder.  It is particularly
  64.     useful when the user is composting a mail response in an external
  65.     editor.  He or she can call this program to pull a copy of the original
  66.     message (or any other message in the folder) into the editor buffer.
  67.  
  68.     One of the first things we do is look for a folder state file.
  69.     If we are running as a subprocess to Elm this file should tell us
  70.     what folder is currently being read, the seek offsets to all the
  71.     messages in the folder, and what message(s) in the folder is/are
  72.     selected.  We will load in all that info and use it for defaults,
  73.     as applicable.
  74.  
  75.     If a state file does not exist, then the default is to show whatever
  76.     messages the user specifies on the command line.  Unless specified
  77.     otherwise, this would be from the user's incoming mail folder.
  78.  
  79.     Even if a state file exists, the user can override the defaults
  80.     and select a different set of messages to extract, or select an
  81.     entirely different folder.
  82.  
  83.     Messages can be selected on the command line as follows:
  84.  
  85.     readmsg [-options] *
  86.  
  87.         Selects all messages in the folder.
  88.  
  89.     readmsg [-options] pattern ...
  90.  
  91.         Selects messsage(s) which match "pattern ...".  The selected
  92.         message will contain the "pattern ..." somewhere within
  93.         the header or body, and it must be an exact (case sensitive)
  94.         match.  Normally selects only the first match.  The "-a"
  95.         selects all matches.
  96.  
  97.     readmsg [-options] sel ...
  98.  
  99.         where:  sel == a number -- selects that message number
  100.             sel == $ -- selects last message in folder
  101.             sel == 0 -- selects last message in folder
  102.  
  103.     The undocumented "-I" option is a kludge to deal with an Elm race
  104.     condition.  The problem is that Elm does piping/printing/etc. by
  105.     running "readmsg|command" and placing the mail message selection
  106.     into a folder state file.  However, if the "command" portion of
  107.     the pipeline craps out, Elm might regain control before "readmsg"
  108.     completes.  The first thing Elm does is unlink the folder state
  109.     file.  Thus "readmsg" can't figure out what to do -- there is no
  110.     state file or command line args to select a message.  In this
  111.     case, "readmsg" normally gives a usage diagnostic message.  The
  112.     "-I" option says to ignore this condition and silently terminate.
  113.  
  114. **/
  115.  
  116. #include "elmutil.h"
  117. #include "s_readmsg.h"
  118. #include <ctype.h>
  119.  
  120. /** three defines for what level of headers to display **/
  121. #define ALL        1
  122. #define WEED        2
  123. #define NONE        3
  124.  
  125. #define metachar(c)    (c == '=' || c == '+' || c == '%')
  126.  
  127. /* increment for growing dynamic lists */
  128. #define ALLOC_INCR    256
  129.  
  130. /* program name for diagnostics */
  131. char *prog;
  132.  
  133. /*
  134.  * The "folder_idx_list" is a list of seek offsets into the folder,
  135.  * indexed by message number.  The "folder_size" value indicates the
  136.  * number of messages in the folder, as well as the length of the index.
  137.  * This index will be used if we need to access messages by message
  138.  * number.  If possible, the index will be loaded from the external state
  139.  * file, otherwise we will have to make a pass through the entire folder
  140.  * to build up the index.  The index is not needed if we are printing
  141.  * everything ("*" specified on the command line) or if we are searching
  142.  * for pattern match.
  143.  */
  144. int folder_size;
  145. long *folder_idx_list;
  146.  
  147. /*
  148.  * local procedures
  149.  */
  150. void load_folder_index();
  151. int print_patmatch_mssg();
  152. int print_mssg();
  153. void weed_headers();
  154. long mssg_num_to_index();
  155. char *skip_word();
  156.  
  157. extern char *optarg;            /* for parsing the ...        */
  158. extern int   optind;            /*  .. starting arguments    */
  159.  
  160. #ifdef DEBUG
  161. int debug = 0;
  162. FILE *debugfile = stderr;
  163. #endif
  164.  
  165.  
  166. void usage_error()
  167. {
  168.     fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet, ReadmsgUsage,
  169.     "Usage: %s [-anhp] [-f Folder] {MessageNum ... | pattern | *}\n"),
  170.     prog);
  171.     exit(1);
  172. }
  173.  
  174.  
  175. void malloc_fail_handler(proc, size)
  176. char *proc;
  177. unsigned size;
  178. {
  179.     fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet, ReadmsgOutOfMemory,
  180.     "%s: Out of memory [malloc failed].\n"), prog);
  181.     exit(1);
  182. }
  183.  
  184.  
  185. main(argc, argv)
  186. int argc;
  187. char *argv[];
  188. {
  189.     struct folder_state fstate;    /* information from external state file    */
  190.     char folder_name[SLEN];    /* pathname to the mail folder        */
  191.     int fstate_valid;        /* does "fstate" have valid info?    */
  192.     int hdr_disp_level;        /* amount of headers to show        */
  193.     int do_page_breaks;        /* true to FORMFEED between messages    */
  194.     int do_all_matches;        /* true to show all mssgs which match pat*/
  195.     int ign_no_request;        /* terminate if no actions requested    */
  196.     int exit_status;        /* normally zero, set to one on error    */
  197.     FILE *fp;            /* file stream for opened folder    */
  198.     long idx;            /* seek offset within folder        */
  199.     char buf[SLEN], *cp;
  200.     int i;
  201.  
  202.     /**** start of the actual program ****/
  203.  
  204.                     /* Gee...isn't that special?
  205.                      * Somebody wake me up when we
  206.                      * get to the imitation program.
  207.                      *  -chip  */
  208.  
  209. #ifdef I_LOCALE
  210.     setlocale(LC_ALL, "");
  211. #endif
  212.  
  213.     elm_msg_cat = catopen("elm2.4", 0);
  214.     prog = argv[0];
  215.     folder_name[0] = '\0';    /* no folder specified yet        */
  216.     folder_size = -1;        /* message index not loaded yet        */
  217.     hdr_disp_level = WEED;    /* only display interesting headers    */
  218.     do_page_breaks = FALSE;    /* suppress formfeed between mssgs    */
  219.     do_all_matches = FALSE;    /* only show 1st mssg which matches pat    */
  220.     ign_no_request = FALSE;    /* no action requested is an error    */
  221.     exit_status = 0;        /* will set nonzero on error        */
  222.  
  223.     /* install trap for safe_malloc() failure */
  224.     safe_malloc_fail_handler = malloc_fail_handler;
  225.  
  226.     /* see if an external folder state file exists */
  227.     if (load_folder_state_file(&fstate) != 0) {
  228.     fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet,
  229.         ReadmsgStateFileCorrupt,
  230.         "%s: Elm folder state file appears to be corrupt!\n"), prog);
  231.     exit(1);
  232.     }
  233.     fstate_valid = (fstate.folder_name != NULL);
  234.  
  235.     /* crack the command line */
  236.     while ((i = getopt(argc, argv, "anhf:pI")) != EOF) {
  237.     switch (i) {
  238.     case 'a' :
  239.         do_all_matches = TRUE;
  240.         break;
  241.     case 'n' :
  242.         hdr_disp_level = NONE;
  243.         break;
  244.     case 'h' :
  245.         hdr_disp_level = ALL;
  246.         break;
  247.     case 'f' :
  248.         strcpy(folder_name, optarg);
  249.         if (metachar(folder_name[0]) && !expand(folder_name)) {
  250.         fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet,
  251.             ReadmsgCannotExpandFolderName,
  252.             "%s: Cannot expand folder name \"%s\".\n"),
  253.             prog, folder_name);
  254.         exit(1);
  255.         }
  256.         folder_size = -1;    /* zot out index info from extern state file */
  257.         break;
  258.     case 'p' :
  259.         do_page_breaks = TRUE;
  260.         break;
  261.     case 'I':
  262.         ign_no_request = TRUE;
  263.         break;
  264.     default:
  265.         usage_error();
  266.     }
  267.     }
  268.  
  269.     /* figure out where the folder is */
  270.     if (folder_name[0] == '\0') {
  271.     if (fstate_valid)
  272.         strcpy(folder_name, fstate.folder_name);
  273.     else if ((cp = getenv("MAIL")) != NULL)
  274.         strcpy(folder_name, cp);
  275.     else if ((cp = getenv("LOGNAME")) != NULL)
  276.         sprintf(folder_name, "%s/%s", mailhome, cp);
  277.     else if ((cp = getenv("USER")) != NULL)
  278.         sprintf(folder_name, "%s/%s", mailhome, cp);
  279.     else {
  280.         fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet,
  281.         ReadmsgCannotGetIncomingName,
  282.         "%s: Cannot figure out name of your incoming mail folder.\n"),
  283.         prog);
  284.         exit(1);
  285.     }
  286.     }
  287.  
  288.     /* open up the message folder */
  289.     if ((fp = fopen(folder_name, "r")) == NULL) {
  290.     fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet,
  291.         ReadmsgFolderEmpty, "%s: Folder \"%s\" is empty.\n"),
  292.         prog, folder_name);
  293.     exit(0);
  294.     }
  295.  
  296.     /* if this is the folder in the state file then grab the index */
  297.     if (fstate_valid && strcmp(folder_name, fstate.folder_name) == 0) {
  298.     folder_size = fstate.num_mssgs;
  299.     folder_idx_list = fstate.idx_list;
  300.     } else {
  301.     fstate_valid = FALSE;
  302.     }
  303.  
  304.     /* if no selections on cmd line then show selected mssgs from state file */
  305.     if (argc == optind) {
  306.     if (!fstate_valid) {
  307.         /* no applicable state file or it didn't select anything */
  308.         if (ign_no_request)
  309.             exit(0);
  310.         usage_error();
  311.     }
  312.     if (folder_size < 1) {
  313.         fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet, ReadmsgFolderEmpty,
  314.         "%s: Folder \"%s\" is empty.\n"), prog, folder_name);
  315.         exit(0);
  316.     }
  317.     for (i = 0 ; i < fstate.num_sel ; ++i) {
  318.         if ((idx = mssg_num_to_index(fstate.sel_list[i])) == -1L)
  319.         exit_status = 1;
  320.         else if (print_mssg(fp, idx, hdr_disp_level, do_page_breaks) != 0)
  321.         exit_status = 1;
  322.     }
  323.     exit(exit_status);
  324.     }
  325.  
  326.     /* see if we are trying to match a pattern */
  327.     if (index("0123456789$*", argv[optind][0]) == NULL) {
  328.     strcpy(buf, argv[optind]);
  329.     while (++optind < argc)
  330.         strcat(strcat(buf, " "), argv[optind]);
  331.     if (print_patmatch_mssg(fp, buf, do_all_matches,
  332.             hdr_disp_level, do_page_breaks) != 0)
  333.         exit_status = 1;
  334.     exit(exit_status);
  335.     }
  336.  
  337.     /* if we do not have an index from the state file then go build one */
  338.     if (folder_size < 0)
  339.     load_folder_index(fp);
  340.  
  341.     /* make sure there is something there to look at */
  342.     if (folder_size < 1) {
  343.     fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet, ReadmsgFolderEmpty,
  344.         "%s: Folder \"%s\" is empty.\n"), prog, folder_name);
  345.     exit(0);
  346.     }
  347.  
  348.     /* see if all messages should be shown */
  349.     if (argc-optind == 1 && strcmp(argv[optind], "*") == 0) {
  350.     for (i = 1 ; i <= folder_size ; ++i) {
  351.         if ((idx = mssg_num_to_index(i)) == -1L)
  352.         exit_status = 1;
  353.         else if (print_mssg(fp, idx, hdr_disp_level, do_page_breaks) != 0)
  354.         exit_status = 1;
  355.     }
  356.     exit(exit_status);
  357.     }
  358.  
  359.     /* print out all the messages specified on the command line */
  360.     for ( ; optind < argc ; ++optind) {
  361.  
  362.     /* get the message number */
  363.     if (strcmp(argv[optind], "$") == 0 || strcmp(argv[optind], "0") == 0) {
  364.         i = folder_size;
  365.     } else if ((i = atoi(argv[optind])) == 0) {
  366.         fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet,
  367.         ReadmsgIDontUnderstand,
  368.         "%s: I don't understand what \"%s\" means.\n"),
  369.         prog, argv[optind]);
  370.         exit(1);
  371.     }
  372.  
  373.     /*print it */
  374.     if ((idx = mssg_num_to_index(i)) == -1L)
  375.         exit_status = 1;
  376.     else if (print_mssg(fp, idx, hdr_disp_level, do_page_breaks) != 0)
  377.         exit_status = 1;
  378.  
  379.     }
  380.  
  381.     exit(exit_status);
  382.     /*NOTREACHED*/
  383. }
  384.  
  385.  
  386. /*
  387.  * Scan through the entire folder and build an index of seek offsets.
  388.  */
  389. void load_folder_index(fp)
  390. FILE *fp;
  391. {
  392.     long offset;
  393.     int alloc_size;
  394.     char buf[SLEN];
  395. #ifdef MMDF
  396.     int  newheader = 0;
  397. #endif /* MMDF */
  398.  
  399.     /* zero out the folder seek offsets index */
  400.     folder_size = 0;
  401.     alloc_size = 0;
  402.     folder_idx_list = NULL;
  403.  
  404.     /* go through the folder a line at a time looking for messages */
  405.     rewind(fp);
  406.     while (offset = ftell(fp), mail_gets(buf, sizeof(buf), fp) != 0) {
  407. #ifdef MMDF
  408.     if ((strcmp(buf, MSG_SEPARATOR) == 0) && (++newheader % 2) != 0)
  409. #else
  410.     if (first_word(buf, "From ") &&
  411.         real_from(buf, (struct header_rec *)NULL))
  412. #endif
  413.     {
  414.         if (folder_size >= alloc_size) {
  415.         alloc_size += ALLOC_INCR;
  416.         folder_idx_list = (long *) safe_realloc((char *)folder_idx_list,
  417.             alloc_size*sizeof(long));
  418.         }
  419.         folder_idx_list[folder_size++] = offset;
  420.     }
  421.     }
  422.  
  423. }
  424.  
  425.  
  426. /*
  427.  * Scan through a folder and print message(s) which match the pattern.
  428.  */
  429. int print_patmatch_mssg(fp, pat, do_all_matches, hdr_disp_level, do_page_breaks)
  430. FILE *fp;
  431. char *pat;
  432. int do_all_matches;
  433. int hdr_disp_level;
  434. int do_page_breaks;
  435. {
  436.     long offset, mssg_idx;
  437.     char buf[SLEN];
  438.     int look_for_pat;
  439. #ifdef MMDF
  440.     int  newheader = 0;
  441. #endif /* MMDF */
  442.  
  443.     /*
  444.      * This flag ensures that we don't reprint a message if a single
  445.      * message matches the pattern several times.  We turn it on when
  446.      * we get to the top of a message.
  447.      */
  448.     look_for_pat = FALSE;
  449.  
  450.     rewind(fp);
  451.     while (offset = ftell(fp), mail_gets(buf, sizeof(buf), fp) != 0) {
  452.  
  453. #ifdef MMDF
  454.     if ((strcmp(buf, MSG_SEPARATOR) == 0) && (++newheader % 2) != 0)
  455. #else
  456.     if (first_word(buf, "From ") &&
  457.         real_from(buf, (struct header_rec *)NULL))
  458. #endif
  459.     {
  460.         mssg_idx = offset;
  461.         look_for_pat = TRUE;
  462.     }
  463.  
  464.     if (look_for_pat && strstr(buf, pat) != NULL) {
  465.         offset = ftell(fp);
  466.         if (print_mssg(fp, mssg_idx, hdr_disp_level, do_page_breaks) != 0)
  467.         return -1;
  468.         fseek(fp, offset, 0);
  469.         if (!do_all_matches)
  470.         break;
  471.         look_for_pat = FALSE;
  472.     }
  473.  
  474.     }
  475.  
  476.     return 0;
  477. }
  478.  
  479.  
  480. /*
  481.  * Print the message at the indicated location.
  482.  */
  483. int print_mssg(fp, offset, hdr_disp_level, do_page_breaks)
  484. FILE *fp;
  485. long offset;
  486. int hdr_disp_level;
  487. int do_page_breaks;
  488. {
  489.     char buf[SLEN];
  490.     int first_line, is_seperator, in_header, buf_len, newlines;
  491.     static int num_mssgs_listed = 0;
  492.  
  493.     in_header = TRUE;
  494.     first_line = TRUE;
  495.  
  496.     if (num_mssgs_listed++ == 0)
  497.     ; /* no seperator before first message */
  498.     else if (do_page_breaks)
  499.     putchar(FORMFEED);
  500.     else
  501.     puts("\n------------------------------------------------------------------------------\n");
  502.  
  503.     /* move to the beginning of the selected message */
  504.     if (fseek(fp, offset, 0) != 0) {
  505.     fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet, ReadmsgCannotSeek,
  506.         "%s: Cannot seek to selected message. [offset=%ld]\n"),
  507.         prog, offset);
  508.     return -1;
  509.     }
  510.  
  511.     /* we will chop off newlines at the end of the message */
  512.     newlines = 0;
  513.  
  514.     /* print the message a line at a time */
  515.     while ((buf_len = mail_gets(buf, SLEN, fp)) != 0) {
  516.  
  517. #ifdef MMDF
  518.     is_seperator = (strcmp(buf, MSG_SEPARATOR) == 0);
  519. #else
  520.     is_seperator = first_word(buf, "From ") &&
  521.                real_from(buf, (struct header_rec *)NULL);
  522. #endif
  523.  
  524.     /* the first line of the message better be the seperator */
  525.     /* next time we encounter the seperator marks the end of the message */
  526.     if (first_line) {
  527.         if (!is_seperator) {
  528.         fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet,
  529.             ReadmsgCannotFindStart,
  530.             "%s: Cannot find start of selected message. [offset=%ld]\n"),
  531.             prog, offset);
  532.         return -1;
  533.         }
  534.         first_line = FALSE;
  535.     } else {
  536.         if (is_seperator)
  537.         break;
  538.     }
  539.  
  540.     /* just accumulate newlines */
  541.     if (buf[0] == '\n' && buf[1] == '\0') {
  542.         if (in_header) {
  543.         if (hdr_disp_level == WEED)
  544.             weed_headers((char *)NULL);
  545.         in_header = FALSE;
  546.         }
  547.         ++newlines;
  548.         continue;
  549.     }
  550.  
  551.     if (in_header) {
  552.         switch (hdr_disp_level) {
  553.         case NONE:
  554.         break;
  555.         case WEED:
  556.         weed_headers(buf);
  557.         break;
  558.         case ALL:
  559.         default:
  560.         fwrite(buf, 1, buf_len, stdout);
  561.         break;
  562.         }
  563.     } else {
  564.         while (--newlines >= 0)
  565.         putchar('\n');
  566.         newlines = 0;
  567.         fwrite(buf, 1, buf_len, stdout);
  568.     }
  569.  
  570.     }
  571.  
  572.     /* make sure we didn't do something like seek beyond the */
  573.     /* end of the folder and thus never get anything to print */
  574.     if (first_line) {
  575.     fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet, ReadmsgCannotFindStart,
  576.         "%s: Cannot find start of selected message. [offset=%ld]\n"),
  577.         prog, offset);
  578.     return -1;
  579.     }
  580.  
  581.     return 0;
  582. }
  583.  
  584.  
  585. #define Hdrmatch(buf, hdr)    (strincmp((buf), (hdr), sizeof(hdr)-1) == 0)
  586.  
  587. /*
  588.  * Process interesting mail headers.
  589.  *
  590.  * When in WEED mode, this routine should be called with each header line
  591.  * in sequence, and with a NULL at the end of the header.  If the header
  592.  * is interesting then we will print it out.  Continued header lines (i.e.
  593.  * ones starting with whitespace) are also handled.  When the end of the
  594.  * header is reached, if either the From: or Date: was missing then we
  595.  * will generate replacements from the From_ header.
  596.  *
  597.  * Strict intepretation of RFC822 says { '\\' '\n' } at the end of a header
  598.  * line should (at least in some cases) continue to the next line, but we
  599.  * don't handle that.
  600.  */
  601. void weed_headers(buf)
  602. char *buf;
  603. {
  604.     static int got_from = FALSE;    /* already printed From:    */
  605.     static int got_date = FALSE;    /* already printed Date:    */
  606.     static int is_interesting = FALSE;    /* print out curr hdr line    */
  607.     static char save_from[SLEN];    /* From_ hdr from current mssg    */
  608.     char *s;
  609.  
  610.     /* finish off headers on NULL value */
  611.     if (buf == (char *) NULL) {
  612.     if (!got_from) {
  613.         fputs("From: ", stdout);
  614.         for (s = skip_word(save_from) ; *s != '\0' && !isspace(*s) ; ++s)
  615.         putchar(*s);
  616.         putchar('\n');
  617.     }
  618.     if (!got_date)
  619.         printf("Date: %s", skip_word(skip_word(save_from)));
  620.     is_interesting = got_from = got_date = FALSE;
  621.     save_from[0] = '\0';
  622.     return;
  623.     }
  624.  
  625.     if (Hdrmatch(buf, "From ")) {
  626.     strcpy(save_from, buf);
  627.     is_interesting = FALSE;
  628.     } else if (Hdrmatch(buf, "To:") || Hdrmatch(buf, "Subject:"))
  629.     is_interesting = TRUE;
  630.     else if (Hdrmatch(buf, "Date:"))
  631.     got_date = is_interesting = TRUE;
  632.     else if (Hdrmatch(buf, "From:"))
  633.     got_from = is_interesting = TRUE;
  634.     else if (!isspace(buf[0]))
  635.     is_interesting = FALSE;
  636.     /* else */
  637.     /* this is a continuation header - preserve "is_interesting" value */
  638.  
  639.     if (is_interesting)
  640.     fputs(buf, stdout);
  641. }
  642.  
  643.  
  644.  
  645. /*
  646.  * Convert a mailbox message number to a seek location.
  647.  */
  648. long mssg_num_to_index(mssgno)
  649. int mssgno;
  650. {
  651.     if (mssgno < 1 || mssgno > folder_size) {
  652.     fprintf(stderr, catgets(elm_msg_cat, ReadmsgSet,
  653.         ReadmsgCannotFindMessage, "%s: Cannot find message number %d.\n"),
  654.         prog, mssgno);
  655.     return -1L;
  656.     }
  657.     return folder_idx_list[mssgno-1];
  658. }
  659.  
  660.  
  661. /*
  662.  * Advance to the start of the next word.
  663.  */
  664. char *skip_word(str)
  665. char *str;
  666. {
  667.     while (*str != '\0' && !isspace(*str))
  668.     ++str;
  669.     while (isspace(*str))
  670.     ++str;
  671.     return str;
  672. }
  673.  
  674.