#include "args.h"
#include "config.h"

/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

/*
 * set.c - support for the "set" command
 *
 * All the internal variables which can be explicitly changed by the
 * user are declared here, along with the code implementing the "set"
 * command itself.
 */

#include "mm.h"
#include "parse.h"
#include "set.h"
#include "cmds.h"

#define CHAR_SQ '\''			/* \047 */
#define CHAR_BS '\\'			/* \0134 */

#define isquote(x) (x == CHAR_SQ)
#define isbackslash(x) (x == CHAR_BS)

static char *show_filename ARGS((FILE *fp, const char *longname, int disp));
static int geol ARGS((FILE *fp));
static int gnum ARGS((FILE *fp, int *num));
static int gsstr ARGS((FILE *fp, char *cp));
static int gstr ARGS((FILE *fp, char **cp));
static int set_variable ARGS((int n));
static void peol ARGS((FILE *fp));
static void pnum ARGS((FILE *fp, int n));
static void pstr ARGS((FILE *fp, const char *str));
static void show_all_variables ARGS((FILE *out));
#define tilde_expand Stilde_expand
				/* avoid conflict with global tilde_expand() */
static char *tilde_expand ARGS((char *dirname));

int	aliases_use_groups = SET_NO,	/* aliases != groups */
        append_new_mail = SET_NO,       /* use old style fetchmail() */
        append_signature = SET_NO,	/* don't append .signature file */
	auto_create_files = SET_ASK,	/* ask about new files */
	auto_startup_get = SET_YES,	/* get your mailfile on startup */
        autowrap_column,		/* set up in initialize */
	browse_clear_screen = SET_YES,	/* clear before showing each header */
        browse_pause = SET_YES,		/* pause between messages on browse */
  	check_interval = 300,		/* interval for newmail alarm() */
        clear_screen = SET_YES,		/* clear screen by default */
        continuous_check = SET_NO,	/* check constantly for new mail */
        control_d_automatic_send = SET_NO, /* don't send automatically on ^D */
	control_e_editor = SET_YES,	/* ^E invokes editor */
    	control_l_confirm = SET_NO,	/* don't confirm on ^L */
	control_n_abort = SET_ASK,	/* ask about abort */
	directory_folders = SET_NO,	/* can folders be directories */
	display_flagged_messages = SET_YES, /* show flagged messages */
  	display_outgoing_message = SET_NO, /* show msg when prompting */
        escape_automatic_send = SET_NO,	/* send automatically on esc */
	expunge_on_bye = SET_ASK,	/* expunge when they say bye */
	fast_init_file = SET_YES,	/* use fast init file */
  	gnuemacs_mmail = SET_YES,	/* we have gnuemacs mmail mode */
    	handle_changed_modtime = SET_YES, /* recover from mtime change */
  	list_include_headers = SET_YES,	/* put index before messages */
  	list_on_separate_pages = SET_NO, /* don't put formfeed between msgs */
        modify_read_only = SET_YES,	/* don't ask whether to modify */
	new_file_mode = 0600,		/* default mode */
	prompt_for_bcc = SET_NO,	/* prompt for bcc on send */
	prompt_for_cc = SET_YES,	/* prompt for cc on send */
	prompt_for_fcc = SET_NO,	/* prompt for fcc on send */
        prompt_rcpt_always = SET_NO,	/* prompt even when "send foo" */
	reply_all = SET_NO,		/* don't reply to everyone */
        reply_include_me = SET_NO,	/* replies go to self too */
    	reply_initial_display = SET_NO,	/* display To, Cc, etc when replying */
	reply_insert = SET_NO,		/* don't include original in reply */
  	send_verbose = SET_YES,		/* show all recipients when sending */
  	sendmail_verbose = SET_NO,	/* don't be verbose when sending */
  	sendmail_background = SET_YES,	/* Send mail in the background */
        suspend_on_exit = SET_NO,	/* suspend when a quit is done */
        suspend_on_quit = SET_NO,	/* suspend when a quit is done */
        terse_text_prompt = SET_NO,	/* don't be terse when prompting */
	use_editor_always = SET_NO,	/* don't use the editor by default */
    	use_crt_filter_always = SET_NO,	/* don't always filter things */
	use_invalid_address = SET_ASK;	/* ask about invalid addresses */

#ifdef FDC_EDITFIX
int     keep_tmp_files = SET_YES;	/* keep temp files by default */
#else
int     keep_tmp_files = SET_NO;	/* don't keep temp files */
#endif /* FDC_EDITFIX */

#define NULSTR { "" }
string	crt_filter = { PAGER },
        mime_filter = { PAGER },	/* fdc 2014/02/23 */
        default_from = NULSTR,
  	default_read_command = { "next" },
	default_reply_to = NULSTR,
  	default_send_command = NULSTR,
        download_filter = { "kermit -Ts - -a %s" },
        finger_command = { "finger" },
  	header_options_file = { "/dev/null" },
  	help_file = { HELPFILE },
  	help_dir = { HELPDIR },
	mail_directory = { "." },
	mail_file = NULSTR,
        mmail_path = { MMAIL_PATH },
  	movemail_path = { MOVEMAIL },
	personal_name = { 0 },
        print_filter = { "lpr" },
	read_prompt = { "R>" },
	reply_indent = { "> " },
	saved_messages_file = { "/dev/null" },
	send_prompt = { "S>" },
  	temp_directory = NULSTR,	/* set in init.c:initialize() */
	top_level_prompt = { "MM>" },
        user_name = NULSTR;
#undef NULSTR

addresslist
	default_bcc_list = { nil, nil },
	default_cc_list = { nil, nil };

keylist	dont_print_headers = nil,
    	dont_type_headers = nil,
	user_keywords = nil,
    	only_print_headers = nil,
	only_type_headers = nil,
	user_headers = nil,
	incoming_mail = nil,
	editor = (char **)NULL,
	default_fcc_list = nil,
        speller = (char **)NULL;

extern Mail_aliases mail_aliases;

extern fdb aliasfdb;
extern keytab read_keytab;
extern keytab send_keytab;

static keywrd user_levels[] = {
    { "novice", 0, NOVICE },
    { "expert", 0, EXPERT },
};

static keytab user_levtab = { sizeof(user_levels) / sizeof(keywrd),
			      user_levels };
fdb user_level_fdb = { _CMKEY, 0, nil, (pdat) &user_levtab, "user level" };

