home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1709 / tio.c < prev    next >
C/C++ Source or Header  |  1990-12-28  |  14KB  |  716 lines

  1. /* tio.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    16820 SW Tallac Way
  6.  *    Beaverton, OR 97006
  7.  *    kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
  8.  */
  9.  
  10.  
  11. /* This file contains terminal I/O functions */
  12.  
  13. #include "config.h"
  14. #include <signal.h>
  15. #include "vi.h"
  16.  
  17.  
  18. /* This function reads in a line from the terminal. */
  19. int vgets(prompt, buf, bsize)
  20.     char    prompt;    /* the prompt character, or '\0' for none */
  21.     char    *buf;    /* buffer into which the string is read */
  22.     int    bsize;    /* size of the buffer */
  23. {
  24.     int    len;    /* how much we've read so far */
  25.     int    ch;    /* a character from the user */
  26.     int    quoted;    /* is the next char quoted? */
  27.     int    tab;    /* column position of cursor */
  28.     char    widths[132];    /* widths of characters */
  29.  
  30.     /* show the prompt */
  31.     move(LINES - 1, 0);
  32.     tab = 0;
  33.     if (prompt)
  34.     {
  35.         addch(prompt);
  36.         tab = 1;
  37.     }
  38.     clrtoeol();
  39.     refresh();
  40.  
  41.     /* read in the line */
  42.     quoted = len = 0;
  43.     for (;;)
  44.     {
  45.         ch = getkey(quoted ? 0 : WHEN_EX);
  46.  
  47.         /* some special conversions */
  48.         if (ch == ctrl('D') && len == 0)
  49.             ch = ctrl('[');
  50.  
  51.         /* inhibit detection of special chars (except ^J) after a ^V */
  52.         if (quoted && ch != '\n')
  53.         {
  54.             ch |= 256;
  55.         }
  56.  
  57.         /* process the character */
  58.         switch(ch)
  59.         {
  60.           case ctrl('V'):
  61.             qaddch('^');
  62.             qaddch('\b');
  63.             quoted = TRUE;
  64.             break;
  65.  
  66.           case ctrl('['):
  67.             return -1;
  68.  
  69.           case '\n':
  70.           case '\r':
  71.             clrtoeol();
  72.             goto BreakBreak;
  73.  
  74.           case '\b':
  75.             if (len > 0)
  76.             {
  77.                 len--;
  78.                 addstr("\b\b\b\b\b\b\b\b" + 8 - widths[len]);
  79.                 if (mode == MODE_EX)
  80.                 {
  81.                     clrtoeol();
  82.                 }
  83.                 tab -= widths[len];
  84.             }
  85.             else
  86.             {
  87.                 return -1;
  88.             }
  89.             break;
  90.  
  91.           default:
  92.             /* strip off quotation bit */
  93.             if (ch & 256)
  94.             {
  95.                 ch &= ~256;
  96.                 quoted = FALSE;
  97.                 qaddch(' ');
  98.                 qaddch('\b');
  99.             }
  100.             /* add & echo the char */
  101.             if (len < bsize - 1)
  102.             {
  103.                 if (ch == '\t')
  104.                 {
  105.                     widths[len] = *o_tabstop - (tab % *o_tabstop);
  106.                     addstr("        " + 8 - widths[len]);
  107.                     tab += widths[len];
  108.                 }
  109.                 else if (ch > 0 && ch < ' ') /* > 0 by GB */
  110.                 {
  111.                     addch('^');
  112.                     addch(ch + '@');
  113.                     widths[len] = 2;
  114.                     tab += 2;
  115.                 }
  116.                 else if (ch == '\177')
  117.                 {
  118.                     addch('^');
  119.                     addch('?');
  120.                     widths[len] = 2;
  121.                     tab += 2;
  122.                 }
  123.                 else
  124.                 {
  125.                     addch(ch);
  126.                     widths[len] = 1;
  127.                     tab++;
  128.                 }
  129.                 buf[len++] = ch;
  130.             }
  131.             else
  132.             {
  133.                 beep();
  134.             }
  135.         }
  136.     }
  137. BreakBreak:
  138.     refresh();
  139.     buf[len] = '\0';
  140.     return len;
  141. }
  142.  
  143.  
  144. /* ring the terminal's bell */
  145. beep()
  146. {
  147.     if (*o_vbell)
  148.     {
  149.         do_VB();
  150.         refresh();
  151.     }
  152.     else if (*o_errorbells)
  153.     {
  154.         ttywrite("\007", 1);
  155.     }
  156. }
  157.  
  158. static manymsgs; /* This variable keeps msgs from overwriting each other */
  159.  
  160. /* Write a message in an appropriate way.  This should really be a varargs
  161.  * function, but there is no such thing as vwprintw.  Hack!!!  Also uses a
  162.  * little sleaze in the way it saves messages for repetition later.
  163.  *
  164.  * msg((char *)0)    - repeats the previous message
  165.  * msg("")        - clears the message line
  166.  * msg("%s %d", ...)    - does a printf onto the message line
  167.  */
  168. /*VARARGS1*/
  169. msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
  170.     char    *fmt;
  171.     long    arg1, arg2, arg3, arg4, arg5, arg6, arg7;
  172. {
  173.     static char    pmsg[80];    /* previous message */
  174.     char        *start;        /* start of current message */
  175.  
  176.     if (mode != MODE_VI)
  177.     {
  178.         sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
  179.         qaddstr(pmsg);
  180.         addch('\n');
  181.         exrefresh();
  182.     }
  183.     else
  184.     {
  185.         /* redrawing previous message? */
  186.         if (!fmt)
  187.         {
  188.             move(LINES - 1, 0);
  189.             standout();
  190.             qaddch(' ');
  191.             addstr(pmsg);
  192.             qaddch(' ');
  193.             standend();
  194.             clrtoeol();
  195.             return;
  196.         }
  197.  
  198.         /* just blanking out message line? */
  199.         if (!*fmt)
  200.         {
  201.             if (!*pmsg) return;
  202.             *pmsg = '\0';
  203.             move(LINES - 1, 0);
  204.             clrtoeol();
  205.             return;
  206.         }
  207.  
  208.         /* wait for keypress between consecutive msgs */
  209.         if (manymsgs)
  210.         {
  211.             qaddstr("[More...]");
  212.             wqrefresh(stdscr);
  213.             getkey(0);
  214.         }
  215.  
  216.         /* real message */
  217.         move(LINES - 1, 0);
  218.         standout();
  219.         qaddch(' ');
  220.         sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
  221.         qaddstr(pmsg);
  222.         qaddch(' ');
  223.         standend();
  224.         clrtoeol();
  225.         refresh();
  226.     }
  227.     manymsgs = TRUE;
  228. }
  229.  
  230.  
  231. /* This function calls refresh() if the option exrefresh is set */
  232. exrefresh()
  233. {
  234.     char    *scan;
  235.  
  236.     /* If this ex command wrote ANYTHING set exwrote so vi's  :  command
  237.      * can tell that it must wait for a user keystroke before redrawing.
  238.      */
  239.     for (scan=kbuf; scan<stdscr; scan++)
  240.         if (*scan == '\n')
  241.             exwrote = TRUE;
  242.  
  243. #if    MICROSOFT            /* avoid compiler bug */
  244.     scan = stdscr;
  245. #define    stdscr    scan
  246. #endif
  247.     /* now we do the refresh thing */
  248.     if (*o_exrefresh)
  249.     {
  250.         refresh();
  251.     }
  252.     else
  253.     {
  254.         wqrefresh(stdscr);
  255.     }
  256. #if    MICROSOFT
  257. #undef    stdscr
  258.     stdscr = scan;
  259. #endif    
  260.     manymsgs = FALSE;
  261. }
  262.  
  263.  
  264. /* This variable holds a single ungotten key, or 0 for no key */
  265. static int ungotten;
  266. ungetkey(key)
  267.     int    key;
  268. {
  269.     ungotten = key;
  270. }
  271.  
  272. /* This array describes mapped key sequences */
  273. static struct _keymap
  274. {
  275.     char    *name;        /* name of the key, or NULL */
  276.     char    rawin[LONGKEY];    /* the unmapped version of input */
  277.     char    cooked[80];    /* the mapped version of input */
  278.     int    len;        /* length of the unmapped version */
  279.     int    when;        /* when is this key mapped? */
  280. }
  281.     mapped[MAXMAPS];
  282.  
  283. #if !MSDOS && !TOS
  284. static int dummy(){} /* for timeout */
  285. #endif
  286.  
  287. /* This function reads in a keystroke for VI mode.  It automatically handles
  288.  * key mapping.
  289.  */
  290. int getkey(when)
  291.     int        when;        /* which bits must be ON? */
  292. {
  293.     static char    keybuf[100];    /* array of already-read keys */
  294.     static int    nkeys;        /* total number of keys in keybuf */
  295.     static int    next;        /* index of next key to return */
  296.     static char    *cooked;    /* rawin, or pointer to converted key */ 
  297.     static int    oldwhen;    /* "when" from last time */
  298.     static int    oldleft;
  299.     static long    oldtop;
  300.     static long    oldnlines;
  301.     static char    *cshape;    /* current cursor shape */
  302.     register char    *kptr;        /* &keybuf[next] */
  303.     register struct _keymap *km;    /* used to count through keymap */
  304.     register int    i, j, k;
  305.  
  306.     /* if this key is needed for delay between multiple error messages,
  307.      * then reset the manymsgs flag and abort any mapped key sequence.
  308.      */
  309.     if (manymsgs)
  310.     {
  311.         manymsgs = FALSE;
  312.         cooked = (char *)0;
  313.         ungotten = 0;
  314.     }
  315.  
  316.     /* if we have an ungotten key, use it */
  317.     if (ungotten != 0)
  318.     {
  319.         k = ungotten;
  320.         ungotten = 0;
  321.         return k;
  322.     }
  323.  
  324.     /* if we're doing a mapped key, get the next char */
  325.     if (cooked && *cooked)
  326.     {
  327.         return *cooked++;
  328.     }
  329.  
  330.     /* if keybuf is empty, fill it */
  331.     if (next == nkeys)
  332.     {
  333. #ifndef NO_CURSORSHAPE
  334.         /* make sure the cursor is the right shape */
  335.         if (has_CQ)
  336.         {
  337.             cooked = cshape;
  338.             switch (when)
  339.             {
  340.               case WHEN_EX:        cooked = CX;    break;
  341.               case WHEN_VICMD:    cooked = CV;    break;
  342.               case WHEN_VIINP:    cooked = CI;    break;
  343.               case WHEN_VIREP:    cooked = CR;    break;
  344.             }
  345.             if (cooked != cshape)
  346.             {
  347.                 cshape = cooked;
  348.                 switch (when)
  349.                 {
  350.                   case WHEN_EX:        do_CX();    break;
  351.                   case WHEN_VICMD:    do_CV();    break;
  352.                   case WHEN_VIINP:    do_CI();    break;
  353.                   case WHEN_VIREP:    do_CR();    break;
  354.                 }
  355.             }
  356.             cooked = (char *)0;
  357.         }
  358. #endif
  359.  
  360. #ifndef NO_SHOWMODE
  361.         /* if "showmode" then say which mode we're in */
  362.         if (*o_showmode
  363.          && mode == MODE_VI
  364.          && (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines))
  365.         {
  366.             oldwhen = when;
  367.             oldtop = topline;
  368.             oldleft = leftcol;
  369.             oldnlines = nlines;
  370.  
  371.             if (when & WHEN_VICMD)
  372.             {
  373.                 redraw(cursor, FALSE);
  374.                 move(LINES - 1, COLS - 10);
  375.                 standout();
  376.                 addstr("Command");
  377.                 standend();
  378.                 redraw(cursor, FALSE);
  379.             }
  380.             else if (when & WHEN_VIINP)
  381.             {
  382.                 redraw(cursor, TRUE);
  383.                 move(LINES - 1, COLS - 10);
  384.                 standout();
  385.                 addstr(" Input ");
  386.                 standend();
  387.                 redraw(cursor, TRUE);
  388.             }
  389.             else if (when & WHEN_VIREP)
  390.             {
  391.                 redraw(cursor, TRUE);
  392.                 move(LINES - 1, COLS - 10);
  393.                 standout();
  394.                 addstr("Replace");
  395.                 standend();
  396.                 redraw(cursor, TRUE);
  397.             }
  398.         }
  399.         else
  400. #endif
  401.  
  402.         /* redraw if getting a VI command */
  403.         if (when & WHEN_VICMD)
  404.         {
  405.             redraw(cursor, FALSE);
  406.         }
  407.  
  408.         /* read the rawin keystrokes */
  409.         refresh();
  410.         while ((nkeys = ttyread(keybuf, sizeof keybuf)) <= 0)
  411.         {
  412.             /* terminal was probably resized */
  413.             *o_lines = LINES;
  414.             *o_columns = COLS;
  415.             if (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))
  416.             {
  417.                 redraw(MARK_UNSET, FALSE);
  418.                 redraw(cursor, (when & WHEN_VICMD) == 0);
  419.                 refresh();
  420.             }
  421.         }
  422.         next = 0;
  423.     }
  424.  
  425.     /* see how many mapped keys this might be */
  426.     kptr = &keybuf[next];
  427.     for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
  428.     {
  429.         if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
  430.         {
  431.             if (km->len > nkeys - next)
  432.             {
  433.                 if (!strncmp(km->rawin, kptr, nkeys - next))
  434.                 {
  435.                     j++;
  436.                 }
  437.             }
  438.             else
  439.             {
  440.                 if (!strncmp(km->rawin, kptr, km->len))
  441.                 {
  442.                     j++;
  443.                     k = i;
  444.                 }
  445.             }
  446.         }
  447.     }
  448.  
  449.     /* if more than one, try to read some more */
  450.     while (j > 1)
  451.     {
  452. #if ANY_UNIX
  453.         signal(SIGALRM, dummy);
  454. #endif
  455.         alarm((unsigned)*o_keytime);
  456.         i = nkeys;
  457.         if ((k = ttyread(keybuf + nkeys, sizeof keybuf - nkeys)) >= 0)
  458.         {
  459.             nkeys += k;
  460.         }
  461.         alarm(0);
  462.  
  463.         /* if we couldn't read any more, pretend 0 mapped keys */
  464.         if (i == nkeys)
  465.         {
  466.             j = 0;
  467.         }
  468.         else /* else we got some more - try again */
  469.         {
  470.             for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
  471.             {
  472.                 if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
  473.                 {
  474.                     if (km->len > nkeys - next)
  475.                     {
  476.                         if (!strncmp(km->rawin, kptr, nkeys - next))
  477.                         {
  478.                             j++;
  479.                         }
  480.                     }
  481.                     else
  482.                     {
  483.                         if (!strncmp(km->rawin, kptr, km->len))
  484.                         {
  485.                             j++;
  486.                             k = i;
  487.                         }
  488.                     }
  489.                 }
  490.             }
  491.         }
  492.     }
  493.  
  494.     /* if unambiguously mapped key, use it! */
  495.     if (j == 1 && k >= 0)
  496.     {
  497.         next += mapped[k].len;
  498.         cooked = mapped[k].cooked;
  499. #ifndef NO_EXTENSIONS
  500.         if ((when & (WHEN_VIINP|WHEN_VIREP))
  501.          && (mapped[k].when & WHEN_INMV))
  502.         {
  503.             return 0; /* special case, means "a movement char follows" */
  504.         }
  505.         else
  506. #endif
  507.         {
  508.             return *cooked++;
  509.         }
  510.     }
  511.     else
  512.     /* assume key is unmapped, but still translate weird erase key to '\b' */
  513.     if (keybuf[next] == ERASEKEY && when != 0)
  514.     {
  515.         next++;
  516.         return '\b';
  517.     }
  518.     else
  519.     {
  520.         return keybuf[next++];
  521.     }
  522. }
  523.  
  524.  
  525. /* This function maps or unmaps a key */
  526. mapkey(rawin, cooked, when, name)
  527.     char    *rawin;    /* the input key sequence, before mapping */
  528.     char    *cooked;/* after mapping */
  529.     short    when;    /* bitmap of when mapping should happen */
  530.     char    *name;    /* name of the key, if any */
  531. {
  532.     int    i, j;
  533.  
  534.     /* if the mapped version starts with the word "visual" then set WHEN_INMV */
  535.     if (!strncmp(cooked, "visual ", 7))
  536.     {
  537.         when |= WHEN_INMV;
  538.         cooked += 7;
  539.     }
  540.     /* if WHEN_INMV is set, then WHEN_VIINP and WHEN_VIREP must be set */
  541.     if (when & WHEN_INMV)
  542.     {
  543.         when |= (WHEN_VIINP | WHEN_VIREP);
  544.     }
  545.  
  546.     /* see if the key sequence was mapped before */
  547.     j = strlen(rawin);
  548.     for (i = 0; i < MAXMAPS; i++)
  549.     {
  550.         if (mapped[i].len == j
  551.          && !strncmp(mapped[i].rawin, rawin, j)
  552.          && (mapped[i].when & when))
  553.         {
  554.             break;
  555.         }
  556.     }
  557.  
  558.     /* if not already mapped, then try to find a new slot to use */
  559.     if (i == MAXMAPS)
  560.     {
  561.         for (i = 0; i < MAXMAPS && mapped[i].len > 0; i++)
  562.         {
  563.         }
  564.     }
  565.  
  566.     /* no room for the new key? */
  567.     if (i == MAXMAPS)
  568.     {
  569.         msg("No room left in the key map table");
  570.         return;
  571.     }
  572.  
  573.     /* map the key */
  574.     if (cooked && *cooked)
  575.     {
  576.         /* Map the key */
  577.         mapped[i].len = j;
  578.         strncpy(mapped[i].rawin, rawin, j);
  579.         strcpy(mapped[i].cooked, cooked);
  580.         mapped[i].when = when;
  581.         mapped[i].name = name;
  582.     }
  583.     else /* unmap the key */
  584.     {
  585.         mapped[i].len = 0;
  586.     }
  587. }
  588.  
  589. /* Dump keys of a given type - WHEN_VICMD dumps the ":map" keys, and
  590.  * WHEN_VIINP|WHEN_VIREP dumps the ":map!" keys
  591.  */
  592. dumpkey(when)
  593. {
  594.     int    i, len, mlen;
  595.     char    *scan;
  596.     char    *mraw;
  597.  
  598.     for (i = 0; i < MAXMAPS; i++)
  599.     {
  600.         /* skip unused entries, or entries that don't match "when" */
  601.         if (mapped[i].len <= 0 || !(mapped[i].when & when))
  602.         {
  603.             continue;
  604.         }
  605.  
  606.         /* dump the key label, if any */
  607.         len = 8;
  608.         if (mapped[i].name)
  609.         {
  610.             qaddstr(mapped[i].name);
  611.             len -= strlen(mapped[i].name);
  612.         }
  613.         do
  614.         {
  615.             qaddch(' ');
  616.         } while (len-- > 0);
  617.  
  618.         /* dump the raw version */
  619.         len = 0;
  620.         mlen = mapped[i].len;
  621.         mraw = mapped[i].rawin;
  622.         for (scan = mraw; scan < mraw + mlen; scan++)
  623.         {
  624.             if (UCHAR(*scan) < ' ' || *scan == '\177')
  625.             {
  626.                 qaddch('^');
  627.                 qaddch(*scan ^ '@');
  628.                 len += 2;
  629.             }
  630.             else
  631.             {
  632.                 qaddch(*scan);
  633.                 len++;
  634.             }
  635.         }
  636.         do
  637.         {
  638.             qaddch(' ');
  639.         } while (++len < 8);
  640.  
  641.         /* dump the mapped version */
  642.         if ((mapped[i].when & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
  643.         {
  644.             qaddstr("visual ");
  645.         }
  646.         for (scan = mapped[i].cooked; *scan; scan++)
  647.         {
  648.             if (UCHAR(*scan) < ' ' || *scan == '\177')
  649.             {
  650.                 qaddch('^');
  651.                 qaddch(*scan ^ '@');
  652.             }
  653.             else
  654.             {
  655.                 qaddch(*scan);
  656.             }
  657.         }
  658.  
  659.         addch('\n');
  660.         exrefresh();
  661.     }
  662. }
  663.  
  664.  
  665.  
  666. /* This function saves the current configuration of mapped keys to a file */
  667. savekeys(fd)
  668.     int    fd;    /* file descriptor to save them to */
  669. {
  670.     int    i;
  671.     char    buf[80];
  672.  
  673.     /* now write a map command for each key other than the arrows */
  674.     for (i = 0; i < MAXMAPS; i++)
  675.     {
  676.         /* ignore keys that came from termcap */
  677.         if (mapped[i].name)
  678.         {
  679.             continue;
  680.         }
  681.  
  682.         /* If this isn't used, ignore it */
  683.         if (mapped[i].len <= 0)
  684.         {
  685.             continue;
  686.         }
  687.  
  688.         /* write the map command */
  689.         if (mapped[i].when & WHEN_INMV)
  690.         {
  691.             sprintf(buf, "map%s %.*s visual %s\n",
  692.                 (mapped[i].when & WHEN_VICMD) ? "" : "!",
  693.                 mapped[i].len, mapped[i].rawin,
  694.                 mapped[i].cooked);
  695.             twrite(fd, buf, strlen(buf));
  696.         }
  697.         else
  698.         {
  699.             if (mapped[i].when & WHEN_VICMD)
  700.             {
  701.                 sprintf(buf, "map %.*s %s\n",
  702.                     mapped[i].len, mapped[i].rawin,
  703.                     mapped[i].cooked);
  704.                 twrite(fd, buf, strlen(buf));
  705.             }
  706.             if (mapped[i].when & (WHEN_VIINP | WHEN_VIREP))
  707.             {
  708.                 sprintf(buf, "map! %.*s %s\n",
  709.                     mapped[i].len, mapped[i].rawin,
  710.                     mapped[i].cooked);
  711.                 twrite(fd, buf, strlen(buf));
  712.             }
  713.         }
  714.     }
  715. }
  716.