/*
 * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University
 * in the City of New York.  Permission is granted to any individual or
 * institution to use, copy, or redistribute this software so long as it
 * is not sold for profit, provided this copyright notice is retained.
 */

#ifndef lint
static char *rcsid = "$Header: parse.c,v 1.40 88/07/22 20:03:15 howie Exp $";
#endif

/*
 * parse.c - miscellaneous parsing functions
 */

#include "mm.h"
#include "parse.h"
#include <varargs.h>

/*
 * miscellaneous function descriptor blocks
 */ 

static brktab shell_brk = {
    {
	0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
    },
    {
	0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
    }
};

fdb shell_fdb = {
    _CMTOK, CM_SDH|TOK_WAK, nil, (pdat) "!", "\"!\" for shell escape", NULL, 
    &shell_brk
};

fdb cfm_fdb = { _CMCFM, CM_SDH, nil, nil, "confirm with carriage return" };


static brktab fldbrk = {
    {
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f,
	0x80, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x1f,
    },
    {
	0xff, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0x00, 0x1f,
	0x00, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x1f,
    },
};

/* lend this to all the other modules */
fdb aliasfdb = { _CMKEY, 0, nil, nil, "mail alias, ", NULL, &fldbrk };


/* 
 * This routine is dangerous in that it may cause memory leaks; it
 * should not be used where it might orphan chunks of memory allocated
 * with malloc (the same is true of any call to a parsing routine).
 */
void
cmerr(va_alist) va_dcl {
    char *fmt;
    va_list arg_ptr;

    va_start(arg_ptr);
    fmt = va_arg(arg_ptr, char *);

    if ((cmcsb._cmflg & CM_TTY) == 0) {
	fprintf(cmcsb._cmej, "%s: ", progname);
	vfprintf(cmcsb._cmej,fmt,arg_ptr);
	va_end(arg_ptr);
	cmerjnp(0);		       /* reprompt N.B. argument is ignored */
    }
    else {
	cmflush(cmcsb._cmij);
	if (cmcpos() != 0)
	    cmnl(cmcsb._cmoj);
	cmcsb._cmcol = 0;
	fprintf(cmcsb._cmej, "?");
	vfprintf(cmcsb._cmej,fmt,arg_ptr);
	fprintf(cmcsb._cmej,"\n");
	va_end(arg_ptr);
#ifdef undef
	cmnl(cmcsb._cmoj);		/* tie it off with a newline */
#endif
	cmerjnp(0);			/* reprompt N.B. argument is ignored */
    }
}


time_t
datimetogmt (d)
datime *d;
{
    time_t gmt;
    struct tm t;

    t.tm_sec = d->_dtsec;
    t.tm_min = d->_dtmin;
    t.tm_hour = d->_dthr;
    t.tm_mday = d->_dtday + 1;		/* sad but true */
    t.tm_mon = d->_dtmon;
    t.tm_year = d->_dtyr - 1900;
    t.tm_wday = d->_dtdow;
    t.tm_yday = 0;			/* XXX ccmd doesn't support this */
    t.tm_isdst = 0;			/* fake this */
    gmt = itime (&t);			/* get almost-gmt */
    gmt -= (minutes_west - d->_dttz) * 60;
    gmt -= d->_dtdst * 60;
    return gmt;
}

#define T_SUNDAY -7
#define T_MONDAY -6
#define T_TUESDAY -5
#define T_WEDNESDAY -4
#define T_THURSDAY -3
#define T_FRIDAY -2
#define T_SATURDAY -1

keywrd timekeys[] = {
    { "friday", 0, (keyval) T_FRIDAY },
    { "monday", 0, (keyval) T_MONDAY },
#define T_NEVER 0
    { "never", 0, T_NEVER },
#define T_NOW 1
    { "now", 0, (keyval) T_NOW },
    { "sunday", 0, (keyval) T_SUNDAY },
    { "saturday", 0, (keyval) T_SATURDAY },
    { "thursday", 0, (keyval) T_THURSDAY },
#define T_TODAY 2
    { "today", 0, (keyval) T_TODAY },
#define T_TOMORROW 3
    { "tomorrow", 0, (keyval) T_TOMORROW },
    { "tuesday", 0, (keyval) T_TUESDAY },
    { "wednesday", 0, (keyval) T_WEDNESDAY },
#define T_YESTERDAY 4
    { "yesterday", 0, (keyval) T_YESTERDAY }
};

