home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / cl / cl_read.c < prev    next >
C/C++ Source or Header  |  1997-06-27  |  12KB  |  500 lines

  1. /*-
  2.  * Copyright (c) 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 1993, 1994, 1995, 1996
  5.  *    Keith Bostic.  All rights reserved.
  6.  *
  7.  * See the LICENSE file for redistribution information.
  8.  */
  9.  
  10. #include "config.h"
  11.  
  12. #ifndef lint
  13. static const char sccsid[] = "@(#)cl_read.c    10.15 (Berkeley) 9/24/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18. #ifdef HAVE_SYS_SELECT_H
  19. #include <sys/select.h>
  20. #endif
  21. #include <sys/time.h>
  22.  
  23. #include <bitstring.h>
  24. #include <curses.h>
  25. #include <errno.h>
  26. #include <fcntl.h>
  27. #include <signal.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <termios.h>
  32. #include <unistd.h>
  33.  
  34. #include "../common/common.h"
  35. #include "../ex/script.h"
  36. #include "cl.h"
  37.  
  38. static input_t    cl_read __P((SCR *,
  39.     u_int32_t, CHAR_T *, size_t, int *, struct timeval *));
  40. static int    cl_resize __P((SCR *, size_t, size_t));
  41.  
  42. #ifdef __EMX__
  43. #ifdef EMX_MOUSE
  44. static int    mou_read __P((SCR *, char *, int));
  45. #endif
  46. #endif
  47.  
  48. /*
  49.  * cl_event --
  50.  *    Return a single event.
  51.  *
  52.  * PUBLIC: int cl_event __P((SCR *, EVENT *, u_int32_t, int));
  53.  */
  54. int
  55. cl_event(sp, evp, flags, ms)
  56.     SCR *sp;
  57.     EVENT *evp;
  58.     u_int32_t flags;
  59.     int ms;
  60. {
  61.     struct timeval t, *tp;
  62.     CL_PRIVATE *clp;
  63.     size_t lines, columns;
  64.     int changed, nr;
  65.  
  66.     /*
  67.      * Queue signal based events.  We never clear SIGHUP or SIGTERM events,
  68.      * so that we just keep returning them until the editor dies.
  69.      */
  70.     clp = CLP(sp);
  71. retest:    if (LF_ISSET(EC_INTERRUPT) || F_ISSET(clp, CL_SIGINT)) {
  72.         if (F_ISSET(clp, CL_SIGINT)) {
  73.             F_CLR(clp, CL_SIGINT);
  74.             evp->e_event = E_INTERRUPT;
  75.         } else
  76.             evp->e_event = E_TIMEOUT;
  77.         return (0);
  78.     }
  79.     if (F_ISSET(clp, CL_SIGHUP | CL_SIGTERM | CL_SIGWINCH)) {
  80.         if (F_ISSET(clp, CL_SIGHUP)) {
  81.             evp->e_event = E_SIGHUP;
  82.             return (0);
  83.         }
  84.         if (F_ISSET(clp, CL_SIGTERM)) {
  85.             evp->e_event = E_SIGTERM;
  86.             return (0);
  87.         }
  88.         if (F_ISSET(clp, CL_SIGWINCH)) {
  89.             F_CLR(clp, CL_SIGWINCH);
  90.             if (cl_ssize(sp, 1, &lines, &columns, &changed))
  91.                 return (1);
  92.             if (changed) {
  93.                 (void)cl_resize(sp, lines, columns);
  94.                 evp->e_event = E_WRESIZE;
  95.                 return (0);
  96.             }
  97.             /* No real change, ignore the signal. */
  98.         }
  99.     }
  100.  
  101.     /* Set timer. */
  102.     if (ms == 0)
  103.         tp = NULL;
  104.     else {
  105.         t.tv_sec = ms / 1000;
  106.         t.tv_usec = (ms % 1000) * 1000;
  107.         tp = &t;
  108.     }
  109.  
  110.     /* Read input characters. */
  111.     switch (cl_read(sp, LF_ISSET(EC_QUOTED | EC_RAW),
  112.         clp->ibuf, sizeof(clp->ibuf), &nr, tp)) {
  113.     case INP_OK:
  114.         evp->e_csp = clp->ibuf;
  115.         evp->e_len = nr;
  116.         evp->e_event = E_STRING;
  117.         break;
  118.     case INP_EOF:
  119.         evp->e_event = E_EOF;
  120.         break;
  121.     case INP_ERR:
  122.         evp->e_event = E_ERR;
  123.         break;
  124.     case INP_INTR:
  125.         goto retest;
  126.     case INP_TIMEOUT:
  127.         evp->e_event = E_TIMEOUT;
  128.         break;
  129.     default:
  130.         abort();
  131.     }
  132.     return (0);
  133. }
  134.  
  135. /*
  136.  * cl_read --
  137.  *    Read characters from the input.
  138.  */
  139. static input_t
  140. cl_read(sp, flags, bp, blen, nrp, tp)
  141.     SCR *sp;
  142.     u_int32_t flags;
  143.     CHAR_T *bp;
  144.     size_t blen;
  145.     int *nrp;
  146.     struct timeval *tp;
  147. {
  148.     struct termios term1, term2;
  149.     struct timeval poll;
  150.     CL_PRIVATE *clp;
  151.     GS *gp;
  152.     SCR *tsp;
  153.     fd_set rdfd;
  154.     input_t rval;
  155.     int maxfd, nr, term_reset;
  156.  
  157.     gp = sp->gp;
  158.     clp = CLP(sp);
  159.     term_reset = 0;
  160.  
  161.     /*
  162.      * 1: A read from a file or a pipe.  In this case, the reads
  163.      *    never timeout regardless.  This means that we can hang
  164.      *    when trying to complete a map, but we're going to hang
  165.      *    on the next read anyway.
  166.      */
  167.     if (!F_ISSET(clp, CL_STDIN_TTY)) {
  168.         switch (nr = read(STDIN_FILENO, bp, blen)) {
  169.         case 0:
  170.             return (INP_EOF);
  171.         case -1:
  172.             goto err;
  173.         default:
  174.             *nrp = nr;
  175.             return (INP_OK);
  176.         }
  177.         /* NOTREACHED */
  178.     }
  179.  
  180.     /*
  181.      * 2: A read with an associated timeout, e.g., trying to complete
  182.      *    a map sequence.  If input exists, we fall into #3.
  183.      */
  184.     FD_ZERO(&rdfd);
  185.     poll.tv_sec = 0;
  186.     poll.tv_usec = 0;
  187.     if (tp != NULL) {
  188.         FD_SET(STDIN_FILENO, &rdfd);
  189.         switch (select(STDIN_FILENO + 1,
  190.             &rdfd, NULL, NULL, tp == NULL ? &poll : tp)) {
  191.         case 0:
  192.             return (INP_TIMEOUT);
  193.         case -1:
  194.             goto err;
  195.         default:
  196.             break;
  197.         }
  198.     }
  199.     
  200.     /*
  201.      * The user can enter a key in the editor to quote a character.  If we
  202.      * get here and the next key is supposed to be quoted, do what we can.
  203.      * Reset the tty so that the user can enter a ^C, ^Q, ^S.  There's an
  204.      * obvious race here, when the key has already been entered, but there's
  205.      * nothing that we can do to fix that problem.
  206.      *
  207.      * The editor can ask for the next literal character even thought it's
  208.      * generally running in line-at-a-time mode.  Do what we can.
  209.      */
  210.     if (LF_ISSET(EC_QUOTED | EC_RAW) && !tcgetattr(STDIN_FILENO, &term1)) {
  211.         term_reset = 1;
  212.         if (LF_ISSET(EC_QUOTED)) {
  213.             term2 = term1;
  214.             term2.c_lflag &= ~ISIG;
  215.             term2.c_iflag &= ~(IXON | IXOFF);
  216.             (void)tcsetattr(STDIN_FILENO,
  217.                 TCSASOFT | TCSADRAIN, &term2);
  218.         } else
  219.             (void)tcsetattr(STDIN_FILENO,
  220.                 TCSASOFT | TCSADRAIN, &clp->vi_enter);
  221.     }
  222.  
  223.     /*
  224.      * 3: Wait for input.
  225.      *
  226.      * Select on the command input and scripting window file descriptors.
  227.      * It's ugly that we wait on scripting file descriptors here, but it's
  228.      * the only way to keep from locking out scripting windows.
  229.      */
  230. #if defined(__EMX__) && defined(EMX_MOUSE)
  231.     if ((clp->mou_pipe != -1 && !term_reset) || F_ISSET(gp, G_SCRWIN)) {
  232. #else
  233.     if (F_ISSET(gp, G_SCRWIN)) {
  234. #endif
  235. loop:        FD_ZERO(&rdfd);
  236.         FD_SET(STDIN_FILENO, &rdfd);
  237.         maxfd = STDIN_FILENO;
  238. #ifdef __EMX__
  239. #ifdef EMX_MOUSE
  240.         if (clp->mou_pipe != -1)
  241.         {
  242.             FD_SET(clp->mou_pipe, &rdfd);
  243.             if (clp->mou_pipe > maxfd)
  244.             maxfd = clp->mou_pipe;
  245.         }
  246. #endif
  247. #endif
  248.         for (tsp = gp->dq.cqh_first;
  249.             tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
  250.             if (F_ISSET(sp, SC_SCRIPT)) {
  251. #if VI_DOSISH
  252.                 FD_SET(sp->script->sh_master >> 16, &rdfd);
  253.                 if ((sp->script->sh_master >> 16) > maxfd)
  254.                     maxfd = sp->script->sh_master >> 16;
  255. #else
  256.                 FD_SET(sp->script->sh_master, &rdfd);
  257.                 if (sp->script->sh_master > maxfd)
  258.                     maxfd = sp->script->sh_master;
  259. #endif
  260.             }
  261.         switch (select(maxfd + 1, &rdfd, NULL, NULL, NULL)) {
  262.         case 0:
  263.             abort();
  264.         case -1:
  265.             goto err;
  266.         default:
  267.             break;
  268.         }
  269.         if (!FD_ISSET(STDIN_FILENO, &rdfd)) {
  270. #ifdef __EMX__
  271. #ifdef EMX_MOUSE
  272.             if (FD_ISSET(clp->mou_pipe, &rdfd))
  273.             {
  274.                 if ((nr = mou_read(sp, bp, blen)) == -1)
  275.                 {
  276.                 close(clp->mou_pipe);
  277.                 clp->mou_pipe = -1;
  278.                 }
  279.                 else if (nr > 0)
  280.                 {
  281.                 *nrp = nr;
  282.                 clp->eof_count = 0;
  283.                 return INP_OK;
  284.                 }
  285.             }
  286.             else
  287. #endif
  288. #endif
  289.             if (sscr_input(sp))
  290.                 return (INP_ERR);
  291.             goto loop;
  292.         }
  293.     }
  294.  
  295.     /*
  296.      * 4: Read the input.
  297.      *
  298.      * !!!
  299.      * What's going on here is some scary stuff.  Ex runs the terminal in
  300.      * canonical mode.  So, the <newline> character terminating a line of
  301.      * input is returned in the buffer, but a trailing <EOF> character is
  302.      * not similarly included.  As ex uses 0<EOF> and ^<EOF> as autoindent
  303.      * commands, it has to see the trailing <EOF> characters to determine
  304.      * the difference between the user entering "0ab" and "0<EOF>ab".  We
  305.      * leave an extra slot in the buffer, so that we can add a trailing
  306.      * <EOF> character if the buffer isn't terminated by a <newline>.  We
  307.      * lose if the buffer is too small for the line and exactly N characters
  308.      * are entered followed by an <EOF> character.
  309.      */
  310. #define    ONE_FOR_EOF    1
  311.     switch (nr = read(STDIN_FILENO, bp, blen - ONE_FOR_EOF)) {
  312.     case  0:                /* EOF. */
  313.         /*
  314.          * ^D in canonical mode returns a read of 0, i.e. EOF.  EOF is
  315.          * a valid command, but we don't want to loop forever because
  316.          * the terminal driver is returning EOF because the user has
  317.          * disconnected. The editor will almost certainly try to write
  318.          * something before this fires, which should kill us, but You
  319.          * Never Know.
  320.          */
  321.         if (++clp->eof_count < 50) {
  322.             bp[0] = clp->orig.c_cc[VEOF];
  323.             *nrp = 1;
  324.             rval = INP_OK;
  325.  
  326.         } else
  327.             rval = INP_EOF;
  328.         break;
  329.     case -1:                /* Error or interrupt. */
  330. err:        if (errno == EINTR)
  331.             rval = INP_INTR;
  332.         else {
  333.             rval = INP_ERR;
  334.             msgq(sp, M_SYSERR, "input");
  335.         }
  336.         break;
  337.     default:                /* Input characters. */
  338.         if (F_ISSET(sp, SC_EX) && bp[nr - 1] != '\n')
  339.             bp[nr++] = clp->orig.c_cc[VEOF];
  340.         *nrp = nr;
  341.         clp->eof_count = 0;
  342.         rval = INP_OK;
  343.         break;
  344.     }
  345.  
  346.     /* Restore the terminal state if it was modified. */
  347.     if (term_reset)
  348.         (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &term1);
  349.     return (rval);
  350. }
  351.  
  352. /* 
  353.  * cl_resize --
  354.  *    Reset the options for a resize event.
  355.  */
  356. static int
  357. cl_resize(sp, lines, columns)
  358.     SCR *sp;
  359.     size_t lines, columns;
  360. {
  361.     ARGS *argv[2], a, b;
  362.     char b1[1024];
  363.  
  364.     a.bp = b1;
  365.     b.bp = NULL;
  366.     a.len = b.len = 0;
  367.     argv[0] = &a;
  368.     argv[1] = &b;
  369.  
  370.     (void)snprintf(b1, sizeof(b1), "lines=%lu", (u_long)lines);
  371.     a.len = strlen(b1);
  372.     if (opts_set(sp, argv, NULL))
  373.         return (1);
  374.     (void)snprintf(b1, sizeof(b1), "columns=%lu", (u_long)columns);
  375.     a.len = strlen(b1);
  376.     if (opts_set(sp, argv, NULL))
  377.         return (1);
  378.     return (0);
  379. }
  380.  
  381. #ifdef __EMX__
  382. #ifdef EMX_MOUSE
  383. /*
  384.  * mou_read --
  385.  *    Read a "digested" mouse event and return a command string.
  386.  *    For now, all mouse buttons are equivalent and we only generate
  387.  *    movement commands.  Said commands are not optimized.
  388.  */
  389. static int mou_read(sp, bp, len)
  390.     SCR *sp;
  391.     char *bp;
  392.     int len;
  393. {
  394.     struct msev ev;
  395.     char xbuf[sizeof ev];
  396.     int c, f;
  397.     CL_PRIVATE *clp;
  398.     GS *gp;
  399.     SCR *n;
  400.  
  401.     gp = sp->gp;
  402.     clp = CLP(sp);
  403.     c = 0;
  404.     while (c + (f = read(clp->mou_pipe, xbuf + c, sizeof ev - c)) < sizeof ev)
  405.     {
  406.     if (f == -1)
  407.         return -1;
  408.     c += f;
  409.     }
  410.     memcpy(&ev, xbuf, sizeof ev);
  411.     if (!F_SET(sp, SC_VI)) /* mouse actions only in vi */
  412.     return 0;
  413.     /*
  414.      * This gets fairly disgusting.  First, we have to escape out of any
  415.      * insert etc. mode.  Then we have to check to see if we're switching
  416.      * screens; then we have to position within the screen.  If this lands
  417.      * us in a prompt area, we push a colon command instead.  If not, we
  418.      * must reestablish the previous input mode.  And if we run out of space
  419.      * doing any of this, we beep and return 0.
  420.      *
  421.      * !!!
  422.      * No attempt is made to deal with mapped keys; we're called at too low
  423.      * a level.  Likewise, invoking this when a motion is expected to complete
  424.      * a command will fail.  Fixing this would pretty much require a new,
  425.      * magic "go directly to specified screen R/C" command and, again, we are
  426.      * at too low a level to pull it off.
  427.      */
  428.     c = 0;
  429.     if (sp->showmode != SM_COMMAND)
  430.     {
  431.     /* escape to command mode */
  432.     *bp++ = '\033';
  433.     len--;
  434.     c++;
  435.     }
  436.     for (n = sp, f = 0; n != sp || !f; n = n->q.cqe_next, f = 1)
  437.     {
  438.     if (n->woff <= ev.y && n->woff + n->rows > ev.y)
  439.         break;
  440.     if (!len)
  441.         break;
  442.     *bp++ = '\027';    /* ^W */
  443.     len--;
  444.     c++;
  445.     }
  446.     if (!len)
  447.     return 0;
  448.     if (n->woff + n->rows - 1 == ev.y)
  449.     {
  450.     /* enter command mode */
  451.     *bp = ':';
  452.     return c + 1;
  453.     }
  454.     /* now we have to figure out where in the window we are... */
  455.     /* getting current screen position is a lost cause, it seems */
  456.     ev.y -= n->woff - 1;
  457.     if (len < 1 + (ev.y == 1? 0: ev.y < 10? 1: ev.y < 100? 2: 3))
  458.     return 0;
  459.     if (ev.y != 1)
  460.     {
  461.     sprintf(bp, "%d", ev.y);
  462.     while (*bp)
  463.     {
  464.         bp++;
  465.         len--;
  466.         c++;
  467.     }
  468.     }
  469.     *bp++ = 'H';
  470.     len--;
  471.     c++;
  472.     /*
  473.      * The columns are really fun, because tabs and non-ASCII throw them off.
  474.      * And we can't "see" the file to know what to do.  :-(
  475.      * We just use '0' and/or '|' and hope.
  476.      */
  477.     ev.x++;
  478.     if (len < 1 + (ev.x == 1? 0: ev.x < 10? 1: ev.x < 100? 2: 3))
  479.     return 0;
  480.     if (ev.x == 1)
  481.     {
  482.     *bp++ = '0';
  483.     len--;
  484.     c++;
  485.     }
  486.     else
  487.     {
  488.     sprintf(bp, "%d|", ev.x);
  489.     while (*bp)
  490.     {
  491.         bp++;
  492.         len--;
  493.         c++;
  494.     }
  495.     }
  496.     return c;
  497. }
  498. #endif
  499. #endif
  500.