#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: pine.c,v 4.225 1994/10/11 00:27:14 mikes Exp $";
#endif
/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Builiding, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"

   Copyright 1989-1994  University of Washington

   USENET News reading additions in part by L Lundblade / NorthWestNet, 1993
   lgl@nwnet.net

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee to the University of
   Washington is hereby granted, provided that the above copyright notice
   appears in all copies and that both the above copyright notice and this
   permission notice appear in supporting documentation, and that the name
   of the University of Washington not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.  This software is made available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  
   Pine and Pico are trademarks of the University of Washington.
   No commercial use of these trademarks may be made without prior
   written permission of the University of Washington.

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  Revision: 2.13                             *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

  ----------------------------------------------------------------------*/

#include "headers.h"


/*
 * Handly local definitions...
 */
#define	LEGAL_NOTICE \
   "Copyright 1989-1994.  PINE is a trademark of the University of Washington."


/*
 * Globals referenced throughout pine...
 */
struct pine *ps_global;				/* THE global variable! */
char	    *pine_version = PINE_VERSION;	/* version string */
int          timeout	  = NEW_MAIL_TIME;	/* referenced in pico */


/*----------------------------------------------------------------------
  General use big buffer. It is used in the following places:
    compose_mail:    while parsing header of postponed message
    append_message2: while writing header into folder
    q_status_messageX: while doing printf formatting
    addr_book: Used to return expanded address in. (Can only use here 
               because mm_log doesn't q_status on PARSE errors !)
    pine.c: When address specified on command line
    init.c: When expanding variable values

 ----*/
char         tmp_20k_buf[20480];  


#ifdef ANSI
static int setup_mini_menu(int);
void do_menu(int);
void main_redrawer();
void show_main_screen(struct pine *, int, OtherMenu, struct key_menu *);
void new_user_or_version(char []);
void truncated_listvars_warning(int *);		/** temporary **/
void phone_home_blurb(int);
#else
static int setup_mini_menu();
void do_menu();
void main_redrawer();
void show_main_screen();
void new_user_or_version();
void truncated_listvars_warning(); 		/** temporary **/
void phone_home_blurb();
#endif
void do_setup_task();
void upgrade_old_postponed();

#ifndef DOS
void select_printer();
void redraw_printer_select();
#endif

#ifdef	DOS
static char first_time_message[] = "\
                   Welcome to PC-Pine...\n\
\n\
a Program for Internet News and Email.  Pine offers the ability to:\n\
  -Access local and remote message folders using a simple user-interface\n\
  -Send documents, graphics, etc (via the MIME standard for attachments)\n\
\n\
COMMANDS IN PINE:  Available commands are always listed on the last\n\
  two lines of the screen.  If there are more than can be displayed, the\n\
  \"O\" command will cycle their display.  Except in function key mode,\n\
  commands can be executed even though they are not displayed.\n\
\n\
PINE CONFIGURATION:  If you haven't yet filled out your Pine configuration\n\
  file, Pine will ask you for this information as it's needed.  For further \n\
  customization, use the Setup/Config Screen (\"S\" then \"C\" in Main Menu).";

static char new_version_message[] = "\
             <<<This message will appear only once.>>>\n\
\n\
             Welcome to the latest version of Pine!\n\
\n\
Your Pine configuration file shows that you have not used Pine 3.90\n\
before.  You'll see very few changes to Pine's standard behavior, but\n\
there are many new features that you may enable via the new Config\n\
screen under the Main Menu SETUP command.  See the Release Notes\n\
(\"R\" on the Main Menu) for a more detailed list of changes. \n";

#else
static char first_time_message[] = "\
                   Welcome to Pine...\n\
\n\
a Program for Internet News and Email.  Pine offers the ability to:\n\
  -Access local and remote message folders using a simple user-interface\n\
  -Send documents, graphics, etc (via the MIME standard for attachments)\n\
\n\
COMMANDS IN PINE:  Available commands are always listed on the last\n\
 two lines of the screen.  If there are more than can be displayed, the\n\
  \"O\" command will cycle their display.  Except in function key mode,\n\
  commands can be executed even though they are not displayed.\n\
\n\
PINE CONFIGURATION:  Pine has created a default configuration file for you.\n\
  To customize pine's behavior, use the Setup/Config (\"S\" then \"C\"\n\
  in Main Menu).  We also suggest seeing pine's main help (\"?\" in Main \
Menu).";


static char new_version_message[] = "\
             <<<This message will appear only once.>>>\n\
\n\
             Welcome to the latest version of Pine!\n\
\n\
Your Pine configuration file shows that you have not used Pine 3.90\n\
before.  You'll see very few changes to Pine's standard behavior, but\n\
there are many new features that you may enable via the new Config\n\
screen under the Main Menu SETUP command.  See the Release Notes\n\
(\"R\" on the Main Menu) for a more detailed list of changes.";
#endif




static struct key main_keys[] =
       {{"?","Help",0},        {"O","OTHER CMDS",0},  {NULL,NULL,0},    
        {NULL,NULL, 0},        {"P","PrevCmd",0},     {"N","NextCmd",0},
	{NULL,NULL,0},         {NULL,NULL,0},         {"R","RelNotes",0},
	{"K","KBLock",0},      {NULL,NULL,0},         {NULL,NULL,0},

	{"?","Help",0},        {"O","OTHER CMDS",0},  {"Q","Quit",0},
        {"C","Compose",0},     {"L","ListFldrs",0},   {"G","GotoFldr",0},
	{"I","Index",0},       {NULL,NULL,0},         {"S","Setup",0},
	{"A","AddrBook",0},    {"B","Report Bug",0},  {NULL,NULL,0}};
static struct key_menu main_keymenu =
	{sizeof(main_keys)/(sizeof(main_keys[0])*12), 0, 0,0,0,0, main_keys};
#define MAIN_HELP_KEY		0
#define MAIN_DEFAULT_KEY	3
#define MAIN_KBLOCK_KEY		9
#define MAIN_HELP_KEY2		12
#define MAIN_QUIT_KEY		14
#define MAIN_COMPOSE_KEY	15
#define MAIN_FOLDER_KEY		16
#define MAIN_INDEX_KEY		18
#define MAIN_SETUP_KEY		20
#define MAIN_ADDRESS_KEY	21

/*
 * length of longest label from keymenu, of labels corresponding to
 * commands in the middle of the screen.  9 is length of ListFldrs
 */
#define LONGEST_LABEL 9  /* length of longest label from keymenu */
#define LONGEST_NAME 1   /* length of longest name from keymenu */



/*----------------------------------------------------------------------
     main routine -- entry point

  Args: argv, argc -- The command line arguments


 Initialize pine, parse arguments and so on

 If there is a user address on the command line go into send mode and exit,
 otherwise loop executing the various screens in Pine.

 NOTE: The Windows 3.1 port def's this to "app_main"
  ----*/