keytab timetab = { sizeof (timekeys) / sizeof (keywrd), timekeys };

time_t
p_date(help)
char *help;
{
    static fdb tadfdb = { _CMTAD };
    static fdb keytimefdb = { _CMKEY, 0, NULL, NULL, "relative date, " };
    tadfdb._cmhlp = (help ? help : "date");
    keytimefdb._cmdat = (pdat) &timetab;
    parse(fdbchn(&tadfdb, &keytimefdb, NULL), &pv, &used);
    if (used == &tadfdb)
	return (datimetogmt(&pv._pvtad));

    return key2time(pv._pvint);
}

time_t
key2time(n)
int n;
{

    time_t tx;
    struct tm *t;

    if (n == T_NEVER)
	return 0;

    time(&tx);

    if (n == T_NOW)
	return tx;

    if (n < 0) {
	t = localtime(&tx);
	/* reduce tx by some number of days */
	tx -= ((t->tm_wday + 7 - n) % 7) * (60*60*24);
	/* reduce tx by seconds since midnight */
	tx -= t->tm_sec + (t->tm_min * 60) + (t->tm_hour * (60*60));
	return tx;
    }

    switch (n) {
      case T_TOMORROW:
	tx += 60*60*24;
	goto cvt;
      case T_YESTERDAY:
	tx -= 60*60*24;
	goto cvt;
      case T_TODAY:
cvt:	t = localtime(&tx);
	t->tm_sec = 1;
	t->tm_min = 0;
	t->tm_hour = 0;
	return (itime(t));
      default:
	cmerr("Unknown time keyword (%d) parsed", n);
    }
}

int
p_num(help)
char *help;
{
    static fdb numfdb = { _CMNUM, CM_SDH, 0, (pdat) 10 };

    numfdb._cmhlp = (help ? help : "number");
    parse(&numfdb, &pv, &used);
    return pv._pvint;
}


/* confirmit - prompt for and parse crlf */

void
confirmit(promptstring)
char *promptstring;
{
    if (promptstring == (char *) 0)
	promptstring = "[Confirm]";

    cmseteof();
    cmseter();
    prompt(promptstring);
    cmsetrp();				/* back here on reparse */
    confirm();				/* go for confirmation */
}

static keywrd yesnokeys[] = {
    { "always", 0, SET_ALWAYS },	/* 0 */
    { "false", 0, SET_NO },		/* 1 */
    { "never", 0, SET_NO },		/* 2 */
    { "no", 0, SET_NO },		/* 3 */
    { "ok", 0, SET_YES },		/* 4 */
    { "true", 0, SET_YES },		/* 5 */
    { "yes", 0, SET_YES },		/* 6 */
    { "0", KEY_INV, SET_NO },		/* 7 */
    { "1", KEY_INV, SET_YES },		/* 8 */
    { "n", KEY_ABR|KEY_INV, (keyval) 3 } /* this means "no" */
};

keytab yesnotab = { sizeof(yesnokeys) / sizeof(keywrd), yesnokeys };
fdb yesno_fdb = { _CMKEY, 0, NULL, (pdat) &yesnotab, "keyword, " };

static keywrd yesnoask_keys[] = {
    { "always", 0, SET_ALWAYS },
    { "ask", 0, SET_ASK },
    { "never", 0, SET_NEVER },
    { "no", 0, SET_NEVER },
    { "yes", 0, SET_ALWAYS },
    { "n", KEY_ABR|KEY_INV, (keyval) 3 },
};

keytab yesnoasktab = { sizeof(yesnoask_keys)/sizeof(keywrd), yesnoask_keys };
fdb yesnoask_fdb = { _CMKEY, 0, NULL, (pdat) &yesnoasktab, "keyword, " };

int
yesno(promptstring, def)
char *promptstring, *def;
{
    int n;
    save_parse_context();		/* save reparse/error handlers */

    if (promptstring == NULL)
	promptstring = "Yes or no? ";

    cmseteof();				/* errors come back here */
    cmseter();				/* errors come back here */
    if (cmcsb._cmerr == CMxEOF)
	panic ("Unexpected EOF in yesno()");

    prompt(promptstring);		/* issue the prompt */
    cmsetrp();				/* come back here on reparse */
    n = parse_yesno (def);
    restore_parse_context();		/* restore error/reparse handlers */
    return (int) n;			/* return the answer */
}

