static char rcsid[] = "@(#)$Id: from.c,v 1.55 2006/05/07 08:35:31 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.55 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 ******************************************************************************
 *  The Elm Mail System 
 *
 * 			Copyright (c) 1988-1992 USENET Community Trust
 * 			Copyright (c) 1986,1987 Dave Taylor
 *****************************************************************************/

/** print out whom each message is from in the pending folder or specified 
    one, including a subject line if available.

**/

#include "elmutil.h"
#include "mboxlib.h"
#include "s_from.h"
#include "s_elm.h"

DEBUG_VAR(Debug,__FILE__,"util");

static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str) 
     char *str;
{
    return (unsigned char *)str;
}
     
#ifdef PWDINSYS
#  include <sys/pwd.h>
#else
#  include <pwd.h>
#endif
#include <sys/stat.h>

#define LINEFEED	(char) 10

#define metachar(c)	(c == '=' || c == '+' || c == '%')

/* ancient wisdom */
#ifndef TRUE
#  define TRUE	1
#  define FALSE	0
#endif

/* for explain(), positive and negative */
#define POS	1
#define NEG	0

/* defines for selecting messages by Status: */
#define NEW_MSG		0x1
#define OLD_MSG		0x2
#define READ_MSG	0x4
#define UNKNOWN		0x8

#define ALL_MSGS	0xf

/* exit statuses */
#define	EXIT_SELECTED	0	/* Selected messages present */
#define	EXIT_MAIL	1	/* Mail present, but no selected messages */
#define	EXIT_NO_MAIL	2	/* No messages at all */
#define	EXIT_ERROR	3	/* Error */

int   number = FALSE,	/* should we number the messages?? */
      veryquiet = FALSE,/* should we be print any output at all? */
      quiet = FALSE,	/* only print mail/no mail and/or summary */
      selct = FALSE,	/* select these types of messages */
      tidy  = FALSE,    /* tidy output with long 'from's */
      summarize = FALSE,/* print a summary of how many messages of each type */
      verbose = FALSE;	/* and should we prepend a header? */

char infile[SLEN];	/* current file name */

static char * explain P_((
			  int selection,
			  int how_to_say));
static void usage P_((char *prog));
static void print_help P_((void));

static char * whos_mail P_((char *filename,
			    char *realname));

static void read_headers P_((struct folder_info *folder,
			     struct read_folder_state * read_state_ptr,
			     int user_mailbox,
			     int *total_msgs,
			     int *selected,
			     char *realname));