Setkey default_mail_type = { &formattab, "mbox" },
       user_level = { &user_levtab, "expert" };

static keywrd set_cmds[] = {
    { "aliases-use-groups",	0, (keyval) SET_ALIASES_USE_GROUPS },
    { "append-new-mail",        0, (keyval) SET_APPEND_NEW_MAIL },
    { "append-signature",	0, (keyval) SET_APPEND_SIGNATURE },
    { "auto-create-files",	0, (keyval) SET_AUTO_CREATE_FILES },
    { "auto-startup-get", 	0, (keyval) SET_AUTO_STARTUP_GET },
    { "autowrap-column", 	0, (keyval) SET_AUTOWRAP_COLUMN },
    { "browse-clear-screen",	0, (keyval) SET_BROWSE_CLEAR_SCREEN },
    { "browse-pause", 		0, (keyval) SET_BROWSE_PAUSE },
    { "check-interval",		0, (keyval) SET_CHECK_INTERVAL },
    { "clear-screen", 		0, (keyval) SET_CLEAR_SCREEN },
    { "continuous-check",	0, (keyval) SET_CONTINUOUS_CHECK },
    { "control-d-automatic-send", 0, (keyval) SET_CONTROL_D_AUTOMATIC_SEND },
    { "control-e-editor", 	0, (keyval) SET_CONTROL_E_EDITOR },
    { "control-l-confirm", 	0, (keyval) SET_CONTROL_L_CONFIRM },
    { "control-n-abort", 	0, (keyval) SET_CONTROL_N_ABORT },
    { "crt-filter", 		0, (keyval) SET_CRT_FILTER },
    { "default-bcc-list",	0, (keyval) SET_DEFAULT_BCC_LIST },
    { "default-cc-list",	0, (keyval) SET_DEFAULT_CC_LIST },
    { "default-fcc-list",	0, (keyval) SET_DEFAULT_FCC_LIST },
    { "default-from",		0, (keyval) SET_DEFAULT_FROM },
    { "default-mail-type",	0, (keyval) SET_DEFAULT_MAIL_TYPE },
    { "default-read-command",	0, (keyval) SET_DEFAULT_READ_COMMAND },
    { "default-reply-to",	0, (keyval) SET_DEFAULT_REPLY_TO },
    { "default-send-command",	0, (keyval) SET_DEFAULT_SEND_COMMAND },
    { "directory-folders",	0, (keyval) SET_DIRECTORY_FOLDERS },
    { "display-flagged-messages",0, (keyval) SET_DISPLAY_FLAGGED_MESSAGES },
    { "display-outgoing-message",0, (keyval) SET_DISPLAY_OUTGOING_MESSAGE },
    { "dont-print-headers",	0, (keyval) SET_DONT_PRINT_HEADERS },
    { "dont-type-headers", 	0, (keyval) SET_DONT_TYPE_HEADERS },
    { "download-filter",	0, (keyval) SET_DOWNLOAD_FILTER },
    { "editor",		 	0, (keyval) SET_EDITOR },
    { "escape-automatic-send",	0, (keyval) SET_ESCAPE_AUTOMATIC_SEND },
    { "expunge-on-bye",		0, (keyval) SET_EXPUNGE_ON_BYE },
    { "fast-init-file",		0, (keyval) SET_FAST_INIT_FILE },
    { "finger-command",		0, (keyval) SET_FINGER_COMMAND },
    { "gnuemacs-mmail",		0, (keyval) SET_GNUEMACS_MMAIL },
    { "handle-changed-modtime", KEY_INV, (keyval) SET_HANDLE_CHANGED_MODTIME },
    { "header-options-file", 	0, (keyval) SET_HEADER_OPTIONS_FILE },
    { "help-file",	  KEY_INV, (keyval) SET_HELP_FILE },
    { "help-dir",	  KEY_INV, (keyval) SET_HELP_DIR },
    { "incoming-mail", 		0, (keyval) SET_INCOMING_MAIL },
    { "keywords", 		0, (keyval) SET_KEYWORDS },
    { "list-include-headers",	0, (keyval) SET_LIST_INCLUDE_HEADERS },
    { "list-on-separate-pages", 0, (keyval) SET_LIST_ON_SEPARATE_PAGES },
    { "mail-aliases", 	  	0, (keyval) SET_MAIL_ALIASES },
    { "mail-directory", 	0, (keyval) SET_MAIL_DIRECTORY },
    { "mail-file", 		0, (keyval) SET_MAIL_FILE },
    { "mime-filter",            0, (keyval) SET_MIME_FILTER },
    { "mmail-path",	  KEY_INV, (keyval) SET_MMAIL_PATH },
    { "modify-read-only",	0, (keyval) SET_MODIFY_READ_ONLY },
    { "movemail-path",	  KEY_INV, (keyval) SET_MOVEMAIL_PATH },
    { "new-file-mode", 		0, (keyval) SET_NEW_FILE_MODE },
    { "only-print-headers",	0, (keyval) SET_ONLY_PRINT_HEADERS },
    { "only-type-headers", 	0, (keyval) SET_ONLY_TYPE_HEADERS },
    { "personal-name", 		0, (keyval) SET_PERSONAL_NAME },
    { "print-filter", 		0, (keyval) SET_PRINT_FILTER },
    { "prompt-for-bcc", 	0, (keyval) SET_PROMPT_FOR_BCC },
    { "prompt-for-cc",	 	0, (keyval) SET_PROMPT_FOR_CC },
    { "prompt-for-fcc", 	0, (keyval) SET_PROMPT_FOR_FCC },
    { "prompt-rcpt-always",	0, (keyval) SET_PROMPT_RCPT_ALWAYS },
    { "read-prompt", 		0, (keyval) SET_READ_PROMPT },
    { "reply-all", 		0, (keyval) SET_REPLY_ALL },
    { "reply-include-me",	0, (keyval) SET_REPLY_INCLUDE_ME },
    { "reply-indent", 		0, (keyval) SET_REPLY_INDENT },
    { "reply-initial-display",	0, (keyval) SET_REPLY_INITIAL_DISPLAY },
    { "reply-insert", 		0, (keyval) SET_REPLY_INSERT },
    { "saved-messages-file", 	0, (keyval) SET_SAVED_MESSAGES_FILE },
    { "send-prompt", 		0, (keyval) SET_SEND_PROMPT },
    { "send-verbose",		0, (keyval) SET_SEND_VERBOSE },
    { "sendmail-background",	0, (keyval) SET_SENDMAIL_BACKGROUND },
    { "sendmail-verbose",	0, (keyval) SET_SENDMAIL_VERBOSE },
    { "speller",		0, (keyval) SET_SPELLER },
    { "suspend-on-exit", 	0, (keyval) SET_SUSPEND_ON_EXIT },
    { "suspend-on-quit", 	0, (keyval) SET_SUSPEND_ON_QUIT },
    { "temp-directory",		0, (keyval) SET_TEMP_DIRECTORY },
    { "terse-text-prompt",	0, (keyval) SET_TERSE_TEXT_PROMPT },
    { "top-level-prompt", 	0, (keyval) SET_TOP_LEVEL_PROMPT },
    { "use-crt-filter-always",	0, (keyval) SET_USE_CRT_FILTER_ALWAYS },
    { "use-editor-always", 	0, (keyval) SET_USE_EDITOR_ALWAYS },
    { "use-invalid-address", 	0, (keyval) SET_USE_INVALID_ADDRESS },
    { "user-headers", 		0, (keyval) SET_USER_HEADERS },
    { "user-level",		0, (keyval) SET_USER_LEVEL },
    { "user-name",		0, (keyval) SET_USER_NAME },
    { "user-temp-files-keep",	0, (keyval) SET_USER_TEMP_FILES_KEEP },
};