int
parse_yesno (def)
char *def;
{
    yesno_fdb._cmdef = def;
    parse (&yesno_fdb, &pv, &used);
    confirm();
    return pv._pvint;
}

int
parse_yesnoask (def)
char *def;
{
    yesnoask_fdb._cmdef = def;
    parse (&yesnoask_fdb, &pv, &used);
    confirm();
    return pv._pvint;
}

/*
 * parse_text:
 * parse some text, ending with CR
 * NOTE that nothing is malloced so nothing should be freed!
 */
char *
parse_text (help, def)
char *help, *def;
{
    static fdb text_fdb = { _CMTXT, CM_SDH };

    text_fdb._cmhlp = (help != nil) ? help : "string";
    text_fdb._cmdef = def;
    parse (&text_fdb, &pv, &used);
    return (char *) atmbuf;
}

char *
parse_prompt (help)
char *help;
{
    static fdb qstr_fdb = { _CMQST, CM_SDH };
    static fdb text_fdb = { _CMTXT, CM_SDH };

    qstr_fdb._cmhlp = (help != nil) ? help : "quoted prompt";
    parse (fdbchn(&qstr_fdb, &text_fdb, NULL), &pv, &used);
    if (used == &qstr_fdb)
	confirm();
    return (char *) atmbuf;
}

char *
parse_quoted (help, def)
char *help, *def;
{
    static fdb qstr_fdb = { _CMQST, CM_SDH };

    qstr_fdb._cmhlp = (help != nil) ? help : "string";
    qstr_fdb._cmdef = def;
    parse (&qstr_fdb, &pv, &used);
    return (char *) atmbuf;
}

char *
parse_username (help, def)
char *help, *def;
{
    static fdb uname_fdb = { _CMUSR, CM_SDH };

    uname_fdb._cmhlp = (help != nil) ? help : "Username";
    uname_fdb._cmdef = def;
    parse (&uname_fdb, &pv, &used);
    return (pv._pvusr[0]->pw_name);
}

void
brkch(p, s, s2)
brktab *p;
unsigned char *s, *s2;
{
    unsigned char c;

    /* process characters which are break characters anywhere in the field */
    while ((c = *s++) && (c < 128)) {
	p->_br1st[c/8] |= (0x80 >> (c % 8));
	p->_brrest[c/8] |= (0x80 >> (c % 8));
    }
    /* process characters which break only if they're the first character */
    if (s2) while ((c = *s2++) && (c < 128))
	p->_br1st[c/8] |= (0x80 >> (c % 8));
}

void
unbrk(p,s)
brktab *p;
unsigned char *s;
{
    unsigned char c;

    while ((c = *s++) && (c < 128)) {
	p->_br1st[c/8] &= ~(0x80 >> (c % 8));
	p->_brrest[c/8] &= ~(0x80 >> (c % 8));
    }
}

p_file(deflt, input)
char *deflt;
{
    static fdb filefdb = {
	_CMFIL, FIL_SDH|FIL_PO, NULL, NULL, NULL, NULL, NULL};
    fdb *f = &filefdb;

    f->_cmhlp = (input ? "input filename" : "output filename");
    f->_cmdef = deflt;
    parse(f, &pv, &used);
    strncpy(atmbuf, pv._pvfil[0], sizeof(atmbuf) -1);
    if (input) {
	if (access(atmbuf, F_OK|R_OK) < 0)
	    cmerr(errstr(-1));
    }
    else {
	int ok;
	char c = 0;
	char *p = rindex(atmbuf, '/');

	if (p == NULL) p = atmbuf;	/* XXX depends on ("" == ".") */
	c = *p; *p = 0;			/* consider only the directory */
	ok = access(atmbuf, F_OK|W_OK);	/* can we write on it? */

	*p = c;				/* fix the path again */
	if (ok < 0)
	    cmerr(errstr(-1));		/* nope, complain */
    }
}

#define IOSTACKLIMIT 16
struct io_fileset {
    FILE *i;
    FILE *o;
    FILE *e;
} io_stack[IOSTACKLIMIT];
int io_stackp = -1;

void
stack_input(fd)
FILE *fd;
{
    if ((io_stackp + 1) < IOSTACKLIMIT) {
	io_stackp++;
	io_stack[io_stackp].i = cmcsb._cmij;
	io_stack[io_stackp].o = cmcsb._cmoj;
	io_stack[io_stackp].e = cmcsb._cmej;
	cmseti(fd, cmcsb._cmoj, cmcsb._cmej);
    }
    else cmerr("Command input stack overflow");
}