main(argc, argv)
     int   argc;
     char *argv[];
{
    char            *folder_to_open;
    int              rv;
    struct pine     *pine_state;
    char             int_mail[MAXPATH+1];
#ifdef DYN
    char stdiobuf[64];
#endif

    /*----------------------------------------------------------------------
          Set up buffering and some data structures
      ----------------------------------------------------------------------*/

    pine_state                 = (struct pine *)fs_get(sizeof (struct pine));
    memset((void *)pine_state, 0, sizeof(struct pine));
    ps_global                  = pine_state;
    ps_global->def_sort        = SortArrival;
    ps_global->sort_types[0]   = SortSubject;
    ps_global->sort_types[1]   = SortArrival;
    ps_global->sort_types[2]   = SortFrom;
    ps_global->sort_types[3]   = SortDate;
    ps_global->sort_types[4]   = SortSize;
    ps_global->sort_types[5]   = SortSubject2;
    ps_global->sort_types[6]   = EndofList;
    ps_global->atmts           = (ATTACH_S *) fs_get(sizeof(ATTACH_S));
    ps_global->atmts_allocated = 1;
    ps_global->atmts->description = NULL;
    ps_global->low_speed       = 1;
    mn_init(&ps_global->msgmap, 0L);
    init_init_vars(ps_global);

#ifndef	DOS
    srandom(getpid());  /* for new mail notification which uses random */
#endif

#ifdef DYN
    /*-------------------------------------------------------------------
      There's a bug in DYNIX that causes the terminal driver to lose
      characters when large I/O writes are done on slow lines. Like
      a 1Kb write(2) on a 1200 baud line. Usually CR is output which
      causes a flush before the buffer is too full, some the pine composer
      doesn't output newlines a lot. Either stdio should be fixed to
      continue with more writes when the write request is partial, or
      fix the tty driver to always complete the write.
     */
    setbuffer(stdout, stdiobuf, 64);
#endif

    /* need home directory early */
    get_user_info(&ps_global->ui);
    if(getenv("HOME") != NULL){
	pine_state->home_dir = cpystr(getenv("HOME"));
    }else{
	pine_state->home_dir = cpystr(ps_global->ui.homedir);
    }

    /*----------------------------------------------------------------------
           Parse arguments and initialize debugging
      ----------------------------------------------------------------------*/
    folder_to_open = pine_args(pine_state, argc, argv, &argv);

    if(pine_state->more_mode && folder_to_open[0] == '-'
       && folder_to_open[1] == '\0' && !isatty(0)){
	/*
	 * monkey with descriptors so our normal tty i/o routines don't
	 * choke...
	 */
	dup2(0, 5);		/* assign redirected data to inocuous desc  */
	dup2(2, 0);		/* assign desc associated with tty to stdin */
    }

#ifdef DEBUG
    /* Since this is specific debugging we don't mind if the
       ifdef is the type of system. See conf/templets.h
     */
#ifdef HAVE_SMALLOC 
    if(debug > 8)
      malloc_debug(2);
#endif 
#ifdef NXT
    if(debug > 8)
      malloc_debug(32); 
#endif 

    init_debug();

#ifdef	_WINDOWS
    mswin_setdebug(debug, debugfile);
#endif
#endif  /* DEBUG */

#ifdef	DOS
    /*
     * install c-client callback to manage cache data outside
     * free memory
     */
    mail_parameters(NULL, SET_CACHE, (void *)dos_cache);

    /*
     * Set up a c-client read timeout and timeout handler.  In general,
     * it shouldn't happend, but a server crash or dead link can cause
     * pine to appear wedged if we don't set this up...
     */
    mail_parameters(NULL, SET_READTIMEOUT, (void *)15L);
    mail_parameters(NULL, SET_TIMEOUT, (void *)dos_tcptimeout);

    /*
     * Sniff the environment for timezone offset.  We need to do this
     * here since Windows needs help figuring out UTC, and will adjust
     * what time() returns based on TZ.  THIS WILL SCREW US because
     * we use time() differences to manage status messages.  So, if 
     * rfc822_date, which calls localtime() and thus needs tzset(),
     * is called while a status message is displayed, it's possible
     * for time() to return a time *before* what we remember as the
     * time we put the status message on the display.  Sheesh.
     */
    tzset();
#endif

    /*------- Set up c-client drivers -------------- */ 
#include "linkage.c"

    init_vars(pine_state);

    if(init_username(pine_state) < 0)
      exit(-1);

    if(init_hostname(pine_state) < 0)
      exit(-1);

    if(!pine_state->nr_mode)
      write_pinerc(pine_state);

    if(!pine_state->more_mode && *argv == NULL)
      /*
       * do mail dir if we're not in send only mode...
       */
      if(init_mail_dir(pine_state) < 0){
#ifdef	_WINDOWS
	  /* Under windows, the output goes to the window.  If we exit,
	   * they will not have time to read the message.
	   */
	  mswin_flush ();
	  mswin_pause (15);
#endif
	  exit(-1);
      }

    init_signals();

    /*--- input side ---*/
    if(init_tty_driver(pine_state)){
#ifndef	DOS				/* always succeeds under DOS! */
        fprintf(stderr, "Can't access terminal or input is not a terminal. ");
        fprintf(stderr, "Redirection of\nstandard input is not allowed. For ");
        fprintf(stderr, "example \"pine < file\" doesn't work.\n\007");
        exit(-1);
#endif
    }
        

    /*--- output side ---*/
    rv = config_screen(&(pine_state->ttyo));
#ifndef	DOS				/* always succeeds under DOS! */
    if(rv) {
        switch(rv) {
          case -1:
	    printf("Terminal type (environment variable TERM) not set.\n");
            break;
          case -2:
	    printf("Terminal type \"%s\", is unknown.\n", getenv("TERM"));
            break;
          case -3:
            printf("Can't open termcap file; check TERMCAP");
	    printf(" variable and/or system manager.\n");
            break;
          case -4:
            printf("Your terminal, of type \"%s\",", getenv("TERM"));
	    printf(" is lacking functions needed to run pine.\n");
            break;
        }
        printf("\r");
        end_tty_driver(pine_state);
        exit(-1);
    }
#endif
    init_screen();
    init_keyboard(pine_state->orig_use_fkeys);
    strcpy(pine_state->inbox_name, INBOX_NAME);
    init_folders(pine_state);		/* digest folder spec's */

    pine_state->in_init_seq = 0;	/* so output (& ClearScreen) show up */
    pine_state->dont_use_init_cmds = 1;	/* don't use up initial_commands yet */
    ClearScreen();
    if(!pine_state->more_mode
       && (pine_state->first_time_user || pine_state->show_new_version)){
	pine_state->mangled_header = 1;
	show_main_screen(pine_state, 0, 0, NULL);
	if(!pine_state->nr_mode){
	    if(pine_state->first_time_user)
	      new_user_or_version(first_time_message);
	    else
	      new_user_or_version(new_version_message);
	}

	ClearScreen();
    }
    
    /* put back in case we need to suppress output */
    pine_state->in_init_seq = pine_state->save_in_init_seq;
            
    if(pine_state->more_mode){
	int dice = 1, redir = 0;

	if(pine_state->in_init_seq){
	    pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
	    fs_give((void **)&(pine_state->initial_cmds));
	}

	/*======= Requested that we simply page the given folder =======*/
	if(folder_to_open){		/* Open the requested folder... */
	    SourceType  src;
	    STORE_S    *store = NULL;
	    char       *decode_error = NULL;

	    if(folder_to_open[0] == '-' && folder_to_open[1] == '\0'){
		redir++;
		src = CharStar;
		if(isatty(0) && (store = so_get(src, NULL, EDIT_ACCESS))){
		    gf_io_t pc;
		    char    bigbuf[1025];
		    int     i;

		    strcpy(ps_global->cur_folder, "Standard-Input");
		    gf_set_so_writec(&pc, store);
		    while((i = read(5, bigbuf, 1024)) > 0){
			bigbuf[i] = '\0';
			gf_puts(bigbuf, pc);
		    }

		    if(i < 0)		/* Bummer. */
		      dice = 0;
		}
	    }
	    else{
		src = FileStar;
		strcpy(ps_global->cur_folder, folder_to_open);
		if((store = so_get(src, folder_to_open, READ_ACCESS)) == NULL)
		  dice = 0;
	    }

	    if(dice){
		scrolltool((void *)so_text(store), "FILE VIEW", NULL,
			   SimpleText, src, NULL);
		printf("\n\n");
		so_give(&store);
	    }
	}

        end_screen();
        end_keyboard(F_ON(F_USE_FK,pine_state));
        end_tty_driver(pine_state);
	if(!dice)
	  printf("\n\nCan't display \"%s\": %s\n\n", 
		 (redir) ? "Standard Input" 
			 : folder_to_open ? folder_to_open : "NULL",
		 error_description(errno));
	else
	  printf("\n\n\nPine finished\n\n");

        exit(0);
    }
    else if(*argv == NULL) {
        struct key_menu *km = &main_keymenu;

        /*========== Normal pine mail reading mode ==========*/
            
        pine_state->mail_stream    = NULL;
        pine_state->inbox_stream   = NULL;
        pine_state->mangled_screen = 1;
    
        if(!pine_state->start_in_index) {

	    /* flash message about executing initial commands */
	    if(pine_state->in_init_seq) {
	        pine_state->in_init_seq    = 0;
		pine_state->mangled_header = 1;
		pine_state->mangled_footer = 1;
		pine_state->mangled_screen = 0;
		/* show that this is Pine */
		show_main_screen(pine_state, 0, FirstMenu, km);
		pine_state->mangled_screen = 1;
		pine_state->painted_footer_on_startup = 1;
	        PutLine0(4, 0,
		         "           Executing initial-keystroke-list......");
	        pine_state->in_init_seq = 1;
	    }else {
                show_main_screen(pine_state, 0, FirstMenu, km);
		pine_state->painted_body_on_startup   = 1;
		pine_state->painted_footer_on_startup = 1;
	    }
        }
	else {
	    /* cancel any initial commands, overridden by cmd line */
	    if(pine_state->in_init_seq) {
		pine_state->in_init_seq      = 0;
		pine_state->save_in_init_seq = 0;
		if (pine_state->initial_cmds)
		  *pine_state->initial_cmds = 0;

		F_SET(F_USE_FK,pine_state, pine_state->orig_use_fkeys);
	    }

            do_index_border(pine_state->context_current,
			    pine_state->cur_folder, pine_state->mail_stream,
			    pine_state->msgmap, MsgIndex, NULL,
			    INDX_CLEAR|INDX_HEADER|INDX_FOOTER);
	    pine_state->painted_footer_on_startup = 1;
            PutLine1(4, 0, "           Please wait, opening %s......",
                     pine_state->nr_mode ? "news messages" : "mail folder");
        }

        fflush(stdout);

	pine_state->in_init_seq = 0;

        if(folder_to_open != NULL) {
            if(do_broach_folder(folder_to_open, 
				pine_state->context_current) <= 0) {
                display_message('x');
                sleep(4);
                end_screen();
                end_keyboard(F_ON(F_USE_FK,pine_state));
                end_tty_driver(pine_state);
            
                clear_index_cache();
                completely_done_with_adrbks();
                printf("\n\nUnable to open %s \"%s\". Pine finished\n\n",
                       pine_state->nr_mode ? "news messages" : "folder",
                       folder_to_open);
                exit(-1);
            }
        }
	else {
#ifdef	DOS
            /*
	     * need to ask for the inbox name if no default under DOS
	     * since there is no "inbox"
	     */

	    if(!pine_state->VAR_INBOX_PATH || !pine_state->VAR_INBOX_PATH[0]
	       || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0){
		HelpType help = NO_HELP;
		static   ESCKEY_S ekey[] = {{ctrl(T), 2, "^T", "To Fldrs"},
					  {-1, 0, NULL, NULL}};

		pine_state->mangled_footer = 1;
		int_mail[0] = '\0';
    		while(1) {
        	    rv = optionally_enter(int_mail, -3, 0, MAXPATH, 1, 0,
				      "No inbox!  Folder to open as inbox : ",
				      ekey, help, 0);
        	    if(rv == 3){
			help = (help == NO_HELP) ? h_sticky_inbox : NO_HELP;
			continue;
		    }

        	    if(rv != 4)
			break;
    		}

    		if(rv == 1){
		    q_status_message(0, 0, 2 ,"Folder open cancelled");
		    rv = 0;		/* reset rv */
		} 
		else if(rv == 2){
        	    if(!folder_lister(pine_state, OpenFolder, NULL,
			       &(pine_state->context_current),
			       int_mail, pine_state->context_current, NULL))
		      *int_mail = '\0';	/* user cancelled! */

                    show_main_screen(pine_state, 0, FirstMenu, km);
		}

		if(*int_mail){
		    removing_trailing_white_space(int_mail);
		    removing_leading_white_space(int_mail);
		    if((!pine_state->VAR_INBOX_PATH 
			|| strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0)
		     && want_to("Preserve folder as \"inbox-path\" in PINERC", 
				'y', 'n', NO_HELP, 0, 0) == 'y'){
			set_variable(V_INBOX_PATH, int_mail, 1);
		    }
		    else{
			if(pine_state->VAR_INBOX_PATH)
			  fs_give((void **)&pine_state->VAR_INBOX_PATH);

			pine_state->VAR_INBOX_PATH = cpystr(int_mail);
		    }

		    do_broach_folder(pine_state->inbox_name, 
				     pine_state->context_list);
    		}
		else
		  q_status_message(0, 0,2 ,"\007No folder opened");

	    }
	    else

#endif
            do_broach_folder(pine_state->inbox_name, pine_state->context_list);
        }

        if(pine_state->mangled_footer)
	  pine_state->painted_footer_on_startup = 0;

        if(!pine_state->nr_mode)
          if(expire_sent_mail())
	    pine_state->painted_footer_on_startup = 0;

	/*
	 * Initialize the defaults.  Initializing here means that
	 * if they're remote, the user isn't prompted for an imap login
	 * before the display's drawn, AND there's the chance that
	 * we can climb onto the already opened folder's stream...
	 */
	if(ps_global->first_time_user || ps_global->show_new_version)
	  init_save_defaults();	/* initialize default save folders */

        build_path(int_mail, pine_state->home_dir, INTERRUPTED_MAIL);
        if(!pine_state->nr_mode && folder_exists("[]", int_mail))
	  q_status_message(1, 4, 5, 
		   "\007Use compose command to continue interrupted message.");

#if defined(USE_QUOTAS)
	{
	    long q;
	    int  over;
	    q = disk_quota(pine_state->home_dir, &over);
	    if(q > 0 && over) {
		q_status_message2(1, 4,5,
			"\007WARNING! Over your disk quota by %s bytes (%s)",
			comatose(q),byte_string(q));
	    }
	}
#endif

	pine_state->in_init_seq = pine_state->save_in_init_seq;
	pine_state->dont_use_init_cmds = 0;

	if(pine_state->give_fixed_warning)
	    q_status_message(0, 2, 10,
"Note: some of your config options conflict with site policy and are ignored");

        /*-------------------------------------------------------------------
                         Loop executing the commands
    
            This is done like this so that one command screen can cause
            another one to execute it with out going through the main
            menu. 
            BUG- need for comments
    
          ------------------------------------------------------------------*/
        pine_state->next_screen = pine_state->start_in_index ?
                                         mail_index_screen : 
                                         main_menu_screen;
        while(1) {
            if(pine_state->next_screen == SCREEN_FUN_NULL) 
              pine_state->next_screen = main_menu_screen;

#ifdef	_WINDOWS
	    /* make sure each screen sets exit char and it's gray otherwise */
	    mswin_allowexit (MSWIN_EXIT_DENY, NULL, '\0');
#endif

            (*(pine_state->next_screen))(pine_state);
        }
    }
    else {
        /*======= address on command line/send one message mode ============*/
        char *to, **t, *error, *addr, *fcc;
        int   len, good_addr;

        /*----- Format the To: line with commas for the composer ---*/
        for(t = argv, len = 0; *t != NULL; len += strlen(*t++) + 2);
        to = fs_get(len + 5);
        to[0] = '\0';
        for(t = argv, len = 0; *t != NULL; t++) {
            if(to[0] != '\0')
              strcat(to, ", ");
            strcat(to, *t);
        }

        good_addr = (build_address(to, &addr, &error, &fcc) >= 0);

	if(good_addr)
	  compose_mail(addr, fcc);

	if(addr)
	  fs_give((void **)&addr);

	if(fcc)
	  fs_give((void **)&fcc);

        fs_give((void **)&to);
        end_screen();
        end_keyboard(F_ON(F_USE_FK,pine_state));
        end_tty_driver(pine_state);
        if(!good_addr) {
            fprintf(stderr, "Bad address: %s\n", error);
            exit(-1);
        }
        exit(0);
    }
}


