home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume18 / mush6.4 / part06 / curs_io.c next >
C/C++ Source or Header  |  1989-03-12  |  13KB  |  468 lines

  1. /* @(#)curs_io.c    (c) copyright 3/18/87 (Dan Heller) */
  2.  
  3. /* curs_io.c -- curses based I/O */
  4. #include "mush.h"
  5. #include "bindings.h"
  6.  
  7. static backspace();
  8.  
  9. char *_unctrl[] = {
  10.     "^@", "^A", "^B", "^C", "^D", "^E", "^F", "^G", "^H", "^I", "^J", "^K",
  11.     "^L", "^M", "^N", "^O", "^P", "^Q", "^R", "^S", "^T", "^U", "^V", "^W",
  12.     "^X", "^Y", "^Z", "^[", "^\\", "^]", "^~", "^_",
  13.     " ", "!", "\"", "#", "$",  "%", "&", "'", "(", ")", "*", "+", ",", "-",
  14.     ".", "/", "0",  "1", "2",  "3", "4", "5", "6", "7", "8", "9", ":", ";",
  15.     "<", "=", ">",  "?", "@",  "A", "B", "C", "D", "E", "F", "G", "H", "I",
  16.     "J", "K", "L",  "M", "N",  "O", "P", "Q", "R", "S", "T", "U", "V", "W",
  17.     "X", "Y", "Z",  "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e",
  18.     "f", "g", "h",  "i", "j",  "k", "l", "m", "n", "o", "p", "q", "r", "s",
  19.     "t", "u", "v",  "w", "x",  "y", "z", "{", "|", "}", "~", "^?"
  20. };
  21.  
  22. char    del_line;    /* tty delete line character */
  23. char    del_word;    /* tty delete word character */
  24. char    del_char;    /* backspace */
  25. char    reprint_line;    /* usually ^R */
  26. char    eofc;        /* usually ^D */
  27. char    lit_next;    /* usually ^V */
  28.  
  29. tty_settings()
  30. {
  31.     savetty();
  32.  
  33. #ifdef SYSV
  34.     eofc = _tty.c_cc[VEOF];
  35. #else
  36. #ifdef BSD
  37.     if (ioctl(0, TIOCGETC, &tchars) != -1)
  38.     eofc = tchars.t_eofc;
  39.     else
  40. #endif /* BSD */
  41.     eofc = CTRL(D);
  42. #endif /* SYSV */
  43.  
  44.     if (!isatty(0)) {
  45.     del_line = CTRL(U);
  46.     del_char = CTRL(H);
  47.     } else {
  48.     del_line = _tty.sg_kill;
  49.     del_char = _tty.sg_erase;
  50.     }
  51.  
  52. #ifdef TIOCGLTC
  53.     if (ioctl(0, TIOCGLTC, <chars) != -1) {
  54.     del_word = ltchars.t_werasc;
  55.     reprint_line = ltchars.t_rprntc;
  56.     lit_next = ltchars.t_lnextc;
  57.     } else
  58. #endif /* TIOCGLTC */
  59.     {
  60.     del_word = CTRL(W);
  61.     reprint_line = CTRL(R);
  62.     lit_next = CTRL(V);
  63.     }
  64. }
  65.  
  66. #ifdef Addch
  67. #undef Addch
  68. #endif /* Addch */
  69.  
  70. #ifndef CURSES
  71.  
  72. /* Make sure all ifs have matching elses! */
  73.  
  74. #define Addch(c) \
  75.     if (ison(glob_flags, ECHO_FLAG)) \
  76.     {;} \
  77.     else \
  78.     fputc(c, stdout), fflush(stdout)
  79.  
  80. #else
  81.  
  82. /* see end of Getstr */
  83. #define Addch(c)  \
  84.     if (iscurses) \
  85.     addch(c), refresh(); \
  86.     else if (ison(glob_flags, ECHO_FLAG)) \
  87.     {;} \
  88.     else \
  89.     fputc(c, stdout), fflush(stdout)
  90. #endif /* CURSES */
  91.  
  92. /*
  93.  * get a string of at most 'length' chars.
  94.  * allow backspace-space-backspace, kill word and kill line
  95.  * (options set by user in stty).
  96.  * length is the max length this string can get. offset is from beginning
  97.  * of string.
  98.  * input of ^D returns -1; otherwise, return the number of chars in string.
  99.  */
  100. Getstr(String, length, offset)
  101. char String[];
  102. register int length;
  103. {
  104.     register int c, literal_next = FALSE, lit_bs = FALSE;
  105.     struct cmd_map *curr_map;
  106.     int count = offset, save_wc = wrapcolumn;
  107.  
  108.     fflush(stdout); /* make sure everything is flushed before getting input */
  109.  
  110.     if (mac_hide) {
  111.     curr_map = NULL_MAP;
  112.     wrapcolumn = 0;
  113.     } else if (ison(glob_flags, IS_GETTING))
  114.     curr_map = bang_map;
  115.     else if (iscurses)
  116.     curr_map = NULL_MAP;
  117.     else
  118.     curr_map = line_map;
  119.  
  120.     while ((c = m_getchar()) != '\n' && c != '\r' && c != EOF &&
  121.         isoff(glob_flags, WAS_INTR)) {
  122.     /* echo isn't set, so whatever the character, enter it */
  123.     if (ison(glob_flags, QUOTE_MACRO) || ison(glob_flags, ECHO_FLAG)) {
  124.         if (count < length) {
  125.         String[count++] = c;
  126.         /* Note: Addch includes ECHO_FLAG test */
  127.         if (iscntrl(c)) {
  128.             Addch('^');
  129.             Addch(_unctrl[c][1]);
  130.         } else
  131.             Addch(c);
  132.         } else {
  133.         print("Warning: string too long. Truncated at %d chars.\n",
  134.             length);
  135.         break;
  136.         }
  137.     }
  138.     /* ^D as the first char on a line or two ^D's in a row is EOF */
  139.     else if (c == eofc && !count)
  140.         break;
  141.     else if (c == '\\' && count < length) {
  142.         literal_next = TRUE, lit_bs = FALSE;
  143.         Addch(String[count++] = '\\');
  144.         } else if (c == lit_next && count < length) {
  145.         literal_next = lit_bs = TRUE;
  146.         String[count++] = '\\';
  147.         if (!in_macro()) {
  148.         /* if (iscntrl(c)) */
  149.             Addch('^');
  150.         /* Addch(_unctrl[c][1]); */
  151.         }
  152.     } else if (literal_next) {
  153.         struct cmd_map *list;
  154.  
  155.         literal_next = FALSE;
  156.         if (iscntrl(c) || c == del_line || c == del_char || c == del_word
  157.             || c == lit_next || lit_bs)
  158.         if (!in_macro() || !lit_bs)
  159.             backspace(String, &count);
  160.         else
  161.             --count;
  162.         else if (in_macro() && c == MAC_LONG_CMD)
  163.         --count;
  164.         /* check to see if user is escaping a map or map! */
  165.         else
  166.         for (list = curr_map; list; list = list->m_next)
  167.             if (list->m_str[0] == c) {
  168.             if (!in_macro())
  169.                 backspace(String, &count);
  170.             else
  171.                 --count;
  172.             break;
  173.             }
  174.         /* A literal-next advances the macro offset */
  175.         String[count++] = c;
  176.         if (iscntrl(c) || c == del_char) {
  177.         if (iscntrl(c)) {
  178.             /*
  179.              * Decrement wrapcolumn because two chars added.
  180.              * It will be restored from save_wc before return.
  181.              */
  182.             if (wrapcolumn > 1)
  183.             wrapcolumn--;
  184.             Addch('^');
  185.         }
  186.         Addch(_unctrl[c][1]);
  187.         } else
  188.         Addch(c);
  189.     } else if (c == del_line) {
  190.         if (count) {
  191.         do
  192.             backspace(String, &count);
  193.         while (count);
  194.         }
  195.     } else
  196.     if (c == reprint_line)
  197.         String[count] = 0, wprint("\n%s", String);
  198.         else
  199.     if (c == del_word) /* word erase */
  200.         while (count) {
  201.         backspace(String, &count);
  202.         if (!count ||
  203.             isspace(String[count-1]) && !isspace(String[count]) ||
  204.             !isalnum(String[count-1]) && isalnum(String[count]))
  205.             break;
  206.         }
  207.     else if (c == del_char || c == CTRL(H) || c == 127 /* CTRL(?) */) {
  208.         if (count)
  209.         backspace(String, &count);
  210.         /* if iscurses, then backspacing too far cancels a function */
  211.         else if (!count && iscurses && isoff(glob_flags, LINE_MACRO)) {
  212.         mac_flush();
  213.         String[0] = '\0';
  214.         wrapcolumn = save_wc;
  215.         return -1;
  216.         }
  217.     } else if (count == length)
  218.         bell();
  219.     else if (c == '\t')
  220.         do  {
  221.         /* Yuck -- tabs break map! */
  222.         Addch(' ');
  223.         String[count] = ' ';
  224.         } while (++count % 8 && count < length);
  225.     else if (in_macro() && c == MAC_LONG_CMD) {
  226.         char cbuf[MAX_LONG_CMD + 1];
  227.  
  228.         if ((c = read_long_cmd(cbuf)) == 0) {
  229.         c = MAC_LONG_CMD;
  230.         goto check_expand;    /* How could I avoid this? */
  231.         } else if (c > 0) {
  232.         int ok;
  233.  
  234.         String[count] = '\0';
  235.         if ((ok = reserved_cmd(cbuf, TRUE)) > 0) {
  236.             /* Reprint the line */
  237.             if (iscurses)
  238.             print(":%s", String);
  239.             else
  240.             wprint("\r%s", String);
  241.             continue;    /* Get next char without changing count */
  242.         } else if (ok < 0) {
  243.             String[offset] = '\0';
  244.             wrapcolumn = save_wc;
  245.             return ok;
  246.         } else
  247.             goto push_back;
  248.         } else {
  249.         /*
  250.          * Ooops.  We read a bunch of stuff we should not
  251.          * have read, because this isn't really a long command.
  252.          * Use a trick to push the whole thing back, ala ungetc.
  253.          * Wouldn't it be nifty if stdio worked this way? :-)
  254.          */
  255. push_back:
  256.         if (c > 0) {
  257.             cbuf[c++] = MAC_LONG_END;
  258.             cbuf[c] = '\0';
  259.         }
  260.         c = MAC_LONG_CMD;
  261.         Ungetstr(cbuf);
  262.         goto check_expand;    /* How could I avoid this goto? */
  263.         }
  264.     } else {
  265. check_expand:
  266.         if (!curr_map || !check_map(c, curr_map)) {
  267.         /* else if (match != MATCH) */
  268.         if (c != '\t' && iscntrl(c)) {
  269.             Addch('^');
  270.             Addch(_unctrl[c][1]);
  271.             /* Decrement wrapcolumn as above */
  272.             if (wrapcolumn > 1)
  273.             wrapcolumn--;
  274.         } else
  275.             Addch(c);
  276.         String[count++] = c;
  277.         }
  278.     }
  279.     /* Null-terminate for macro lookup purposes.
  280.      * This will be overwritten by the next character.
  281.      */
  282.     String[count] = '\0';
  283.     if (line_wrap(String, &count))
  284.         break;
  285.     }
  286.     fflush(stdout); /* for sys-v folks */
  287.  
  288.     if (c == eofc || c == EOF || ison(glob_flags, WAS_INTR)) {
  289.     if (feof(stdin))
  290.         clearerr(stdin);
  291.     wrapcolumn = save_wc;
  292.     return -1;
  293.     }
  294.     if (count && String[count-1] == '\\') {
  295.     int count2;
  296.     if (isoff(glob_flags, ECHO_FLAG))
  297.         putchar('\n');
  298.     wrapcolumn = save_wc;
  299.     /*
  300.      * NOTE: If the offset passed here is ever made greater than 0,
  301.      * the value of wrapcolumn must again be changed/restored ...
  302.      */
  303.     if ((count2 = Getstr(&String[count-1], length - count + 1, 0)) == -1)
  304.         return -1;
  305.     return count + count2;
  306.     }
  307.     if (!iscurses && isoff(glob_flags, ECHO_FLAG))
  308.     putchar('\n');
  309.     /* Should be null-terminated already, but just in case */
  310.     String[count] = '\0';
  311.     wrapcolumn = save_wc;
  312.     return count;
  313. }
  314.  
  315. static
  316. backspace(str, n)
  317. register char *str;
  318. int *n;
  319. {
  320.     (*n)--;
  321.     Addch('\b'); Addch(' '); Addch('\b');
  322.     if (iscntrl(str[*n])) {
  323.     Addch('\b'); Addch(' '); Addch('\b');
  324.     /* Re-increment wrapcolumn -- see Getstr */
  325.     if (wrapcolumn)
  326.         wrapcolumn++;
  327.     }
  328. }
  329.  
  330. #undef Addch
  331.  
  332. /*
  333.  * Check to see if what the user is typing is supposed to be expanded
  334.  * into a longer string.  The first char is 'c' and the map list to use
  335.  * is in map_list.  Continue looping (reading chars from stdin or a
  336.  * currently active mapping) until a match happens or we've determined
  337.  * that there is no match.
  338.  */
  339. check_map(c, map_list)
  340. char c;
  341. struct cmd_map *map_list;
  342. {
  343.     char mbuf[MAX_MACRO_LEN], *p = mbuf;
  344.     struct cmd_map *list;
  345.     int m, n, match;
  346.  
  347.     *p++ = c;
  348.  
  349.     while (isoff(glob_flags, WAS_INTR)) {
  350.     m = 0;
  351.     *p = 0; /* make sure it's null terminated */
  352.     /*
  353.      * loop thru the list of maps and check to see if the typed
  354.      * char matches the mapping.  If it matches completely, substitute
  355.      * the stuff in x_str and return.  If a partial match occurs, then
  356.      * read the next char until a timeout or no match.
  357.      */
  358.     for (list = map_list; list; list = list->m_next) {
  359.         if ((match = prefix(mbuf, list->m_str)) == MATCH) {
  360.         /* Must turn on flags BEFORE pushing */
  361.         line_macro(list->x_str);
  362.         return 1;
  363.         } else if (match != NO_MATCH)
  364.         m++; /* something matched partially */
  365.     }
  366.     if (!m)
  367.         break;
  368.     /* see if there's anything on the queue to read... */
  369.     if (mac_pending()
  370. #ifdef FIONREAD
  371.         || !ioctl(0, FIONREAD, &n) && n > 0
  372. #endif /* FIONREAD */
  373.                            )
  374.         *p++ = m_getchar();
  375.     else {
  376.     /* The user has typed the first part of a map or macro.  Give him
  377.      * a chance to finish it.
  378.      */
  379. #if defined(BSD) || defined(SELECT)
  380.         /* If the system has select(), use it.  It's much faster and
  381.          * more aesthetic since there is no mandatory timeout.
  382.          */
  383.         struct timeval timer;
  384. #ifdef FD_SET
  385.         fd_set rmask, wmask, xmask;
  386.         FD_SET(0, &rmask);    /* Test stdin for read */
  387.         FD_ZERO(&wmask);    /* Don't care about write */
  388.         FD_ZERO(&xmask);    /* Don't care about exception */
  389. #else
  390.         int rmask = 1, wmask = 0, xmask = 0;
  391. #endif /* FD_SET */
  392.         timer.tv_sec = 1;
  393.         timer.tv_usec = 0;
  394.         n = select(1, &rmask, &wmask, &xmask, &timer);
  395. #else /* !SELECT */
  396. #ifdef FIONREAD
  397.         /* system doesn't have select(), so use FIONREAD to see if
  398.          * there are any chars on the queue to read.
  399.          */
  400.         (void) sleep(1);
  401.         (void) ioctl(0, FIONREAD, &n);
  402. #else
  403.         /* system has neither select() nor FIONREAD, so just set n
  404.          * and force the user to either complete the map or fail it
  405.          * without a timeout.  Chars won't echo till he does one or
  406.          * the other.
  407.          */
  408.         n = 1;
  409. #endif /* FIONREAD */
  410. #endif /* SELECT */
  411.         if (n > 0)
  412.         /* don't read all 'n' chars -- there may be a match early */
  413.         *p++ = m_getchar();    /* To flush macros and reset flags */
  414.         else /* still nothing to read? User doesn't want to use map */
  415.         break;
  416.     }
  417.     }
  418.     /* no match or a timeout.  This isn't a map, just return. */
  419.     *p = 0;
  420.     if (mbuf[1])
  421.     mac_push(mbuf + 1);
  422.     return 0;
  423. }
  424.  
  425. /*
  426.  * Check for line wrap.  This should happen only in composition mode and
  427.  * only when the variable wrapcolumn has a value greater than zero.  Line
  428.  * wrap is implemented using Ungetstr [that is, mac_push()].
  429.  *
  430.  * Returns 1 if the line was wrapped, 0 if not.
  431.  */
  432. line_wrap(string, count)
  433. char *string;    /* The string to be wrapped */
  434. int *count;    /* Offset of string terminator */
  435. {
  436.     char *tail = NULL;
  437.     int n = *count;
  438.  
  439.     if (wrapcolumn < 1 || *count <= wrapcolumn
  440.         || isoff(glob_flags, IS_GETTING)    /* Wrap only in msg body */
  441.         || ison(glob_flags, QUOTE_MACRO)    /* Don't wrap quoted macros */
  442.         || ison(glob_flags, ECHO_FLAG))    /* Can't wrap in echo mode */
  443.     return 0;
  444.  
  445.     /* Back up past the wrapcolumn point */
  446.     for (; n > wrapcolumn; --n)
  447.         ;
  448.     /* Look for a space */
  449.     while (n && !isspace(string[n]))
  450.     --n;
  451.     /* If no break found, return no wrap */
  452.     if (!n)
  453.     return 0;
  454.     tail = &string[n+1];
  455.     /* Skip the break char and any whitespace */
  456.     while (n && isspace(string[n]))
  457.     --n;
  458.     ++n; /* move back into the whitespace */
  459.     /* Erase the stuff that will wrap */
  460.     while (*count > n)
  461.     backspace(string,count);
  462.     string[*count] = '\0';
  463.     /* Push the tail, if any */
  464.     if (*tail)
  465.     Ungetstr(tail);
  466.     return 1;
  467. }
  468.