int
pop_input()
{
    if (io_stackp < 0)
	panic ("input file stack is empty in pop_input");
    if (cmcsb._cmij != io_stack[io_stackp].i)
	(void) fclose(cmcsb._cmij);
    cmseti(io_stack[io_stackp].i,io_stack[io_stackp].o,io_stack[io_stackp].e);
    cmcsb._cmwrp = autowrap_column;	/* put that back */
    io_stackp--;
    return CMxEOF;
}

/*
 * this routine is a replacement for the ccmd-supplied error handler
 */

jmp_buf eofjmpb;

int
ccmd_error(err)
int err;
{
    if (err == CMxEOF)			/* handle EOF specially */
	longjmp(eofjmpb,err);
    cmperr(err);			/* print the associated error string */
    longjmp(cmerjb,err);
}

int
try_parse(fdblist, value, used)
fdb *fdblist;
pval *value;
fdb **used;
{
    int result;
    int (*fn)();

    fn = cmcsb._cmerh;
    cmcsb._cmerh = (int (*)()) 0;
    result = parse(fdblist, value, used);
    cmcsb._cmerh = fn;
    return result;
}

int
parse_number (radix, help, def)
int radix;
char *help, *def;
{
    static fdb numfdb = { _CMNUM, CM_SDH, 0, (pdat) 10 };
    numfdb._cmhlp = (help == nil) ? help : "number";
    numfdb._cmdat = (pdat) radix;
    numfdb._cmdef = def;
    parse (&numfdb, &pv, &used);
    return pv._pvint;
}

/*
 * Parse a list of keywords, returning a (char **) pointing to the list.
 *
 * This routine recursively parses keywords in a comma-separated list until
 * a confirm is found or a parse error occurs; after successfully parsing the
 * list and a confirm, the bottom-most invocation allocates enough space to
 * hold the list of pointers and the strings themselves, which are stored
 * into the allocated buffer by each level as recursion unwinds.  The
 * cumulative byte and string count are passed downwards so the bottom-most
 * level knows how much space to allocate; it in turn passes the size of the
 * list back in dest[0] so the higher levels know where the pointers end and
 * the string space begins.
 *
 * Admittedly one of the ugliest routines I ever wrote.
 */

char **
parse_keylist (count, nbytes, help)
int count, nbytes; char *help;
{
    int keylen;
    keyword key;
    char **dest;
    static fdb key_cfm_fdb = { _CMCFM, CM_SDH, nil, nil,
				   "confirm to delete list" };
    static fdb key_fdb = { _CMFLD, CM_SDH };
    static fdb comma_fdb = { _CMTOK, CM_SDH, nil, (pdat) ",",
			 "comma and another keyword" };

    key_fdb._cmhlp = ((help == nil) ? "keyword" : help);

    if (count == 0) {
	parse (fdbchn(&key_cfm_fdb, &key_fdb, nil), &pv, &used);
	if (used == &key_cfm_fdb)
	    return nil;
	if (strlen (atmbuf) == 0)
	    cmerr ("Invalid null %s", help);
    }
    else
	if ((try_parse (&key_fdb, &pv, &used) != CMxOK) || (atmbuf[0] == 0))
	    cmerr ("Invalid %s after comma", help);

    if ((keylen = strlen (atmbuf)) >= sizeof (key))
	cmerr ("String too long for %s buffer: %s", key_fdb._cmhlp, atmbuf);

    count++;				/* account for this string */
    nbytes += keylen + 1;
    strcpy (key, atmbuf);		/* save it on the stack */

    parse (fdbchn (&comma_fdb, &cfm_fdb, nil), &pv, &used);
    if (used == &comma_fdb) {
	/* parsed a comma, so call ourself recursively for more */
	dest = parse_keylist (count, nbytes, help);
    }
    else {
	dest = (char **) malloc (((count+1) * sizeof (char *)) + nbytes);
	dest[0] = (char *) count;	/* pass string count back up */
	dest[count] = nil;		/* mark the end of the list */
    }

    dest[count-1] = ((char *) dest) +
	((((int) dest[0]) + 1) * sizeof (char *)) + nbytes - (keylen + 1);
    strcpy (dest[count-1], key);
    return dest;
}