keytab set_keytab = { sizeof (set_cmds) / sizeof (keywrd), set_cmds };
fdb set_cmd_fdb = { _CMKEY, 0, nil, (pdat) &set_keytab, "variable, " };

/*
 * The following structure is a gross hack, since all of the variable
 * addresses are coerced to (char *) and back.  Need to rewrite someday.
 *
 * Anyway, the purpose is to encode in a single structure the name of
 * the variable, its address, size, and a brief description, to make
 * it easy to set/dump all the variables, etc.
 */
#if HAVE_STDC
#define XXs(name, type) VAR_##type, (char *) name, sizeof (name)
#define  XX(name, type) VAR_##type, (char *) &name, sizeof (name)
#else
#define XXs(name, type) VAR_/**/type, (char *) name, sizeof (name)
#define  XX(name, type) VAR_/**/type, (char *) &name, sizeof (name)
#endif

variable set_variables[] = {
    { "aliases-use-groups", XX(aliases_use_groups,BOOLEAN)},
    { "append-new-mail", XX(append_new_mail,BOOLEAN)},
    { "append-signature", XX(append_signature,MAYBE)},
    { "auto-create-files", XX(auto_create_files,MAYBE)},
    { "auto-startup-get", XX(auto_startup_get,BOOLEAN)},
    { "autowrap-column", XX(autowrap_column,INTEGER)},
    { "browse-clear-screen", XX(browse_clear_screen,BOOLEAN)},
    { "browse-pause", XX(browse_pause,BOOLEAN)},
    { "check-interval", XX(check_interval,INTEGER)},
    { "clear-screen", XX(clear_screen,BOOLEAN)},
    { "continuous-check", XX(continuous_check,BOOLEAN)},
    { "control-d-automatic-send", XX(control_d_automatic_send,BOOLEAN)},
    { "control-e-editor", XX(control_e_editor,BOOLEAN)},
    { "control-l-confirm", XX(control_l_confirm,BOOLEAN)},
    { "control-n-abort", XX(control_n_abort,MAYBE)},
    { "crt-filter", XXs(crt_filter,COMMAND)},
    { "default-bcc-list", XX(default_bcc_list,ADDRLIST)},
    { "default-cc-list", XX(default_cc_list,ADDRLIST)},
    { "default-fcc-list", XX(default_fcc_list,FILES)},
    { "default-from", XXs(default_from,TEXT)},
    { "default-mail-type", XX(default_mail_type,KEYWORDS)},
    { "default-read-command", XXs(default_read_command,CMDKEYS)},
    { "default-reply-to", XXs(default_reply_to,TEXT)},
    { "default-send-command", XXs(default_send_command,CMDKEYS)},
    { "directory-folders", XX(directory_folders,BOOLEAN)},
    { "display-flagged-messages", XX(display_flagged_messages,BOOLEAN)},
    { "display-outgoing-message", XX(display_outgoing_message,BOOLEAN)},
    { "dont-print-headers", XX(dont_print_headers,KEYLIST)},
    { "dont-type-headers", XX(dont_type_headers,KEYLIST)},
    { "download-filter", XXs(download_filter,COMMAND)},
    { "editor", XX(editor,CMDARGS)},
    { "escape-automatic-send",	XX(escape_automatic_send,BOOLEAN)},
    { "expunge-on-bye", XX(expunge_on_bye,MAYBE)},
    { "fast-init-file", XX(fast_init_file,MAYBE)},
    { "finger-command", XXs(finger_command,COMMAND)},
    { "gnuemacs-mmail", XX(gnuemacs_mmail,BOOLEAN)},
    { "handle-changed-modtime", XX(handle_changed_modtime,MAYBE)},
    { "header-options-file", XXs(header_options_file,FILE)},
    { "help-file", XXs(help_file,FILE)},
    { "help-dir", XXs(help_dir,DIRECTORY)},
    { "incoming-mail", XX(incoming_mail,FILES)},
    { "keywords", XX(user_keywords,KEYLIST)},
    { "list-include-headers", XX(list_include_headers,BOOLEAN)},
    { "list-on-separate-pages", XX(list_on_separate_pages,BOOLEAN)},
    { "mail-aliases", XX(mail_aliases,ALIAS)},
    { "mail-directory", XXs(mail_directory,DIRECTORY)},
    { "mail-file", XXs(mail_file,OFILE)},
    { "mime-filter", XXs(mime_filter,COMMAND)},
    { "mmail-path", XXs(mmail_path,FILE)},
    { "modify-read-only", XX(modify_read_only,MAYBE)},
    { "movemail-path", XXs(movemail_path,COMMAND)},
    { "new-file-mode", XX(new_file_mode,INTEGER)},
    { "only-print-headers", XX(only_print_headers,KEYLIST)},
    { "only-type-headers", XX(only_type_headers,KEYLIST)},
    { "personal-name", XXs(personal_name,TEXT)},
    { "print-filter", XXs(print_filter,COMMAND)},
    { "prompt-for-bcc", XX(prompt_for_bcc,BOOLEAN)},
    { "prompt-for-cc", XX(prompt_for_cc,BOOLEAN)},
    { "prompt-for-fcc", XX(prompt_for_fcc,BOOLEAN)},
    { "prompt-rcpt-always", XX(prompt_rcpt_always ,BOOLEAN)},
    { "read-prompt", XXs(read_prompt,PROMPT)},
    { "reply-all", XX(reply_all,BOOLEAN)},
    { "reply-include-me", XX(reply_include_me,BOOLEAN)},
    { "reply-indent", XXs(reply_indent,QUOTEDSTR)},
    { "reply-initial-display", XX(reply_initial_display,BOOLEAN)},
    { "reply-insert", XX(reply_insert,BOOLEAN)},
    { "saved-messages-file", XXs(saved_messages_file,OFILE)},
    { "send-prompt", XXs(send_prompt,PROMPT)},
    { "send-verbose", XX(send_verbose,BOOLEAN)},
    { "sendmail-background", XX(sendmail_background,BOOLEAN)},
    { "sendmail-verbose", XX(sendmail_verbose,BOOLEAN)},
    { "speller", XX(speller,CMDARGS)},
    { "suspend-on-exit", XX(suspend_on_exit,BOOLEAN)},
    { "suspend-on-quit", XX(suspend_on_quit,BOOLEAN)},
    { "temp-directory", XXs(temp_directory,DIRECTORY)},
    { "terse-text-prompt", XX(terse_text_prompt,BOOLEAN)},
    { "top-level-prompt", XXs(top_level_prompt,PROMPT)},
    { "use-crt-filter-always", XX(use_crt_filter_always,BOOLEAN)},
    { "use-editor-always", XX(use_editor_always,BOOLEAN)},
    { "use-invalid-address", XX(use_invalid_address,MAYBE)},
    { "user-headers", XX(user_headers,KEYLIST)},
    { "user-level", XX(user_level,KEYWORDS)},
    { "user-name", XXs(user_name,USERNAME)},
    { "user-temp-files-keep", XX(keep_tmp_files,BOOLEAN)},
};
#undef XX
#undef XXs