/* this default is from the array of structs below */
#define DEFAULT_MENU_ITEM 6		/* LIST FOLDERS */
#define MAX_DEFAULT_MENU_ITEM 12
#define UNUSED 0
static unsigned char current_default_menu_item = DEFAULT_MENU_ITEM;

/*
 * One of these for each line that gets printed in the middle of the
 * screen in the main menu.
 */
static struct menu_key {
    char         *key_and_name,
		 *news_addition;
    unsigned int f_key;           /* function key that invokes this action */
    unsigned int key;             /* alpha key that invokes this action */
    unsigned int keymenu_number;  /* index into keymenu array for this cmd */
} mkeys[] = {
    {"   %s     HELP               -  Get help using Pine", NULL,
       PF1, '?', MAIN_HELP_KEY},
    {"", NULL, UNUSED, UNUSED, UNUSED},
    {"   %s     COMPOSE MESSAGE    -  Compose and send%s a message",
       "/post", OPF4, 'C', MAIN_COMPOSE_KEY},
    {"", NULL, UNUSED, UNUSED, UNUSED},
    {"   %s     FOLDER INDEX       -  View messages in current folder",
       NULL, OPF7, 'I', MAIN_INDEX_KEY},
    {"", NULL, UNUSED, UNUSED, UNUSED},
    {"   %s     FOLDER LIST        -  Select a folder%s to view",
       " OR news group", OPF5, 'L', MAIN_FOLDER_KEY},
    {"", NULL, UNUSED, UNUSED, UNUSED},
    {"   %s     ADDRESS BOOK       -  Update address book",
       NULL, OPF10, 'A', MAIN_ADDRESS_KEY},
    {"", NULL, UNUSED, UNUSED, UNUSED},
    {"   %s     SETUP              -  Configure or update Pine",
       NULL, OPF9, 'S', MAIN_SETUP_KEY},
    {"", NULL, UNUSED, UNUSED, UNUSED},
    {"   %s     QUIT               -  Exit the Pine program",
       NULL, OPF3, 'Q', MAIN_QUIT_KEY},
    {NULL, NULL, UNUSED, UNUSED, UNUSED}
};



/*----------------------------------------------------------------------
      display main menu and execute main menu commands

    Args: The usual pine structure

  Result: main menu commands are executed


              M A I N   M E N U    S C R E E N

   Paint the main menu on the screen, get the commands and either execute
the function or pass back the name of the function to execute for the menu
selection. Only simple functions that always return here can be executed
here.

This functions handling of new mail, redrawing, errors and such can 
serve as a template for the other screen that do much the same thing.

There is a loop that fetchs and executes commands until a command to leave
this screen is given. Then the name of the next screen to display is
stored in next_screen member of the structure and this function is exited
with a return.

First a check for new mail is performed. This might involve reading the new
mail into the inbox which might then cause the screen to be repainted.

Then the general screen painting is done. This is usually controlled
by a few flags and some other position variables. If they change they
tell this part of the code what to repaint. This will include cursor
motion and so on.
  ----*/
void
main_menu_screen(pine_state)
     struct pine *pine_state;
{
    int  ch, orig_ch, question_line, setup_command, just_a_navigate_cmd;
    char            *new_folder;
    CONTEXT_S       *tc;
    struct key_menu *km;
    OtherMenu       what;
#ifdef	DOS
    extern void (*while_waiting)();
#endif

    ps_global                 = pine_state;
    question_line             = -3;
    just_a_navigate_cmd       = 0;
    current_default_menu_item = DEFAULT_MENU_ITEM;
    what                      = FirstMenu;  /* which keymenu to display */
    ch                        = 'x'; /* For display_message 1st time through */
    pine_state->prev_screen   = main_menu_screen;

    mailcap_free(); /* free resources we won't be using for a while */

    if(!pine_state->painted_body_on_startup 
       && !pine_state->painted_footer_on_startup){
	ClearScreen();
	pine_state->mangled_screen = 1;
    }

    km = &main_keymenu;

    dprint(1, (debugfile, "\n\n    ---- MAIN_MENU_SCREEN ----\n"));