char **
parse_filelist (count, nbytes, help, del)
int count, nbytes; char *help;
{
    int filelen;
    buffer file;
    char **dest;
    static fdb file_cfm_fdb = { _CMCFM, CM_SDH, nil, nil, nil };
    static fdb file_fdb = { _CMFIL, CM_SDH|FIL_PO };
    static fdb comma_fdb = { _CMTOK, CM_SDH, nil, (pdat) ",",
			 "comma and another filename" };

    file_fdb._cmhlp = ((help == nil) ? "filename" : help);
    file_cfm_fdb._cmhlp = (count == 0 && del) ?
	"confirm to delete the list" : "confirm with carriage return";

    if (directory_folders)
	file_fdb._cmffl &= ~FIL_NODIR;
    else
	file_fdb._cmffl |= FIL_NODIR;
    if (count > 0)
	parse (fdbchn(&file_cfm_fdb, &comma_fdb, &file_fdb, nil), &pv, &used);
    else
	parse (fdbchn(&file_cfm_fdb, &file_fdb, nil), &pv, &used);
    if (used == &file_cfm_fdb)
	return nil;
    if (used == &comma_fdb)
	return(parse_filelist (count, nbytes, help, del));
    if (strlen (pv._pvfil[0]) == 0)
	cmerr ("Invalid null %s", help);
    if ((filelen = strlen (pv._pvfil[0])) >= sizeof (file))
	cmerr ("String too long for %s buffer: %s", file_fdb._cmhlp, atmbuf);

    count++;				/* account for this string */
    nbytes += filelen + 1;
    strcpy (file, pv._pvfil[0]);		/* save it on the stack */

    if (!(dest = parse_filelist (count, nbytes, help, del))) {
	dest = (char **) malloc (((count+1) * sizeof (char *)) + nbytes);
	dest[0] = (char *) count;	/* pass string count back up */
	dest[count] = nil;		/* mark the end of the list */
    }

    dest[count-1] = ((char *) dest) +
	((((int) dest[0]) + 1) * sizeof (char *)) + nbytes - (filelen + 1);
    strcpy (dest[count-1], file);
    return dest;
}

char *parse_in_out_file();

/*
 * parse the name of a file.  returns the absolute pathname.
 * Note: caller must free storage.
 */


char *
parse_input_file (help, def, allowdir)
char *help, *def;
{
    static fdb filefdb = { _CMFIL, FIL_SDH|FIL_OLD|FIL_RD };

    if (allowdir && directory_folders)
	filefdb._cmffl &= ~FIL_NODIR;
    else
	filefdb._cmffl |= FIL_NODIR;
    return (parse_in_out_file (help, def, filefdb));
}

/*
 * parse the name of a file.  returns the absolute pathname.
 * Note: caller must free storage.
 */

char *
parse_output_file (help, def, allowdir)
char *help, *def;
{
    static fdb filefdb = { _CMFIL, FIL_SDH|FIL_PO };

    if (allowdir && directory_folders)
	filefdb._cmffl &= ~FIL_NODIR;
    else
	filefdb._cmffl |= FIL_NODIR;
    return (parse_in_out_file (help, def, filefdb));
}


/*
 * parse_in_out_file:
 * do the work for parse_input_file or parse_output_file
 */
char *
parse_in_out_file (help, def, filefdb)
char *help, *def;
fdb filefdb;
{
    char wd[MAXPATHLEN];
    char *getwd();
    char *fname;
    char *cp;
    filblk fb;
    static char *dirs[] = { nil, "." ,  nil };

    dirs[0] = mail_directory;
    fb.pathv = dirs;
    fb.exceptionspec = nil;
    fb.def_extension = nil;
    filefdb._cmdat = (pdat) &fb;
    filefdb._cmhlp = help;
    filefdb._cmdef = def;

    parse (&filefdb, &pv, &used);
    if (*pv._pvfil[0] == '/') {		/* absolute path specified */
      fname = (char *) malloc (strlen(pv._pvfil[0])+1);
      strcpy (fname, pv._pvfil[0]);
      return (fname);
    }
    if (getwd (wd) == NULL) { 		/* got some kind of error */
      fprintf (stderr, "%s\n", wd);	/* print the error message */
      fname = (char *) malloc (strlen(pv._pvfil[0])+1);
      strcpy (fname, pv._pvfil[0]);
      return (fname);
    }
    cp = pv._pvfil[0];
    if (cp[0] == '.' && cp[1] == '/')
      cp += 2;
    fname = (char *) malloc (strlen(cp)+strlen(wd)+2);
    sprintf (fname, "%s/%s", wd, cp);
    return (fname);
}