int set_variable_count = sizeof (set_variables) / sizeof (set_variables[0]);

static int
#if HAVE_STDC
set_variable (int n)
#else /* K&R style */
set_variable (n)
int n;
#endif /* HAVE_STDC */
{
    int x;
    char *p;
    variable *v = &set_variables[n];
    caddr_t dangling = 0;

    switch (v->type) {
      case VAR_BOOLEAN:
	x = parse_yesno ("yes");
	*((int *) v->addr) = x;
	break;
      case VAR_MAYBE:
	*((int *) v->addr) = parse_yesnoask ("ask");
	break;
      case VAR_INTEGER:
	if (n == SET_NEW_FILE_MODE)
	    x = parse_number (8, "octal mode", nil);
	else
	    x = parse_number (10, "decimal number", nil);
	confirm ();
	*((int *) v->addr) = x;
	if (n == SET_AUTOWRAP_COLUMN)
	    cmcsb._cmwrp = autowrap_column;
	if (n == SET_NEW_FILE_MODE)
	    umask (~(new_file_mode&04777));
	break;
      case VAR_ADDRLIST:
	dangling = nil;
	parse_addresses((addresslist *)v->addr);
	break;
      case VAR_KEYLIST:
	dangling = *((char **) v->addr);
	*((char ***) v->addr) = parse_keylist (0, 0, "header keyword");
	break;
      case VAR_DIRECTORY:
	p = parse_directory ("directory name", v->addr);
	strcpy (v->addr, p);
	safe_free(p);
	break;
      case VAR_FILES:
	dangling = *((char **) v->addr);
	*((char ***) v->addr) = parse_filelist (0, 0, "filename", true);
	break;
      case VAR_FILE:
	p = parse_input_file ("filename", v->addr, false);
	confirm();
	strcpy(v->addr, p);
	safe_free(p);
	break;
      case VAR_OFILE:
	p = parse_output_file("filename", v->addr, true);
	confirm();
	strcpy(v->addr, p);
	safe_free(p);
	if (n == SET_MAIL_FILE) {	/* changed their primary mail file */
	    if (cf != (msgvec *)NULL) {
		if (strcmp(cf->filename, p) == 0)
		    cf->flags |= MF_MAILBOX; /* primary mailbox? */
		else
		    cf->flags &= ~MF_MAILBOX; /* not any more */
	    }
	}
	break;
      case VAR_CMDARGS:
	{
	    char **cp;
	    p = parse_text ("text string", nil); /* NB: p is not malloc'ed! */
	    for (cp = *((char ***) v->addr); cp && *cp; cp++)
	        safe_free (*cp);
	    safe_free (*((char ***) v->addr));
	    *((char ***) v->addr) = split_args(p);
	}
	break;
      case VAR_COMMAND: /* should parse for an executable file, but too slow */
      case VAR_TEXT:
	p = parse_text ("text string", nil); /* NB: p is not malloc'ed! */
	if ((int)strlen (p) >= v->size)
	    cmerr ("Variable \"%s\" can be no more than %d characters long",
		   v->name, v->size - 1);
	strcpy (v->addr, p);
	break;
      case VAR_PROMPT:
	p = (char *) parse_prompt("string to prompt with");
					/* NB: p is not malloc'ed! */
	if ((int)strlen(p) >= v->size)
	    cmerr ("Variable \"%s\" can be no more than %d characters long",
		   v->name, v->size -1);
	strcpy (v->addr, p);
	break;
      case VAR_QUOTEDSTR:
	p = parse_quoted("quoted string", nil);
	confirm();
	if ((int)strlen (p) >= v->size)
	    cmerr ("Variable \"%s\" can be no more than %d characters long",
		   v->name, v->size - 1);
	strcpy (v->addr, p);
	break;
      case VAR_USERNAME:
	p = parse_username ("username", user_name);
	confirm();
	if (n == SET_USER_NAME) {
	  struct passwd *p1;
	  p1 = getpwnam(p);
	  if (p1 == NULL || p1->pw_uid != getuid())
	      cmerr("Username \"%s\" does not match your uid", p1->pw_name);
	  sethome(p);
        }
	strcpy(v->addr, p);
	break;
      case VAR_KEYWORDS:
	p = parse_keyword(((Setkey *)v->addr)->the_keytab, FALSE);
	if (p != NULL) {
	    confirm();
	    strcpy(((Setkey *)v->addr)->current,p);
	}
	else
	    ((Setkey *)v->addr)->current[0] = '\0'; /* no default */
	break;
      case VAR_CMDKEYS:	{
	fdb *fdbs;
	static fdb cfmfdb = { _CMCFM, CM_SDH|CM_NLH, NULL, NULL,
				"confirm to unset", NULL, NULL, NULL };

	switch (n) {
	case SET_DEFAULT_READ_COMMAND:
	  fdbs = fdbchn (&mm_read_fdb_abbr, &mm_read_fdb_1,
			 &mm_read_fdb_2, &mm_read_fdb_3, &mm_read_fdb_4,
			 &mm_read_fdb_5, &mm_read_fdb_6, &mm_read_fdb_7,
			 &mm_read_fdb_inv,
			 &cfmfdb, nil);

	  break;
	case SET_DEFAULT_SEND_COMMAND:
	  fdbs = fdbchn (&mm_send_fdb_abbr, &mm_send_fdb_1,
			 &mm_send_fdb_2, &mm_send_fdb_3, &mm_send_fdb_4,
			 &mm_send_fdb_5, &mm_send_fdb_inv,
			 &cfmfdb, nil);
	  break;
	}
	parse (fdbs, &pv, &used);
	if (used == &cfmfdb)
	  *v->addr = '\0';
	else {
	  confirm();
	  strcpy (v->addr, ((keywrd *) pv._pvkey)->_kwkwd);
	}
	break;				/* dont lose this! */
      }
      case VAR_ALIAS:
	cmd_define(CMD_DEFINE);
	break;
      default:
	printf ("set_variable called to set unknown variable type %d\n",
		v->type);
    }
    v->changed = TRUE;
    if (dangling) {
	safe_free (dangling);
    }
    return true;
}