int main P_((int argc, char *argv[]));
int main(argc, argv)
     int argc;
     char *argv[];
{
	char *cp;
	int  multiple_files = FALSE, output_files = FALSE;
	int  no_files, c;
	int total_msgs = 0, selected_msgs = 0;
	char realname[SLEN];

	int df = give_dt_enumerate_as_int(&def_folder_status);

	extern int optind;
	extern char *optarg;

#if DEBUG
	init_debugfile("FRM");
#endif
	locale_init();
	user_init();
	strfcpy(realname,username,sizeof realname);
	
	init_mboxlib();

	init_defaults();
	read_rc_file(0);

	/*
	 * check the first character of the command basename to
	 * use as the selection criterion.
	 */
	cp = argv[0] + strlen(argv[0]) - 1;
	while (cp != argv[0] && cp[-1] != '/')
	  cp--;
	switch (*cp) {
	  case 'n': selct |= NEW_MSG;  break;
	  case 'u':
	  case 'o': selct |= OLD_MSG;  break;
	  case 'r': selct |= READ_MSG; break;
	}

	while ((c = getopt(argc, argv, "hnQqSs:tvd:X:")) != EOF) 
	  switch (c) {
	  case 'd' : 
#if DEBUG
	      set_debugging(optarg);	   
#else
	      lib_error(CATGETS(elm_msg_cat, FromSet, FromArgsIngoringDebug,
				"Warning: system created without debugging enabled - request ignored\n"));
#endif
	      break;
	  case 'n': number++;	break;
	  case 'Q': veryquiet++;	break;
	  case 'q': quiet++;	break;
	  case 'S': summarize++; break; 
	  case 't': tidy++;      break;
	  case 'v': verbose++;	break;
	  case 's': if (optarg[1] == '\0') {
	    switch (*optarg) {
	    case 'n':
	    case 'N': selct |= NEW_MSG;  break;
	    case 'o':
	    case 'O':
	    case 'u':
	    case 'U': selct |= OLD_MSG;  break;
	    case 'r':
	    case 'R': selct |= READ_MSG; break;
	    default:       usage(argv[0]);
	      exit(EXIT_ERROR);
	    }
	  } else if (istrcmp(optarg,"new") == 0)
	    selct |= NEW_MSG;
	  else if (istrcmp(optarg,"old") == 0)
	    selct |= OLD_MSG;
	  else if (istrcmp(optarg,"unread") == 0)
	    selct |= OLD_MSG;
	  else if (istrcmp(optarg,"read") == 0)
	    selct |= READ_MSG;
	  else {
	    usage(argv[0]);
	    exit(EXIT_ERROR);
	  }
	  break;
	  case 'h': print_help();
			   exit(EXIT_ERROR);
	  case 'X' : 
#ifdef REMOTE_MBX	    
	      if (!set_transaction_file(optarg))
		  exit(1);
#endif
	      break;
	  case '?': usage(argv[0]);
	    lib_error(CATGETS(elm_msg_cat,
			      FromSet,FromForMoreInfo,
			      "For more information, type \"%s -h\"\n"),
		      argv[0]);
	    exit(EXIT_ERROR);
	  }

	if (quiet && verbose) {
	  lib_error(CATGETS(elm_msg_cat,FromSet,FromNoQuietVerbose,
			    "Can't have quiet *and* verbose!\n"));
	  exit(EXIT_ERROR);
	}

	if (veryquiet) {
	  if (freopen("/dev/null", "w", stdout) == NULL) {
	    lib_error(CATGETS(elm_msg_cat,FromSet,FromCantOpenDevNull,
			      "Can't open /dev/null for \"very quiet\" mode.\n"));
	    exit(EXIT_ERROR);
	  }
	}


	elm_sfprintf(version_buff, sizeof version_buff,
		     FRM("%s PL%s"), VERSION, PATCHLEVEL);
	
	user_init();

#ifdef DEBUG
    { 
	int d = panic_dprint("\n\
======================================================\n\
Debug output of the FRM program (version %s).\n",
			     version_buff);

	if (d >= 50) {
	    panic_dprint("WARNING: Edit manually out sensitive information from that file!\n");
    
	    lib_error(FRM("WARNING: Debug file may include passwords -- edit it!"));
#if POLL_METHOD
	    wait_for_timeout(5+sleepmsg);	    
#else
	    sleep(5+sleepmsg);
#endif
	}

    }
#endif

	/* default is all messages */
	if (selct == 0 || selct == (NEW_MSG|OLD_MSG|READ_MSG))
	  selct = ALL_MSGS;

	infile[0] = '\0';

	if (no_files = (optind == argc)) { /* assignment intentional */
	    char * default_val = give_dt_estr_as_str(&defaultfile_e,"incoming-mailbox");

	    if (!default_val)
		exit(1);
	    strfcpy(infile, default_val, sizeof infile);
	    optind -= 1;	/* ensure one pass through loop */
	}

	multiple_files = (argc - optind > 1);

	for ( ; optind < argc; optind++) {
	    struct folder_info *folder = NULL;
	    struct read_folder_state * read_state_ptr = NULL;
	    CONST char *msg;
	    enum sessionlock_mode m;

	  /* copy next argument into infile */

	  if (multiple_files) {
	    strfcpy(infile, argv[optind], sizeof infile);
	    printf("%s%s: \n", output_files++ > 0 ? "\n":"", infile);
	  }
	  else if (infile[0] == '\0')
	    strfcpy(infile, argv[optind], sizeof infile);

	  if (metachar(infile[0])) {

	      SIGDPRINT(Debug,4, (&Debug,  
				  "%s => ",infile));
	      if (expand(infile, sizeof infile) == 0) {
		  lib_error(CATGETS(elm_msg_cat,
				    FromSet,FromCouldntExpandFilename,
				    "%s: couldn't expand filename %s!\n"), 
			    argv[0], infile);
		  exit(EXIT_ERROR);
	      }
	      SIGDPRINT(Debug,4, (&Debug,  
				  "%s  (expanded)\n",infile));

	  } else if (NULL == strpbrk(infile,"/:@") &&
		     access(infile,ACCESS_EXISTS) == -1 &&
		     !no_files) {

	      SIGDPRINT(Debug,4, (&Debug,  
				  "%s => ",infile));

	      /* only try mailhome if file not found */
	      strfcpy(infile,mailhome,sizeof infile);
	      strfcat(infile,argv[optind],sizeof infile);

	      SIGDPRINT(Debug,4, (&Debug,  
				  "%s (as user)\n",infile));

	      if (access(infile,ACCESS_EXISTS) == -1) {
		  lib_error(CATGETS(elm_msg_cat,
				    FromSet,FromCouldntOpenFolderOrMailbox,
				    "Couldn't open folder \"%s\" or mailbox \"%s\".\n"),
			    argv[optind], infile);
		  continue;	/* let's try the next file */
	      }
	  } else {
	      SIGDPRINT(Debug,4, (&Debug,  
				  "%s (no expansion)\n",infile));
	  }

	  if (strncmp(infile, mailhome, strlen(mailhome)) == 0) {
	      /*
	       * if this is a mailbox, use the identity of the mailbox owner.
	       * this affects the "To" processing.
	       */

	      strfcpy(username, infile+strlen(mailhome), sizeof username);

	      /*
	       * then get full username
	       */
	      if((cp = get_full_name(username)) != NULL)
		  strfcpy(full_username, cp,sizeof full_username);
	      else
		  strfcpy(full_username, username, sizeof full_username);
	      
	  } else {
	      /* reset values */
	      user_init();
	  }



	  /* check if this is a mailbox or not, and attempt to open it */

	  folder = enter_new_folder(infile);
	  if (!folder)
	      continue;

	  m = SESSIONLOCK_NONE;
	  if ((selct == NEW_MSG) &&
	      FOLDER_FILE == get_folder_mode(folder) &&
	      FOLDER_STATUS_NEW != df) {


	      lib_error(CATGETS(elm_msg_cat,
				FromSet,FromNotMailbox,
				"WARNING: %s is folder, not mailbox!\n  Delivery of new mail to folder when it is open may corrupt it.\n"),
			infile);
		

	      m = SESSIONLOCK_NONE_CHECKNEW;
	  }


	  if (!sessionlock_folder(folder,m)) {
	      CONST char * X = folder_type(folder);
	      lib_error(CATGETS(elm_msg_cat,FromSet,FromCouldntOpenX,
				"Couldn't open %s \"%s\".\n"), X,infile);
	      goto clean;
	  }

	  if (!prepare_read_folder(folder,PREPARE_NOLOCK,&read_state_ptr)) {
	      goto clean;
	  }
	  
	  if ((msg = is_forwarded_folder(folder,read_state_ptr)) != NULL) {
	      printf(catgets(elm_msg_cat, ElmSet, ElmMailBeingForwardTo,
			      "Mail being forwarded to %s"), 
		      msg);	
	      putchar('\n');
	  } else {
	      int user_mailbox = 
		  strncmp(infile, mailhome, strlen(mailhome)) == 0;

	      read_headers(folder,read_state_ptr,
			   user_mailbox, 
			   &total_msgs, &selected_msgs,realname);


	    /*
	     * we know what to say; now we have to figure out *how*
	     * to say it!
	     */

	      /* no messages at all? */
	      if (total_msgs == 0) {
		  if (user_mailbox)
		      elm_fprintf(stdout,
				  CATGETS(elm_msg_cat,FromSet,FromStringNoMail,
					  "%s no mail.\n"), 
				  whos_mail(infile, realname));
		  else
		      if (!summarize) {
			  if (get_folder_mode(folder) & FOLDER_MBOX)
			      elm_fprintf(stdout,
					  CATGETS(elm_msg_cat,FromSet,
						  FromNoMesgInMailbox,
						  "No messages in that mailbox!\n"));
			  
			  else
			      elm_fprintf(stdout,
					  CATGETS(elm_msg_cat,FromSet,
						  FromNoMesgInFolder,
						  "No messages in that folder!\n"));
		      }
	      }
	      else
		  /* no selected messages then? */
		  if (selected_msgs == 0) {
		      if (user_mailbox)
			  printf(catgets(elm_msg_cat,FromSet,FromNoExplainMail,
					 "%s no %s mail.\n"), 
				 whos_mail(infile, realname),
				 explain(selct,NEG));
		      else
			  if (!summarize) {
			      if (get_folder_mode(folder) & FOLDER_MBOX)
				  printf(catgets(elm_msg_cat,
						 FromSet,FromNoExplainMessagesMbox,
						 "No %s messages in that mailbox.\n"),
					 explain(selct,NEG));
			      else
				  printf(catgets(elm_msg_cat,
						 FromSet,FromNoExplainMessages,
						 "No %s messages in that folder.\n"),
					 explain(selct,NEG));
			  }
		  }
		  else
		      /* there's mail, but we just want a one-liner */
		      if (quiet && !summarize) {
			  if (user_mailbox)
			      printf(catgets(elm_msg_cat,
					     FromSet,FromStringStringMail,
					     "%s %s mail.\n"), 
				     whos_mail(infile,realname),
				     explain(selct,POS));
			  else
			      printf(catgets(elm_msg_cat,FromSet,
					     FromThereAreMesg,
					     "There are %s messages in that folder.\n"),
				     explain(selct,POS));
		}
	  }

	  end_read_folder(folder,&read_state_ptr,0);

	clean:
	  /* FREE folder */
	  leave_old_folder(&folder,CLOSE_NORMAL); 
	} /* for each arg */

	/*
	 * return "shell true" (0) if there are selected messages;
	 * 1 if there are messages, but no selected messages;
	 * 2 if there are no messages at all.
	 */
	if (selected_msgs > 0)
	  exit(EXIT_SELECTED);
	else if (total_msgs > 0)
	  exit(EXIT_MAIL);
	else
	  exit(EXIT_NO_MAIL);
	/*NOTREACHED*/
}

