home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 198_01 / input.c < prev    next >
C/C++ Source or Header  |  1990-01-23  |  16KB  |  732 lines

  1. /*    INPUT:    Various input routines for MicroEMACS
  2.         written by Daniel Lawrence
  3.         5/9/86                        */
  4.  
  5. #include    <stdio.h>
  6. #include    "estruct.h"
  7. #include    "edef.h"
  8.  
  9. /*
  10.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  11.  * ABORT. The ABORT status is returned if the user bumps out of the question
  12.  * with a ^G. Used any time a confirmation is required.
  13.  */
  14.  
  15. mlyesno(prompt)
  16.  
  17. char *prompt;
  18.  
  19. {
  20.     char c;            /* input character */
  21.     char buf[NPAT];        /* prompt to user */
  22.  
  23.     /* build and prompt the user */
  24.     strcpy(buf, prompt);
  25.     strcat(buf, " [y/n]? ");
  26.     mlwrite(buf);
  27.  
  28.     for (;;) {
  29.         /* get the responce */
  30.         c = tgetc();
  31.  
  32.         if (c == ectoc(abortc))        /* Bail out! */
  33.             return(ABORT);
  34.  
  35.         if (c=='y' || c=='Y')
  36.             return(TRUE);
  37.  
  38.         if (c=='n' || c=='N')
  39.             return(FALSE);
  40.     }
  41. }
  42.  
  43. /*
  44.  * Write a prompt into the message line, then read back a response. Keep
  45.  * track of the physical position of the cursor. If we are in a keyboard
  46.  * macro throw the prompt away, and return the remembered response. This
  47.  * lets macros run at full speed. The reply is always terminated by a carriage
  48.  * return. Handle erase, kill, and abort keys.
  49.  */
  50.  
  51. mlreply(prompt, buf, nbuf)
  52.     char *prompt;
  53.     char *buf;
  54. {
  55.     return(nextarg(prompt, buf, nbuf, ctoec('\n')));
  56. }
  57.  
  58. mltreply(prompt, buf, nbuf, eolchar)
  59.  
  60. char *prompt;
  61. char *buf;
  62. int eolchar;
  63.  
  64. {
  65.     return(nextarg(prompt, buf, nbuf, eolchar));
  66. }
  67.  
  68. /*    ectoc:    expanded character to character
  69.         colapse the CTRL and SPEC flags back into an ascii code   */
  70.  
  71. ectoc(c)
  72.  
  73. int c;
  74.  
  75. {
  76.     if (c & CTRL)
  77.         c = c & ~(CTRL | 0x40);
  78.     if (c & SPEC)
  79.         c= c & 255;
  80.     return(c);
  81. }
  82.  
  83. /*    ctoec:    character to extended character
  84.         pull out the CTRL and SPEC prefixes (if possible)    */
  85.  
  86. ctoec(c)
  87.  
  88. int c;
  89.  
  90. {
  91.         if (c>=0x00 && c<=0x1F)
  92.                 c = CTRL | (c+'@');
  93.         return (c);
  94. }
  95.  
  96. /* get a command name from the command line. Command completion means
  97.    that pressing a <SPACE> will attempt to complete an unfinished command
  98.    name if it is unique.
  99. */
  100.  
  101. int (*getname())()
  102.  
  103. {
  104. #if    ST520 & LATTICE
  105. #define register        
  106. #endif
  107.     register int cpos;    /* current column on screen output */
  108.     register int c;
  109.     register char *sp;    /* pointer to string for output */
  110.     register NBIND *ffp;    /* first ptr to entry in name binding table */
  111.     register NBIND *cffp;    /* current ptr to entry in name binding table */
  112.     register NBIND *lffp;    /* last ptr to entry in name binding table */
  113.     char buf[NSTRING];    /* buffer to hold tentative command name */
  114.     int (*fncmatch())();
  115.  
  116.     /* starting at the beginning of the string buffer */
  117.     cpos = 0;
  118.  
  119.     /* if we are executing a command line get the next arg and match it */
  120.     if (clexec) {
  121.         if (macarg(buf) != TRUE)
  122.             return(FALSE);
  123.         return(fncmatch(&buf[0]));
  124.     }
  125.  
  126.     /* build a name string from the keyboard */
  127.     while (TRUE) {
  128.         c = tgetc();
  129.  
  130.         /* if we are at the end, just match it */
  131.         if (c == 0x0d) {
  132.             buf[cpos] = 0;
  133.  
  134.             /* and match it off */
  135.             return(fncmatch(&buf[0]));
  136.  
  137.         } else if (c == ectoc(abortc)) {    /* Bell, abort */
  138.             ctrlg(FALSE, 0);
  139.             TTflush();
  140.             return( (int (*)()) NULL);
  141.  
  142.         } else if (c == 0x7F || c == 0x08) {    /* rubout/erase */
  143.             if (cpos != 0) {
  144.                 TTputc('\b');
  145.                 TTputc(' ');
  146.                 TTputc('\b');
  147.                 --ttcol;
  148.                 --cpos;
  149.                 TTflush();
  150.             }
  151.  
  152.         } else if (c == 0x15) {    /* C-U, kill */
  153.             while (cpos != 0) {
  154.                 TTputc('\b');
  155.                 TTputc(' ');
  156.                 TTputc('\b');
  157.                 --cpos;
  158.                 --ttcol;
  159.             }
  160.  
  161.             TTflush();
  162.  
  163.         } else if (c == ' ') {
  164. /* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  165.     /* attempt a completion */
  166.     buf[cpos] = 0;        /* terminate it for us */
  167.     ffp = &names[0];    /* scan for matches */
  168.     while (ffp->n_func != NULL) {
  169.         if (strncmp(buf, ffp->n_name, strlen(buf)) == 0) {
  170.             /* a possible match! More than one? */
  171.             if ((ffp + 1)->n_func == NULL ||
  172.                (strncmp(buf, (ffp+1)->n_name, strlen(buf)) != 0)) {
  173.                 /* no...we match, print it */
  174.                 sp = ffp->n_name + cpos;
  175.                 while (*sp)
  176.                     TTputc(*sp++);
  177.                 TTflush();
  178.                 return(ffp->n_func);
  179.             } else {
  180. /* << << << << << << << << << << << << << << << << << */
  181.     /* try for a partial match against the list */
  182.  
  183.     /* first scan down until we no longer match the current input */
  184.     lffp = (ffp + 1);
  185.     while ((lffp+1)->n_func != NULL) {
  186.         if (strncmp(buf, (lffp+1)->n_name, strlen(buf)) != 0)
  187.             break;
  188.         ++lffp;
  189.     }
  190.  
  191.     /* and now, attempt to partial complete the string, char at a time */
  192.     while (TRUE) {
  193.         /* add the next char in */
  194.         buf[cpos] = ffp->n_name[cpos];
  195.  
  196.         /* scan through the candidates */
  197.         cffp = ffp + 1;
  198.         while (cffp <= lffp) {
  199.             if (cffp->n_name[cpos] != buf[cpos])
  200.                 goto onward;
  201.             ++cffp;
  202.         }
  203.  
  204.         /* add the character */
  205.         TTputc(buf[cpos++]);
  206.     }
  207. /* << << << << << << << << << << << << << << << << << */
  208.             }
  209.         }
  210.         ++ffp;
  211.     }
  212.  
  213.     /* no match.....beep and onward */
  214.     TTbeep();
  215. onward:;
  216.     TTflush();
  217. /* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  218.         } else {
  219.             if (cpos < NSTRING-1 && c > ' ') {
  220.                 buf[cpos++] = c;
  221.                 TTputc(c);
  222.             }
  223.  
  224.             ++ttcol;
  225.             TTflush();
  226.         }
  227.     }
  228. }
  229.  
  230. #if    BCOMPL
  231. /*
  232.  * Read in the name of an buffer using TENEX style expansion and
  233.  * return the pointer to it.  If argument 'createf' is non-zero, a new
  234.  * buffer will be created, if requested.  'prompt' can be NULL, in
  235.  * which case neither a prompt or the default is printed in the
  236.  * minibuffer.  'def_name' may be a default buffer name or NULL.  If
  237.  * 'prompt' is present, it will be printed in the minibuffer and can
  238.  * be chosen by simply hitting RETURN.
  239.  *
  240.  * returns NULL, iff the user ABORTs or can't / isn't allowed to
  241.  * create a new buffer.
  242.  */
  243.  
  244. BUFFER *tenexbuf(createf, prompt, def_bname)
  245.  
  246. int createf;
  247. char *prompt, *def_bname;
  248.  
  249. {
  250.     register int cpos;    /* current column on screen output */
  251.     register int c;
  252.     char bufname[NBUFN];    /* buffer to hold tentative buffer name */
  253.  
  254.     /* starting at the beginning of the string buffer */
  255.     cpos = 0;
  256.  
  257.     /* if we are executing a command line get the next arg and match it */
  258.     if (clexec) {
  259.         if (macarg(bufname) != TRUE)
  260.             return(NULL);
  261.         return(bfind(&bufname[0], createf, 0));
  262.     }
  263.  
  264.     if (prompt)
  265.         mlwrite ("%s[%s]: ", prompt,
  266.              def_bname ? def_bname : "no default");
  267.  
  268.     /* build a name string from the keyboard */
  269.     while (TRUE) {
  270.         c = tgetc();
  271.  
  272.         /* if we are at the end, just match it */
  273.         if (c == '\n'  ||  c == '\r') {
  274.             if (def_bname && cpos==0)
  275.                 return(bfind(def_bname, createf, 0));
  276.             else {
  277.                 bufname[cpos] = 0;
  278.                 return(bfind(&bufname[0], createf, 0));
  279.             }
  280.  
  281.         } else if (c == ectoc(abortc)) {    /* Bell, abort */
  282.             ctrlg(FALSE, 0);
  283.             TTflush();
  284.             return NULL;
  285.  
  286.         } else if (c == 0x7F || c == 0x08) {    /* rubout/erase */
  287.             if (cpos != 0) {
  288.                 TTputc('\b');
  289.                 TTputc(' ');
  290.                 TTputc('\b');
  291.                 --ttcol;
  292.                 --cpos;
  293.                 TTflush();
  294.             }
  295.  
  296.         } else if (c == 0x15) {    /* C-U, kill */
  297.             while (cpos != 0) {
  298.                 TTputc('\b');
  299.                 TTputc(' ');
  300.                 TTputc('\b');
  301.                 --cpos;
  302.                 --ttcol;
  303.             }
  304.  
  305.             TTflush();
  306.  
  307.         } else if (c == ' ') {        /* attempt a completion */
  308.  
  309.             BUFFER *bp, *expbuf;
  310.             int exp_pos, i;
  311.             
  312.             bufname[cpos] = 0;    /* terminate it for us */
  313.             /*
  314.              * Search for the next common expansion.  expbuf points
  315.              * to the first matching buffer, exp_pos gets
  316.              * initialized to the full buffer name.  Each further
  317.              * matching buffer will shrink (via exp_pos) the most
  318.              * common name prefix.
  319.              */
  320.             for (expbuf=NULL, exp_pos=0, bp = bheadp;
  321.                  bp;
  322.                  bp = bp->b_bufp) {
  323.                 if ( strlen (bp->b_bname) > cpos+1  &&
  324.                      0==strncmp (bufname, bp->b_bname, cpos)) {
  325.                     /*
  326.                      * If you want a more
  327.                      * 'agressive' completion, you
  328.                      * could count the matches
  329.                      * right here after this
  330.                      * comment.  Then, if we found
  331.                      * exactly one matching
  332.                      * buffer, we could directly
  333.                      * switch to it (expbuf).
  334.                      */
  335.                     if ( ! expbuf ) {
  336.                         /* install first match */
  337.                         expbuf = bp;
  338.                         exp_pos = strlen
  339.                             (expbuf->b_bname) -1;
  340.                         continue;
  341.                     }
  342.                     /* another match; reduce prefix size */
  343.                     i=cpos;
  344.                     while (i<=exp_pos)
  345.                         if (bp->b_bname[i] !=
  346.                             expbuf->b_bname[i])
  347.                             break;
  348.                         else
  349.                             ++i;
  350.                     exp_pos = i-1;
  351.                 }
  352.             }
  353.  
  354.             if (!expbuf) {
  355.                 TTbeep();
  356.                 TTflush();
  357.                 continue;
  358.             }
  359.             
  360.             while (cpos <= exp_pos) {
  361.                 /* add the next char in */
  362.                 bufname[cpos] = expbuf->b_bname[cpos];
  363.                 TTputc(bufname[cpos++]);
  364.             }
  365.             TTflush();
  366.         } else {
  367.             if (cpos < NBUFN-1 && c > ' ') {
  368.                 bufname[cpos++] = c;
  369.                 TTputc(c);
  370.             }
  371.  
  372.             ++ttcol;
  373.             TTflush();
  374.         }
  375.     }
  376. }
  377. #endif
  378.  
  379. /*    tgetc:    Get a key from the terminal driver, resolve any keyboard
  380.         macro action                    */
  381.  
  382. int tgetc()
  383.  
  384. {
  385.     int c;    /* fetched character */
  386.  
  387.     /* if we are playing a keyboard macro back, */
  388.     if (kbdmode == PLAY) {
  389.  
  390.         /* if there is some left... */
  391.         if (kbdptr < kbdend)
  392.             return((int)*kbdptr++);
  393.  
  394.         /* at the end of last repitition? */
  395.         if (--kbdrep < 1) {
  396.             kbdmode = STOP;
  397. #if    VISMAC == 0
  398.             /* force a screen update after all is done */
  399.             update(FALSE);
  400. #endif
  401.         } else {
  402.  
  403.             /* reset the macro to the begining for the next rep */
  404.             kbdptr = &kbdm[0];
  405.             return((int)*kbdptr++);
  406.         }
  407.     }
  408.  
  409.     /* fetch a character from the terminal driver */
  410.     c = TTgetc();
  411.  
  412.     /* record it for $lastkey */
  413.     lastkey = c;
  414.  
  415.     /* save it if we need to */
  416.     if (kbdmode == RECORD) {
  417.         *kbdptr++ = c;
  418.         kbdend = kbdptr;
  419.  
  420.         /* don't overrun the buffer */
  421.         if (kbdptr == &kbdm[NKBDM - 1]) {
  422.             kbdmode = STOP;
  423.             TTbeep();
  424.         }
  425.     }
  426.  
  427.     /* and finally give the char back */
  428.     return(c);
  429. }
  430.  
  431. /*    GET1KEY:    Get one keystroke. The only prefixs legal here
  432.             are the SPEC and CTRL prefixes.
  433.                                 */
  434.  
  435. #if    DECEDT
  436. int save1key = 0;
  437. #endif
  438.  
  439. #if    VMS
  440. extern int tacnt;
  441. #endif
  442.  
  443. get1key()
  444.  
  445. {
  446.     int    c, code, i;
  447. #if    AMIGA
  448.     int    d;
  449. #endif
  450.  
  451.     /* get a keystroke */
  452. #if    DECEDT
  453.     if (save1key) {c = save1key; save1key = 0;} else
  454. #endif
  455.         c = tgetc();
  456.  
  457. #if    MSDOS | ST520
  458.     if (c == 0) {                /* Apply SPEC prefix    */
  459.             c = tgetc();
  460.             if (c>=0x00 && c<=0x1F)        /* control key? */
  461.                     c = CTRL | (c+'@');
  462.         return(SPEC | c);
  463.     }
  464. #endif
  465.  
  466. #if    DECEDT
  467.     /* catch arrow keys and function keys */
  468.     if (c == 27) {
  469. #if    TYPEAH & (VMS | BSD | MSDOS | (USG & MSC))
  470.         if (kbdmode != PLAY && !vt100keys) {
  471. #if    VMS
  472.             tacnt = 0;
  473. #endif
  474.             if (!typahead()) return(CTRL | (c+'@'));
  475.         }
  476. #endif
  477.         save1key = tgetc();
  478.         if (save1key == '[' || save1key == 'O') {
  479.             save1key = 0; c = tgetc();
  480.             if (c >= '0' && c <= '9') {
  481.                 code = 0;
  482.                 for(i = 0; i < 3 && c >= '0' && c <= '9'; i++)
  483.                     {code = code*10+c-'0'; c = tgetc();}
  484.                 if (c == 27) save1key=27;
  485.                 c = code;
  486.                     if (c>=0x00 && c<=0x1F) c = CTRL | (c+'@');
  487.             }
  488.             return(SPEC | c);
  489.         }
  490.     }
  491. #endif
  492.  
  493. #if    AMIGA
  494.     /* apply SPEC prefix */
  495.     if ((unsigned)c == 155) {
  496.         c = tgetc();
  497.  
  498.         /* first try to see if it is a cursor key */
  499.         if ((c >= 'A' && c <= 'D') || c == 'S' || c == 'T')
  500.             return(SPEC | c);
  501.  
  502.         /* next, a 2 char sequence */
  503.         d = tgetc();
  504.         if (d == '~')
  505.             return(SPEC | c);
  506.  
  507.         /* decode a 3 char sequence */
  508.         c = d + 32;
  509.         /* if a shifted function key, eat the tilde */
  510.         if (d >= '0' && d <= '9')
  511.             d = tgetc();
  512.         return(SPEC | c);
  513.     }
  514. #endif
  515.  
  516. #if  WANGPC
  517.     if (c == 0x1F) {            /* Apply SPEC prefix    */
  518.             c = tgetc();
  519.         return(SPEC | c);
  520.     }
  521. #endif
  522.  
  523.         if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  524.                 c = CTRL | (c+'@');
  525.         return (c);
  526. }
  527.  
  528. /*    GETCMD:    Get a command from the keyboard. Process all applicable
  529.         prefix keys
  530.                             */
  531.  
  532. extern int inmeta, incex, inspecial;
  533.  
  534. getcmd()
  535.  
  536. {
  537.     int c;        /* fetched keystroke */
  538.     int oldmeta;
  539.     int code, i;
  540.  
  541.     /* get initial character */
  542.     c = get1key();
  543.  
  544.     /* process prefixes */
  545.     if (inmeta == TRUE || c == metac ||
  546.         incex == TRUE || c == ctlxc) {
  547.         oldmeta = inmeta;
  548.         if (c == ctlxc) {c = get1key(); incex = TRUE;}
  549.         if (c == metac) {
  550.             c = get1key(); inmeta = TRUE;
  551.             if (oldmeta == FALSE && c == metac)
  552.                 {c = get1key(); oldmeta = TRUE;}
  553.         }
  554.             if (islower(c))        /* Force to upper */
  555.                     c ^= DIFCASE;
  556.             if (c>=0x00 && c<=0x1F)        /* control key */
  557.                 c = CTRL | (c+'@');
  558.         /* temporary ESC sequence fix......... */
  559.         if (inmeta == TRUE && (c == '[' || c == 'O')) {
  560.             c = get1key();
  561.             if (c >= '0' && c <= '9') {
  562.                 code = 0;
  563.                 for(i = 0; i < 3 && c >= '0' && c <= '9'; i++)
  564.                     {code = code*10+c-'0'; c = get1key();}
  565. #if DECEDT
  566.                 if (c == (CTRL|(27+'@'))) save1key=27;
  567. #endif
  568.                 c = code;
  569.                     if (c>=0x00 && c<=0x1F) c = CTRL | (c+'@');
  570.             }
  571.             inmeta = oldmeta;
  572.             inspecial = TRUE;
  573.         }
  574.         if (inmeta == TRUE)    {inmeta = FALSE; c |= META;}
  575.         if (incex == TRUE)    {incex = FALSE;    c |= CTLX;}
  576.     }
  577.  
  578.     if (inspecial == TRUE)    {inspecial = FALSE; c |= SPEC;}
  579.  
  580.     /* otherwise, just return it */
  581.     return(c);
  582. }
  583.  
  584. /*    A more generalized prompt/reply function allowing the caller
  585.     to specify the proper terminator. If the terminator is not
  586.     a return ('\n') it will echo as "<NL>"
  587.                             */
  588. getstring(prompt, buf, nbuf, eolchar)
  589.  
  590. char *prompt;
  591. char *buf;
  592. int eolchar;
  593.  
  594. {
  595.     register int cpos;    /* current character position in string */
  596.     register int c;
  597.     register int quotef;    /* are we quoting the next char? */
  598.     int len;
  599.     char *str, chbuf[2];
  600.     char *getkill();
  601.  
  602.     cpos = 0;
  603.     quotef = FALSE;
  604.  
  605.     /* prompt the user for the input string */
  606.     mlwrite(prompt);
  607.  
  608.     for (;;) {
  609.         /* get a character from the user */
  610.         c = get1key();
  611.  
  612.         /* If it is a <ret>, change it to a <NL> */
  613.         if (c == (CTRL | 0x4d) && quotef == FALSE)
  614.             c = CTRL | 0x40 | '\n';
  615.  
  616.         /* if they hit the line terminate, wrap it up */
  617. #if    DECEDT
  618.         if (quotef == FALSE && (c & SPEC)) c = eolchar;
  619. #endif
  620.         if (c == eolchar && quotef == FALSE) {
  621.             buf[cpos++] = 0;
  622.  
  623.             /* clear the message line */
  624.             mlwrite("");
  625.             TTflush();
  626.  
  627.             /* if we default the buffer, return FALSE */
  628.             if (buf[0] == 0)
  629.                 return(FALSE);
  630.  
  631.             return(TRUE);
  632.         }
  633.  
  634.         /* change from command form back to character form */
  635.         c = ectoc(c);
  636.  
  637.         if (c == ectoc(abortc) && quotef == FALSE) {
  638.             /* Abort the input? */
  639.             ctrlg(FALSE, 0);
  640.             TTflush();
  641.             return(ABORT);
  642.         } else if ((c==0x7F || c==0x08) && quotef==FALSE) {
  643.             /* rubout/erase */
  644.             if (cpos != 0) {
  645.                 outstring("\b \b");
  646.                 --ttcol;
  647.  
  648.                 if ((buf[--cpos]&0x7f) < 0x20 ||
  649.                     (buf[cpos]&0x7f) == 0x7f) {
  650.                     outstring("\b \b");
  651.                     --ttcol;
  652.                 }
  653.  
  654.                 if (buf[cpos] == '\n') {
  655.                     outstring("\b\b  \b\b");
  656.                     ttcol -= 2;
  657.                 }
  658.                 TTflush();
  659.             }
  660.  
  661.         } else if (c == 0x15 && quotef == FALSE) {
  662.             /* C-U, kill */
  663.             while (cpos != 0) {
  664.                 outstring("\b \b");
  665.                 --ttcol;
  666.  
  667.                 if ((buf[--cpos]&0x7f) < 0x20 ||
  668.                     (buf[cpos]&0x7f) == 0x7f) {
  669.                     outstring("\b \b");
  670.                     --ttcol;
  671.                 }
  672.  
  673.                 if (buf[cpos] == '\n') {
  674.                     outstring("\b\b  \b\b");
  675.                     ttcol -= 2;
  676.                 }
  677.             }
  678.             TTflush();
  679.  
  680.         } else if (c == quotec && quotef == FALSE) {
  681.             quotef = TRUE;
  682.         } else {
  683.             if (c == 0x19 && quotef == FALSE) {
  684.                 str = getkill();
  685.                 len = strlen(str);
  686.             }
  687.             else { str = chbuf; *chbuf = c; len = 1; }
  688.             quotef = FALSE;
  689.  
  690.             while (cpos < nbuf-1 && len-- > 0) {
  691.                 c = buf[cpos++] = *str++;
  692.  
  693.                 if (c == '\n') { /* put out <NL> for <ret> */
  694.                     outstring("<NL>");
  695.                     ttcol += 3;
  696.                 } else {
  697.                     if ((c&0x7f) < 0x20 ||
  698.                         (c&0x7f) == 0x7f) {
  699.                         if (disinp) TTputc('^');
  700.                         ++ttcol;
  701.                         c = ((c&0x7f) ^ 0x40);
  702.                     }
  703.                     if (disinp) TTputc(c);
  704.                 }
  705.                 ++ttcol;
  706.             }
  707.             TTflush();
  708.         }
  709.     }
  710. }
  711.  
  712. outstring(s)    /* output a string of input characters */
  713.  
  714. char *s;    /* string to output */
  715.  
  716. {
  717.     if (disinp)
  718.         while (*s)
  719.             TTputc(*s++);
  720. }
  721.  
  722. ostring(s)    /* output a string of output characters */
  723.  
  724. char *s;    /* string to output */
  725.  
  726. {
  727.     if (discmd)
  728.         while (*s)
  729.             TTputc(*s++);
  730. }
  731.  
  732.