int
#if HAVE_STDC
show_variable (FILE *fp, int n, int verbose)
#else /* K&R style */
show_variable (fp, n, verbose)
FILE *fp;
int n, verbose;
#endif /* HAVE_STDC */
{
    char **ap;
    variable *v = &set_variables[n];
    int i;

    if (verbose && n != SET_MAIL_ALIASES)
	fprintf (fp, "set %s ", v->name);
    switch (set_variables[n].type) {
      case VAR_BOOLEAN:
	fprintf (fp, "%s", (*((int *) v->addr) ? "yes" : "no"));
	break;
      case VAR_MAYBE:
	switch (*((int *) v->addr)) {
	  case SET_NEVER:
	    fprintf (fp, "never");
	    break;
	  case SET_ALWAYS:
	    fprintf (fp, "always");
	    break;
	  case SET_ASK:
	    fprintf (fp, "ask");
	    break;
	}
	break;
      case VAR_INTEGER:
	if (n == SET_NEW_FILE_MODE)
	    fprintf (fp, "%o", (unsigned int)(*((int *) v->addr)));
	else
	    fprintf (fp, "%d", *((int *) v->addr));
	break;
      case VAR_TEXT:
      case VAR_USERNAME:
      case VAR_CMDKEYS:
	fprintf (fp, "%s", v->addr);
	break;
      case VAR_DIRECTORY:
      case VAR_COMMAND:
      case VAR_FILE:
      case VAR_OFILE:
	show_filename (fp, v->addr, true);
	break;
      case VAR_QUOTEDSTR:
      case VAR_PROMPT:
	fprintf(fp, "\"%s\"", v->addr);
	break;
      case VAR_FILES:			/* these aren't comma-separated */
	for (ap = *((char ***) v->addr); ap && *ap; ap++) {
	    show_filename (fp, *ap,true);
	    if (*(ap+1))
		putc (' ',fp);
	}
	break;
      case VAR_KEYLIST:
	for (ap = *((char ***) v->addr); ap && *ap; ap++)
	    fprintf (fp, "%s%s", *ap, (*(ap+1) ? ", " : ""));
	break;
      case VAR_CMDARGS:
	for (ap = *((char ***) v->addr); ap && *ap; ap++)
	    fprintf (fp, "%s ", *ap);
	break;
      case VAR_ADDRLIST:
	disp_addresses(fp, nil, (addresslist *)v->addr, true, false, false,
		       true);
	break;
      case VAR_KEYWORDS:
	fprintf(fp,"%s", ((Setkey *)v->addr)->current);
	break;
      case VAR_ALIAS:
	for(i = 0; i < mail_aliases.count; i++)
	    disp_alias(fp, i, true,true);
	break;
    }
    if (verbose)
	putc ('\n', fp);
    return true;
}

int
#if HAVE_STDC
cmd_set (int n)
#else /* K&R style */
cmd_set (n)
int n;
#endif /* HAVE_STDC */
{
    static fdb cfmfdb = { _CMCFM, CM_SDH, nil, nil,
			      "confirm to show all variable settings",
			      nil, nil, nil };

    noise ("variable");
    parse (fdbchn (&cfmfdb,&set_cmd_fdb, nil), &pv, &used);
    if (used == &cfmfdb)
	show_all_variables(cmcsb._cmoj ? cmcsb._cmoj : stdout);
    else
	(void) set_variable (pv._pvint);
    return true;
}

int
#if HAVE_STDC
cmd_show (int n)
#else /* K&R style */
cmd_show (n)
int n;
#endif /* HAVE_STDC */
{
    int i;
    FILE *out;				/* for output */

    noise ("settings");

    aliasfdb._cmdat = (pdat) mk_alias_keys();
    aliasfdb._cmdef = nil;
    parse (fdbchn(&cfm_fdb, &aliasfdb, &set_cmd_fdb, nil), &pv, &used);
    out = cmcsb._cmoj ? cmcsb._cmoj : stdout;
    if (used == &cfm_fdb) {
	show_all_variables(out);
    }
    else if (used == &set_cmd_fdb) {
	i = pv._pvint;
	confirm ();
	show_variable (out, i, true);
    }
    else {
	i = pv._pvint;
	confirm();
	disp_alias(out, i, true,true);
    }
    return true;
}

/*
 * show_filename:
 * show a filename, abbreviating to ~ where appropriate
 */