static int parse_header_routine P_((struct folder_info *folder,
				    READ_STATE read_state_ptr,
				    struct header_rec *entry,
				    header_list_ptr parsed_headers ));
static int parse_header_routine(folder,read_state_ptr,entry,parsed_headers)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
     struct header_rec *entry;
     header_list_ptr parsed_headers;
{
    header_list_ptr tmphdr;
    
    /* copy_envelope_folder() may have set entry->content_length */
    /* copy_envelope_folder() have set entry->header_charset */
 
    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"From"))) {	    
	if (entry->from)
	    free_addr_items(entry->from);
	entry->from = 
	    break_down_address(tmphdr->body, 
			       !(entry->status & NOHDRENCODING),
			       entry->header_charset);	    
    }
        
    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						    "To"))) {	    
	if (entry->to)
	    free_addr_items(entry->to);
	entry->to = 
	    break_down_address(tmphdr->body, 
			       !(entry->status & NOHDRENCODING),
			       entry->header_charset);	    
    }
    
    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"Cc"))) {	    
	if (entry->cc)
	    free_addr_items(entry->cc);
	entry->cc = 
	    break_down_address(tmphdr->body, 
			       !(entry->status & NOHDRENCODING),
			       entry->header_charset);	    
    }
    
    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"Status"))) {	    
	strfcpy(entry->mailx_status, tmphdr->body, WLEN);
    }
    
    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"Subject"))) {	    
	
	if (entry->subject)
	    free_string(&(entry->subject));
	
	entry->subject = 
	    hdr_to_string(HDR_TEXT,
			  tmphdr->body,
			  entry->header_charset,
			  !(entry->status & NOHDRENCODING));	  
    } else if (!entry->subject)
	entry->subject = new_string(display_charset);

    return 1;
}

