home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mm / ccmd / cmswi.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-18  |  14.7 KB  |  411 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.  Author: Andrew Lowry
  8. */
  9. /* cmswi
  10. **
  11. ** Code to parse switches.  Parsing succeeds if current input
  12. ** uniquely matches a non-ignored switch in the supplied switch
  13. ** table.  Global variable 'cmswbeg' contains the character that
  14. ** must precede the switch text, and global variable 'cmswend'
  15. ** contains a character that may be used to terminate a switch.
  16. ** The initial values for these variables are "/" for cmswbeg, and
  17. ** ":" for cmswend.
  18. **
  19. ** A switch must begin with the character in cmswbeg.  Subsequent
  20. ** text up to but not including the cmswend character or a break
  21. ** character (according to the break table in effect) is then matched
  22. ** against the supplied switch table.  If a unique match results,
  23. ** the parse value is the _swval field of the matching switch.
  24. ** If the switch was terminated by the cmswend character, that
  25. ** character is consumed by the parse, and the CM_SWT flag is set
  26. ** in the CSB.  Otherwise, the flag is cleared, and the delimiter
  27. ** character is not consumed by the parse.  The cmswend character is
  28. ** allowed, however, only on switches for which the character actually 
  29. ** appears in the switch table.
  30. **
  31. ** For completion, if the current input uniquely matches a single switch,
  32. ** that switch is completed, with the cmswend character if it is present
  33. ** on the switch in the switch table.  Partial completion completes
  34. ** only up to punctuation.  Full completion adds a space and wakeup
  35. ** as well.
  36. **
  37. ** Standard help prints a list of all visible switches that might match
  38. ** the current input.  The cmswbeg character is prepended to each switch
  39. ** in the listing, and the cmswend character will appear on those switches
  40. ** in which it appears in the supplied switch table.
  41. **
  42. ** The break table specifies what characters are allowed in the interior
  43. ** of switches (that is, everything except the cmswbeg character at the
  44. ** beginning and the cmswend character, if present, at the end).  Note
  45. ** that any white space following the cmswbeg character is considered
  46. ** part of the switch text, so whitespace will generally kill the parse.
  47. **/
  48.  
  49. #define    SWIERR                /* switch error tbl allocated here */
  50.  
  51. #include "ccmdlib.h"            /* get standard symbols */
  52. #include "cmfncs.h"        /* and internal symbols */
  53.  
  54. /* Forward declaration of handler routines */
  55.  
  56. int swiprs(), swihlp(), swicplt();
  57.         
  58. static brktab swibrk = {                /* standard break table */
  59.   {                    /* 1st char break array */
  60.                     /* all but letters, digits, hyphen */
  61.     0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x00, 0x3f, 
  62.     0x80, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x1f
  63.   },
  64.   {                    /* subsequent char break array */
  65.                     /* same as above */
  66.     0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x00, 0x3f, 
  67.     0x80, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x1f
  68.   }
  69. };
  70.  
  71. ftspec ft_swi = { swiprs, swihlp, swicplt, 0, &swibrk }; /* handler structure */
  72.  
  73. char cmswbeg = '/';        /* standard punctuation to start switch */
  74. char cmswend = ':';        /* standard switch terminator */
  75.  
  76.  
  77.  
  78.  
  79. /* swiprs - Check that the first character in the input matches the cmswbeg
  80. ** character.  Then find matching switches, and succeed if there is only
  81. ** one.
  82. **/
  83.  
  84. PASSEDSTATIC int
  85. swiprs(text,textlen,fdbp,parselen,value)
  86. char *text;
  87. int textlen,*parselen;
  88. fdb *fdbp;
  89. pval *value;
  90. {
  91.   int mcount;                /* number of matches */
  92.   swtch *mat;                /* the matching switch */
  93.   char *term;                /* char terminating the switch */
  94.   switab *st;                /* the switch table */
  95.   
  96.   if (textlen == 0)
  97.     return(CMxINC);            /* nothing to parse */
  98.   if ((*text & CC_CHR) != cmswbeg) 
  99.     return(SWIxBEG);            /* beginning char doesn't match */
  100.   text++;                /* skip past opening char */
  101.   textlen--;
  102.  
  103.   mcount = match(text,textlen,fdbp,&mat,&term);    /* find matching switches */
  104.   if (mcount == 0)
  105.     return(SWIxNM);            /* no match */
  106.   else if (term == NULL)        /* not terminated? */
  107.     return(CMxINC);            /* ambiguous parse */
  108.   else if (mcount > 1)
  109.     return(SWIxAMB);            /* ambiguous */
  110.  
  111.   st = (switab *) fdbp->_cmdat;        /* unique - get table address */
  112.   while ((mat)->_swflg & SWI_ABR)    /* track down abbreviations */
  113.     if (((mat)->_swval >= st->_stcnt) || /* abbrev ptr out of bounds? */
  114.     ((mat)->_swval < 0)
  115.        )
  116.       return(SWIxABR);            /* bad abbrev chain */
  117.     else
  118.       mat = &(st->_stwds[mat->_swval]);    /* move down chain */
  119.  
  120.   if ((*term & CC_CHR) == cmswend) {    /* terminated with cmswend? */
  121.     if (mat->_swswi[strlen(mat->_swswi)-1] != cmswend) /* but not in tbl */
  122.       return(SWIxEND);            /* not allowed */
  123.     term++;                /* consume special terminator */
  124.     cmcsb._cmflg |= CM_SWT;        /* and flag that it was present */
  125.   }
  126.   else
  127.     cmcsb._cmflg &= ~CM_SWT;        /* else clear the flag */
  128.  
  129.   *parselen = (term - text) + 1;    /* set # of consumed chars */
  130.                     /*  (including opening punctuation) */
  131.   value->_pvkey = mat->_swval;        /* set return value */
  132.   return(CMxOK);            /* and return success */
  133. }
  134.  
  135.  
  136.  
  137. /* swicplt - If the current input is ambiguous, we beep.  Otherwise
  138. ** we complete with the remainder of the identified switch.  In the
  139. ** latter case, full completion adds a space and a wakeup, and partial
  140. ** completion stops at punctuation.  If the cmswend character is present
  141. ** in the identified switch, it is included in the completion text.
  142. **/
  143.  
  144. PASSEDSTATIC int
  145. swicplt(text,textlen,fdbp,full,cplt,cpltlen)
  146. char *text,**cplt;
  147. int textlen,full,*cpltlen;
  148. fdb *fdbp;
  149. {
  150.   int mcount;                /* number of matches */
  151.   swtch *mat;                /* the matching switch */
  152.   char *term;                /* char terminating the switch */
  153.   switab *st;                /* the switch table */
  154.   char lastchar;            /* last char in completion text */
  155.  
  156.   *cplt = NULL;                /* assume empty completion */
  157.  
  158.   if (textlen <= 1)            /* no input (besides opening char) */
  159.     return(CMP_BEL);            /* so just beep */
  160.  
  161.   text++; textlen--;            /* skip past opening char */
  162.   mcount = match(text,textlen,fdbp,&mat,&term); /* find matches */
  163.   if (mcount > 1)            /* ambiguous */
  164.     return(CMP_BEL);            /* so just beep */
  165.   else {
  166.     st = (switab *) fdbp->_cmdat;    /* unique - get table address */
  167.     while ((mat)->_swflg & SWI_ABR)    /* track down abbreviations */
  168.       mat = &(st->_stwds[mat->_swval]);    /* move down chain */
  169.  
  170.     *cplt = mat->_swswi+textlen;    /* point to completion text */
  171.     *cpltlen = strlen(mat->_swswi)-textlen; /* get length of remainder */
  172.     if (*cpltlen < 0) {            /* can happen on weird abbrev */
  173.       *cplt = NULL;            /* no completion in that case */
  174.       lastchar = NULCHAR;
  175.     }
  176.     else
  177.       lastchar = (*cplt)[(*cpltlen)-1];    /* else get last completion char */
  178.     if (full)                /* full completion? */
  179.       if (lastchar == cmswend)        /* cplt contains  special char? */
  180.     return(CMP_GO);            /* then no space but wakeup */
  181.       else
  182.         return(CMP_SPC | CMP_GO);    /* otherwise add space and wakeup */
  183.     else
  184.       return(CMP_PNC);            /* partial stops after punctuation */
  185.   }
  186. }
  187.  
  188.  
  189.  
  190. /* swihlp - Construct a set of matching switches for the current input,
  191. ** then print them in tabular form.  Do not print invisible switches.
  192. ** If no switches match, print a special indication.  The help message
  193. ** starts with "switch, one of the following:".  If custom help has
  194. ** been given, "switch, " is left off of this string.
  195. **/
  196.  
  197. PASSEDSTATIC int
  198. swihlp(text,textlen,fdbp,cust,lines)
  199. char *text;
  200. int textlen,cust;
  201. fdb *fdbp;
  202. int lines;
  203. {
  204.   int mcount;                /* number of matches */
  205.   swtch *mat;                /* a matching switch */
  206.   char *term;                /* ptr to input terminator */
  207.   switab *st;                /* switch table */
  208.   int i,j;
  209.   int swtlen;                /* lengths of individual switchs */
  210.   int cols;                /* # of switches printed per line */
  211.   int curcol;                /* current table column */
  212.   int maxlen = 0;            /* maximum switch length */
  213.   int mylines = 1;
  214.  
  215.   if (!cust)
  216.     cmxputs("switch, ");        /* start of msg with no custom help */
  217.   cmxputs("one of the following:");    /* remainder of the first line */
  218.       
  219.   if (textlen == 0)            /* no input? */
  220.     mcount = match(text,0,fdbp,&mat,&term); /* then all will match */
  221.   else
  222.     mcount = match(text+1,textlen-1,fdbp,&mat,&term); /* find matches */
  223.   if (mcount == 0) {            /* no match */
  224.     cmxputs(" (No switches match current input)"); /* indicate void */
  225.     return(lines-1);
  226.   }
  227.   st = (switab *) fdbp->_cmdat;        /* get switch table */
  228.   mat = st->_stwds;            /* point to first switch */
  229.   for (i = 0; i < st->_stcnt; i++) {    /* first pass to find longest swt */
  230.     if ((mat->_swflg & SWI_MAT) &&     /* matching switch... */
  231.         ((mat->_swflg & SWI_INV) == 0)    /* and not invisible */
  232.        ) {
  233.       swtlen = strlen(mat->_swswi);    /* get switch length  */
  234.       if (swtlen > maxlen)
  235.     maxlen = swtlen;        /* update longest size */
  236.     }
  237.     mat++;                /* and move to next switch */
  238.   }
  239.   maxlen += 4;                /* adjust for prefix and */
  240.                     /*  column separation */
  241.  
  242.   /* Following calculation goes as follows:
  243.   ** Let w be the screen width.  Naively, we would calculate that
  244.   ** we could list floor(w/maxlen) switches per line, each taking up
  245.   ** maxlen columns on the screen.  But since each line is indented
  246.   ** two spaces, we should subtract 2 from the effective screen width.
  247.   ** Then we add 3 to the effective width because we will not need to
  248.   ** print three spaces after the last switch in a line.  So the
  249.   ** effective screen width is w-2+3 = w+1 = cmcsb._cmcmx+2, since
  250.   ** cmcsb._cmcmx is one less than the screen width.  So we want
  251.   ** floor((cmcsb._cmcmx+2)/maxlen) switches per line.
  252.   **/
  253.  
  254.   /* XXX see my comments in cmkey.c about this -- chris */
  255.  
  256.   cols = (cmcsb._cmcmx+2) / maxlen;    /* number of columns per line */
  257.  
  258.   mat = st->_stwds;            /* point to first switch */
  259.   curcol = 0;                /* currently printing first column */
  260.   for (i = 0; i < st->_stcnt; i++) {    /* second pass to print matches */
  261.     if ((mat->_swflg & SWI_MAT) &&    /* matching switch? */
  262.     ((mat->_swflg & SWI_INV) == 0)    /* and visible? */
  263.        ) {
  264.       if (curcol == 0) {
  265.     cmxnl();            /* new line for first column */
  266.     if (mylines >= lines) {
  267.       if (!cmhelp_more("--space to continue, Q to stop--"))
  268.           return(-1);
  269.       else {
  270.           lines = cmcsb._cmrmx;
  271.           mylines = 0;
  272.       }
  273.     }
  274.     mylines++;
  275.     cmxputs(" ");            /* and offset a bit */
  276.       }
  277.       cmxputc(cmswbeg);            /* print opening punctuation */
  278.       cmxputs(mat->_swswi);        /* print the switch */
  279.       if (curcol < (cols-1))        /* space out if not last column */
  280.     for (j = strlen(mat->_swswi); j < maxlen; j++)
  281.        cmxputc(SPACE);
  282.       curcol = (curcol+1) % cols;    /* and move to next column */
  283.     }
  284.     mat++;                /* move to next switch */
  285.   }
  286.   return(lines-mylines);        /* all done */
  287. }
  288.  
  289.  
  290.  
  291. /* match - Auxiliary routine used by switch handlers.
  292. **
  293. ** Purpose:
  294. **   Step through all the switches in a table, and set their SWI_MAT
  295. **   flags according to whether or not they match a given input string.
  296. **   Returns the number of matching switches, and if only one switch
  297. **   matches, a pointer to that switch.  If more than one switch
  298. **   matches, but one switch matches exactly, it is returned as if
  299. **   it were the only matching switch.  SWI_INV, SWI_NOR and SWI_ABR
  300. **   flags do not affect this operation, except that an exact match
  301. **   to a SWI_NOR flagged switch is not considered an exact match.
  302. **   If an individual switch is terminated by the cmswend character,
  303. **   that character is considered not present for matching purposes.
  304. **
  305. ** Input arguments:
  306. **   text - A pointer to the first input character.
  307. **   textlen - The number of input characters.
  308. **   fdbp - A pointer to the FDB pointing to the switch table
  309. **     and break table to be used.
  310. ** 
  311. ** Output arguments:
  312. **   mat - A pointer to the matching switch, if exactly one switch
  313. **     matched or if one of the matching switches was an exact match.
  314. **   term - A pointer to the first character following the input that
  315. **     was used to match switches, if any characters were left after
  316. **     that input.  Otherwise, NULL is returned here.  (If the cmswend
  317. **     character follows the switch, term will be left pointing at it.)
  318. **
  319. ** Returns: The number of matching switches (or 1 for an exact match).
  320. **/
  321.  
  322. static int
  323. match(text,textlen,fdbp,mat,term)
  324. char *text,**term;
  325. int textlen;
  326. fdb *fdbp;
  327. swtch **mat;
  328. {
  329.   int mcount = 0;            /* number of matches seen */
  330.   int inlen;                /* # of chars to match in input */
  331.   int len;                /* size of switch without cmswend */
  332.   int i;
  333.   int exact = FALSE;            /* true if exact match occurs */
  334.   swtch *swis;                /* for stepping through table */
  335.   brktab *btab;                /* break table to use */
  336.   switab *st;                /* switch table to search */
  337.   
  338.   if ((btab = fdbp->_cmbrk) == NULL)    /* get supplied break table */
  339.     btab = &swibrk;            /* or use default */
  340.  
  341.   for (inlen = 0; inlen < textlen; inlen++) /* find # of usable chars */
  342.     if (((text[inlen] & CC_CHR) == cmswend) || /* stop at special end char */
  343.         BREAK(btab,text[inlen],inlen)    /* or first break char */
  344.        )
  345.       break;
  346.   if (inlen == textlen)            /* no break char? */
  347.     *term = NULL;            /* then set no terminator */
  348.   else
  349.     *term = text+inlen;            /* else point to it for caller */
  350.   
  351.   st = (switab *) fdbp->_cmdat;        /* point to switch table */
  352.   swis = st->_stwds;            /* point to first switch */
  353.   for (i = 0; i < st->_stcnt; i++) {    /* step through table */
  354.     if (match1(swis->_swswi,text,inlen)) {
  355.       swis->_swflg |= SWI_MAT;        /* this switch matches */
  356.       if (!exact) {            /* if no prior exact match */
  357.     *mat = swis;            /* then save pointer to return */
  358.         mcount++;            /* and count it */
  359.       }
  360.       len = strlen(swis->_swswi);    /* get length of matching switch */
  361.       if (swis->_swswi[len-1] == cmswend) /* but don't count ending punct */
  362.     len--;
  363.       if (len == inlen)
  364.         if ((swis->_swflg & SWI_NOR) == 0) { /* and not ignored? */
  365.       exact = TRUE;            /* flag this exact match */
  366.       mcount = 1;            /* only one match now */
  367.         }
  368.     }
  369.     else
  370.       swis->_swflg &= ~SWI_MAT;        /* no match -- turn off flag */
  371.  
  372.     swis++;                /* move on to next switch */
  373.   }
  374.   return(mcount);            /* give back # of matches */
  375. }
  376.  
  377.  
  378.  
  379. /* match1 - Auxiliary routine for match 
  380. **
  381. ** Purpose:
  382. **   Decides whether or not two strings match up to a given number of
  383. **   characters.  Case of letters is ignored in the comparison.
  384. **
  385. ** Input arguments:
  386. **   s1, s2 - Pointers to the strings to be compared.
  387. **   slen - Number of characters to compare.
  388. **
  389. ** Output arguments: None.
  390. ** Returns: TRUE for a match, FALSE otherwise.
  391. **/
  392.  
  393. static int
  394. match1(s1,s2,slen)
  395. char *s1,*s2;
  396. int slen;
  397. {
  398.   char c1,c2;            /* individual chars to compare */
  399.   while (slen-- > 0) {        /* step through strings */
  400.     c1 = (*s1++) & CC_CHR;        /* pick up next pair of chars */
  401.     c2 = (*s2++) & CC_CHR;
  402.     if ((c1 >= 'a') && (c1 <= 'z'))
  403.       c1 -= 'a'-'A';        /* upper case first char */
  404.     if ((c2 >= 'a') && (c2 <= 'z'))
  405.       c2 -= 'a'-'A';        /* upper case second char */
  406.     if (c1 != c2)
  407.       return(FALSE);        /* mismatch */
  408.   }
  409.   return(TRUE);            /* all chars matched */
  410. }
  411.