static char *
#if HAVE_STDC
show_filename (FILE *fp, const char *longname, int disp)
#else /* K&R style */
show_filename (fp, longname, disp)
FILE *fp;
const char *longname;
int disp;
#endif /* HAVE_STDC */
{
    int len = (int)strlen (HOME);
    static char buf[100];
    if ((strcmp(HOME, "/") != 0) &&	/* don't abbreviate "/" */
	(strncmp (longname, HOME, len) == 0)) { /* something in homedir */
	if (longname[len] != '\0')
	    sprintf (buf, "~%s", &longname[len]);
	else				/* exactly homedir */
	    sprintf (buf, "~");
    }
    else
	sprintf (buf, "%s", longname);
    if (disp)
	fprintf(fp,"%s",buf);
    return(buf);
}

#if HAVE_STDC
static char *
tilde_expand(char *dir)
#else /* K&R style */
static char *
tilde_expand(dir)
char *dir;
#endif /* HAVE_STDC */
{
  static char buf[MAXPATHLEN];
  if (*dir != '~') return(dir);
  strcpy(buf,HOME);
  strcat(buf,&dir[1]);
  return(buf);
}


/* The following routines handle a fast start up by directly reading
 * junk in from an .mmfast file.  They are here instead of in init.c
 * or doinit.c because the code there merely calls them.  Since they
 * are, in a way, parsing commands, it makes partial sense to put them
 * in here.  If fast init files turn out to be a bad idea (horrors!),
 * then this code can be easily changed to something like what MM-20
 * does (ie, a fast hacky text parser).
 */
#define fail(fp) {fclose(fp); return(false);}
int
read_fast_init_file(VOID)	/* Reads the fast init file */
{
  int vindex;			/* Variable index--not register, guess why! */
  FILE *fp;
  buffer fastname,		/* Fast init file name */
  	 initname;		/* Regular init file name */
  struct stat faststat,		/* struct to stat time of fast init file */
  	      initstat;		/* Also to stat the init file */
  char filetext[BUFSIZ];
  char *name, *cp;
  char **p;
  int count,i;
  variable *v;
  addresslist *al,al1;
  int vlen, olen;

  if (HOME == NULL)		/* Can't do much without a home */
    return false;

  sprintf (initname, "%s/.mminit", HOME);
  sprintf (fastname, "%s/.mmfast", HOME);
  if (stat(fastname,&faststat) != 0)	/* Is the fast file even there? */
    return false;		/* Not there, so can't read it */

  if (fast_init_file == SET_ASK)
    if (!yesno("Fast init file exists -- read it? ", "yes"))
      return (false);

  if (stat(initname,&initstat) == 0)	/* Is the init file even there? */
      if (faststat.st_mtime <	/* Is the fast file older then the */
	  initstat.st_mtime)	/* fast file? */
	  return false;		/* The fast init file is out of date */

  fp = fopen(fastname, "r");

  if (fp == NULL)
    return false;		/* Can't parse it if can't open it */

  vlen = (int)strlen(mm_version);
#ifdef FASTBIN
  olen = (int)strlen(OStype);
#else
  olen = -1;
#endif

  if (fgets(filetext,vlen+olen+1+2,fp) == NULL) /* Chew newline */
    return false;		/* It's bad if we can't read version string */

  if (strncmp(filetext, mm_version, vlen) != 0) /* Ignore newline */
    return false;		/* Version has changed */
#ifdef FASTBIN
  if (strncmp(&filetext[vlen+1],OStype,olen) != 0) /* OSTYPE matters if */
    return false;			/* binary fastfile */
#endif

  while (gnum(fp,&vindex)) {
    v = &set_variables[vindex];
    switch(v->type) {
      case VAR_BOOLEAN:	/* Simple variables just want numbers */
      case VAR_MAYBE:
      case VAR_INTEGER:
	if (!gnum(fp,(int *)v->addr)) fail(fp);
	break;
      case VAR_TEXT:		/* All text is counted */
      case VAR_CMDKEYS:
      case VAR_QUOTEDSTR:
      case VAR_PROMPT:
      case VAR_USERNAME:
      case VAR_COMMAND:
	if (!gsstr(fp, v->addr)) fail(fp);
	  break;
      case VAR_DIRECTORY:
      case VAR_FILE:
      case VAR_OFILE:
	if (!gstr(fp,&cp)) fail(fp);
	strcpy(v->addr,
	       tilde_expand(cp)); /* De~ the filename */
	safe_free(cp);
	break;

      case VAR_FILES:
	if (!gnum(fp,&count)) fail(fp);
	if (count == 0) {
	    *((char ***) v->addr) = nil;
	    break;
	}
	p = (char **) malloc((count+1)*sizeof(char *));
	*(char ***)v->addr = p;
	for(i = 0; i < count; i++) {
	  char *cp2;
	  if (!gstr(fp,&cp2)) fail(fp);
	  p[i] = safe_strcpy(tilde_expand(cp2));
	  safe_free(cp2);
	}
	p[count] = nil;
	break;

      case VAR_KEYLIST:
      case VAR_CMDARGS:
	if (!gnum(fp,&count)) fail(fp);
	if (count == 0) {
	  *((char ***) v->addr) = nil;
	  break;
	}
	p = (char **) malloc((count+1)*sizeof(char *));
	*(char ***)v->addr = p;
	for(i = 0; i < count; i++) {
	  char *cp2;
	  if (!gstr(fp,&cp2)) fail(fp);
	  p[i] = cp2;
	}
	p[count] = nil;
	break;

      case VAR_ADDRLIST:
	if (!gnum(fp,&count)) fail(fp);
	al = (addresslist *)v->addr;
	al->first = al->last = nil;
	for(i = 0; i < count; i++) {
	  char *cp2;
	  int type;
	  if (!gnum(fp,&type)) fail(fp);
	  if (!gstr(fp,&cp2)) fail(fp);
	  add_addresslist(al,cp2,type);
	  safe_free(cp2);
	}
	break;

      case VAR_ALIAS:
	if (!gstr(fp,&name)) fail(fp);
	if (!gnum(fp,&count)) fail(fp);
	al1.first = al1.last = nil;
	for(i = 0; i < count; i++) {
	  char *cp2;
	  int type;
	  if (!gnum(fp,&type)) fail(fp);
	  if (!gstr(fp,&cp2)) fail(fp);
	  add_addresslist(&al1,cp2,type);
	  safe_free(cp2);
	}
	set_alias(name,&al1,MA_USER);
	break;		/* Read it real fast */

      case VAR_KEYWORDS:
	if (!gstr(fp,&cp)) fail(fp);
	strcpy(((Setkey *)v->addr)->current, cp);
	safe_free(cp);
	break;

      default:		/* This is very bad and should not happen */
	fprintf(stderr,"? %d,**UNKNOWN Variable index**\n",
		vindex);
	fail(fp);
	break;
      }				/* End of switch */
    geol(fp);
    v->changed = TRUE;
  }
  if (!feof(fp)) fail(fp);
  fclose(fp);			/* Done parsing the file */
  return true;
}