static int parse_body_routine P_((struct folder_info *folder,
			   READ_STATE read_state_ptr,
			   struct header_rec *entry,
			   header_list_ptr parsed_headers,
			   struct counter_data *counter
			   ));
static int parse_body_routine(folder,read_state_ptr,entry,parsed_headers,
			      counter)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
     struct header_rec *entry;
     header_list_ptr parsed_headers;
     struct counter_data *counter;
{
    char *buffer;
    int len;
    long content_remaining                = -1L;

    content_remaining = entry->content_length;        

 reset_body:
    
    while (copy_body_folder(folder,read_state_ptr,
			    &buffer,&len,&content_remaining)) {
	if (buffer)
	    free(buffer);
    }
    
    if (!copy_envelope_end_folder(folder,read_state_ptr)) {
	if (entry->content_length >= 0L) {
	    entry->content_length = -1L;
	    
	    if (copy_envelope_reset_body(folder,read_state_ptr,
					 &content_remaining))
		goto reset_body;
	    
	}		
	SIGDPRINT(Debug,10, (&Debug, 
			     "-- Message parsing FAILED.\n"));
	return 0;
    }
    SIGDPRINT(Debug,10, (&Debug, 
			 "-- Message parsed.\n"));

    entry->body_parsed = 1;

    return 1;
}