    while (1) {
	/*
	 * fix up redrawer just in case some submenu caused it to get
	 * reassigned...
	 */
	pine_state->redrawer = main_redrawer;

	/*----------- Check for new mail -----------*/
        if(new_mail(NULL, 0,ch==NO_OP_IDLE ? 0 : ch==NO_OP_COMMAND ?1 :2) >= 0)
          pine_state->mangled_header = 1;

        if(streams_died())
          pine_state->mangled_header = 1;

        show_main_screen(pine_state, just_a_navigate_cmd, what, km);
        just_a_navigate_cmd = 0;
	what = SameTwelve;

	/*---- This displays new mail notification, or errors ---*/
        display_message(ch);

        /*------ Read the command from the keyboard ----*/      
        MoveCursor(min(0, pine_state->ttyo->screen_rows - 3), 0);
#ifdef	DOS
#ifdef	MOUSE
	{
	    extern int register_mfunc();
	    extern unsigned long mouse_in_main();

	    register_mfunc(mouse_in_main, 2, 0,
			   pine_state->ttyo->screen_rows-4,
			   pine_state->ttyo->screen_cols);
	}
#endif
	/*
	 * AND pre-build header lines.  This works just fine under
	 * DOS since we wait for characters in a loop. Something will
         * will have to change under UNIX if we want to do the same.
	 */
	while_waiting = build_header_cache;
#ifdef	_WINDOWS
	mswin_allowexit (MSWIN_EXIT_SENDCHAR, NULL,
			 F_ON(F_USE_FK, ps_global) ? OPF3 : 'q');
#endif
#endif
        ch = read_command();
#ifdef	DOS
	while_waiting = NULL;
#ifdef	MOUSE
	{
	    extern void clear_mfunc();
	    clear_mfunc();
	}
#endif
#ifdef	_WINDOWS
	mswin_allowexit (MSWIN_EXIT_DENY, "Complete current operation", '\0');
#endif
#endif
        orig_ch = ch;

        if(ch < 0x0100 && isupper(ch))
          ch = tolower(ch);
	else if(ch >= PF1 && ch <= PF12 && km->which == 1)
	  ch = PF2OPF(ch);

	/*----- Validate the command ----*/
	if(ch == ctrl('M') || ch == ctrl('J') || ch == PF4){
	  ch = F_ON(F_USE_FK,pine_state)
                                       ? mkeys[current_default_menu_item].f_key
                                       : mkeys[current_default_menu_item].key;
          if(ch <= 0xff && isupper(ch))
            ch = tolower(ch);
	}


        /*
	 * 'q' is always valid as a way to exit pine even in function key mode
         */
        if(ch != 'q')
          ch = validatekeys(ch);

	/*------ Execute the command ------*/
	switch (ch){
#if	defined(DOS) && !defined(_WINDOWS)
/* while we're testing DOS */
	  case 'h': 
	    {
#include <malloc.h>
		int    heapresult;
		int    totalused = 0;
		int    totalfree = 0;
		long   totalusedbytes = 0L;
		long   totalfreebytes = 0L;
		long   largestinuse = 0L;
		long   largestfree = 0L, freeaccum = 0L;

		_HEAPINFO hinfo;
		extern long coreleft();
		extern void dumpmetacache();

		hinfo._pentry = NULL;
		while((heapresult = _heapwalk(&hinfo)) == _HEAPOK){
		    if(hinfo._useflag == _USEDENTRY){
			totalused++;
			totalusedbytes += (long)hinfo._size; 
			if(largestinuse < (long)hinfo._size)
			  largestinuse = (long)hinfo._size;
		    }
		    else{
			totalfree++;
			totalfreebytes += (long)hinfo._size;
		    }

		    if(hinfo._useflag == _USEDENTRY){
			if(freeaccum > largestfree) /* remember largest run */
			  largestfree = freeaccum;
			
			freeaccum = 0L;
		    }
		    else
		      freeaccum += (long)hinfo._size;
		}

		sprintf(tmp_20k_buf,
		  "use: %d (%ld, %ld lrg), free: %d (%ld, %ld lrg), DOS: %ld", 
			totalused, totalusedbytes, largestinuse,
			totalfree, totalfreebytes, largestfree, coreleft());
		q_status_message(0, 5, 7, tmp_20k_buf);

		switch(heapresult/* = _heapchk()*/){
		  case _HEAPBADPTR:
		    q_status_message(0,1,2 ,"\007ERROR - Bad start of heap");
		    break;
		  case _HEAPBADBEGIN:
		    q_status_message(0,1,2 ,"\007ERROR - Bad start of heap");
		    break;
		  case _HEAPBADNODE:
		    q_status_message(0,1,2 ,"\007ERROR - Bad node in heap");
		    break;
		  case _HEAPEMPTY:
		    q_status_message(0,1,2 ,"Heap OK - empty");
		    break;
		  case _HEAPEND:
		    q_status_message(0,1,2 ,"Heap checks out!");
		    break;
		  case _HEAPOK:
		    q_status_message(0,1,2 ,"Heap checks out!");
		    break;
		    default:
		    q_status_message1(0, 1, 2, "\007BS from heapchk: %d",
				      (void *)heapresult);
		    break;
		}

		/*       dumpmetacache(ps_global->mail_stream);*/
		/* DEBUG: heapcheck() */
		/*       q_status_message1(0, 1, 3,
                         " * * There's %ld bytes of core left for Pine * * ", 
                         (void *)coreleft());*/
	    }
	    break;
#endif	/* DOS for testing */

	  /*---------- help ------*/
	  case PF1:
	  case OPF1:
	  case ctrl('G'):
	  case '?':
            helper(main_menu_tx, "HELP FOR MAIN MENU", 0);
	    pine_state->mangled_screen = 1;
	    break;
  
	  /*---------- display other key bindings ------*/
	  case PF2:
	  case OPF2:
          case 'o' :
            if(ch == 'o')
	      warn_other_cmds();
	    what = NextTwelve;
	    pine_state->mangled_footer = 1;
	    break;

	  /* case PF4: */
	  /* PF4 is handled above switch */

          /*---------- Previous item in menu ----------*/
	  case PF5:
	  case 'p':
	  case ctrl('P'):
	  case KEY_UP:
	    if(current_default_menu_item > 1) {
	      current_default_menu_item -= 2;  /* 2 to skip the blank lines */
	      pine_state->mangled_body = 1;
	      if(km->which == 0)
	        pine_state->mangled_footer = 1;
	      just_a_navigate_cmd++;
	    }else {
	      q_status_message(0, 0, 2, "\007Already at top of list");
	    }
	    break;

          /*---------- Next item in menu ----------*/
	  case PF6:
	  case 'n':
	  case ctrl('N'):
	  case KEY_DOWN:
	    if(current_default_menu_item < (unsigned)(MAX_DEFAULT_MENU_ITEM-1)){
	      current_default_menu_item += 2;
	      pine_state->mangled_body = 1;
	      if(km->which == 0)
	        pine_state->mangled_footer = 1;
	      just_a_navigate_cmd++;
	    }else {
	      q_status_message(0, 0, 2, "\007Already at bottom of list");
	    }
	    break;

          /*---------- Release Notes ----------*/
	  case PF9:
	  case 'r':
	    helper(h_news, "NEWS ABOUT PINE", 0);
	    pine_state->mangled_screen = 1;
	    break;

#ifndef NO_KEYBOARD_LOCK
          /*---------- Keyboard lock ----------*/
	  case PF10:
	  case 'k':
	    if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
              goto bleep;

            (void)lock_keyboard();
	    ClearScreen();
	    pine_state->mangled_screen = 1;
	    break;
#endif /* !NO_KEYBOARD_LOCK */

          /*---------- Quit pine ----------*/
	  case OPF3: 
	  case 'q':
	    pine_state->next_screen = quit_screen;
	    return;
  
          /*---------- Go to composer ----------*/
	  case OPF4:
	  case 'c':
	    pine_state->next_screen = compose_screen;
	    return;
  
          /*---------- Folders ----------*/
	  case OPF5: 
	  case 'l':
	    pine_state->next_screen = folder_screen;
	    return;

          /*---------- Old Folders Command ----------*/
	  case 'f':
	    q_status_message(0, 0, 2, "\007Use \"L\" to list Folders");
	    break;

          /*---------- Goto new folder ----------*/
	  case OPF6:
	  case 'g':
	    tc = ps_global->context_current;
            new_folder = broach_folder(question_line, 1, &tc);
#if	defined(DOS) && !defined(_WINDOWS)
	    if(new_folder && *new_folder == '{' && coreleft() < 20000){
	      q_status_message(0,0,2,
			       "\007Not enough memory to open IMAP folder");
	      new_folder = NULL;
	    }
#endif
            if(new_folder != NULL) {
              dprint(9, (debugfile, "do_broach_folder (%s, %s)\n",
                         new_folder, tc->context));
              if(do_broach_folder(new_folder, tc) > 0)
                  /* so we go out and come back into the index */
                  ps_global->next_screen = mail_index_screen;
            }
	    return;

          /*---------- Go to index ----------*/
	  case OPF7:
	  case 'i':
	    pine_state->next_screen = mail_index_screen;
	    return;

          /*---------- Setup mini menu ----------*/
	  case OPF9:
	  case 's':
	    setup_command = setup_mini_menu(question_line);
	    pine_state->mangled_footer = 1;
	    do_setup_task(setup_command);
            if(ps_global->next_screen != main_menu_screen)
	      return;
	    break;

          /*---------- Go to address book ----------*/
	  case OPF10 :
	  case 'a':
	    pine_state->next_screen = addr_book_screen;
	    return;
  
          /*---------- report a bug ----------*/
	  case OPF11 :
	  case 'b':
	    gripe(ps_global);
	    if(pine_state->mangled_screen)
	      ClearScreen();

	    break;

	  case 'm':
	    q_status_message(0, 1, 3, "\007Already in Main");
	    break;

#if	defined(DEBUG) && !defined(_WINDOWS)
          case '#':
            if(debug > 8 &&
               want_to("Test panic and abort now", 'n','n',NULL,0,0) == 'y')
              panic("Testing panic");
            break;
#endif

          case ctrl('Z'):
            if(!have_job_control())
              goto bleep;
            if(F_OFF(F_CAN_SUSPEND,pine_state)) {
                q_status_message(1, 1,3,
                           "\007Pine suspension not enabled - see help text");
                break;
            } else {
                do_suspend(pine_state);
            }
            /*-- Fall through to redraw --*/

          case KEY_RESIZE:
	  case ctrl('L'):
	    ClearScreen();
	    pine_state->mangled_screen = 1;
	    if(ch == KEY_RESIZE)
	      clear_index_cache();

	    break;
  
	  case NO_OP_COMMAND :
          case NO_OP_IDLE:
            break;	/* noop for timeout loop mail check */
  
	  default:
          bleep:
	    bogus_command(orig_ch, F_ON(F_USE_FK,pine_state) ? "F1" : "?");
	    break;
	 } /* the switch */
    } /* the BIG while loop! */
}



/*----------------------------------------------------------------------
    Re-Draw the main menu

    Args: none.

  Result: main menu is re-displayed
  ----*/