/* Much like the set command nonsense except that we write out the
 * index of the variable in the set_variables array and follow it with
 * the data in the variable.  We then use that integer to index into
 * an array to set the data instead wasting time parsing.  This is
 * pretty straightforward except for lists of objects.  All non-unary
 * objects are preceeded by a length count.  Lists of objects are also
 * preceeded by an object count.
 */

int
write_fast_init_file(VOID)		/* Writes the fast init file */
{
  register int n;		/* Current index in set_variables array */
  char **ap;
  variable * v;
  char *cp;
  FILE *fp;
  buffer filename;
  int count;
  addresslist *al;
  addr_unit *a;

  if (HOME == NULL)		/* Can't do much without a home */
   return false;

  sprintf (filename, "%s/.mmfast", HOME);
  fp = fopen(filename, "w");

#ifdef FASTBIN
  fprintf(fp,"%s,%s\n",mm_version, OStype); /* Stick out version and OSTYPE */
#else
  fprintf(fp,"%s\n",mm_version);	/* Stick out the version */
#endif

  for (n=0,v = &set_variables[n]; /* Loop through all the variables */
       n < (sizeof (set_variables) / sizeof (variable)); /* End of table */
       n++,v = &set_variables[n]) {
    if (!v->changed)
      continue;
    if (v->type != VAR_ALIAS)
      pnum(fp,n);
    switch (set_variables[n].type) {

      case VAR_BOOLEAN:		/* Simple variables just want numbers */
      case VAR_MAYBE:
      case VAR_INTEGER:
	pnum(fp, *((int *)v->addr));
	break;

      case VAR_TEXT:		/* All text is counted */
      case VAR_CMDKEYS:
      case VAR_QUOTEDSTR:
      case VAR_PROMPT:
      case VAR_USERNAME:
      case VAR_COMMAND:
	pstr(fp,v->addr);
	break;

      case VAR_DIRECTORY:
      case VAR_FILE:
      case VAR_OFILE:
	cp = show_filename(fp,v->addr,false);
	pstr(fp,cp);
	break;

      case VAR_FILES:
	for (count=0,ap = *((char ***) v->addr); ap && *ap; ap++,count++);
	pnum(fp,count);
	  for (ap = *((char ***) v->addr); ap && *ap; ap++) {
	    cp = show_filename(fp, *ap, false);
	    pstr(fp,cp);
	  }
	break;

      case VAR_KEYLIST:
      case VAR_CMDARGS:
	for (count=0,ap = *((char ***) v->addr); ap && *ap; ap++,count++);
	pnum(fp,count);
	for (ap = *((char ***) v->addr); ap && *ap; ap++)
	  pstr(fp, *ap);
	break;

      case VAR_ALIAS:			/* an alias */
	for (count = 0; count < mail_aliases.count; count++) {
	  if (mail_aliases.aliases[count].type == MA_USER) {
	    al = &mail_aliases.aliases[count].alias; /* save if user set */
	    pnum(fp,n);
	    if (al->first){
	      int naliases;
	      for(naliases = 0, a = al->first;
		  a != nil;
		  naliases++, a = a->next );
	      pstr(fp,mail_aliases.aliases[count].name);
	      pnum(fp,naliases);
	      for(a = al->first; ; a = a->next) {
		if (a->type == ADR_MLIST)
		  pnum(fp,ADR_LISTFILE);
		else
		  pnum(fp,a->type);
		pstr(fp,a->data);
		if (a == al->last) break;
	      }
	    }
	    peol(fp);
	  }
        }
	break;
      case VAR_ADDRLIST:
	al = (addresslist *) v->addr;
	for(count = 0, a = al->first; a != nil; count++, a = a->next );
	pnum(fp, count);
	if (al->first) {
	  for(a = al->first; ; a = a->next) {
	    if (a->type == ADR_MLIST)
	      pnum(fp,ADR_LISTFILE);
	    else
	      pnum(fp,a->type);
	    pstr(fp,a->data);
	    if (a == al->last) break;
	  }
	}
	break;
      case VAR_KEYWORDS:
	cp = ((Setkey *)v->addr)->current;
	pstr(fp,cp);
	break;


      default:			/* This is very bad and should not happen */
/*	fprintf(fp,"%d,%d,**UNKNOWN**\n",n,set_variables[n].type);*/
	break;
      }
    if (v->type != VAR_ALIAS)
      peol(fp);
  }
  fflush(fp);			/* Get everything out of there */
  fclose(fp);
  return true;
}

static void
#if HAVE_STDC
pnum(FILE *fp, int n)
#else /* K&R style */
pnum(fp, n)
FILE *fp;
int n;
#endif /* HAVE_STDC */
{
#ifdef FASTBIN
  int i;
  char *cp = (char *)&n;
  for(i = 0; i < sizeof(int); i++,cp++)
    putc(*cp,fp);
#else
  fprintf(fp,"%d,",n);
#endif
}

static void
#if HAVE_STDC
pstr(FILE *fp, const char *str)
#else /* K&R style */
pstr(fp, str)
FILE *fp;
const char *str;
#endif /* HAVE_STDC */
{
    int n = (int)strlen(str);
    pnum(fp,n);
#ifdef FASTBIN
    fprintf(fp,"%s",str);
#else
    fprintf(fp,"%s,",str);
#endif
}

static void
#if HAVE_STDC
peol(FILE *fp)
#else /* K&R style */
peol(fp)
FILE *fp;
#endif /* HAVE_STDC */
{
#ifndef FASTBIN
    fprintf(fp,"\n");
#endif
}

static int
#if HAVE_STDC
gnum(FILE *fp, int *num)
#else /* K&R style */
gnum(fp,num)
FILE *fp;
int *num;
#endif /* HAVE_STDC */
{
    int n;
#ifdef FASTBIN
    int i;
    int k;
    char *cp = (char *)&n;

    for(i = 0; i < sizeof(int); i++) {
      k = getc(fp);
      if (k == EOF) return(false);
      *cp++ = k;
    }
    *num = n;
    return(true);
#else
    if (fscanf(fp,"%d,",&n) != 1) return(false);
    *num = n;
    return(true);
#endif
}