static void read_headers(folder,read_state_ptr,
			 user_mailbox, total_msgs, selected, realname)
     struct folder_info *folder;
     struct read_folder_state * read_state_ptr;
     int user_mailbox;
     int *total_msgs;
     int *selected;
     char *realname;
{
    /** Read the headers, output as found.  User-Mailbox is to guarantee
	that we get a reasonably sensible message from the '-v' option
    **/

    struct header_rec hdr;
    register int count = 0, selected_msgs = 0;
    int i,status;
    int summary[ALL_MSGS];

    bzero((void *)&hdr, sizeof hdr);
    hdr.mbx_info  = NULL;
    hdr.binary      = 0;

    for (i=0; i<ALL_MSGS; i++)
	summary[i] = 0;
    
    while(copy_envelope_folder(folder,read_state_ptr,
			       &hdr,
			       parse_header_routine,parse_body_routine,
			       NULL) > 0) {

	status = 0;

	DPRINT(Debug,4,(&Debug,"Message status %0x (%s):",
			hdr.status,hdr.mailx_status));
       

	if (index(hdr.mailx_status, 'R') != NULL) {
	    hdr.status &= ~(NEW | UNREAD);
	    DPRINT(Debug,4,(&Debug," -NEW -UNREAD"));
	}
	else if (index(hdr.mailx_status,'O') != NULL) {
	    hdr.status &= ~NEW;
	    hdr.status |= UNREAD;
	    DPRINT(Debug,4,(&Debug," -NEW +UNREAD"));
	}
	if (index(hdr.mailx_status, 'r') != NULL) {
	    hdr.status |= REPLIED;
	    DPRINT(Debug,4,(&Debug," +REPLIED"));
	}
	DPRINT(Debug,4,(&Debug," "));

	/* NOTE: In frm terms OLD   is same as UNREAD !! */

	if (hdr.status & NEW) {
	    status |= NEW_MSG;
	    DPRINT(Debug,4,(&Debug," New"));
	} else if (hdr.status & UNREAD) {
	    status |= OLD_MSG;
	    DPRINT(Debug,4,(&Debug," Old (= unread)"));
	}

	if (! (hdr.status & UNREAD)) {
	    status |= READ_MSG;
	    DPRINT(Debug,4,(&Debug," Read"));
	}

	DPRINT(Debug,4,(&Debug," = %s\n",explain(status,POS)));

	count++;
	summary[status]++;

	if ((status & selct) != 0) {

	    /* what a mess! */
	    if (verbose && selected_msgs == 0) {
		if (user_mailbox) {
		    if (selct == ALL_MSGS)
			printf(catgets(elm_msg_cat,FromSet,FromFollowingMesg,
				       "%s the following mail messages:\n"),
			       whos_mail(infile, realname));
		    else
			printf(catgets(elm_msg_cat,FromSet,FromStringStringMail,
				       "%s %s mail.\n"), whos_mail(infile, realname),
			       explain(selct,POS));
		}
		else
		    printf(catgets(elm_msg_cat,
				   FromSet,FromFolderContainsFollowing,
				   "Folder contains the following %s messages:\n"),
			   explain(selct,POS));
	    }

	    selected_msgs++;
	    if (! quiet) {
		int lenWho = 0;
		struct string * who = NULL;
		int used_to_line = DisplayAddress(&hdr,&who);

		if (used_to_line) {
		    struct string * buffer = new_string2(display_charset,
							 s2us("To "));
		    struct string * temp = cat_strings(buffer,who,1);
		    free_string(&who);
		    who = temp;
		    free_string(&buffer);
		}

		/***
		 *	Print subject on next line if the Who part blows
		 *	the alignment
		 ***/

		if (tidy) 
		    lenWho = string_len(who);
		else
		    lenWho = 0;			/* forces op on same line */

		if (number)
		    elm_fprintf(stdout,
				FRM("%3d: %-20S%c%*s%S\n"), 
				count, who, 
				lenWho > 20 ? '\n' : ' ',
				lenWho > 20 ? 27   :   1, "",
				hdr.subject);
		else
		    elm_fprintf(stdout,
				FRM("%-20S%c%*s%S\n"), 
				who, 
				lenWho > 20 ? '\n' : ' ',
				lenWho > 20 ? 22   :   1, "",
				hdr.subject);
	    }
	}
	
	free_rec_mbx_info(&hdr);
	if (hdr.header_error)
	    free_header_errors(& (hdr.header_error));
	bzero((void *)&hdr, sizeof hdr);   /* memory leak ... */
	hdr.binary      = 0;
    }
    
    *selected = selected_msgs;
    *total_msgs = count;

    /* print a message type summary */

    if (summarize) {
	int output=FALSE, unknown = 0;

	if (user_mailbox)
	    printf("%s ", whos_mail(infile, realname));
	else
	    elm_fprintf(stdout,CATGETS(elm_msg_cat,FromSet,FromFolderContains,
				       "Folder contains "));

	for (i=0; i<ALL_MSGS; i++) {
	    if (summary[i] > 0) {
		if (output)
		    printf(", ");
		switch (i) {
		case NEW_MSG:
		case OLD_MSG:
		case READ_MSG:  
		    
		    printf("%d %s ",summary[i], explain(i,POS));
		    if (summary[i] == 1)
			printf("%s",catgets(elm_msg_cat,
					    FromSet,FromMessage,"message"));
		    else
			printf("%s",catgets(elm_msg_cat,
					    FromSet,FromMessagePlural,"messages"));
		    
		    output = TRUE;
		    break;
		default:
		    unknown += summary[i];
		}
	    }
	}
	if (unknown)
	    {
		printf("%d ",unknown);
		
		if (unknown == 1)
		    printf("%s",catgets(elm_msg_cat,
					FromSet,FromMessage,"message"));
		else
		    printf("%s",catgets(elm_msg_cat,
					FromSet,FromMessagePlural,"messages"));
		
		printf("%s "," of unknown status");
		output = TRUE;
	    }
	
	if (output)
	    printf(".\n");
	else
	    elm_fprintf(stdout,
			CATGETS(elm_msg_cat,FromSet,FromNoMessages,
				"no messages.\n"));
    }
}