/*
 * parse a directory name.
 * the caller is responsible for freeing the string returned.
 */

char *
parse_directory (help, def)
char *help, *def;
{
    int i;
    char cwd[MAXPATHLEN], *cp;
    static fdb dirfdb = { _CMFIL, CM_SDH|FIL_DIR };

    dirfdb._cmhlp = help ? help : "directory name";
    dirfdb._cmdef = def;
    parse (&dirfdb, &pv, &used);

    cwd[0] = 0;
    if (*pv._pvfil[0] != '/') {
	if (getwd (cwd) == NULL) {
	    fprintf (stderr, "%s\n", cwd);
	    cwd[0] = 0;
	}
    }

    cp = malloc (cwd[0] ? strlen (cwd) + strlen (pv._pvfil[0]) + 2
		 	: strlen (pv._pvfil[0]) + 2);
    if (!cp)
	cmerr ("Out of memory");

    if (cwd[0])
	sprintf (cp, "%s/%s", cwd, pv._pvfil[0]);
    else
	strcpy (cp, pv._pvfil[0]);
    return cp;
}



/*
 * Try to use Andy's ccmd routines to parse a date string
 */

/*
 * This is currently used to parse dates out of the message separators;
 * this is probably horribly inefficient compared to a quick-and-dirty
 * parse that would accept only exactly what we expect in certain cases, but...
 */

int tadflags = 0;			/* no flags (for generality) */

time_t
stringtotime(s)
register char *s;
{
    static fdb dtfdb = { _CMTAD, 0 };
    int len;
    pval pv;
    fdb *used;
    int ret;

    if ((ret = match(s, strlen(s), &dtfdb, &pv, &used, &len)) == CMxOK)
	return(datime_to_time(&pv._pvtad));
    return(0);
}

char *
parse_keyword(kt,nothing)
keytab *kt;
int nothing;				/* is nothing okay? */
{
    pval pv;
    fdb *used;
    int i;
    char *safe_strcpy();
    static fdb keyfdb = {_CMKEY };
    static fdb cfm_fdb = {_CMCFM, CM_SDH };

    keyfdb._cmdat = (pdat) kt;
    if (nothing)
	parse(fdbchn(&keyfdb,&cfm_fdb, NULL), &pv, &used);
    else
	parse(fdbchn(&keyfdb,NULL), &pv, &used);
    if (used == &cfm_fdb)
	return(NULL);
    i = 0;
    while (kt->_ktwds[i]._kwval != pv._pvkey)
	i++;
    return(kt->_ktwds[i]._kwkwd);
}

char *
parse_field(help,def) 
char *help,*def;
{
    static fdb fldfdb = { _CMFLD, CM_SDH };
    pval pv;
    fdb *used;
    
    fldfdb._cmhlp = help;
    fldfdb._cmdef = def;
    parse(&fldfdb, &pv, &used);
    return(atmbuf);
}

keytab *
mk_alias_keys()
{
    static keywrd *aliaskeys = nil;
    static keytab aliastab;
    int i;

    if (aliaskeys != nil) {
	free(aliaskeys);
    }
    if (mail_aliases.count != 0)
	aliaskeys = (keywrd *)malloc(mail_aliases.count * sizeof(keywrd));
    else
	aliaskeys = nil;
    for(i = 0; i < mail_aliases.count; i++) {
	aliaskeys[i]._kwkwd = mail_aliases.aliases[i].name;
	aliaskeys[i]._kwflg = 0;
	aliaskeys[i]._kwval = i;
    }
    aliastab._ktcnt = mail_aliases.count;
    aliastab._ktwds = aliaskeys;
    return(&aliastab);
}

parse_alias(help,def)
char *help, *def;
{
    static fdb aliasfdb = { _CMKEY };
    pval pv;
    fdb *used;

    aliasfdb._cmhlp = help;
    aliasfdb._cmdef = def;
    aliasfdb._cmdat = (pdat) mk_alias_keys();
    parse(&aliasfdb, &pv, &used);
    return(pv._pvkey);
}