static int
#if HAVE_STDC
gstr(FILE *fp, char **cp)
#else /* K&R style */
gstr(fp,cp)
FILE *fp;
char **cp;
#endif /* HAVE_STDC */
{
    int n,c;
    char *buf;

#ifdef FASTBIN
    if (gnum(fp,&n) == false) return(false);
    buf = (char *)malloc(n+1);
    if (n != 0)
      if (fread(buf,sizeof(char),n,fp) <= 0) return(false);
    buf[n] = '\0';
    *cp = buf;
    return(true);
#else
    if (fscanf(fp,"%d,",&n) != 1) return(false);
    buf = (char *)malloc(n+1);
    if (n != 0)
      if ((int)fread(buf,sizeof(char),n,fp) <= 0) return(false);
    c = getc(fp);
    if (c == EOF) return(false);
    if (c != ',') {
	ungetc(c,fp);
	return(false);
    }
    buf[n] = '\0';
    *cp = buf;
    return(true);
#endif
}

static int
#if HAVE_STDC
gsstr(FILE *fp, char *cp)
#else /* K&R style */
gsstr(fp,cp)
FILE *fp;
char *cp;
#endif /* HAVE_STDC */
{
    int n,c;

#ifdef FASTBIN
    if (gnum(fp,&n) == false) return(false);
    if (n != 0)
      if (fread(cp,sizeof(char),n,fp) <= 0) return(false);
    cp[n] = '\0';
    return(true);
#else
    if (fscanf(fp,"%d,",&n) != 1) return(false);
    if (n != 0)
      if ((int)fread(cp,sizeof(char),n,fp) <= 0) return(false);
    cp[n] = '\0';
    c = getc(fp);
    if (c == EOF) return(false);
    if (c != ',') {
	ungetc(c,fp);
	return(false);
    }
    return(true);
#endif
}

static int
#if HAVE_STDC
geol(FILE *fp)
#else /* K&R style */
geol(fp)
FILE *fp;
#endif /* HAVE_STDC */
{
#ifndef FASTBIN
    int c;
    c = getc(fp);
    if (c == EOF) return(false);
    if (c != '\n') {
	ungetc(c,fp);
	return(false);
    }
#endif
    return(true);
}

/*
 * split_args:
 * takes a string containing a command and splits it up into
 * individual arg's and returns a char ** (argv) pointing at them.
 * note: arguments in single quotes are taken literally.  The
 * backslash character makes the following character be taken literally.
 */

char **
#if HAVE_STDC
split_args (const char *str)
#else /* K&R style */
split_args (str)
const char *str;
#endif /* HAVE_STDC */
{
    const char *cp;
    char *temp = NULL;			/* temp buffer to build arguments */
    int i = 0;				/* current position in temp buffer */
    int count = 0;			/* number of slots in args[] */
    int anum = 0;			/* slots in use in args[] */
    char **args = NULL;			/* argument list to be returned */

    if (str == NULL)			/* null string, no args */
	return (NULL);
    if ((temp = (char *) malloc (strlen (str) + 1)) == NULL)
	return (NULL);

    cp = str;
    while (*cp) {			/* count number of spaces */
	if (isspace(*cp))		/* this should give us an */
	    count++;			/* idea of the number of args */
	cp++;
    }
    count += 2;				/* and a couple more */
    /* get space for arg list */
    if ((args = (char **) malloc (count*sizeof(char *))) == NULL) {
      free(temp);
      return (NULL);
    }
    cp = str;
    while (*cp) {			/* break up into args */
	while (*cp && isspace(*cp))	/* skip initial spaces */
	    cp++;
	if (!*cp)
	    break;			/* no more args */

	if (isquote(*cp)) {		/* starts with quote */
	    cp++;			/* skip the quote */
	    while (*cp && !isquote(*cp)) { /* till matching quote is found */
		if (isbackslash(*cp))
		    cp++;		/* skip over the backslash */
		temp[i++] = *cp;
		cp++;
	    }
	    if (*cp)			/* string didn't end  */
		cp++;			/* skip the end quote */
	}
	else {				/* begins with something else */
	    while (*cp && !isspace(*cp)) { /* go till end of word (space) */
		if (isbackslash(*cp))
		    cp++;		/* skip over the backslash */
		temp[i++] = *cp;
		cp++;
	    }
	}
	temp[i] = '\0';			/* tie off an arg */

	if (anum >= count) {		/* need more space? */
	    if ((args = (char **)
		 realloc (args, (++count)*sizeof(char *))) == NULL) {
	      anum--;			/* back up */
	      while (anum >= 0)
		safe_free (args[anum--]);
	      safe_free (args);
	      free(temp);
	      return (NULL);
	    }
	}
	if ((args[anum] = (char *) malloc (strlen(temp)+1)) == NULL) {
	    anum--;			/* back up */
	    while (anum >= 0)
		safe_free (args[anum--]);
	    safe_free (args);
	    free(temp);
	    return (NULL);
	}
	strcpy (args[anum++], temp);	/* save this argument */
	i = 0;				/* reset to beginning of buffer */
    }
    args[anum] = NULL;			/* tie off arg list */
    free(temp);
    return (args);			/* return list */
}

/*
 * show_all_variables:
 * show the values of all variables
 */
static void
#if HAVE_STDC
show_all_variables(FILE *out)
#else /* K&R style */
show_all_variables(out)
FILE *out;
#endif /* HAVE_STDC */
{
    int i,nv;
    FILE *fp;				/* to pipe long output */

    nv = sizeof (set_variables) / sizeof (variable);
    if ((nv + mail_aliases.count) >= cmcsb._cmrmx)
	fp = more_pipe_open(out,crt_filter); /* 1 line/variable or alias... */
    else
	fp = out;
    for (i = 0; i < nv; i++)
	if (i != SET_MAIL_ALIASES)
	    show_variable (fp, i, true);
    for (i = 0; i < mail_aliases.count; i++)
	disp_alias (fp, i, true,true);
    if (fp == out)			/* not a pipe */
	fflush (fp);
    else
	more_pipe_close(fp);	/* really a pipe */
}