/*
 * Return an appropriate string as to whom this mailbox belongs.
 */
char * whos_mail(filename,
		 realname)
     char *filename;
     char *realname;
{
	static char whos_who[SLEN];
	char *mailname;

	if (strncmp(filename, mailhome, strlen(mailhome)) == 0) {
	  mailname = filename + strlen(mailhome);
	  if (*mailname == '/')
	    mailname++;
	  if (strcmp(mailname, realname) == 0)
	    strfcpy(whos_who,catgets(elm_msg_cat,
				     FromSet,FromYouHave,"You have"),
		    sizeof whos_who);
	  else {
	    strfcpy(whos_who, mailname, sizeof whos_who);
	    strfcat(whos_who,catgets(elm_msg_cat,FromSet,FromHas, " has"),
		    sizeof whos_who);
	  }
	}
	else
	/* punt... */
	  strfcpy(whos_who,catgets(elm_msg_cat,
				   FromSet,FromYouHave,"You have"),
		  sizeof whos_who);

	return whos_who;
}

static void usage(prog)
     char *prog;
{
     printf(catgets(elm_msg_cat,FromSet,FromUsage,
	"Usage: %s [-n] [-v] [-t] [-s {new|old|read}] [filename | username] ...\n"),
	    prog);
}

static void print_help()
{

     elm_fprintf(stdout,
		 CATGETS(elm_msg_cat,FromSet,FromHelpTitle,
			 "frm -- list from and subject lines of messages in mailbox or folder\n"));
		    
     usage("frm");
     elm_fprintf(stdout,
		 CATGETS(elm_msg_cat,FromSet,FromHelpText,
"\noption summary:\n\
-h\tprint this help message.\n\
-n\tdisplay the message number of each message printed.\n\
-Q\tvery quiet -- no output is produced.  This option allows shell\n\
\tscripts to check frm's return status without having output.\n\
-q\tquiet -- only print summaries for each mailbox or folder.\n\
-S\tsummarize the number of messages in each mailbox or folder.\n\
-s status only -- select messages with the specified status.\n\
\t'status' is one of \"new\", \"old\", \"unread\" (same as \"old\"),\n\
\tor \"read\".  Only the first letter need be specified.\n\
-t\ttry to align subjects even if 'from' text is long.\n\
-v\tprint a verbose header.\n"));

}

