home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / mm / mm-ccmd-0.91-20031009.tar.gz / mm-ccmd-0.91-20031009.tar / work / ccmd / ccmd.c < prev    next >
C/C++ Source or Header  |  2002-09-17  |  42KB  |  1,315 lines

  1. /*
  2.  Copyright (c) 1986, 2002 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.  Author: Andrew Lowry
  8. */
  9. /* ccmd.c
  10. **
  11. ** Primary module of the ccmd package.  Following routines are accessible
  12. ** to user programs:
  13. **
  14. **/
  15.  
  16. #include "ccmdlib.h"        /* get standard package symbols */
  17. #include "cmfncs.h"        /* and internal symbols */
  18.  
  19. extern int (*(stdact[]))();    /* standard action table */
  20. extern int (*(preact[]))();    /* standard action table */
  21.  
  22. csb cmcsb = {            /* CSB for all parsing */
  23.     0,            /* all flags initially off */
  24.     0,            /* second flag word too. */
  25.     NULL,            /* input from console */
  26.     NULL,            /* output to console */
  27.     NULL,            /* error output */
  28.     NULL,            /* no prompt set yet */
  29.     NULL,            /* no command line buffer yet */
  30.     NULL,                /* no characters to parse */
  31.     0,            /* size of nonexistent command buffer */
  32.     0,            /* no unparsed characters */
  33.     NULL,            /* no history point */
  34.     NULL,            /* no atom buffer yet */
  35.     0,            /* size of nonexistent atom buffer */
  36.     NULL,            /* no work buffer yet */
  37.     0,            /* size of nonexistent work buffer */
  38.     stdact,            /* set up standard action table */
  39.     NULCHAR,        /* no saved break character */
  40.     -1,            /* maximum column position unknown */
  41.     0,            /* current column position */
  42.     CMxOK,            /* last parse error */
  43.     NULL,            /* no FDB giving incomplete parse */
  44.     NULL,            /* no reparse handler */
  45.     NULL,            /* no error handler */
  46.     ";",            /* comment to eol */
  47.     "!",            /* start delimited comment */
  48.     "!",            /* end delimited comment */
  49.     1000,            /* max help tokens */
  50.     NULL,            /* nonblocking I/O handler */
  51.     -1,            /* wrap column */
  52.     NULL,            /* command history */
  53.     -1,            /* max rows */
  54.     NULL,            /* no current position yet */
  55.     preact,            /* prefix actions */
  56. };
  57.  
  58.  
  59. static int getbrk(fdb *fdblist, brktab **btab);
  60. static int checkcfm(fdb *fdblist, brktab *btab);
  61. static int checkact(fdb *fdblist, brktab *btab, int flags);
  62. static int checkbrk(fdb *fdblist, brktab *btab, char brk, int flags);
  63. static int fill(brktab *btab, char *brk);
  64. static int xstrncmp(int *s1, char *s2, int len);
  65. static int toatom(char *text, int textlen);
  66. static int skipadj(int *buf, int bufsize, int passcnt);
  67. static int checkeof(fdb *fdblist);
  68.  
  69.  
  70.  
  71. /* cmbufs
  72. **
  73. ** Purpose:
  74. **   Declare buffers for use in command parsing.  This routine must
  75. **   be invoked once before any parsing may be performed.  Thereafter,
  76. **   only if one or more buffers need to be changed.  
  77. **
  78. **   Three buffers are declared.  The "command buffer" is used to
  79. **   collect user input during the parse.  The "atom buffer" is used
  80. **   to hold the text from a single parsed field.  Each time a parse
  81. **   succeeds, the characters consumed by the parse are copied into
  82. **   the atom buffer.  The "work buffer" is used internally by the
  83. **   ccmd package to hold text to be passed to the individual field
  84. **   parsers.  The user program should generally never touch the work
  85. **   buffer for any reason.
  86. **
  87. **   If any of the buffers overflows during parsing, an error is
  88. **   signaled to the caller.  To prevent atom buffer and work buffer
  89. **   overflow, they should be made as large as the command buffer.
  90. **   Generally, however, these two auxiliary buffers can be smaller,
  91. **   especially the atom buffer, unless individual fields are likely
  92. **   to be large.
  93. **
  94. ** Input arguments:
  95. **   cmdbuf - A pointer to an int buffer to be used to hold
  96. **     the command line during parsing.  Each character is stored
  97. **     with a flag byte in the left half of its int entry.
  98. **   cmdlen - The total number of characters available in the command
  99. **     line buffer.
  100. **   atombuf - A pointer to an auxiliary "atom buffer" to hold the text
  101. **     comprising individual fields during the parse.  Some parsing
  102. **     functions return string results via the atom buffer.
  103. **   atomlen - The total number of characters available in the atom buffer.
  104. **   workbuf - A pointer to an auxiliary "work buffer" used internally by
  105. **     the parsing routines.
  106. **   worklen - The total number of characters available in the work
  107. **     buffer.
  108. **
  109. ** Output arguments: None.
  110. **
  111. ** Returns: CMxOK always
  112. **
  113. ** As a side effect, the first time this is called, the CCMD environment
  114. ** variable is read and interpreted.  This doesn't really belong here, but
  115. ** there is no other init routine which must get called first...
  116. ** KLH: Ditto setting stdin/stdout/stderr defaults; must be done at
  117. ** run time since they are not necessarily compile-time constants.
  118. **/
  119.  
  120. int
  121. cmbufs(cmdbuf,cmdlen,atombuf,atomlen,workbuf,worklen)
  122. int *cmdbuf;
  123. char *atombuf,*workbuf;
  124. int cmdlen,atomlen,worklen;
  125. {
  126.   static int firsttime = TRUE;
  127.  
  128.   if (firsttime) {
  129.       firsttime = FALSE;
  130.       cmgetenv();
  131.  
  132.       cmcsb._cmij = stdin;        /* set input stream in CSB */
  133.       cmcsb._cmoj = stdout;        /* set output stream in CSB */
  134.       cmcsb._cmej = stderr;        /* set errout stream in CSB */
  135.   }
  136.  
  137.   cmcsb._cmbfp = cmdbuf;    /* set up buffer attributes in CSB */
  138.   cmcsb._cmcnt = cmdlen;
  139.   cmcsb._cmabp = atombuf;
  140.   cmcsb._cmabc = atomlen;
  141.   cmcsb._cmwbp = workbuf;
  142.   cmcsb._cmwbc = worklen;
  143.   
  144.   cmcsb._cmcur = cmcsb._cmptr = cmdbuf;    /* no parsed characters in buffer */
  145.   cmcsb._cminc = 0;        /* and no unparsed characters either */
  146.   cmcsb._cmhst = cmdbuf;    /* prevent history attempts */
  147.   return(CMxOK);
  148. }
  149.  
  150.  
  151. /*
  152. ** cmraise, cmwake, cmecho
  153. **
  154. ** Purpose:
  155. **   Set or clear flags in the CSB.  Cmraise controls the CM_RAI flag,
  156. **   which determines whether or not characters are automatically converted
  157. **   to upper case on input.  Cmwake controls the CM_WKF flag, which controls
  158. **   whether or not a parsing "wakeup" occurs for non-action break characters
  159. **   that are encountered in the input.  Cmecho controls the CM_NEC flag,
  160. **   which determines whether or not input characters are echoed back to
  161. **   the user.
  162. **
  163. ** Input arguments:
  164. **   flag - TRUE to turn on an action, FALSE to turn it off.
  165. **
  166. ** Output arguments: None
  167. ** Returns: CMxOK always
  168. **/
  169.  
  170. int
  171. cmraise(flag)
  172. int flag;
  173. {
  174.   if (flag)
  175.     cmcsb._cmflg |= CM_RAI;    /* set the flag */
  176.   else
  177.     cmcsb._cmflg &= ~CM_RAI;    /* or clear it */
  178.   return(CMxOK);
  179. }
  180.  
  181. int
  182. cmwake(flag)
  183. int flag;
  184. {
  185.   if (flag)
  186.     cmcsb._cmflg |= CM_WKF;    /* set the flag */
  187.   else
  188.     cmcsb._cmflg &= ~CM_WKF;    /* or clear it */
  189.   return(CMxOK);
  190. }
  191.  
  192. int
  193. cmecho(flag)
  194. int flag;
  195. {
  196.   if (flag)
  197.     cmcsb._cmflg &= ~CM_NEC;    /* clear "no echo" flag */
  198.   else
  199.     cmcsb._cmflg |= CM_NEC;    /* or set it */
  200. }
  201.  
  202.  
  203.  
  204. /* cmact
  205. **
  206. ** Purpose:
  207. **   Install a user-specified action character table.  When a break
  208. **   character is encountered in the input, this table is examined,
  209. **   indexed by the character's ASCII code.  If the entry is non-NULL,
  210. **   it points to a function that will be invoked to perform an action
  211. **   on behalf of that character.
  212. **
  213. ** Input arguments:
  214. **   acttab - Pointer to the beginning of the action table, which
  215. **     should be an array of pointers to integer functions.  If
  216. **     NULL is passed, the standard action table will be installed.
  217. **
  218. ** Output arguments: None.
  219. ** Returns: CMxOK always.
  220. **/
  221.  
  222. int
  223. cmact(acttab)
  224. int (**acttab)();
  225. {
  226.   if (acttab == NULL)
  227.     cmcsb._cmact = stdact;        /* install standard table */
  228.   else
  229.     cmcsb._cmact = acttab;        /* or caller's table */
  230.   return(CMxOK);
  231. }
  232.  
  233.  
  234.  
  235. /* cmseti
  236. **
  237. ** Purpose:
  238. **   Set the input stream for command parsing.  If NULL is passed, 
  239. **   subsequent parsing will be taken from the console.  If the
  240. **   stream is associated with a terminal device, echoing and other
  241. **   output will be performed to the same terminal.  If that terminal
  242. **   is capable of cursor control, command line editing will cause
  243. **   screen updates.  Otherwise, other mechanisms will be employed
  244. **   for presenting edits to the user.  If the source is not a terminal,
  245. **   echoing and other output will be suppressed.
  246. **
  247. ** Input arguments:
  248. **   input: input file stream (normally stdin)
  249. **   output: output file stream (normally stdout)
  250. ** Output arguments: None.
  251. ** Returns: CMxOK always.
  252. **/
  253.  
  254. int
  255. cmseti(input,output,error)
  256. FILE *input,*output,*error;
  257. {
  258.   static int priorset = FALSE;    /* true only after first invocation */
  259.   if (priorset) 
  260.     cmtend();            /* break down prior source if any */
  261.   else
  262.     priorset = TRUE;        /* do that every time after first */
  263.   cmcsb._cmij = input;        /* set input stream in CSB */
  264.   cmcsb._cmoj = output;        /* set output stream in CSB */
  265.   cmcsb._cmej = error;        /* set errout stream in CSB */
  266.   cmtset();                   /* do any necessary terminal initialization */
  267.   return(CMxOK);
  268. }
  269.  
  270.  
  271.  
  272. /* cmdone
  273. **
  274. ** Purpose:
  275. **   Invoked when the program is finished with command parsing, to
  276. **   give the terminal cleanup routines a chance to clean up the
  277. **   most recent command source.
  278. **
  279. ** Input arguments: None.
  280. ** Output arguments: None.
  281. ** Returns: Nothing.
  282. **/
  283.  
  284. cmdone()
  285. {
  286.   cmtend();            /* clean up routine */
  287. }
  288.  
  289.  
  290.  
  291. /* prompt
  292. **
  293. ** Purpose:
  294. **   Issues a prompt to the user and sets a CSB in an appropriate
  295. **   state to begin parsing a new command line.  Prompt should
  296. **   be used whenever a command line is to be parsed from the beginning
  297. **   for any reason EXCEPT a reparse.  For a reparse, skip the prompt
  298. **   call and proceed immediately with calls to 'parse' to parse the
  299. **   fields of the command as required.
  300. **
  301. ** Input arguments:
  302. **   prompt - The prompt string to be issued.  The string must be preserved
  303. **     throughout the parsing of the command line, as it will be referenced
  304. **     whenever the command line must be redisplayed (after a help request,
  305. **     when the user types ^R, etc.).  In other words, do not pass
  306. **     a pointer to dynamic storage that may be deallocated before parsing
  307. **     is complete.
  308. **
  309. ** Output arguments: none
  310. **
  311. ** Returns: Standard return code.
  312. **/
  313.  
  314.  
  315. int
  316. prompt(p)
  317. char *p;
  318. {
  319.   int ret;
  320.   cmcsb._cmflg &= ~CM_CMT;    /* new line...not in a comment */
  321.   if ((cmcsb._cmbfp == NULL) ||
  322.       (cmcsb._cmabp == NULL) ||
  323.       (cmcsb._cmwbp == NULL)
  324.      )
  325.     return(CMxBUFS);        /* no buffers set up yet */
  326.  
  327.   if (cmcsb._cmcmx == -1) {    /* haven't set CONSOLE max col position? */
  328.     ret = cmseti(stdin,stdout,stderr);    /* set up all the source attributes */
  329.     if (ret != CMxOK)
  330.       return(ret);        /* propagate problems */
  331.   }
  332.   cmxbol();            /* get to beginning of line */
  333.   cmxputs(p);            /* and issue prompt */
  334.   cmcsb._cmrty = p;        /* save pointer to prompt string */
  335.   cmcsb._cmflg &= ~(CM_ESC | CM_RPT | CM_PFE | CM_DRT | CM_CFM |
  336.             CM_SWT | CM_ACT | CM_PRS);
  337.                 /* set flags in a known state */
  338.   cmcsb._cmhst = cmcsb._cmptr+cmcsb._cminc; /* save history parse point */
  339.                 /* clear buffer */
  340.   cmcsb._cmcnt += cmcsb._cminc + (cmcsb._cmptr - cmcsb._cmbfp);
  341.   cmcsb._cmcur = cmcsb._cmptr = cmcsb._cmbfp;
  342.   cmcsb._cminc = 0;
  343.   cmcsb._cmerr = CMxOK;        /* clear last parse error */
  344.   return(CMxOK);        /* return success */
  345. }
  346.  
  347.  
  348.  
  349. /* parse
  350. **
  351. ** Purpose:
  352. **   Attempt to parse a single field of a command line, using a list
  353. **   of alternate FDB's describing the possible field contents.  Gather
  354. **   more input if required, and perform  actions as required for action
  355. **   characters, and defaulting as appropriate.
  356. **
  357. ** Input arguments:
  358. **   fdblist - A pointer to the first FDB in a list of alternative FDB's
  359. **     to be used for the parse.
  360. **
  361. ** Output arguments:
  362. **   value - A pointer to a pval item which will be filled in with the
  363. **     value resulting from a successful parse, as appropriate for the 
  364. **     type of field parsed.  (A pointer to an existing pval item must
  365. **     be passed.  No item will be allocated by the ccmd package.  Rather,
  366. **     the passed item will be filled in by ccmd.)
  367. **   usedfdb - A pointer to the FDB that succeeded in a successful parse.
  368. **
  369. ** Returns: Standard return code (CMxOK for good parse)
  370. **/
  371.  
  372. int
  373. parse(fdblist,value,usedfdb)
  374. fdb *fdblist;
  375. pval *value;
  376. fdb **usedfdb;
  377. {
  378.   int ret;            /* return codes from aux routines */
  379.   char brk;            /* break character from command input */
  380.   brktab *btab;            /* break table for current field */
  381.   fdb *f;            /* for stepping through chain */
  382.   int clean = !(cmcsb._cmflg & CM_DRT); /* if buffer is clean */
  383.  
  384.   ret = CMxOK;            /* assume everything will be fine */
  385.   if (cmcsb._cmrty == NULL)
  386.     ret = CMxPMT;        /* can't do this until prompt issued */
  387.  
  388.   else if (fdblist == NULL)    /* Check for empty list of alternatives */
  389.     ret = CMxNFDB;
  390.   else 
  391.     for (f = fdblist; f != NULL; f = f->_cmlst)
  392.       if (badfnc(f->_cmfnc))    /* Check each FDB's function code */
  393.         ret = CMxUNKF;
  394.  
  395.   if (ret == CMxOK)        /* OK so far? */
  396.     ret = getbrk(fdblist,&btab); /* find correct break table for this field */
  397.  
  398.   if (ret == CMxOK)        /* still OK? */
  399.     do {            /* try parsing until success or error */
  400.       cmcsb._cmflg &= ~CM_ESC;
  401.       skipws(btab);        /* skip whitespace */
  402.       ret = checkcfm(fdblist,btab); /* stuff default string if needed */
  403.       if (ret != CMxOK)
  404.     return(ret);        /* propagate errors */
  405.       skipws(btab);        /* skip any space that got stuffed */
  406.       if (cmcsb._cmcur > cmcsb._cmptr) {
  407.       int temp = *cmcsb._cmptr & 0x7f;
  408.       if (BREAK(btab,temp,cmcsb._cminc)) {
  409.           if (*cmcsb._cmptr & CC_ACT) {
  410.           cmcsb._cmflg |= CM_ACT;
  411.           ret = checkbrk(fdblist,btab,(char) *cmcsb._cmptr, 
  412.                      *cmcsb._cmptr);
  413.           if (ret == CMxGO)
  414.               ret = checkact(fdblist,btab,*cmcsb._cmptr);
  415.           }
  416.           }
  417.       }
  418.       
  419.       if (!clean) {
  420.       ret = tryparse(fdblist,value,usedfdb); /* attempt a parse */
  421.       if (ret == CMxOK)
  422.           cmcsb._cmflg |= CM_PRS; /* something parsed now */
  423.             
  424.       if (ret != CMxINC)    /* keep going if more input needed */
  425.           break;        /* anything else... exit loop and handle */
  426.       cmcsb._cmifd = *usedfdb; /* save pointer to FDB that gave CMxINC */
  427.       ret = checkact(fdblist,btab,0);
  428.       if (ret == CMxGO)    /* if action wants wakeup... */
  429.           continue;        /*  then back to top of this loop */
  430.       }
  431.       else {
  432.       cmcsb._cmifd = NULL;    /* XXX */
  433.       ret = CMxOK;
  434.       }
  435.  
  436.       while (ret == CMxOK) {    /* if action ok, loop until wakeup or error */
  437.     ret = fill(btab,&brk);    /* get more input from user */
  438.     clean = FALSE;
  439.     if (cmcsb._cmptr + cmcsb._cminc > cmcsb._cmbfp)
  440.         cmcsb._cmflg |= CM_DRT;
  441.     if (ret == CMxEOF) {
  442.         if (checkeof(fdblist)) {
  443.         cmcsb._cmflg2 |= CM_EOF;
  444.         ret =  CMxGO;
  445.         }
  446.     }
  447.     if (ret != CMxOK)    /* problems with filling... */
  448.       break;        /*  then exit loops to handle */
  449.                 /* handle break character */
  450.     ret = checkbrk(fdblist,btab,brk,0);
  451.       }
  452.     } while (ret == CMxGO);    /* continue loop only if wakeup requested */
  453.  
  454.   /* We get here after a successful parse, or a reparse, or an error */
  455.  
  456.   if (ret == CMxRPT) {        /* reparse needed? */
  457.     cmcsb._cminc += (cmcsb._cmptr - cmcsb._cmbfp); /* all chars unparsed */
  458.     cmcsb._cmptr = cmcsb._cmbfp;
  459.     cmcsb._cmflg &= ~CM_ESC;
  460.     cmcsb._cmflg &= ~CM_CMT;    /* no longer inside a comment */
  461.     cmcsb._cmflg |= CM_RPT;    /* flag the condition */
  462.     cmcsb._cmflg &= ~CM_PRS;    /* back to nothing parsed */
  463.     if (cmcsb._cmrph != NULL)    /* reparse handler available? */
  464.       ret = (*cmcsb._cmrph)();    /* then call it */
  465.   }
  466.   if ((ret != CMxOK) && (cmcsb._cmerh != NULL)) { /* handlable error? */
  467.     fdb *f;
  468.     
  469.     remember();
  470.     cmcsb._cmerr = ret;        /* save the error */
  471.     for(f = fdblist; f ; f = f->_cmlst) /* check if there is a specialized */
  472.       if (f->_cmest) {            /* error string. */
  473.                     /* and invoke the handler */
  474.     ret = (*cmcsb._cmerh)(ret, f->_cmest,f->_cmffl);
  475.     break;
  476.       }
  477.     ret = (*cmcsb._cmerh)(ret, NULL, 0); /* and invoke the handler */
  478.   }
  479.   /* error without handler, or handler failed, or good parse gets here */
  480.   if (ret != CMxOK)        /* save parse error code in CSB */
  481.     cmcsb._cmerr = ret;
  482.   return(ret);
  483. }
  484.  
  485.  
  486.  
  487. /* getbrk
  488. **
  489. ** Purpose:
  490. **   Locate and return the correct break table to be used in parsing a 
  491. **   field.  If a user-supplied break table is encountered in any of the
  492. **   supplied FDB's, that is returned.  Otherwise, the default break table
  493. **   for the first FDB is returned.
  494. **
  495. ** Input arguments:
  496. **   fdblist - A pointer to the first fdb in the chain of alternates.
  497. **
  498. ** Output arguments:
  499. **   btab - A pointer to the break table to be used.
  500. **
  501. ** Returns: Standard return code (CMxOK always).
  502. **/
  503.  
  504. static int
  505. getbrk(fdb *fdblist, brktab **btab)
  506. {
  507.   *btab = cmfntb[fdblist->_cmfnc-1]->_ftbrk; /* std tbl until overruled */
  508.   while (fdblist != NULL)     /* search through given FDB's */
  509.     if (fdblist->_cmbrk != NULL) { /* user supplied table? */
  510.       *btab = fdblist->_cmbrk;    /* yup, overrule standard table */
  511.       break;            /* and stop looking */
  512.     }
  513.     else
  514.       fdblist = fdblist->_cmlst; /* otherwise move on down the list */
  515.  
  516.   return(CMxOK);        /* That's all... */
  517. }
  518.  
  519.  
  520.  
  521. /* checkcfm
  522. **
  523. ** Purpose:
  524. **   Checks to see whether the current field needs a default value
  525. **   filled in by reason that the command line has been confirmed.
  526. **   If so, the default is stuffed quietly into the command buffer.
  527. **   To qualify, the CM_CFM flag must be on in the CSB, and the command
  528. **   line buffer must contain exactly one unparsed character, that
  529. **   matching the character stored in _cmbkc of the CSB, the character
  530. **   that caused the confirm in the first place.  In addition, the
  531. **   current chain of FDB's must not contain one whose handler has
  532. **   the FN_DFX default-blocking flag turned on.  If a field meets
  533. **   these criteria but the confirming character is not considered
  534. **   a break character for this field, then the CM_CFM flag is turned
  535. **   off and no default is stuffed.  
  536. **
  537.  
  538. **   The following no longer applies...
  539. **   As a special case, if all criteria
  540. **   for defaulting are met, but the CM_PRS flag is off in the CSB,
  541. **   implying that only whitespace preceded the confirmation, the entire
  542. **   line buffer will be discarded and the prompt reissued, effectively
  543. **   restarting the parse from the top.
  544.  
  545. **
  546. ** Input arguments:
  547. **   fdblist - A pointer to the first FDB in the chain of alternates.
  548. **   btab - A pointer to the break table to be used for this field.
  549. **
  550. ** Output arguments: None
  551. ** Returns: CMxOK for all normal returns, anything else is an error.
  552. **/
  553.  
  554. static int
  555. checkcfm(fdb *fdblist, brktab *btab)
  556. {
  557.   int ret = CMxOK;        /* assume everything will be OK */
  558.   int blocked = FALSE;        /* assume not blocked */
  559.   int cc;            /* next unparsed char in buffer */
  560.   char c;
  561.   fdb *f;            /* for stepping through FDB chain */
  562.   
  563.   for (f = fdblist; f != NULL; f = f->_cmlst)
  564.     if (cmfntb[f->_cmfnc-1]->_ftflg & FT_DFX) {
  565.       blocked = TRUE;        /* blocked */
  566.       break;            /* stop looking */
  567.     }
  568.  
  569.   c = (cc = *cmcsb._cmptr) & CC_CHR; /* get first unparsed char */
  570.   if (!blocked &&        /* if we're not blocked */
  571.       (cmcsb._cmflg & CM_CFM) && /* and flag is on */
  572.       (cmcsb._cminc == 1) &&    /* and exactly one char in buffer */
  573.       (c == cmcsb._cmbkc)    /* and it's the confirming char */
  574.      ) 
  575.     if (BREAK1(btab,c)) {    /* confirming char still break? */
  576.       char *defstr = NULL;
  577.       for (f = fdblist; f != NULL; f= f->_cmlst)
  578.     if (f->_cmdef) {
  579.       defstr = f->_cmdef;
  580.       break;
  581.     }
  582.                 /* nothing yet parsed? and no default? */
  583.       if ((cmcsb._cmflg & CM_PRS) == 0 && defstr == NULL) {
  584.     cmcsb._cmcnt += cmcsb._cminc + (cmcsb._cmptr - cmcsb._cmbfp);
  585.     cmcsb._cminc = 0;    /* right, empty the buffer */
  586.     cmcsb._cmcur = cmcsb._cmptr = cmcsb._cmbfp;
  587.     cmcsb._cmflg &= ~CM_CFM; /* buffer no longer confirmed */
  588.     cmxbol();        /* and reissue prompt on terminal */
  589.     cmxputs(cmcsb._cmrty);
  590.     return(CMxOK);        /* now go get more input */
  591.       }
  592.       cmcsb._cminc = 0;        /* remove the unparsed confirm char */
  593.       cmcsb._cmcur--;            /* XXXX */
  594.       cmcsb._cmcnt++;
  595.       ret = cmsti1(SPACE,CC_HID); /* hidden separator */
  596.       if (ret == CMxOK)
  597.         ret = cmsti(defstr,CC_HID); /* stuff default, hidden */
  598.       if (ret == CMxOK)
  599.     ret = cmsti1(c,CC_NEC); /* and put back confirm char */
  600.       if (ret == CMxOK)
  601.     *(cmcsb._cmcur) = cc; /* and restore flags */
  602.     }
  603.     else            /* confirm char no longer breaks */
  604.       cmcsb._cmflg &= ~CM_CFM;    /* so turn off confirm flag */
  605.  
  606.  
  607.   return(ret);            /* CMxOK, unless error with stuffing */
  608. }
  609.  
  610.  
  611.  
  612. /* checkact
  613. **
  614. ** Purpose:
  615. **   This routine is invoked when an attempt to parse a field has
  616. **   returned code CMxINC (incomplete parse -- more data required).
  617. **   If there is a deferred action waiting to be invoked, the break
  618. **   character that calls for the action is first checked to make
  619. **   sure it is still considered a break character in the current
  620. **   parsing context.  If not, the character is appended to the
  621. **   command buffer, and the CM_ACT flag is cleared.  Otherwise,
  622. **   the indicated action is invoked.
  623. **
  624. ** Input arguments:
  625. **   fdblist - A pointer to the first FDB on the chain of alternates.
  626. **   btab - A pointer to the break table currently in effect.
  627. **
  628. ** Output arguments: None
  629. **
  630. ** Returns: CMxGO if an action was invoked and indicated that a wakeup
  631. **   should be performed, CMxOK if no action pending or invoked action
  632. **   did not request wakeup, or other standard return code on error.
  633. **/
  634.  
  635. static int
  636. checkact(fdb *fdblist, brktab *btab, int flags)
  637. {
  638.   int ret;
  639.   
  640.   if (cmcsb._cmflg & CM_ACT) {    /* action pending? */
  641.     if (BREAK(btab,cmcsb._cmbkc,cmcsb._cminc)) { /* still a break char? */
  642.       if (cmcsb._cmact[cmcsb._cmbkc]) {
  643.     ret = (*cmcsb._cmact[cmcsb._cmbkc])(fdblist,cmcsb._cmbkc,TRUE,flags);
  644.                 /* yes, invoke routine in deferred mode */
  645.       }
  646.       else ret = CMxOK;
  647.     }
  648.     else
  649.       ret = cmsti1(cmcsb._cmbkc,0); /* deposit non-break char */
  650.  
  651.     cmcsb._cmflg &= ~CM_ACT;    /* turn off action flag either way */
  652.     return(ret);        /* and return result */
  653.   }
  654.   else
  655.     return(CMxOK);        /* no action pending => no error */
  656. }
  657.  
  658.  
  659.  
  660.  
  661. /* checkbrk
  662. **
  663. ** Purpose:
  664. **   This routine is invoked after some input has been successfully
  665. **   collected for the command line.  The break character is checked
  666. **   to see whether it is an action character, and if so, the action
  667. **   is invoked.  If the return code indicates that the action should
  668. **   be deferred, the CSB is updated accordingly.
  669. **
  670. ** Input arguments:
  671. **   fdblist - A pointer to the chain of alternate FDB's for the current
  672. **     field.
  673. **   btab - A pointer to the break table currently in effect.
  674. **   brk - The character that broke the input process.
  675. **
  676. ** Output arguments: None
  677. **
  678. ** Returns: CMxGO if a wakeup should be performed, CMxOK if more
  679. **   input should be collected, or a standard error code.
  680. **/
  681.  
  682. static int
  683. checkbrk(fdb *fdblist, brktab *btab, char brk, int flags)
  684. {
  685.   int ret;
  686.   int (*act)();            /* pointer to action routine */
  687.   
  688.   if ((act = cmcsb._cmact[brk]) != NULL) { /* is there an action routine? */
  689.     ret = (*act)(fdblist,brk,FALSE,flags); /* invoke it non-deferred */
  690.     if (ret == CMxDFR) {    /* action wants to defer? */
  691.       cmcsb._cmflg |= CM_ACT;    /* set the flag */
  692.       cmcsb._cmbkc = brk;    /* save the action char */
  693.       ret = CMxGO;        /* and request a wakeup */
  694.     }
  695.   }
  696.   else {            /* No action... */
  697.     ret = cmsti1(brk,0);    /* deposit brk char into command buffer */
  698.     if (ret == CMxOK)
  699.       if (cmcsb._cmflg & CM_WKF) /* waking on every field? */
  700.     ret = CMxGO;        /* yup, set return code to request wakeup */
  701.   }
  702.   return(ret);            /* back to caller... */
  703. }
  704.  
  705.  
  706.  
  707. /* tryparse
  708. **
  709. ** Purpose:
  710. **   Invokes the parsing functions specified by a chain of FDB's, passing
  711. **   the current unparsed command input for them to parse.  Each function
  712. **   either fails or not, depending on whether it is able to parse the
  713. **   current input.  In the case of failure, a standard error code is
  714. **   returned.  Otherwise, either CMxOK or CMxINC is returned.  The former
  715. **   indicates that a complete parse was performed, generally indicating
  716. **   the presence of a suitable break character in addition to the field
  717. **   contents.  CMxINC indicates that the current input is not sufficient
  718. **   for a successful parse, but further input may result in a successful
  719. **   parse.
  720. **
  721. **   If all functions fail, the code returned by the first is given back
  722. **   to the caller.  Otherwise, the first CMxOK or CMxINC code returned
  723. **   by a parsing function is passed on, without invoking the rest of the
  724. **   parsing functions.  In the case of success, the CSB is adjusted so
  725. **   as to consume the newly parsed characters, and any value returned
  726. **   by the parse is passed on to the caller, as well as a pointer to the
  727. **   succeeding FDB.  In addition, on a successful parse, the parsed
  728. **   characters are copied into the atom buffer.  On anything other than
  729. **   success, the CM_PFE flag is turned off in the CSB in order to prevent
  730. **   a following noise word from expanding, in case this field got completion.
  731. **   When CMxINC is returned, a pointer to the FDB that gave an incomplete
  732. **   parse is returned to the user.
  733. **
  734. ** Input arguments:
  735. **   fdblist - A pointer to the first FDB in the list of parsing alternatives.
  736. **
  737. ** Output arguments
  738. **   value - A pointer to a pval item which will be filled in with the
  739. **     value resulting from a successful parse, as appropriate for the 
  740. **     type of field parsed.  (A pointer to an existing pval item must
  741. **     be passed.  No item will be allocated by the ccmd package.  Rather,
  742. **     the passed item will be filled in by ccmd.)
  743. **   usedfdb - A pointer to the FDB that succeeded in a successful parse.
  744. **
  745. ** Returns: standard return code.
  746. **/
  747.  
  748. int
  749. tryparse(fdblist,value,usedfdb)
  750. fdb *fdblist;
  751. pval *value;
  752. fdb **usedfdb;
  753. {
  754.   int ret;            /* return codes from parsers */
  755.   int failret = CMxOK;        /* return code of first failing fdb */
  756.   int inputlen;            /* number of chars available for parse */
  757.   int parselen;            /* number of chars successfully parsed */
  758.   ftspec *ft;            /* function handler structure */
  759.  
  760.   ret = cmprep(cmcsb._cmwbp,cmcsb._cmwbc,&inputlen); /* clean up input */
  761.   if (ret != CMxOK)
  762.     return(ret);        /* propagate errors */
  763.   while (fdblist != NULL) {    /* loop through chain of FDB's */
  764.     ft = cmfntb[fdblist->_cmfnc-1]; /* get correct handler structure */
  765.                 /* and call the parser */
  766.     while (TRUE) {        /* loop til handler is finished */
  767.       cmcsb._cmflg &= ~CM_NAC;    /* assume ok to copy good parse to atom */
  768.       ret = (*ft->_ftprs)(cmcsb._cmwbp,inputlen,fdblist,&parselen,value);
  769.       if (ret != CMxAGN)    /* exit if handler did not ask for retry */
  770.     break;
  771.       ret = cmprep(cmcsb._cmwbp,cmcsb._cmwbc,&inputlen); /* rebuild input */
  772.       if (ret != CMxOK)
  773.     return(ret);        /* propagate failure */
  774.     }
  775.     if (ret == CMxOK) {        /* successful parse? */
  776.       *usedfdb = fdblist;    /* save ptr to successful FDB */
  777.       if ((cmcsb._cmflg & CM_NAC) == 0)
  778.         ret = toatom(cmcsb._cmwbp,parselen); /* parsed text to atom bfr */
  779.                  /* adjust cnt for skipped chars */
  780.       parselen = skipadj(cmcsb._cmptr,cmcsb._cminc,parselen);
  781.       cmcsb._cmptr += parselen;    /* update buffer pointers */
  782.       cmcsb._cminc -= parselen;    /*  to account for newly parsed text */
  783.       return(CMxOK);        /* and return success */
  784.     }
  785.     else if (ret == CMxINC) {    /* incomplete parse? */
  786.       *usedfdb = fdblist;    /* give back pointer to this FDB */
  787.       cmcsb._cmflg &= ~CM_PFE;    /* completion did not succeed */
  788.       return(CMxINC);        /* pass incomplete code along */
  789.     }
  790.     else {            /* unsuccessful parse */
  791.       if (failret == CMxOK)    /* first one? */
  792.     failret = ret;        /* yup, save first failure code */
  793.       fdblist = fdblist->_cmlst; /* and move on to next FDB */
  794.     }
  795.   }
  796.   cmcsb._cmflg &= ~CM_PFE;    /* all failed... no noise words */
  797.   return(failret);        /* and return first failure code */
  798. }
  799.  
  800.  
  801.  
  802. /* fill
  803. **
  804. ** Purpose:
  805. **   Accepts characters from the command input source and loads them
  806. **   into the command buffer until a break character or action
  807. **   character is encountered or an error condition occurs.  If
  808. **   a break character terminates the fill operation, the character
  809. **   is returned to the caller via the 'brk' argument, and a return
  810. **   code of CMxOK is given.  If an action character that is not
  811. **   currently a break character is encountered, a return is made
  812. **   with return code CMxGO, indicating that a wakeup should occur,
  813. **   but the 'brk' argument is not filled in, nor is the action
  814. **   character stuffed into the command buffer.  Instead, it is
  815. **   held until the next call to fill, at which time, if it is
  816. **   still not a break character, it is simply stuffed into the
  817. **   buffer like any non-break character.  If by the next call
  818. **   to fill, the break table has changed so that the action character
  819. **   breaks, a normal break-character return will be made to the
  820. **   caller, with 'brk' containing the action character.  The
  821. **   purpose of all these contortions is to ensure that action
  822. **   characters that are turned off for a field will be reactivated
  823. **   when the user finishes typing that field.  The reactivation
  824. **   requires a wakeup, to allow the user program to proceed to
  825. **   following parse fields.
  826. **
  827. **   As a side-effect of this call, CM_PFE flag is turned off in
  828. **   the CSB, in case the current field got completion and then 
  829. **   came up with an incomplete parse anyway.
  830. **
  831. ** Input arguments:
  832. **   btab - A pointer to the break table in effect.
  833. **
  834. ** Output arguments:
  835. **   brk - The character that broke the input.
  836. **
  837. ** Returns: Standard return code.
  838. **/
  839.  
  840. static int
  841. fill(brktab *btab, char *brk)
  842. {
  843.   static int heldact = -1;    /* non-breaking action char from prior call */
  844.   int ret;            /* return codes */
  845.   int c;            /* input character */
  846.   int indirend = FALSE;
  847.   int cmgetc();
  848.   
  849.   cmcsb._cmflg &= ~CM_PFE;    /* no noise word expansion */
  850.   while (cmcsb._cmcnt > 0) {    /* quit when we overflow the buffer */
  851.     if (heldact != -1)
  852.       c = heldact;        /* pick up held character */
  853.     else {
  854.       ret = cmgetc(&c,cmcsb._cmij);    /* or get a new character */
  855.       if (ret != CMxOK) {
  856.     if (ret == CMxEOF && cmcsb._cmblh) { /* nonblocking and a handler? */
  857.         ret = (*cmcsb._cmblh)(ret);    /* invoke the handler */
  858.     }
  859.     if (ret == CMxEOF && cmcsb._cmflg2 & CM_IND) {
  860.         cmindend();
  861.         cmcsb._cmflg |= CM_CFM;
  862.         cmsti1('\n',CC_NEC|CC_HID);
  863.         cmcsb._cmcol = 0;
  864.         return(CMxGO);
  865.     }
  866.     else 
  867.           return(ret);        /* propagate errors */
  868.       }
  869.     }
  870.     if ((cmcsb._cminc == 0) && /* skip white space at beginning of line */
  871.     ((c == SPACE) || (c == TAB)) && /* space or tab? */
  872.     BREAK1(btab,c) &&    /* and a break character for this field? */
  873.     (cmcsb._cmact[c] == NULL) /* and not an action char? */
  874.        ) {
  875.       ret = cmsti1(c,0);    /* add char to buffer and echo */
  876.       heldact = -1;        /* if this was held, it's not anymore */
  877.       if (ret != CMxOK)
  878.     return(ret);        /* propagate error */
  879.       continue;            /* and go get more characters */
  880.     }
  881.     else if (BREAK(btab,c,cmcsb._cminc)) { /* break character? */
  882.       *brk = c;            /* yup, pass it back to caller */
  883.       heldact = -1;        /* not held anymore */
  884.       return(CMxOK);        /* and return success */
  885.     }
  886.     else if ((heldact == -1) && /* this one was not held */
  887.          (cmcsb._cmact[c] != NULL) /* and it's a non-breaking action? */
  888.          ) {
  889.       heldact = c;        /* yup, remember it */
  890.       return(CMxGO);        /* and ask for a wakeup */
  891.     }
  892.     else if (c & CM_ACT) {
  893.       heldact = c & 0xff;
  894.       return(CMxGO);
  895.     }
  896.     else {            /* not skipped ws, not break or action */
  897.       ret = cmsti1(c,0);    /* add char to buffer and echo */
  898.       heldact = -1;        /* we're not holding it */
  899.       if (ret != CMxOK)
  900.     return(ret);        /* propagate problems */
  901.     }
  902.   }
  903.  
  904.   /* Dropped out of loop -- buffer must have overflowed */
  905.   return(CMxBOVF);        
  906. }
  907.  
  908.  
  909.  
  910. /*
  911.  * this also skips comments now 
  912.  */
  913.  
  914. /* skipws
  915. **
  916. ** Purpose:
  917. **   Skip past any spaces and tabs (white space) at the current parse
  918. **   position in the command buffer, unless they are not considered
  919. **   break characters in the current parse field.  Also, all characters
  920. **   with the CC_SKP flag and eligible characters with CC_CSK flag
  921. **   are skipped.  (As a side effect, ineligible CC_CSK flags are
  922. **   cleared.)
  923. **
  924. ** Input arguments:
  925. **   btab - A pointer to the current break table.
  926. **
  927. ** Output arguments: None.
  928. ** Returns: Nothing
  929. **/
  930.  
  931. #define CBEG cmcsb._cmntb
  932. #define CST cmcsb._cmnts
  933. #define CEND cmcsb._cmnte
  934. #define NOCOMMENT 0
  935. #define DELIMITED 1
  936. #define TOEOL 2
  937.  
  938. skipws(btab)
  939. brktab *btab;
  940. {
  941.   int cc;            /* characters from input buffer */
  942.   char c;
  943.   int cskip = 0;        /* number of chars in run of CC_CSK chars */
  944.   int cbeglen, cstlen, cendlen;
  945.   static int commenttype = NOCOMMENT;
  946.  
  947.   cendlen= CEND ? strlen(CEND) : 0;
  948.   cstlen= CST ? strlen(CST) : 0;
  949.   cbeglen= CBEG ? strlen(CBEG) : 0;
  950.  
  951.   if (!(cmcsb._cmflg & CM_CMT))
  952.     commenttype = NOCOMMENT;
  953.  
  954.   while (TRUE) {        /* at most twice, if input ends with */
  955.                 /*  a run of ineligible conditional skips */
  956.                 /* loop until no more unparsed data */
  957.  
  958.     while (cmcsb._cmcur - cmcsb._cmptr > cskip) {
  959.       c = (cc = *(cmcsb._cmptr + cskip)) & CC_QCH; /* get next char */
  960.       if (cc & CC_SKP) {    /* unconditional skip char? */
  961.     cmcsb._cmptr += cskip+1; /* yup, skip conditional skips too */
  962.     cmcsb._cminc -= cskip+1;
  963.     cskip = 0;        /* the run is over */
  964.       }
  965.       else if (cc & CC_CSK)    /* conditional skip char? */
  966.     cskip++;        /* yup, count it */
  967.       else {            /* no type of skip flag */
  968.     if (cskip > 0) {
  969.       while (cskip-- > 0)    /* turn off ineligible conditional skips */
  970.         cmcsb._cmptr[cskip] &= ~CC_CSK;
  971.       continue;        /* and rescan those characters */
  972.     }
  973.     if (((c == SPACE) || (c == TAB)) && BREAK1(btab,c)) {
  974.       cmcsb._cmptr++;    /* skip nonbreaking whitespace */
  975.       cmcsb._cminc--;
  976.     }
  977.     else if (BREAK1(btab,c)) { 
  978.       int i,j;
  979.       if (!(cmcsb._cmflg & CM_CMT)) {
  980.         if (commenttype == NOCOMMENT)
  981.           if (cbeglen > 0 && !xstrncmp(cmcsb._cmptr,CBEG,cbeglen)) {
  982.         commenttype = TOEOL;
  983.         cmcsb._cmflg |= CM_CMT;
  984.         cmcsb._cmptr += cbeglen;
  985.         cmcsb._cminc -= cbeglen;
  986.           }
  987.           else if (cstlen > 0 && !xstrncmp(cmcsb._cmptr,CST,cstlen)) {
  988.         commenttype = DELIMITED;
  989.         cmcsb._cmflg |= CM_CMT;
  990.         cmcsb._cmptr += cstlen;
  991.         cmcsb._cminc -= cstlen;
  992.           }
  993.           else 
  994.         break;
  995.         }
  996.         if (cmcsb._cmflg & CM_CMT) {
  997.           if (commenttype == TOEOL) {
  998.         for (j = cmcsb._cminc,i = 0; i < j; i++) {
  999.           char c = *cmcsb._cmptr;
  1000.           if (cmcsb._cmflg & CM_CFM && cmcsb._cminc == 1) {
  1001.             cmcsb._cmflg &= ~CM_CMT;
  1002.           }
  1003.           else {
  1004.             cmcsb._cmptr++;
  1005.             cmcsb._cminc--;
  1006.           }
  1007.         }
  1008.         break;
  1009.           }
  1010.           else if (commenttype == DELIMITED) {
  1011.         for (j = cmcsb._cminc,i = 0; i < j; i++) {
  1012.           char c = *cmcsb._cmptr;
  1013.           if (cmcsb._cmflg & CM_CFM && cmcsb._cminc == 1) {
  1014.             cmcsb._cmflg &= ~CM_CMT;
  1015.             break;
  1016.           }
  1017.           else if (!xstrncmp(cmcsb._cmptr,CEND,cendlen) && 
  1018.                cmcsb._cminc >= cendlen || cendlen == 0) {
  1019.             cmcsb._cmflg &= ~CM_CMT;
  1020.             cmcsb._cmptr += cendlen;
  1021.             cmcsb._cminc -= cendlen;
  1022.             break;
  1023.           }
  1024.           else {
  1025.             cmcsb._cmptr++;
  1026.             cmcsb._cminc--;
  1027.           }
  1028.         }
  1029.         break;
  1030.           }
  1031.           if (cmcsb._cminc < 0) cmcsb._cminc = 0;
  1032.           return;
  1033.         }
  1034.     }
  1035.     else {
  1036.       if (cmcsb._cminc < 0) cmcsb._cminc = 0;
  1037.       return;        /* else found our break point */
  1038.     }
  1039.       }
  1040.     }
  1041.     if (cskip > 0) {        /* finished with an ineligible run? */
  1042.       while (cskip-- > 0)    /* turn off ineligible conditional skips */
  1043.     cmcsb._cmptr[cskip] &= ~CC_CSK;
  1044.       continue;            /* and rescan it */
  1045.     }
  1046.     else
  1047.       return;            /* skipped the whole buffer */
  1048.   }
  1049. }
  1050.  
  1051. static int
  1052. xstrncmp(int *s1, char *s2, int len)
  1053. {
  1054.   for (; len > 0; len--) {
  1055.     if ((*s1 & 0xff) < *s2) return (-1);
  1056.     if ((*s1 & 0xff) > *s2) return(1);
  1057.     s1++; s2++;
  1058.   }
  1059.   return(0);
  1060. }
  1061.  
  1062.  
  1063.  
  1064. /* toatom
  1065. **
  1066. ** Purpose:
  1067. **   Copy text into the atom buffer, stripping any CC_QUO flags that
  1068. **   might be encountered.
  1069. **
  1070. ** Input arguments:
  1071. **   text - A pointer to the text to be copied.
  1072. **   textlen - The number of characters to copy.
  1073. **
  1074. ** Output arguments: None.
  1075. ** Returns: Standard return code.
  1076. **/
  1077.  
  1078. static int
  1079. toatom(char *text, int textlen)
  1080. {
  1081.   int ret = CMxOK;            /* assume it will fit */
  1082.   char *abp;                /* pointer into atom buffer */
  1083.  
  1084.   if (textlen >= cmcsb._cmabc) {    /* too big? */
  1085.     ret = CMxAOVF;            /* yup, we will return this */
  1086.     textlen = cmcsb._cmabc-1;        /* and only copy this much */
  1087.   }
  1088.   abp = cmcsb._cmabp;            /* point to atom buffer */
  1089.   while (textlen-- > 0)
  1090.     *abp++ = (*text++) & CC_CHR;    /* copy the text */
  1091.   *abp = NULCHAR;            /* tie off with a null */
  1092.   return(ret);
  1093. }
  1094.  
  1095.  
  1096.  
  1097. /* skipadj
  1098. ** 
  1099. ** Purpose:
  1100. **   Given a number of characters to consume in a buffer, adjusts that
  1101. **   count to include any skipped that occur in the buffer.
  1102. **   The passed buffer is assumed to be the command buffer, composed of
  1103. **   characters with flag bytes.
  1104. **
  1105. ** Input arguments:
  1106. **   buf - A pointer to the characters to be counted.
  1107. **   bufsize - Number of characters in buffer, including skips.
  1108. **   passcnt - Number of characters to consume, not including skips.
  1109. **
  1110. ** Output arguments: None.
  1111. ** Returns: Number of characters to consume, including skips.
  1112. **/
  1113.  
  1114. static int
  1115. skipadj(int *buf, int bufsize, int passcnt)
  1116. {
  1117.   int adjusted= 0;        /* char count including skips */
  1118.  
  1119.   while (passcnt > 0) {        /* pass # of chars requested */
  1120.     if ((*buf & (CC_CSK | CC_SKP)) == 0) /* next char not skiped? */
  1121.       passcnt--;        /* no, indicate progress */
  1122.     bufsize--;            /* consume it in any case */
  1123.     buf++;
  1124.     adjusted ++;        /* and bump adjusted count */
  1125.   }
  1126.   return(adjusted);
  1127. }
  1128.  
  1129. extern char *malloc();
  1130. extern char *realloc();
  1131. /* realloc that does malloc if given NULL pointer */
  1132. char *cmrealloc(ptr, size)
  1133. char *ptr;
  1134. int size;
  1135. {
  1136.     return((ptr == NULL) ? malloc(size) : realloc(ptr, size));
  1137. }
  1138.  
  1139. /*
  1140.  * match a string as parse would user input.
  1141.  */
  1142. match(string, len, fdblist, value, usedfdb, parselen)
  1143. char *string;                /* input string */
  1144. int len;                /* length of input string */
  1145. fdb *fdblist;                /* list of fdb's to try parsing */
  1146. pval *value;                /* return value */
  1147. fdb **usedfdb;                /* which fdb got used */
  1148. int *parselen;                /* how much did we parse. */
  1149. {
  1150.   csb savecsb;                /* local csb value */
  1151.   char *atmbuf, *wrkbuf, *malloc();    /* local buffers */
  1152.   int *cmdbuf;
  1153.   int ret;                /* return codes from parsers */
  1154.   brktab* btab;                /* get current break tab */
  1155.   savecsb = cmcsb;            /* save current csb */
  1156.                     /* use our own buffers */
  1157.   atmbuf = malloc(len+1);
  1158.   wrkbuf = malloc(len+1);
  1159.   cmdbuf = (int *) malloc((len + 1) * sizeof(int));
  1160.  
  1161.   cmbufs(cmdbuf,len+1, atmbuf, len+1, wrkbuf, len+1);
  1162.   cmstin(string,len,CC_NEC);        /* STI in the string */
  1163.   ret = getbrk(fdblist,&btab); /* find correct break table for this field */
  1164.   skipws(btab);
  1165.   ret = tryparse(fdblist,value,usedfdb); /* try to parse it */
  1166.   if (ret != CMxOK) {
  1167.       cmsti1('\n',CC_NEC);        /* make it break...kinda kludgy */
  1168.       ret = getbrk(fdblist,&btab);    /* find correct break table for this
  1169.                        field */
  1170.       skipws(btab);
  1171.       ret = tryparse(fdblist,value,usedfdb); /* try to parse it */
  1172.   }
  1173.   *parselen = cmcsb._cmptr - cmcsb._cmbfp;
  1174.   cmcsb = savecsb;            /* restore original csb */
  1175.   free(atmbuf);
  1176.   free(wrkbuf);
  1177.   free(cmdbuf);
  1178.   return(ret);
  1179. }
  1180.  
  1181.  
  1182. /*
  1183.  * turn on/off command history
  1184.  */
  1185.  
  1186. /*
  1187.  * create a "ring" buffer for command history.  
  1188.  * maintain a NULL entry at the end of the ring.  We need to allocate one 
  1189.  * extra buffer to do this.
  1190.  * 
  1191.  * next points to the NULL entry.
  1192.  * current points to the entry to show when going thorugh the history.
  1193.  * len is the length of the entire buffer, including the hole.
  1194.  */
  1195.  
  1196. cmhst(n) {
  1197.     cmhist *h;
  1198.     int i;
  1199.     if (n > 0) {
  1200.     n++;                /* space for a "hole" */
  1201.                     /* no previous history, allocate. */
  1202.     if (cmcsb._cmhist == NULL || cmcsb._cmhist->len == 0) {
  1203.         h=cmcsb._cmhist=(cmhist *)cmrealloc(cmcsb._cmhist,sizeof(cmhist));
  1204.         h->current = 0;
  1205.         h->enabled = TRUE;
  1206.         h->next = 0;
  1207.         h->bufs = (cmhistbuf *)malloc(n * sizeof(cmhistbuf));
  1208.         bzero(h->bufs, n * sizeof(cmhistbuf));
  1209.         h->len = n;
  1210.         return;
  1211.     }
  1212.  
  1213.     h = cmcsb._cmhist;
  1214.  
  1215.     if (n < h->len) {        /* making history smaller */
  1216.         int start = (h->next + h->len - (n - 1)) % h->len;
  1217.         int cnt = 0;
  1218.         cmhistbuf *b = (cmhistbuf *)malloc(n*sizeof(cmhistbuf));
  1219.         bzero(b,n*sizeof(cmhistbuf));
  1220.         for(i = start; i < h->next; i = (i + 1) % h->len)
  1221.         if (h->bufs[i].buf) 
  1222.             break;
  1223.         else
  1224.             start++;
  1225.         for (i = start; i%h->len != h->next; i++) {
  1226.         b[i-start].buf = h->bufs[i%h->len].buf;
  1227.         b[i-start].len = h->bufs[i%h->len].len;
  1228.         h->bufs[i%h->len].buf = NULL;
  1229.         cnt++;
  1230.         }
  1231.         free_histbuf(h->bufs, h->len);
  1232.         h->bufs = b;
  1233.         h->next = h->current = cnt;
  1234.         for(i = cnt; i < n; i++) {
  1235.         b[i].buf = NULL;
  1236.         b[i].len = 0;
  1237.         }
  1238.     }
  1239.     else {                /* enlarging.   easy case */
  1240.         cmhistbuf *b =(cmhistbuf *)cmrealloc(h->bufs, n*sizeof(cmhistbuf));
  1241.         bzero(b,n*sizeof(cmhistbuf));
  1242.         if (h->len > 0) {
  1243.         int start = (h->next +h->len - 1) % h->len;
  1244.         int cnt = 0;
  1245.         
  1246.         for(i = start; h->bufs[i].buf != NULL;
  1247.             i = (i + h->len-1) % h->len)
  1248.             start = i;
  1249.         for(i = start; i%h->len != h->next; i++) {
  1250.             b[i-start].buf = h->bufs[i%h->len].buf;
  1251.             b[i-start].len = h->bufs[i%h->len].len;
  1252.             h->bufs[i%h->len].buf = NULL;
  1253.             cnt++;
  1254.         }
  1255.         free_histbuf(h->bufs, h->len);
  1256.         h->bufs = b;
  1257.         for(i = cnt; i < n; i++) {
  1258.             h->bufs[i].buf = NULL;
  1259.  
  1260.             h->bufs[i].len = 0;
  1261.         }
  1262.         h->next = h->current = cnt;
  1263.         }
  1264.         else {
  1265.         h->current = h->next = 0;
  1266.         h->enabled = TRUE;
  1267.         }
  1268.     }
  1269.     h->len = n;
  1270.     }
  1271.     else {
  1272.     if (h = cmcsb._cmhist) {
  1273.         if (h->bufs) free(h->bufs);
  1274.         h->bufs = NULL;
  1275.         h->len = h->current = h->next = 0;
  1276.     }
  1277.     else {
  1278.          h = cmcsb._cmhist = (cmhist *)malloc(sizeof(cmhist));
  1279.         h->bufs = NULL;
  1280.         h->len = h->current = h->next = 0;
  1281.     }
  1282.     }
  1283. }
  1284.  
  1285.  
  1286. cmhst_enable() {
  1287.     cmcsb._cmhist->enabled = TRUE;
  1288. }
  1289.  
  1290. cmhst_disable() {
  1291.     cmcsb._cmhist->enabled = FALSE;
  1292. }
  1293.  
  1294. free_histbuf(b,n)
  1295. cmhistbuf *b;
  1296. {
  1297.     int i;
  1298.     for(i = 0; i < n; i++)
  1299.     if (b[i].buf) {
  1300.         free(b[i].buf);
  1301.         b[i].buf = NULL;
  1302.     }
  1303. }
  1304.  
  1305. static int
  1306. checkeof(fdb *fdblist)
  1307. {
  1308.     fdb *f;
  1309.  
  1310.     for(f = fdblist; f ; f = f->_cmlst)
  1311.     if (f->_cmffl & CM_NEOF)
  1312.         return(TRUE);
  1313.     return(FALSE);
  1314. }
  1315.