void
main_redrawer()
{
    struct key_menu *km = &main_keymenu;
    ps_global->mangled_screen = 1;
    show_main_screen(ps_global, 0, FirstMenu, km);
}


	
/*----------------------------------------------------------------------
         Draw the main menu

    Args: pine_state - the usual struct
	  quick_draw - tells do_menu() it can skip some drawing
	  what       - tells which section of keymenu to draw
	  km         - the keymenu

  Result: main menu is displayed
  ----*/
void
show_main_screen(ps, quick_draw, what, km)
    struct pine     *ps;
    int		     quick_draw;
    OtherMenu	     what;
    struct key_menu *km;
{
    if(ps->painted_body_on_startup || ps->painted_footer_on_startup){
	ps->mangled_screen = 0;		/* only worry about it here */
	ps->mangled_header = 1;		/* we have to redo header */
	if(!ps->painted_body_on_startup)
	  ps->mangled_body = 1;		/* make sure to paint body*/

	if(!ps->painted_footer_on_startup)
	  ps->mangled_footer = 1;	/* make sure to paint footer*/

	ps->painted_body_on_startup   = 0;
        ps->painted_footer_on_startup = 0;
    }

    if(ps->mangled_screen) {
	ps->mangled_header = 1;
	ps->mangled_body = 1;
	ps->mangled_footer = 1;
	ps->mangled_screen = 0;
    }

    /* paint the titlebar if needed */
    if(ps->mangled_header) {
	set_titlebar("MAIN MENU", ps->mail_stream, ps->context_current,
		     ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0);
	ps->mangled_header = 0;
    }

    /* paint the body if needed */
    if(ps->mangled_body) {
	do_menu(quick_draw);
	ps->mangled_body = 0;
    }

    /* paint the keymenu if needed */
    if(ps->mangled_footer) {
	bitmap_t    bitmap;
	static char label[LONGEST_LABEL + 2 + 1], /* label + brackets + \0 */
		    name[max(LONGEST_NAME,3)+1];  /* longest name+1 (3=F12) */

	setbitmap(bitmap);

#ifndef NO_KEYBOARD_LOCK
	if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
#endif
	  clrbitn(MAIN_KBLOCK_KEY, bitmap);

	/* put brackets around the default action */
	label[0] = '[';  label[1] = '\0';
	strcat(label,
	      km->keys[mkeys[current_default_menu_item].keymenu_number].label);
	strcat(label, "]");
	km->keys[MAIN_DEFAULT_KEY].label = label;
	sprintf(name, "%s", F_ON(F_USE_FK,ps_global) ?
		pretty_command(mkeys[current_default_menu_item].f_key) :
		pretty_command(mkeys[current_default_menu_item].key));
	km->keys[MAIN_DEFAULT_KEY].name = name;
	draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols, -2, 0, what, 0);
	ps->mangled_footer = 0;
    }
}


/*----------------------------------------------------------------------
         Actually display the main menu

    Args: quick_draw - just a next or prev command was typed so we only have
		       to redraw the highlighting

  Result: Main menu is displayed
  ---*/
void
do_menu(quick_draw)
    int quick_draw;
{
    int  dline, use_fkeys;
    char buf[MAX_SCREEN_COLS+1];
    static int last_inverse = -1;

    use_fkeys = F_ON(F_USE_FK,ps_global);
    
    /* leave room for keymenu, status line, and trademark message */
    for(dline = 3; dline < ps_global->ttyo->screen_rows - 4; dline++) {
        if(!mkeys[dline-3].key_and_name)
          break;

	if(quick_draw && !(dline-3 == last_inverse
			   || dline-3 == current_default_menu_item))
	  continue;

        sprintf(buf, mkeys[dline-3].key_and_name,
                use_fkeys ? "" : pretty_command(mkeys[dline-3].key),
		(ps_global->VAR_NEWS_SPEC && mkeys[dline-3].news_addition)
		  ? mkeys[dline-3].news_addition : "");
        buf[ps_global->ttyo->screen_cols] = '\0';
	if(dline-3 == current_default_menu_item)
	  StartInverse();
        PutLine0(dline, 4, buf);
	if(dline-3 == current_default_menu_item)
	  EndInverse();
    }

    last_inverse = current_default_menu_item;

    if(!quick_draw)	/* the devi.. uh, I mean, lawyer made me do it. */
      PutLine0(ps_global->ttyo->screen_rows - 4, 3, LEGAL_NOTICE);

    fflush(stdout);
}


/*----------------------------------------------------------------------

Args: ql -- Line to prompt on
Returns: character selected

  ----*/
static int
setup_mini_menu(ql)
     int ql;
{
    char        *prompt   = "Choose a setup task from the menu below : ";
    char        *printer  = "Printer";
    char        *passwd   = "Newpassword";
    char        *config   = "Config";
    char        *update   = "Update";
    HelpType     help     = h_mini_setup;
    int          deefault = 'p';
    int          s, ekey_num;
    ESCKEY_S     setup_names[5];

    ekey_num = 0;

    /* There isn't anything that is allowed */
    if(ps_global->vars[V_PRINTER].is_fixed &&
       F_ON(F_DISABLE_PASSWORD_CMD,ps_global) &&
       F_ON(F_DISABLE_CONFIG_SCREEN,ps_global) &&
       F_ON(F_DISABLE_UPDATE_CMD,ps_global)){
	  q_status_message(1, 1, 3,
	  "\007All setup tasks turned off or prohibited by Sys. Mgmt.");
	  return('e');
    }

    if(!ps_global->vars[V_PRINTER].is_fixed){ /* printer can be changed */
	setup_names[ekey_num].ch      = 'p';
	setup_names[ekey_num].rval    = 'p';
	setup_names[ekey_num].name    = "P";
	setup_names[ekey_num++].label = printer;
    }

    if(F_OFF(F_DISABLE_PASSWORD_CMD,ps_global)){ /* password is allowed */
	setup_names[ekey_num].ch      = 'n';
	setup_names[ekey_num].rval    = 'n';
	setup_names[ekey_num].name    = "N";
	setup_names[ekey_num++].label = passwd;
    }

    if(F_OFF(F_DISABLE_CONFIG_SCREEN,ps_global)){ /* config allowed */
	setup_names[ekey_num].ch      = 'c';
	setup_names[ekey_num].rval    = 'c';
	setup_names[ekey_num].name    = "C";
	setup_names[ekey_num++].label = config;
    }

    if(F_OFF(F_DISABLE_UPDATE_CMD,ps_global)){ /* update is allowed */
	setup_names[ekey_num].ch      = 'u';
	setup_names[ekey_num].rval    = 'u';
	setup_names[ekey_num].name    = "U";
	setup_names[ekey_num++].label = update;
    }

    setup_names[ekey_num].ch    = -1;

    s = radio_buttons(prompt, ql, 0, setup_names, deefault, 'x', 0, help, 0);
    /* ^C */
    if(s == 'x') {
	q_status_message(0,0,3,"Setup command cancelled");
	s = 'e';
    }

    return(s);
}


/*----------------------------------------------------------------------

Args: command -- command char to perform

  ----*/
void
do_setup_task(command)
    int command;
{
    switch(command) {
/*
 * We decided to hardwire this to a particular name rather than leave it
 * a config option.  A config option would be reasonable for Unix sites, where
 * the sys mgr could config it, but not for DOS, where it would have to
 * appear in everyone's pinerc.  If the load on pine.cac gets to be too
 * much, we can always turn pine.cac into a multi-address name which
 * randomizes.
 */
#define UPDATE_FOLDER "*{pine.cac.washington.edu/anonymous}updates"
        /*----- UPDATE -----*/
      case 'u':
	q_status_message(1, 3, 5, "Connecting to update server");
        if(do_broach_folder(UPDATE_FOLDER, NULL) > 0)
          ps_global->next_screen = mail_index_screen;
	break;

        /*----- CONFIGURE OPTIONS -----*/
      case 'c':
	option_screen(ps_global);
	break;

        /*----- EXIT -----*/
      case 'e':
        break;

        /*----- NEW PASSWORD -----*/
      case 'n':
#ifdef	PASSWD_PROG
        if(ps_global->restricted){
	    q_status_message(1, 3, 5,
	    "Password change unavailable in restricted demo version of Pine.");
            display_message('x');
            sleep(3);
        }else {
	    change_passwd();
	    ClearScreen();
	    ps_global->mangled_screen = 1;
	}
#else
        q_status_message(0, 3, 5,
		 "Password changing not configured for this version of Pine.");
	display_message('x');
#endif	/* DOS */
        break;

        /*----- CHOOSE PRINTER ------*/
      case 'p':
#ifdef	DOS
	q_status_message(1, 3, 5,
	    "Printer configuration not available in the DOS version of Pine.");
	display_message('x');
        sleep(3);
#else
        select_printer(); 
	ClearScreen();
	ps_global->mangled_screen = 1;
#endif
        break;
    }
}

#ifndef	DOS
/*----------------------------------------------------------------------
    The printer selection screen

   Draws the screen and prompts for the printer number and the custom
   command if so selected.

 ----*/