/* explanation of messages visible after selection */
/* usage: "... has the following%s messages ...", explain(selct,POS) */

static char *
explain(selection, how_to_say)
     int selection;
     int how_to_say;
{
	switch (selection) {
	  case NEW_MSG:
	    return catgets(elm_msg_cat,FromSet,FromNew,"new");
	  case OLD_MSG:
	    return catgets(elm_msg_cat,FromSet,FromUnread,"unread");
	  case READ_MSG:
	    return catgets(elm_msg_cat,FromSet,FromRead,"read");
	  case (NEW_MSG|OLD_MSG):
	    if (how_to_say == POS)
	      return catgets(elm_msg_cat,FromSet,FromNewAndUnread,
			     "new and unread");
	    else
	      return catgets(elm_msg_cat,FromSet,FromNewOrUnread,
			     "new or unread");
	  case (NEW_MSG|READ_MSG):
	    if (how_to_say == POS)
	      return catgets(elm_msg_cat,FromSet,FromNewAndRead,
			     "new and read");
	    else
	      return catgets(elm_msg_cat,FromSet,FromNewOrRead,
			     "new or read");
	  case (READ_MSG|OLD_MSG):
	    if (how_to_say == POS)
	      return catgets(elm_msg_cat,FromSet,FromReadAndUnread,
			     "read and unread");
	    else
	      return catgets(elm_msg_cat,FromSet,FromReadOrUnread,
			     "read or unread");
	  case ALL_MSGS:
	    return "";
	  default:
	    return catgets(elm_msg_cat,FromSet,FromUnknown,"unknown");
	}
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1