home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mm / mm-0.90 / parse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-18  |  23.0 KB  |  988 lines

  1. /*
  2.  * Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  * the City of New York.  Permission is granted to any individual or
  4.  * institution to use, copy, or redistribute this software so long as it
  5.  * is not sold for profit, provided this copyright notice is retained.
  6.  */
  7.  
  8. #ifndef lint
  9. static char *rcsid = "$Header: /f/src2/encore.bin/cucca/mm/tarring-it-up/RCS/parse.c,v 2.2 90/10/04 18:25:21 melissa Exp $";
  10. #endif
  11.  
  12. /*
  13.  * parse.c - miscellaneous parsing functions
  14.  */
  15.  
  16. #include "mm.h"
  17. #include "parse.h"
  18. #include <varargs.h>
  19.  
  20. /*
  21.  * miscellaneous function descriptor blocks
  22.  */ 
  23.  
  24. static brktab shell_brk = {
  25.     {
  26.     0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 
  27.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  28.     },
  29.     {
  30.     0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 
  31.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  32.     }
  33. };
  34.  
  35. fdb shell_fdb = {
  36.     _CMTOK, CM_SDH|CM_NLH|TOK_WAK, nil, 
  37.     (pdat) "!", "\"!\" for shell escape", NULL, &shell_brk
  38. };
  39.  
  40. fdb cfm_fdb = { _CMCFM, CM_SDH, nil, nil, "confirm with carriage return" };
  41.  
  42.  
  43. static brktab fldbrk = {
  44.     {
  45.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f,
  46.     0x80, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x1f,
  47.     },
  48.     {
  49.     0xff, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0x00, 0x1f,
  50.     0x00, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x1f,
  51.     },
  52. };
  53.  
  54. /* lend this to all the other modules */
  55. fdb aliasfdb = { _CMKEY, KEY_EMO, nil, nil, "mail alias, ", NULL, &fldbrk };
  56.  
  57.  
  58. /* 
  59.  * This routine is dangerous in that it's use may cause memory leaks; it
  60.  * should not be used where it might orphan chunks of memory allocated
  61.  * with malloc (the same is true of any call to a parsing routine).
  62.  */
  63. void
  64. cmerr(va_alist)
  65. va_dcl
  66. {
  67.     char *fmt;
  68.     va_list arg_ptr;
  69.  
  70.     if ((cmcsb._cmflg & CM_TTY) == 0) {
  71.     va_start(arg_ptr);
  72.     fmt = va_arg(arg_ptr, char *);
  73.     fprintf(cmcsb._cmej, "%s: ", progname);
  74.     vfprintf(cmcsb._cmej,fmt,arg_ptr);
  75.     va_end(arg_ptr);
  76.     cmerjnp(0);               /* reprompt N.B. argument is ignored */
  77.     }
  78.     else {
  79.     va_start(arg_ptr);
  80.     fmt = va_arg(arg_ptr, char *);
  81.     cmflush(cmcsb._cmij);
  82.     if (cmcpos() != 0)
  83.         cmnl(cmcsb._cmoj);
  84.     cmcsb._cmcol = 0;
  85.     fputc('?', cmcsb._cmej);
  86.     vfprintf(cmcsb._cmej,fmt,arg_ptr);
  87.     fputc('\n', cmcsb._cmej);
  88.     va_end(arg_ptr);
  89. #ifdef undef
  90.     cmnl(cmcsb._cmoj);        /* tie it off with a newline */
  91. #endif
  92.     cmerjnp(0);            /* reprompt N.B. argument is ignored */
  93.     }
  94. }
  95.  
  96. /*
  97.  * sorry is identical to cmerr, except that it doesn't unwind.
  98.  */
  99. int
  100. sorry (va_alist)
  101. va_dcl
  102. {
  103.     char *fmt;
  104.     va_list arg_ptr;
  105.  
  106.     if ((cmcsb._cmflg & CM_TTY) == 0) {
  107.     va_start(arg_ptr);
  108.     fmt = va_arg(arg_ptr, char *);
  109.     fprintf(cmcsb._cmej, "%s: ", progname);
  110.     vfprintf(cmcsb._cmej,fmt,arg_ptr);
  111.     va_end(arg_ptr);
  112.     }
  113.     else {
  114.     va_start(arg_ptr);
  115.     fmt = va_arg(arg_ptr, char *);
  116.     cmflush(cmcsb._cmij);
  117.     if (cmcpos() != 0)
  118.         cmnl(cmcsb._cmoj);
  119.     cmcsb._cmcol = 0;
  120.     fputc('?', cmcsb._cmej);
  121.     vfprintf(cmcsb._cmej,fmt,arg_ptr);
  122.     fputc('\n', cmcsb._cmej);
  123.     va_end(arg_ptr);
  124.     }
  125.     return false;
  126. }
  127.  
  128.  
  129. time_t
  130. datimetogmt (d)
  131. datime *d;
  132. {
  133.     time_t gmt;
  134.     struct tm t;
  135.  
  136.     t.tm_sec = d->_dtsec;
  137.     t.tm_min = d->_dtmin;
  138.     t.tm_hour = d->_dthr;
  139.     t.tm_mday = d->_dtday + 1;        /* sad but true */
  140.     t.tm_mon = d->_dtmon;
  141.     t.tm_year = d->_dtyr - 1900;
  142.     t.tm_wday = d->_dtdow;
  143.     t.tm_yday = 0;            /* XXX ccmd doesn't support this */
  144.     t.tm_isdst = 0;            /* fake this */
  145.     gmt = itime (&t);            /* get almost-gmt */
  146.     gmt -= (minutes_west - d->_dttz) * 60;
  147.     /* dtdst contains DST adjustment in minutes (like "-60") */
  148.     gmt += d->_dtdst * 60;
  149.     return gmt;
  150. }
  151.  
  152. #define T_SUNDAY -7
  153. #define T_MONDAY -6
  154. #define T_TUESDAY -5
  155. #define T_WEDNESDAY -4
  156. #define T_THURSDAY -3
  157. #define T_FRIDAY -2
  158. #define T_SATURDAY -1
  159.  
  160. keywrd timekeys[] = {
  161.     { "friday", 0, (keyval) T_FRIDAY },
  162.     { "monday", 0, (keyval) T_MONDAY },
  163. #define T_NEVER 0
  164.     { "never", 0, T_NEVER },
  165. #define T_NOW 1
  166.     { "now", 0, (keyval) T_NOW },
  167.     { "sunday", 0, (keyval) T_SUNDAY },
  168.     { "saturday", 0, (keyval) T_SATURDAY },
  169.     { "thursday", 0, (keyval) T_THURSDAY },
  170. #define T_TODAY 2
  171.     { "today", 0, (keyval) T_TODAY },
  172. #define T_TOMORROW 3
  173.     { "tomorrow", 0, (keyval) T_TOMORROW },
  174.     { "tuesday", 0, (keyval) T_TUESDAY },
  175.     { "wednesday", 0, (keyval) T_WEDNESDAY },
  176. #define T_YESTERDAY 4
  177.     { "yesterday", 0, (keyval) T_YESTERDAY }
  178. };
  179.  
  180. keytab timetab = { sizeof (timekeys) / sizeof (keywrd), timekeys };
  181.  
  182. time_t
  183. p_date(help)
  184. char *help;
  185. {
  186.     static fdb tadfdb = { _CMTAD };
  187.     static fdb keytimefdb = { _CMKEY, 0, NULL, NULL, "relative date, " };
  188.     tadfdb._cmhlp = (help ? help : "date");
  189.     keytimefdb._cmdat = (pdat) &timetab;
  190.     parse(fdbchn(&tadfdb, &keytimefdb, NULL), &pv, &used);
  191.     if (used == &tadfdb)
  192.     return (datimetogmt(&pv._pvtad));
  193.  
  194.     return key2time(pv._pvint);
  195. }
  196.  
  197. time_t
  198. key2time(n)
  199. int n;
  200. {
  201.  
  202.     time_t tx;
  203.     struct tm *t;
  204.  
  205.     if (n == T_NEVER)
  206.     return 0;
  207.  
  208.     time(&tx);
  209.  
  210.     if (n == T_NOW)
  211.     return tx;
  212.  
  213.     if (n < 0) {
  214.     t = localtime(&tx);
  215.     /* reduce tx by some number of days */
  216.     tx -= ((t->tm_wday + 7 - n) % 7) * (60*60*24);
  217.     /* reduce tx by seconds since midnight */
  218.     tx -= t->tm_sec + (t->tm_min * 60) + (t->tm_hour * (60*60));
  219.     return tx;
  220.     }
  221.  
  222.     switch (n) {
  223.       case T_TOMORROW:
  224.     tx += 60*60*24;
  225.     goto cvt;
  226.       case T_YESTERDAY:
  227.     tx -= 60*60*24;
  228.     goto cvt;
  229.       case T_TODAY:
  230. cvt:    t = localtime(&tx);
  231.     t->tm_sec = 1;
  232.     t->tm_min = 0;
  233.     t->tm_hour = 0;
  234.     return (itime(t));
  235.       default:
  236.     cmerr("Unknown time keyword (%d) parsed", n);
  237.     }
  238. }
  239.  
  240. int
  241. p_num(help)
  242. char *help;
  243. {
  244.     static fdb numfdb = { _CMNUM, CM_SDH, 0, (pdat) 10 };
  245.  
  246.     numfdb._cmhlp = (help ? help : "number");
  247.     parse(&numfdb, &pv, &used);
  248.     return pv._pvint;
  249. }
  250.  
  251.  
  252. /* confirmit - prompt for and parse crlf */
  253.  
  254. void
  255. confirmit(promptstring)
  256. char *promptstring;
  257. {
  258.     volatile int doprev = false;
  259.  
  260.     if (promptstring == (char *) 0)
  261.     promptstring = "[Confirm]";
  262.  
  263.     cmseteof();
  264.     if (cmseter ()) {            /* errors return here */
  265.     if (cmcsb._cmerr == CMxEOF) {
  266.         return;
  267.     }
  268.     else
  269.         doprev = true;
  270.     }
  271.     
  272.     prompt(promptstring);
  273.     if (doprev) {
  274.     doprev = false;
  275.     prevcmd();
  276.     }
  277.     cmsetrp();                /* back here on reparse */
  278.     confirm();                /* go for confirmation */
  279. }
  280.  
  281. static keywrd yesnokeys[] = {
  282.     { "always", 0, SET_ALWAYS },    /* 0 */
  283.     { "false", 0, SET_NO },        /* 1 */
  284.     { "never", 0, SET_NO },        /* 2 */
  285.     { "no", 0, SET_NO },        /* 3 */
  286.     { "ok", 0, SET_YES },        /* 4 */
  287.     { "true", 0, SET_YES },        /* 5 */
  288.     { "yes", 0, SET_YES },        /* 6 */
  289.     { "0", KEY_INV, SET_NO },        /* 7 */
  290.     { "1", KEY_INV, SET_YES },        /* 8 */
  291.     { "n", KEY_ABR|KEY_INV, (keyval) 3 } /* this means "no" */
  292. };
  293.  
  294. keytab yesnotab = { sizeof(yesnokeys) / sizeof(keywrd), yesnokeys };
  295. fdb yesno_fdb = { _CMKEY, 0, NULL, (pdat) &yesnotab, "keyword, " };
  296.  
  297. static keywrd yesnoask_keys[] = {
  298.     { "always", 0, SET_ALWAYS },
  299.     { "ask", 0, SET_ASK },
  300.     { "never", 0, SET_NEVER },
  301.     { "no", 0, SET_NEVER },
  302.     { "yes", 0, SET_ALWAYS },
  303.     { "n", KEY_ABR|KEY_INV, (keyval) 3 },
  304. };
  305.  
  306. keytab yesnoasktab = { sizeof(yesnoask_keys)/sizeof(keywrd), yesnoask_keys };
  307. fdb yesnoask_fdb = { _CMKEY, 0, NULL, (pdat) &yesnoasktab, "keyword, " };
  308.  
  309. int
  310. yesno(promptstring, def)
  311. char *promptstring, *def;
  312. {
  313.     int n;
  314.     volatile int doprev = false;
  315.  
  316.     save_parse_context();        /* save reparse/error handlers */
  317.     if (promptstring == NULL)
  318.     promptstring = "Yes or no? ";
  319.  
  320.     cmseteof();                /* errors come back here */
  321.     if (cmseter ()) {            /* errors return here */
  322.     if (cmcsb._cmerr == CMxEOF)
  323.         panic ("Unexpected EOF in yesno()");
  324.     else
  325.         doprev = true;
  326.     }
  327.     prompt(promptstring);
  328.     if (doprev) {
  329.     doprev = false;
  330.     prevcmd();
  331.     }
  332.     cmsetrp();                /* come back here on reparse */
  333.     n = parse_yesno (def);
  334.     restore_parse_context();        /* restore error/reparse handlers */
  335.     return (int) n;            /* return the answer */
  336. }
  337.  
  338. int
  339. parse_yesno (def)
  340. char *def;
  341. {
  342.     yesno_fdb._cmdef = def;
  343.     parse (&yesno_fdb, &pv, &used);
  344.     confirm();
  345.     return pv._pvint;
  346. }
  347.  
  348. int
  349. parse_yesnoask (def)
  350. char *def;
  351. {
  352.     yesnoask_fdb._cmdef = def;
  353.     parse (&yesnoask_fdb, &pv, &used);
  354.     confirm();
  355.     return pv._pvint;
  356. }
  357.  
  358. /*
  359.  * parse_text:
  360.  * parse some text, ending with CR
  361.  * NOTE that nothing is malloced so nothing should be freed!
  362.  */
  363. char *
  364. parse_text (help, def)
  365. char *help, *def;
  366. {
  367.     static fdb text_fdb = { _CMTXT, CM_SDH };
  368.  
  369.     text_fdb._cmhlp = (help != nil) ? help : "string";
  370.     text_fdb._cmdef = def;
  371.     parse (&text_fdb, &pv, &used);
  372.     return (char *) atmbuf;
  373. }
  374.  
  375. char *
  376. parse_prompt (help)
  377. char *help;
  378. {
  379.     static fdb qstr_fdb = { _CMQST, CM_SDH };
  380.     static fdb text_fdb = { _CMTXT, CM_SDH };
  381.  
  382.     qstr_fdb._cmhlp = (help != nil) ? help : "quoted prompt";
  383.     parse (fdbchn(&qstr_fdb, &text_fdb, NULL), &pv, &used);
  384.     if (used == &qstr_fdb)
  385.     confirm();
  386.     return (char *) atmbuf;
  387. }
  388.  
  389. char *
  390. parse_quoted (help, def)
  391. char *help, *def;
  392. {
  393.     static fdb qstr_fdb = { _CMQST, CM_SDH };
  394.  
  395.     qstr_fdb._cmhlp = (help != nil) ? help : "string";
  396.     qstr_fdb._cmdef = def;
  397.     parse (&qstr_fdb, &pv, &used);
  398.     return (char *) atmbuf;
  399. }
  400.  
  401. char *
  402. parse_username (help, def)
  403. char *help, *def;
  404. {
  405.     static fdb uname_fdb = { _CMUSR, CM_SDH };
  406.  
  407.     uname_fdb._cmhlp = (help != nil) ? help : "Username";
  408.     uname_fdb._cmdef = def;
  409.     parse (&uname_fdb, &pv, &used);
  410.     return (pv._pvusr[0]->pw_name);
  411. }
  412.  
  413. void
  414. brkch(p, s, s2)
  415. brktab *p;
  416. unsigned char *s, *s2;
  417. {
  418.     unsigned char c;
  419.  
  420.     /* process characters which are break characters anywhere in the field */
  421.     while ((c = *s++) && (c < 128)) {
  422.     p->_br1st[c/8] |= (0x80 >> (c % 8));
  423.     p->_brrest[c/8] |= (0x80 >> (c % 8));
  424.     }
  425.     /* process characters which break only if they're the first character */
  426.     if (s2) while ((c = *s2++) && (c < 128))
  427.     p->_br1st[c/8] |= (0x80 >> (c % 8));
  428. }
  429.  
  430. void
  431. unbrk(p,s)
  432. brktab *p;
  433. unsigned char *s;
  434. {
  435.     unsigned char c;
  436.  
  437.     while ((c = *s++) && (c < 128)) {
  438.     p->_br1st[c/8] &= ~(0x80 >> (c % 8));
  439.     p->_brrest[c/8] &= ~(0x80 >> (c % 8));
  440.     }
  441. }
  442.  
  443. p_file(deflt, input)
  444. char *deflt;
  445. {
  446.     static fdb filefdb = {
  447.     _CMFIL, FIL_PO|FIL_VAL, NULL, NULL, NULL, NULL, NULL};
  448.     fdb *f = &filefdb;
  449.  
  450.     f->_cmhlp = (input ? "input filename" : "output filename");
  451.     f->_cmdef = deflt;
  452.     parse(f, &pv, &used);
  453.     strncpy(atmbuf, pv._pvfil[0], sizeof(atmbuf) -1);
  454.     if (input) {
  455.     if (access(atmbuf, F_OK|R_OK) < 0)
  456.         cmerr(errstr(-1));
  457.     }
  458.     else {
  459.     int ok;
  460.     char c = 0;
  461.     char *p = rindex(atmbuf, '/');
  462.  
  463.     if (p == NULL) p = atmbuf;    /* XXX depends on ("" == ".") */
  464.     c = *p; *p = 0;            /* consider only the directory */
  465.     ok = access(atmbuf, F_OK|W_OK);    /* can we write on it? */
  466.  
  467.     *p = c;                /* fix the path again */
  468.     if (ok < 0)
  469.         cmerr(errstr(-1));        /* nope, complain */
  470.     }
  471. }
  472.  
  473. #define IOSTACKLIMIT 16
  474. struct io_fileset {
  475.     FILE *i;
  476.     FILE *o;
  477.     FILE *e;
  478. } io_stack[IOSTACKLIMIT];
  479. int io_stackp = -1;
  480.  
  481. void
  482. stack_input(fd)
  483. FILE *fd;
  484. {
  485.     if ((io_stackp + 1) < IOSTACKLIMIT) {
  486.     io_stackp++;
  487.     io_stack[io_stackp].i = cmcsb._cmij;
  488.     io_stack[io_stackp].o = cmcsb._cmoj;
  489.     io_stack[io_stackp].e = cmcsb._cmej;
  490.     cmseti(fd, cmcsb._cmoj, cmcsb._cmej);
  491.     }
  492.     else cmerr("Command input stack overflow");
  493. }
  494.  
  495. int
  496. pop_input()
  497. {
  498.     if (io_stackp < 0)
  499.     panic ("input file stack is empty in pop_input");
  500.     if (cmcsb._cmij != io_stack[io_stackp].i)
  501.     (void) fclose(cmcsb._cmij);
  502.     cmseti(io_stack[io_stackp].i,io_stack[io_stackp].o,io_stack[io_stackp].e);
  503.     cmcsb._cmwrp = autowrap_column;    /* put that back */
  504.     io_stackp--;
  505.     return CMxEOF;
  506. }
  507.  
  508. /*
  509.  * this routine is a replacement for the ccmd-supplied error handler
  510.  */
  511.  
  512. jmp_buf eofjmpb;
  513. static int errchars = 0;
  514.  
  515. prevcmd()
  516. {
  517.   int *cp;                /* pointer into command buffer */
  518.   int count,i;                /* number of chars to reinstate */
  519.  
  520.   count = cmcsb._cmhst - cmcsb._cmbfp - errchars; /* count buffered chars */
  521.   cp = cmcsb._cmbfp;            /* point to beginning of buffer */
  522.   for (i = 0; i < count; i++) {
  523.       if ((cp[i] & CC_CHR) == '\n') {
  524.       count = i;
  525.       break;
  526.       }
  527.   }
  528.   while(count > 0) {
  529.       if (cp[count-1] == (CC_HID|CC_NEC|'\ '))
  530.       count--;
  531.       else
  532.       break;
  533.   }
  534.  
  535.  
  536.  
  537.   cmcsb._cminc = count;            /* this many chars now to parse */
  538.   cmcsb._cmcnt -= count;        /* count their presence */
  539.   cmcsb._cmcur = cmcsb._cmptr + cmcsb._cminc;
  540.   cmcsb._cmflg |= CM_DRT;        /* now the buffer is dirty */
  541.   while (count-- > 0)             /* step through the buffer */
  542.     if (((*cp) & CC_NEC) == 0)        /* originally echoed? */
  543.       cmechx((char) (*cp++) & CC_CHR);    /* then echo it now */
  544.     else
  545.       cp++;                /* else just move on */
  546.   return(CMxOK);
  547. }
  548.  
  549.  
  550. int
  551. ccmd_error(err,str,flags)
  552. int err;
  553. char *str;
  554. int flags;
  555. {
  556.   if (err == CMxEOF)            /* handle EOF specially */
  557.     longjmp(eofjmpb,err);
  558.   errchars = cmcsb._cminc;
  559.   if (str == NULL)
  560.       cmperr(err,flags);
  561.   else
  562.       cmpemsg(str,flags);
  563.   longjmp(cmerjb,err);
  564. }
  565.  
  566.  
  567. int
  568. ccmd_errmsg(str)
  569. int str;
  570. {
  571.   errchars = cmcsb._cminc;
  572.   cmpemsg(str,0);
  573.   longjmp(cmerjb,CMxNOP);
  574. }
  575.  
  576. int
  577. ccmd_errnp(err)
  578. int err;
  579. {
  580.   if (err == CMxEOF)            /* handle EOF specially */
  581.     longjmp(eofjmpb,err);
  582.   errchars = cmcsb._cminc;
  583.   longjmp(cmerjb,err);
  584. }
  585.  
  586.  
  587. int
  588. parse_number (radix, help, def)
  589. int radix;
  590. char *help, *def;
  591. {
  592.     static fdb numfdb = { _CMNUM, CM_SDH, 0, (pdat) 10 };
  593.     numfdb._cmhlp = (help == nil) ? help : "number";
  594.     numfdb._cmdat = (pdat) radix;
  595.     numfdb._cmdef = def;
  596.     parse (&numfdb, &pv, &used);
  597.     return pv._pvint;
  598. }
  599.  
  600. /*
  601.  * Parse a list of keywords, returning a (char **) pointing to the list.
  602.  *
  603.  * This routine recursively parses keywords in a comma-separated list until
  604.  * a confirm is found or a parse error occurs; after successfully parsing the
  605.  * list and a confirm, the bottom-most invocation allocates enough space to
  606.  * hold the list of pointers and the strings themselves, which are stored
  607.  * into the allocated buffer by each level as recursion unwinds.  The
  608.  * cumulative byte and string count are passed downwards so the bottom-most
  609.  * level knows how much space to allocate; it in turn passes the size of the
  610.  * list back in dest[0] so the higher levels know where the pointers end and
  611.  * the string space begins.
  612.  *
  613.  * Admittedly one of the ugliest routines I ever wrote.
  614.  */
  615.  
  616. char **
  617. parse_keylist (count, nbytes, help)
  618. int count, nbytes; char *help;
  619. {
  620.     int keylen;
  621.     keyword key;
  622.     char **dest;
  623.     static fdb key_cfm_fdb = { _CMCFM, CM_SDH, nil, nil,
  624.                    "confirm to delete list" };
  625.     static fdb key_fdb = { _CMFLD, CM_SDH|FLD_EMPTY };
  626.     static fdb comma_fdb = { _CMTOK, CM_SDH, nil, (pdat) ",",
  627.              "comma and another keyword" };
  628.  
  629.     key_fdb._cmhlp = ((help == nil) ? "keyword" : help);
  630.  
  631.     if (count == 0) {
  632.     parse (fdbchn(&key_cfm_fdb, &key_fdb, nil), &pv, &used);
  633.     if (used == &key_cfm_fdb)
  634.         return nil;
  635.     if (strlen (atmbuf) == 0)
  636.         cmerr ("Invalid null %s", help);
  637.     }
  638.     else {
  639.     parse (&key_fdb, &pv, &used);
  640.     if  (atmbuf[0] == 0)
  641.         cmerr ("Invalid %s after comma", help);
  642.     }
  643.     if ((keylen = strlen (atmbuf)) >= sizeof (key))
  644.     cmerr ("String too long for %s buffer: %s", key_fdb._cmhlp, atmbuf);
  645.  
  646.     count++;                /* account for this string */
  647.     nbytes += keylen + 1;
  648.     strcpy (key, atmbuf);        /* save it on the stack */
  649.  
  650.     parse (fdbchn (&comma_fdb, &cfm_fdb, nil), &pv, &used);
  651.     if (used == &comma_fdb) {
  652.     /* parsed a comma, so call ourself recursively for more */
  653.     dest = parse_keylist (count, nbytes, help);
  654.     }
  655.     else {
  656.     dest = (char **) malloc (((count+1) * sizeof (char *)) + nbytes);
  657.     dest[0] = (char *) count;    /* pass string count back up */
  658.     dest[count] = nil;        /* mark the end of the list */
  659.     }
  660.  
  661.     dest[count-1] = ((char *) dest) +
  662.     ((((int) dest[0]) + 1) * sizeof (char *)) + nbytes - (keylen + 1);
  663.     strcpy (dest[count-1], key);
  664.     return dest;
  665. }
  666.  
  667. char **
  668. parse_filelist (count, nbytes, help, del)
  669. int count, nbytes; char *help;
  670. {
  671.     int filelen;
  672.     buffer file;
  673.     char **dest;
  674.     static fdb file_cfm_fdb = { _CMCFM, CM_SDH, nil, nil, nil };
  675.     static fdb file_fdb = { _CMFIL, FIL_PO|FIL_VAL };
  676.     static fdb comma_fdb = { _CMTOK, CM_SDH, nil, (pdat) ",",
  677.              "comma and another filename" };
  678.  
  679.     file_fdb._cmhlp = ((help == nil) ? "filename" : help);
  680.     file_cfm_fdb._cmhlp = (count == 0 && del) ?
  681.     "confirm to delete the list" : "confirm with carriage return";
  682.  
  683.     if (directory_folders)
  684.     file_fdb._cmffl &= ~FIL_NODIR;
  685.     else
  686.     file_fdb._cmffl |= FIL_NODIR;
  687.     if (count > 0)
  688.     parse (fdbchn(&file_cfm_fdb, &comma_fdb, &file_fdb, nil), &pv, &used);
  689.     else
  690.     parse (fdbchn(&file_cfm_fdb, &file_fdb, nil), &pv, &used);
  691.     if (used == &file_cfm_fdb)
  692.     return nil;
  693.     if (used == &comma_fdb)
  694.     return(parse_filelist (count, nbytes, help, del));
  695.     if (strlen (pv._pvfil[0]) == 0)
  696.     cmerr ("Invalid null %s", help);
  697.     if ((filelen = strlen (pv._pvfil[0])) >= sizeof (file))
  698.     cmerr ("String too long for %s buffer: %s", file_fdb._cmhlp, atmbuf);
  699.  
  700.     count++;                /* account for this string */
  701.     nbytes += filelen + 1;
  702.     strcpy (file, pv._pvfil[0]);        /* save it on the stack */
  703.  
  704.     if (!(dest = parse_filelist (count, nbytes, help, del))) {
  705.     dest = (char **) malloc (((count+1) * sizeof (char *)) + nbytes);
  706.     dest[0] = (char *) count;    /* pass string count back up */
  707.     dest[count] = nil;        /* mark the end of the list */
  708.     }
  709.  
  710.     dest[count-1] = ((char *) dest) +
  711.     ((((int) dest[0]) + 1) * sizeof (char *)) + nbytes - (filelen + 1);
  712.     strcpy (dest[count-1], file);
  713.     return dest;
  714. }
  715.  
  716. char *parse_in_out_file();
  717.  
  718. /*
  719.  * parse the name of a file.  returns the absolute pathname.
  720.  * Note: caller must free storage.
  721.  */
  722.  
  723.  
  724. char *
  725. parse_input_file (help, def, allowdir)
  726. char *help, *def;
  727. {
  728.     static fdb filefdb = { _CMFIL, FIL_OLD|FIL_RD };
  729.  
  730.     if (allowdir && directory_folders)
  731.     filefdb._cmffl &= ~FIL_NODIR;
  732.     else
  733.     filefdb._cmffl |= FIL_NODIR;
  734.     return (parse_in_out_file (help, def, filefdb));
  735. }
  736.  
  737. /*
  738.  * parse the name of a file.  returns the absolute pathname.
  739.  * Note: caller must free storage.
  740.  */
  741.  
  742. char *
  743. parse_output_file (help, def, allowdir)
  744. char *help, *def;
  745. {
  746.     static fdb filefdb = { _CMFIL, FIL_PO|FIL_VAL };
  747.  
  748.     if (allowdir && directory_folders)
  749.     filefdb._cmffl &= ~FIL_NODIR;
  750.     else
  751.     filefdb._cmffl |= FIL_NODIR;
  752.     return (parse_in_out_file (help, def, filefdb));
  753. }
  754.  
  755.  
  756. /*
  757.  * parse_in_out_file:
  758.  * do the work for parse_input_file or parse_output_file
  759.  */
  760. char *
  761. parse_in_out_file (help, def, filefdb)
  762. char *help, *def;
  763. fdb filefdb;
  764. {
  765.     char wd[MAXPATHLEN];
  766.     char *getwd();
  767.     char *fname, *index();
  768.     char *cp;
  769.     filblk fb;
  770.     char *dirs[3];
  771.  
  772.     /* don't list files twice, also if mail_directory is unset, it means "." */
  773.     if (mail_directory[0] == '\0' || same_file(mail_directory, ".")) {
  774.     dirs[0] = ".";
  775.     dirs[1] = nil;
  776.     }
  777.     else {
  778.     dirs[0] = mail_directory;
  779.     dirs[1] = ".";
  780.     }
  781.     dirs[2] = nil;
  782.     
  783.     fb.pathv = dirs;
  784.     fb.exceptionspec = nil;
  785.     fb.def_extension = nil;
  786.     filefdb._cmdat = (pdat) &fb;
  787.     filefdb._cmhlp = help;
  788.     filefdb._cmdef = def;
  789.  
  790.     parse (&filefdb, &pv, &used);
  791.     if (*pv._pvfil[0] == '/') {        /* absolute path specified */
  792.       fname = (char *) malloc (strlen(pv._pvfil[0])+1);
  793.       strcpy (fname, pv._pvfil[0]);
  794.       return (fname);
  795.     }
  796.  
  797.     /* 
  798.      * file does not exist in mail-directory (mail-directory is an
  799.      * absolute path), so see if it is in .
  800.      * in which case use it, otherwise, try to default to new file
  801.      * in mail-directory
  802.      * ****  See also, similar code in move.c: parse_copyfile ****
  803.      */
  804.     if ((index(pv._pvfil[0], '/') != NULL) || /* directory specified */
  805.     access (pv._pvfil[0], F_OK) == 0 || /* file exists in . */
  806.     mail_directory[0] == '\0') {    /* or no mail_directory, use wd */
  807.       if (getwd (wd) == NULL) {        /* got some kind of error */
  808.     fprintf (stderr, "%s\n", wd);    /* print the error message */
  809.     fname = (char *) malloc (strlen(pv._pvfil[0])+1);
  810.     strcpy (fname, pv._pvfil[0]);
  811.     return (fname);
  812.       }
  813.       cp = pv._pvfil[0];
  814.       if (cp[0] == '.' && cp[1] == '/')
  815.     cp += 2;
  816.       fname = (char *) malloc (strlen(cp)+strlen(wd)+2);
  817.       sprintf (fname, "%s/%s", wd, cp);
  818.       return (fname);
  819.     }
  820.  
  821.     /* else, use mail_directory */
  822.  
  823.     fname = (char *) malloc (strlen(mail_directory)+strlen(pv._pvfil[0])+2);
  824.     sprintf (fname, "%s/%s", mail_directory, pv._pvfil[0]);
  825.     return (fname);
  826. }
  827.  
  828. /*
  829.  * parse a directory name.
  830.  * the caller is responsible for freeing the string returned.
  831.  */
  832.  
  833. char *
  834. parse_directory (help, def)
  835. char *help, *def;
  836. {
  837.     int len;
  838.     char cwd[MAXPATHLEN], *cp, *pp;
  839.     static fdb dirfdb = { _CMFIL, FIL_DIR, NULL, NULL, 
  840.                   NULL, NULL, NULL };
  841.     static fdb cfmfdb = { _CMCFM, CM_SDH, NULL, NULL, "confirm to unset", 
  842.                   NULL, NULL };
  843.  
  844.     dirfdb._cmhlp = help ? help : "directory name";
  845.     dirfdb._cmdef = def;
  846.     parse (fdbchn(&dirfdb,&cfmfdb,NULL), &pv, &used);
  847.  
  848.     if (used == &cfmfdb) {        /* unset, set to empty string */
  849.     return ("");            /* since caller does a strcpy */
  850.     }
  851.  
  852.     confirm();
  853.  
  854.     cwd[0] = 0;
  855.     pp = pv._pvfil[0];
  856.     if (*pv._pvfil[0] != '/') {
  857.     if (pp[0] == '.' && pp[1] == '/')
  858.         pp += 2;
  859.     if (getwd (cwd) == NULL) {
  860.         fprintf (stderr, "%s\n", cwd);
  861.         cwd[0] = 0;
  862.     }
  863.     }
  864.  
  865.     len = cwd[0] ? strlen (cwd) + strlen (pp) + 1 : strlen (pp);
  866.     cp = malloc (len+1);
  867.     if (!cp)
  868.     cmerr ("Out of memory");
  869.  
  870.     if (cwd[0])
  871.     sprintf (cp, "%s/%s", cwd, pp);
  872.     else
  873.     strcpy (cp, pp);
  874.  
  875.     if (cp[len-1] == '/')        /* ends with / */
  876.         cp[len-1] = '\0';
  877.     if (cp[len-2] == '/' && cp[len-1] == '.') /* ends with /. */
  878.     cp[len-2] = '\0';
  879.     
  880.     return cp;
  881. }
  882.  
  883.  
  884.  
  885. /*
  886.  * Try to use Andy's ccmd routines to parse a date string
  887.  */
  888.  
  889. /*
  890.  * This is currently used to parse dates out of the message separators;
  891.  * this is probably horribly inefficient compared to a quick-and-dirty
  892.  * parse that would accept only exactly what we expect in certain cases, but...
  893.  */
  894.  
  895. int tadflags = 0;            /* no flags (for generality) */
  896.  
  897. time_t
  898. stringtotime(s)
  899. register char *s;
  900. {
  901.     static fdb dtfdb = { _CMTAD, 0 };
  902.     int len;
  903.     pval pv;
  904.     fdb *used;
  905.     int ret;
  906.  
  907.     if ((ret = match(s, strlen(s), &dtfdb, &pv, &used, &len)) == CMxOK)
  908.     return(datime_to_time(&pv._pvtad));
  909.     return(0);
  910. }
  911.  
  912. char *
  913. parse_keyword(kt,nothing)
  914. keytab *kt;
  915. int nothing;                /* is nothing okay? */
  916. {
  917.     pval pv;
  918.     fdb *used;
  919.     int i;
  920.     char *safe_strcpy();
  921.     static fdb keyfdb = {_CMKEY };
  922.     static fdb cfm_fdb = {_CMCFM, CM_SDH };
  923.  
  924.     keyfdb._cmdat = (pdat) kt;
  925.     if (nothing)
  926.     parse(fdbchn(&keyfdb,&cfm_fdb, NULL), &pv, &used);
  927.     else
  928.     parse(fdbchn(&keyfdb,NULL), &pv, &used);
  929.     if (used == &cfm_fdb)
  930.     return(NULL);
  931.     i = 0;
  932.     while (kt->_ktwds[i]._kwval != pv._pvkey)
  933.     i++;
  934.     return(kt->_ktwds[i]._kwkwd);
  935. }
  936.  
  937. char *
  938. parse_field(help,def) 
  939. char *help,*def;
  940. {
  941.     static fdb fldfdb = { _CMFLD, CM_SDH };
  942.     pval pv;
  943.     fdb *used;
  944.     
  945.     fldfdb._cmhlp = help;
  946.     fldfdb._cmdef = def;
  947.     parse(&fldfdb, &pv, &used);
  948.     return(atmbuf);
  949. }
  950.  
  951. keytab *
  952. mk_alias_keys()
  953. {
  954.     static keywrd *aliaskeys = nil;
  955.     static keytab aliastab;
  956.     int i;
  957.  
  958.     if (aliaskeys != nil) {
  959.     free(aliaskeys);
  960.     }
  961.     if (mail_aliases.count != 0)
  962.     aliaskeys = (keywrd *)malloc(mail_aliases.count * sizeof(keywrd));
  963.     else
  964.     aliaskeys = nil;
  965.     for(i = 0; i < mail_aliases.count; i++) {
  966.     aliaskeys[i]._kwkwd = mail_aliases.aliases[i].name;
  967.     aliaskeys[i]._kwflg = 0;
  968.     aliaskeys[i]._kwval = i;
  969.     }
  970.     aliastab._ktcnt = mail_aliases.count;
  971.     aliastab._ktwds = aliaskeys;
  972.     return(&aliastab);
  973. }
  974.  
  975. parse_alias(help,def)
  976. char *help, *def;
  977. {
  978.     static fdb aliasfdb = { _CMKEY };
  979.     pval pv;
  980.     fdb *used;
  981.  
  982.     aliasfdb._cmhlp = help;
  983.     aliasfdb._cmdef = def;
  984.     aliasfdb._cmdat = (pdat) mk_alias_keys();
  985.     parse(&aliasfdb, &pv, &used);
  986.     return(pv._pvkey);
  987. }
  988.