void
select_printer() 
{
    char           *prompt, pnum_string[80], *j, **help;
    int             rc, pnum, printer_count, qline;

    if(ps_global->vars[V_PRINTER].is_fixed){
	q_status_message1(0,1,3,
	    "\007Sys. Mgmt. does not allow changing printer from \"%s\"",
	    ps_global->VAR_PRINTER);
	return;
    }

    printer_count = 3;

    ClearScreen();

    set_titlebar("SELECT PRINTER", ps_global->mail_stream,
		 ps_global->context_current, ps_global->cur_folder, NULL,
		 1, FolderName, 0, 0);

    redraw_printer_select();

    ps_global->redrawer = redraw_printer_select;

    blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);

    display_message('x');

    qline          = -3;
    pnum_string[0] = '\0';
    prompt         = "Enter printer number from above (1-3): ";
    help           = NO_HELP;
    while (1) {
        rc = optionally_enter(pnum_string, qline, 0,
                              sizeof(pnum_string) - 1, 1, 0, prompt,
                              NULL, help, 0);
        if(rc == 3) {
            help = help == NO_HELP ? h_oe_choosep : NO_HELP;
            continue;
        } 

        if(rc == 0 && *pnum_string != '\0') {
            for(j = pnum_string; isdigit(*j) || *j == '-'; j++);
	    if(*j != '\0') {
	        q_status_message(0, 2,2,
                           "\007Invalid number entered. Use only digits 0-9");
            } else {
                pnum = atoi(pnum_string);
                if(pnum < 1) {
	            q_status_message1(0, 2,2,
                               "\007Printer number %s must be greater than 0",
                                      int2string(pnum));
                } else if(pnum > printer_count) {
                    q_status_message1(0, 2, 2,
                                      "\007Printer number must be %s or less",
                                      int2string(printer_count));
                } else {
                    break;
                }
            }
            pnum_string[0] = '\0';
            display_message(NO_OP_COMMAND);
            sleep (3);
            continue;
	}

        if(rc != 4) {
            q_status_message(0, 0, 3, "Printer selection cancelled");
            return;
        }
    }

    if(pnum) {
        if(pnum == 1) {
            rc = set_variable(V_PRINTER, ANSI_PRINTER, 1);
        } else if(pnum == 2) {
            rc = set_variable(V_PRINTER,ps_global->VAR_STANDARD_PRINTER, 1);
        } else if(pnum == 3) {
            char custom_command[100];

            if(ps_global->restricted) {
                q_status_message(1, 2, 4,
                            "\007Can't select custom printer in Pine demo");
                return;
            }

            strcpy(custom_command,
                   ps_global->VAR_PERSONAL_PRINT_COMMAND == NULL ? "" :
                                      ps_global->VAR_PERSONAL_PRINT_COMMAND);
	    if(!ps_global->vars[V_PERSONAL_PRINT_COMMAND].is_fixed){
		help = NO_HELP;
		do {
		    rc = optionally_enter(custom_command, -3, 0,
					  sizeof(custom_command) - 1, 1, 0,
					  "Command for custom printer: ", NULL,
					  help, 0);
		    if(rc == 3) {
			help = help == NO_HELP ? h_oe_customp : NO_HELP;
		    }
		} while(rc == 3 || rc == 4);
		if(rc == 1) {
		    q_status_message(0, 0, 2, "Printer selection cancelled");
		    return;
		}
		set_variable(V_PRINTER, custom_command, 0);
		rc = set_variable(V_PERSONAL_PRINT_COMMAND, custom_command, 1);
	    } else {
	        q_status_message(0,1,3,
	"Sys. Mgmt. does not allow custom print to be changed, just selected");
		rc = set_variable(V_PRINTER, custom_command, 1);
	    }
        }
        if(rc == 0)
          q_status_message1(0,1,3,"Printer set to \"%s\"; configuration saved",
                            ps_global->VAR_PRINTER);
    }
}


/*----------------------------------------------------------------------
    Paint the printer selection menu body on the screen

   No arguments or return value
 ----*/
void
redraw_printer_select()
{
    int   l, matched_printer = 0;

    l = 2;
    PutLine0(l++, 3, "1. Printer attached to IBM PC or compatible, MacIntosh");
    PutLine0(l++, 6,
     "This may not work with all attached printers, and will depend on the");
    PutLine0(l++, 6, 
     "terminal emulation/communications software in use. It is known to work");
    PutLine0(l++, 6,
     "with Kermit and the latest UW version of NCSA telnet on Macs and PCs,");
    PutLine0(l++, 6,
     "Versaterm Pro on Macs, and WRQ Reflections on PCs.");
    PutLine0(l, 10, "Command:");
    if(strcmp(ps_global->VAR_PRINTER, ANSI_PRINTER) == 0) {
        matched_printer = 1;
        StartInverse();
    }
    PutLine0(l++, 19, ANSI_PRINTER);
    if(strcmp(ps_global->VAR_PRINTER, ANSI_PRINTER) == 0)
      EndInverse();

    l++;
    PutLine0(l++, 3, "2. Standard UNIX print command");
    PutLine0(l++,6,
       "Using this option may require setting your \"PRINTER\" or \"LPDEST\"");
    PutLine0(l++, 6,    
       "environment variable using the standard UNIX utilities.");
    PutLine0(l, 10 , "Command: ");
    if(ps_global->VAR_STANDARD_PRINTER != NULL &&
       strcmp(ps_global->VAR_PRINTER, ps_global->VAR_STANDARD_PRINTER) == 0) {
        matched_printer = 1;
        StartInverse();
    }
    PutLine0(l++, 19, ps_global->VAR_STANDARD_PRINTER != NULL ?
             ps_global->VAR_STANDARD_PRINTER : "");
    if(ps_global->VAR_STANDARD_PRINTER != NULL &&
       strcmp(ps_global->VAR_PRINTER, ps_global->VAR_STANDARD_PRINTER) == 0) 
      EndInverse();


    l++;
    PutLine0(l++, 3, "3. Personally selected print command");
    PutLine0(l++, 6,
      "The text to be printed will be piped into the command given here. Some");
    PutLine0(l++, 6,
      "examples are: \"prt\", \"lpr\", \"lp\", or \"enscript\". The command may be given");
    PutLine0(l++, 6,
      "with options, for example \"enscript -2 -r\" or \"lpr -Plpacc170\". The");
    PutLine0(l++, 6,
      "commands and options on your system may be different from these examples.");
    PutLine0(l, 10,  "Command: ");
    if(!matched_printer)  
      StartInverse();
    PutLine0(l, 19, ps_global->VAR_PERSONAL_PRINT_COMMAND == NULL ? "" :
                   ps_global->VAR_PERSONAL_PRINT_COMMAND);
    if(!matched_printer)
      EndInverse();
    fflush(stdout);
}
#endif	/* !DOS */


/*
 * Display a new user or new version message.
 */
void
new_user_or_version(message)
    char message[];
{
    char buf[256];
    int dline, i, j;

    j = 0;
    for(dline = 2; dline < ps_global->ttyo->screen_rows - 4; dline++) {
	for(i = 0; i < 256 && message[j] && message[j] != '\n' ; i++)
	  buf[i] = message[j++];

	buf[i] = '\0';
	if(message[j])
	  j++;
	else if(!i)
	  break;

        PutLine0(dline, 2, buf);
    }

    /*
     * Temporary message for old bug.
     * 3.89 and earlier would delete continuation lines of variables
     * it didn't know about, so new *list* variables added would be
     * truncated to length 1 if a user went back to a previous version.
     * There are many new list vars in 3.90 so it could be a problem now.
     * Check to see if the new list variables are set and may have been
     * truncated, and issue a warning.  This works because 3.89 and earlier
     * rewrite the version number in pinerc even if it goes backwards.
     */
    if(ps_global->pre390)
      truncated_listvars_warning(&dline);

    PutLine0(ps_global->ttyo->screen_rows - 4, 13,
		 "PINE is a trademark of the University of Washington.");

    /*
     * You may think this is weird.  We're trying to offer sending
     * the "phone home" message iff there's no evidence we've ever
     * run pine *or* this is the first time we've run a version
     * a version of pine >= 3.90.  The check for the existence of a var
     * new in 3.90, is to compensate for pre 3.90 pine's rewriting their
     * lower version number.
     */
    if(ps_global->first_time_user || (ps_global->pre390
       && !var_in_pinerc(ps_global->vars[V_NNTP_SERVER].name))){
	phone_home_blurb(dline);
	if(want_to("Request document", 'y', 0, NO_HELP, 0, 1) == 'y')
	  phone_home();
	else
	  q_status_message(0,1,3,"No request sent");

    }
    else{
	StartInverse();
	PutLine0(ps_global->ttyo->screen_rows - 3, 0,
		 "Type any character to continue : ");
	EndInverse();

	/* ignore the character typed, 120 second timeout */
	(void)read_char(120);
    }

    /*
     * Check to see if we have an old style postponed mail folder and
     * that there isn't a new style postponed folder.  If true,
     * fix up the old one, and move it into postition for pine >= 3.90...
     */
    if(!ps_global->first_time_user && ps_global->pre390)
      upgrade_old_postponed();
}


void
upgrade_old_postponed()
{
    int	       i;
    char       file_path[MAXPATH], *status, buf[6];
    STORE_S   *in_so, *out_so;
    CONTEXT_S *save_cntxt = default_save_context(ps_global->context_list);
    STRING     msgtxt;
    gf_io_t    pc, gc;

    /*
     * NOTE: woe to he who redefines things in os.h such that
     * the new and old pastponed folder names are the same.
     * If so, you're on your own...
     */
    build_path(file_path, ps_global->folders_dir, POSTPONED_MAIL);
    if(in_so = so_get(FileStar, file_path, READ_ACCESS)){
	for(i = 0; i < 6 && so_readc((unsigned char *)&buf[i], in_so); i++)
	  ;

	buf[i] = '\0';
	if(strncmp(buf, "From ", 5)){
	    dprint(1, (debugfile,
		       "POSTPONED conversion %s --> <%s>%s\n",
		       file_path,save_cntxt->context,
		       ps_global->VAR_POSTPONED_FOLDER));
	    so_seek(in_so, 0L, 0);
	    if((out_so = so_get(CharStar, NULL, WRITE_ACCESS))
	       && (folder_exists(save_cntxt->context,
				 ps_global->VAR_POSTPONED_FOLDER)
		   || context_create(save_cntxt->context,
				  create_proto(NULL,save_cntxt,
					      ps_global->VAR_POSTPONED_FOLDER),
				     ps_global->VAR_POSTPONED_FOLDER))){
		gf_set_so_readc(&gc, in_so);
		gf_set_so_writec(&pc, out_so);
		gf_filter_init();
		gf_link_filter(gf_local_nvtnl);
		if(!(status = gf_pipe(gc, pc))){
		    so_seek(out_so, 0L, 0);	/* just in case */
		    INIT(&msgtxt, mail_string, so_text(out_so),
			 strlen((char *)so_text(out_so)));

		    if(context_append(save_cntxt->context, NULL, 
				      ps_global->VAR_POSTPONED_FOLDER,
				      &msgtxt)){
			so_give(&in_so);
			unlink(file_path);
		    }
		    else{
			q_status_message(0, 3, 3,
				    "\007Problem upgrading postponed message");
			dprint(1, (debugfile,
				   "Conversion failed: Can't APPEND\n"));
		    }
		}
		else{
		    q_status_message(0, 3, 3,
				    "\007Problem upgrading postponed message");
		    dprint(1,(debugfile,"Conversion failed: %s\n",status));
		}
	    }
	    else{
		q_status_message(0, 3, 3,
				    "\007Problem upgrading postponed message");
		dprint(1, (debugfile,
			   "Conversion failed: Can't create %s\n",
			   (out_so) ? "new postponed folder"
				    : "temp storage object"));
	    }

	    if(out_so)
	      so_give(&out_so);
	}

	if(in_so)
	  so_give(&in_so);
    }
}


void
phone_home_blurb(dline)
    int dline;
{
    static char *blurb[] = {
      "SPECIAL OFFER:  Would you like to receive (via email) a brief document",
      "  entitled \"Secrets of Pine\" ?"
    };

    if(dline+3 < ps_global->ttyo->screen_rows - 3)
      dline++;

    if(dline+2 < ps_global->ttyo->screen_rows - 3){
	PutLine0(dline++, 2, blurb[0]);
	PutLine0(dline++, 2, blurb[1]);
    }
}


void
truncated_listvars_warning(dline)
    int *dline;
{
    char **p;
    int    i;
#define N_LISTS 5
    char **new_lists[N_LISTS];

    new_lists[0] = ps_global->vars[V_COMP_HDRS].user_val.l;
    new_lists[1] = ps_global->vars[V_CUSTOM_HDRS].user_val.l;
    new_lists[2] = ps_global->vars[V_NNTP_SERVER].user_val.l;
    new_lists[3] = ps_global->vars[V_ADDRESSBOOK].user_val.l;
    new_lists[4] = ps_global->vars[V_GLOB_ADDRBOOK].user_val.l;

    for(i=0; i < N_LISTS; i++){
        p = new_lists[i];
        if(p && *p && **p)
	    break;
    }

#define LINES_OF_EXPLANATION 2
    if(i < N_LISTS){
      if((*dline)+LINES_OF_EXPLANATION+1 < ps_global->ttyo->screen_rows - 3)
	  (*dline)++;
      if((*dline)+LINES_OF_EXPLANATION < ps_global->ttyo->screen_rows - 3){
	PutLine0((*dline)++, 2,
    "[You ran an old version of Pine which may have truncated some of your]");
	PutLine0((*dline)++, 2,
    "[new pinerc list variables (because of a bug in the old version).    ]");
      }
    }
}


/*----------------------------------------------------------------------
          Quit pine if the user wants to 

    Args: The usual pine structure

  Result: User is asked if she wants to quit, if yes then execute quit.

       Q U I T    S C R E E N

Not really a full screen. Just count up deletions and ask if we really
want to quit.
  ----*/
void
quit_screen(pine_state)
     struct pine *pine_state;
{
    int cur_is_inbox;
    HelpType help = NO_HELP;

    dprint(1, (debugfile, "\n\n    ---- QUIT SCREEN ----\n"));    

    if(!pine_state->nr_mode && F_OFF(F_QUIT_WO_CONFIRM,pine_state)
       && want_to("Really quit pine", 'y', 0, help, 0, 0) != 'y') {
        pine_state->next_screen = pine_state->prev_screen;
        return;
    }

    cur_is_inbox = (pine_state->inbox_stream == pine_state->mail_stream);

    expunge_and_close(pine_state->mail_stream, pine_state->cur_folder);
    mn_give(&pine_state->msgmap);

    pine_state->redrawer = (void (*)())NULL;

    if(pine_state->inbox_stream != NULL && !cur_is_inbox){
	pine_state->mail_stream = pine_state->inbox_stream;
	pine_state->msgmap      = pine_state->inbox_msgmap;
        expunge_and_close(pine_state->inbox_stream, pine_state->inbox_name);
	mn_give(&pine_state->msgmap);
    }

    if(pine_state->outstanding_pinerc_changes)
      write_pinerc(pine_state);

    end_screen();
    end_keyboard(F_ON(F_USE_FK,pine_state));
    end_tty_driver(pine_state);
    end_signals(0);

    imap_flush_passwd_cache();
    clear_index_cache();
    completely_done_with_adrbks();
    mailcap_free();
    free_folders();

    if(pine_state->hostname != NULL)
      fs_give((void **)&pine_state->hostname);
    if(pine_state->localdomain != NULL)
      fs_give((void **)&pine_state->localdomain);
    if(pine_state->userdomain != NULL)
      fs_give((void **)&pine_state->userdomain);
    if(pine_state->ttyo != NULL)
      fs_give((void **)&pine_state->ttyo);
    if(pine_state->home_dir != NULL)
      fs_give((void **)&pine_state->home_dir);
    if(pine_state->folders_dir != NULL)
      fs_give((void **)&pine_state->folders_dir);
    if(pine_state->ui.homedir)
      fs_give((void **)&pine_state->ui.homedir);
    if(pine_state->ui.login)
      fs_give((void **)&pine_state->ui.login);
    if(pine_state->ui.fullname)
      fs_give((void **)&pine_state->ui.fullname);

    fs_give((void **)&pine_state);

#ifdef DEBUG
    if(debugfile)
      fclose(debugfile);
#endif    

    printf("\n\n\nPine finished\n\n");
    exit(0);
}



/*
 * Useful flag checking macro for 
 */
#define FLAG_MATCH(F, M)   (((((F)&F_SEEN)   ? (M)->seen		     \
				: ((F)&F_UNSEEN)     ? !(M)->seen : 1)	     \
			  && (((F)&F_DEL)    ? (M)->deleted		     \
				: ((F)&F_UNDEL)      ? !(M)->deleted : 1)    \
			  && (((F)&F_ANS)    ? (M)->answered		     \
				: ((F)&F_UNANS)	     ? !(M)->answered : 1)   \
			  && (((F)&F_FLAG) ? (M)->flagged		     \
				: ((F)&F_UNFLAG)   ? !(M)->flagged : 1)	     \
			  && (((F)&F_RECENT) ? (M)->recent		     \
				: ((F)&F_UNRECENT)   ? !(M)->recent : 1))    \
			  || ((((F)&F_OR_SEEN) ? (M)->seen		     \
				: ((F)&F_OR_UNSEEN)   ? !(M)->seen : 0)      \
			  || (((F)&F_OR_DEL)   ? (M)->deleted		     \
				: ((F)&F_OR_UNDEL)    ? !(M)->deleted : 0)   \
			  || (((F)&F_OR_ANS)   ? (M)->answered		     \
				: ((F)&F_OR_ANS)      ? !(M)->answered : 0)  \
			  || (((F)&F_OR_FLAG)? (M)->flagged		     \
				: ((F)&F_OR_UNFLAG) ? !(M)->flagged : 0)     \
			  || (((F)&F_OR_RECENT)? (M)->recent		     \
				: ((F)&F_OR_UNRECENT) ? !(M)->recent : 0)))



/*----------------------------------------------------------------------
     Find the first message with the specified flags set

  Args: flags -- Flags in messagecache to match on
        stream -- The stream/folder to look at message status

 Result: Message number of first message with specified flags set
  ----------------------------------------------------------------------*/
MsgNo
first_sorted_flagged(flags, stream)
    unsigned long  flags;
    MAILSTREAM    *stream;
{
    MsgNo        i;
    MESSAGECACHE *mc;

    FETCH_ALL_FLAGS(stream);

    for(i = 1 ; i < mn_get_total(ps_global->msgmap); i++) {
	mc = mail_elt(stream, mn_m2raw(ps_global->msgmap, i));
	if(mc && FLAG_MATCH(flags, mc))
          break;
    }

    dprint(4, (debugfile, "First unseen returning %ld\n", (long)i));
    return(i);
}



/*----------------------------------------------------------------------
     Find the next message with specified flags set

  Args: flags -- Flags in messagecache to match on
        stream -- The stream/folder to look at message status
        start  -- Place to start looking
        found_is_new -- Set if the message found is actually new.  The only
                        case where this might not be set when the message
                        being returned is the last message in folder.

 Result: Message number of next message with specified flags set
  ----------------------------------------------------------------------*/
MsgNo
next_sorted_flagged(flags, stream, start, found_is_new)
    unsigned long  flags;
    MAILSTREAM    *stream;
    long           start;
    int           *found_is_new;
{
    MsgNo        i;
    MESSAGECACHE *mc;

    if(found_is_new)
      *found_is_new = 0;

    FETCH_ALL_FLAGS(stream);

    for(i = start ; i <= mn_get_total(ps_global->msgmap); i++) {
	mc = mail_elt(stream, mn_m2raw(ps_global->msgmap, i));
        if(mc && FLAG_MATCH(flags, mc)
	   && !get_lflag(stream, ps_global->msgmap, i, MN_HIDE)){
            if(found_is_new)
              *found_is_new = 1;

            break;
        }
    }

    return(min(i, mn_get_total(ps_global->msgmap)));
}



/*----------------------------------------------------------------------
  get the requested LOCAL flag bits for the given pine message number

   Accepts: msgs - pointer to message manipulation struct
            n - message number to get
	    f - bitmap of interesting flags
   Returns: non-zero if flag set, 0 if not set or no elt (error?)

   NOTE: this can be used to test system flags
  ----*/
int
get_lflag(stream, msgs, n, f)
     MAILSTREAM *stream;
     MSGNO_S    *msgs;
     long        n;
     int         f;
{
    MESSAGECACHE *mc;

    if(n < 1L || (msgs && n > mn_get_total(msgs)))
      return(0);

    FETCH_ALL_FLAGS(stream);

    mc = mail_elt(stream, msgs ? mn_m2raw(msgs, n) : n);
    return((!mc) ? 0 : (!f) ? !(mc->spare || mc->spare2 || mc->spare3)
			    : (((f & MN_HIDE) ? mc->spare : 0)
			       || ((f & MN_EXLD) ? mc->spare2 : 0)
			       || ((f & MN_SLCT) ? mc->spare3 : 0)));
}



/*----------------------------------------------------------------------
  set the requested LOCAL flag bits for the given pine message number

   Accepts: msgs - pointer to message manipulation struct
            n - message number to set
	    f - bitmap of interesting flags
	    v - value (on or off) flag should get
   Returns: our index number of first

   NOTE: this isn't to be used for setting IMAP system flags
  ----*/
int
set_lflag(stream, msgs, n, f, v)
     MAILSTREAM *stream;
     MSGNO_S    *msgs;
     long        n;
     int         f, v;
{
    MESSAGECACHE *mc;

    if(n < 1L || n > mn_get_total(msgs))
      return(0L);

    FETCH_ALL_FLAGS(stream);

    if(mc = mail_elt(stream, mn_m2raw(msgs, n))){
	if((f & MN_HIDE) && mc->spare != v){
	    mc->spare = v;
	    msgs->flagged_hid += (v) ? 1L : -1L;
	}

	if((f & MN_EXLD) && mc->spare2 != v){
	    mc->spare2 = v;
	    msgs->flagged_exld += (v) ? 1L : -1L;
	}

	if((f & MN_SLCT) && mc->spare3 != v){
	    mc->spare3 = v;
	    msgs->flagged_tmp += (v) ? 1L : -1L;
	}
    }

    return(1);
}



/*----------------------------------------------------------------------
  return whether the given flag is set somewhere in the folder

   Accepts: msgs - pointer to message manipulation struct
	    f - flag bitmap to act on
   Returns: number of messages with the given flag set.
	    NOTE: the sum, if multiple flags tested, is bogus
  ----*/
long
any_lflagged(msgs, f)
     MSGNO_S    *msgs;
     int         f;
{
    if(!msgs)
      return(0L);

    if(f == MN_NONE)
      return(!(msgs->flagged_hid || msgs->flagged_exld || msgs->flagged_tmp));
    else
      return(((f & MN_HIDE)   ? msgs->flagged_hid  : 0L)
	     + ((f & MN_EXLD) ? msgs->flagged_exld : 0L)
	     + ((f & MN_SLCT) ? msgs->flagged_tmp  : 0L));
}



/*----------------------------------------------------------------------
  Decrement the count of the given flag type messages

   Accepts: msgs - pointer to message manipulation struct
	    f - flag bitmap to act on
	    n - number of flags
   Returns: with the total count adjusted accordingly.
	    NOTE: this function is kind of bogus, and is mostly 
	    necessary because flags are stored in elt's which we can't
	    get at during an mm_expunged callback.
  ----*/
void
dec_lflagged(msgs, f, n)
     MSGNO_S    *msgs;
     int         f;
     long	 n;
{
    if(!msgs)
      return;

    if(f & MN_HIDE)
      msgs->flagged_hid = max(0L, msgs->flagged_hid - n);
    else if(f & MN_EXLD)
      msgs->flagged_exld = max(0L, msgs->flagged_exld - n);
    else if(f & MN_SLCT)
      msgs->flagged_tmp = max(0L, msgs->flagged_tmp - n);
}



/*----------------------------------------------------------------------
    Count messages on stream with specified system flag attributes

  Args: stream -- The stream/folder to look at message status
        flags -- flags on folder/stream to examine

 Result: count of messages with those attributes set
  ----------------------------------------------------------------------*/
long
count_flagged(stream, flags)
     MAILSTREAM *stream;
     char       *flags;
{
    char   *flagp;
    int     old_prefetch;
    extern  MAILSTREAM *mm_search_stream;
    extern  MSGNO_S    *mm_search_map, *mm_exclude_map;
    extern  int	       mm_search_count;

    mm_search_stream = stream;
    mm_search_map    = mm_exclude_map = NULL;
    mm_search_count  = 0L;

    old_prefetch = (int) mail_parameters(stream, SET_PREFETCH, NULL);
    mail_search(stream, flagp = cpystr(flags));
    mail_parameters(stream, SET_PREFETCH, (void *) old_prefetch);
    fs_give((void **)&flagp);

    return(mm_search_count);
}


/*----------------------------------------------------------------------
  See if stream can be used for a mailbox name (courtesy of Mark Crispin)

   Accepts: mailbox name
            candidate stream
   Returns: stream if it can be used, else NIL

  This is called to weed out unnecessary use of c-client streams.
  ----*/
MAILSTREAM *
same_stream(name, stream)
    char *name;
    MAILSTREAM *stream;
{
    char *s,*t,host1[MAILTMPLEN],host2[MAILTMPLEN];
                                /* make sure stream valid */
    if (stream && (s = stream->mailbox) &&
                                /* must be a network stream */
	((*s == '{') || (*s == '*' && s[1] == '{')) &&
                                /* must be a network name */
	((*(t = name) == '{') || (*name == '*' && (t = name)[1] == '{')) &&
                                /* name must be valid for that stream */
	mail_valid_net (t,stream->dtb,host1,NIL) &&
                                /* get host name from stream */
	mail_valid_net (stream->mailbox,stream->dtb,host2,NIL)) {
                                /* return success if same host */
	if (!strucmp (canonical_name(host1),host2))
	  return stream;
    }

    return NIL;                   /* one of the tests failed */
}


/*
 * Simple, handy macro to determine if folder name is remote 
 * (on an imap server)
 */
#define	IS_REMOTE(X)	(*(X) == '{' && *((X) + 1) && *((X) + 1) != '}' \
			 && strchr(((X) + 2), '}'))


/*----------------------------------------------------------------------
  Given a fully qualified folder name, return pointer to
  driver that the name should be created with IF LOCAL

   Accepts: mailbox name
   Returns: stream if it can be used, else NIL

   NOTE: Right now, only used by create when there's nothing else
         there.  open, rename and such just do the usual driver hunt,
         which we specify by handing a NULL stream.

  ----*/
MAILSTREAM *
default_driver(name)
    char *name;
{
    /*
     * if the folder name is local, assign default proto
     * BE SURE TO DO THIS if it is local, as {mail,context}_create
     * requires a driver to create in.  For remote cases, NULL is OK
     * as the driver is infered from the name unambiguously.
     */
    return(IS_REMOTE(name) ? NULL : default_proto());
}


/*
 * context_proto - given a folder and context, return the driver to 
 *                 create the folder in.  The stream passed in has to
 *                 be either NULL or a remote stream
 */
MAILSTREAM *
create_proto(stream, context, folder)
    MAILSTREAM *stream;		/* stream to return if IMAP folder */
    CONTEXT_S  *context;
    char       *folder;
{
    if(context_isambig(folder))
      return(IS_REMOTE(context->context) ? stream : context->proto);
    else
      /*
       * Now things get a bit tougher.  If remote, check to see if
       * a stream's already open, otherwise
       */
      return(IS_REMOTE(folder) ? stream : default_driver(folder));
}
	

/*----------------------------------------------------------------------
   Give hint about Other command being optional.  Some people get the idea
   that it is required to use the commands on the 2nd and 3rd keymenus.
   
   Args: none

 Result: message may be printed to status line
  ----*/
void
warn_other_cmds()
{
    static int other_cmds = 0;

    other_cmds++;
    if(((ps_global->first_time_user || ps_global->show_new_version) &&
	      other_cmds % 3 == 0 && other_cmds < 10) || other_cmds % 20 == 0)
        q_status_message(0, 0, 2,
		    "Remember: the \"O\" command is always optional");
}


/*----------------------------------------------------------------------
    Panic pine - call on detected programmatic errors to exit pine

   Args: message -- message to record in debug file and to be printed for user

 Result: The various tty modes are restored
         If debugging is active a core dump will be generated
         Exits Pine

  This is also called from imap routines and fs_get and fs_resize.
  ----*/
void
panic(message)
     char *message;
{
    MoveCursor(ps_global->ttyo->screen_rows -1, 0);
    NewLine();
    end_screen();
    end_keyboard(ps_global != NULL ? F_ON(F_USE_FK,ps_global) : 0);
    end_tty_driver(ps_global);
    end_signals(1);
    dprint(1, (debugfile, "Pine Panic: %s\n", message));
    fprintf(stderr, "\n\nBug in Pine detected: \"%s\".\nExiting pine.\n",
            message);
#ifdef DEBUG
    if(debugfile)
      save_debug_on_crash(debugfile);
    coredump();   /*--- If we're debugging get a core dump --*/
#endif

    exit(-1);
    fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
}



/*----------------------------------------------------------------------
    Panic pine - call on detected programmatic errors to exit pine, with arg

  Input: message --  printf styule string for panic message (see above)
         arg     --  argument for printf string

 Result: The various tty modes are restored
         If debugging is active a core dump will be generated
         Exits Pine
  ----*/
void
panic1(message, arg)
    char *message;
    char *arg;
{
    char buf[1001];
    if(strlen(message) > 1000) {
        panic("Pine paniced. (Reason for panic is too long to tell)");
    } else {
        sprintf(buf, message, arg);
        panic(buf);
    }
}
