home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / ex / ex_txt.c < prev    next >
C/C++ Source or Header  |  1996-10-10  |  11KB  |  431 lines

  1. /*-
  2.  * Copyright (c) 1992, 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 1992, 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[] = "@(#)ex_txt.c    10.17 (Berkeley) 10/10/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18.  
  19. #include <bitstring.h>
  20. #include <ctype.h>
  21. #include <limits.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25.  
  26. #include "../common/common.h"
  27.  
  28. /*
  29.  * !!!
  30.  * The backslash characters was special when it preceded a newline as part of
  31.  * a substitution replacement pattern.  For example, the input ":a\<cr>" would
  32.  * failed immediately with an error, as the <cr> wasn't part of a substitution
  33.  * replacement pattern.  This implies a frightening integration of the editor
  34.  * and the parser and/or the RE engine.  There's no way I'm going to reproduce
  35.  * those semantics.
  36.  *
  37.  * So, if backslashes are special, this code inserts the backslash and the next
  38.  * character into the string, without regard for the character or the command
  39.  * being entered.  Since "\<cr>" was illegal historically (except for the one
  40.  * special case), and the command will fail eventually, no historical scripts
  41.  * should break (presuming they didn't depend on the failure mode itself or the
  42.  * characters remaining when failure occurred.
  43.  */
  44.  
  45. static int    txt_dent __P((SCR *, TEXT *));
  46. static void    txt_prompt __P((SCR *, TEXT *, ARG_CHAR_T, u_int32_t));
  47.  
  48. /*
  49.  * ex_txt --
  50.  *    Get lines from the terminal for ex.
  51.  *
  52.  * PUBLIC: int ex_txt __P((SCR *, TEXTH *, ARG_CHAR_T, u_int32_t));
  53.  */
  54. int
  55. ex_txt(sp, tiqh, prompt, flags)
  56.     SCR *sp;
  57.     TEXTH *tiqh;
  58.     ARG_CHAR_T prompt;
  59.     u_int32_t flags;
  60. {
  61.     EVENT ev;
  62.     GS *gp;
  63.     TEXT ait, *ntp, *tp;
  64.     carat_t carat_st;
  65.     size_t cnt;
  66.     int rval;
  67.  
  68.     rval = 0;
  69.  
  70.     /*
  71.      * Get a TEXT structure with some initial buffer space, reusing the
  72.      * last one if it's big enough.  (All TEXT bookkeeping fields default
  73.      * to 0 -- text_init() handles this.)
  74.      */
  75.     if (tiqh->cqh_first != (void *)tiqh) {
  76.         tp = tiqh->cqh_first;
  77.         if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) {
  78.             text_lfree(tiqh);
  79.             goto newtp;
  80.         }
  81.         tp->len = 0;
  82.     } else {
  83. newtp:        if ((tp = text_init(sp, NULL, 0, 32)) == NULL)
  84.             goto err;
  85.         CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
  86.     }
  87.  
  88.     /* Set the starting line number. */
  89.     tp->lno = sp->lno + 1;
  90.  
  91.     /*
  92.      * If it's a terminal, set up autoindent, put out the prompt, and
  93.      * set it up so we know we were suspended.  Otherwise, turn off
  94.      * the autoindent flag, as that requires less special casing below.
  95.      *
  96.      * XXX
  97.      * Historic practice is that ^Z suspended command mode (but, because
  98.      * it ran in cooked mode, it was unaffected by the autowrite option.)
  99.      * On restart, any "current" input was discarded, whether in insert
  100.      * mode or not, and ex was in command mode.  This code matches historic
  101.      * practice, but not 'cause it's easier.
  102.      */
  103.     gp = sp->gp;
  104.     if (F_ISSET(gp, G_SCRIPTED))
  105.         LF_CLR(TXT_AUTOINDENT);
  106.     else {
  107.         if (LF_ISSET(TXT_AUTOINDENT)) {
  108.             LF_SET(TXT_EOFCHAR);
  109.             if (v_txt_auto(sp, sp->lno, NULL, 0, tp))
  110.                 goto err;
  111.         }
  112.         txt_prompt(sp, tp, prompt, flags);
  113.     }
  114.  
  115.     for (carat_st = C_NOTSET;;) {
  116.         if (v_event_get(sp, &ev, 0, 0))
  117.             goto err;
  118.  
  119.         /* Deal with all non-character events. */
  120.         switch (ev.e_event) {
  121.         case E_CHARACTER:
  122.             break;
  123.         case E_ERR:
  124.             goto err;
  125.         case E_REPAINT:
  126.         case E_WRESIZE:
  127.             continue;
  128.         case E_EOF:
  129.             rval = 1;
  130.             /* FALLTHROUGH */
  131.         case E_INTERRUPT:
  132.             /*
  133.              * Handle EOF/SIGINT events by discarding partially
  134.              * entered text and returning.  EOF returns failure,
  135.              * E_INTERRUPT returns success.
  136.              */
  137.             goto notlast;
  138.         default:
  139.             v_event_err(sp, &ev);
  140.             goto notlast;
  141.         }
  142.  
  143.         /*
  144.          * Deal with character events.
  145.          *
  146.          * Check to see if the character fits into the input buffer.
  147.          * (Use tp->len, ignore overwrite and non-printable chars.)
  148.          */
  149.         BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1);
  150.  
  151.         switch (ev.e_value) {
  152.         case K_CR:
  153.             /*
  154.              * !!!
  155.              * Historically, <carriage-return>'s in the command
  156.              * weren't special, so the ex parser would return an
  157.              * unknown command error message.  However, if they
  158.              * terminated the command if they were in a map.  I'm
  159.              * pretty sure this still isn't right, but it handles
  160.              * what I've seen so far.
  161.              */
  162.             if (!F_ISSET(&ev.e_ch, CH_MAPPED))
  163.                 goto ins_ch;
  164.             /* FALLTHROUGH */
  165.         case K_NL:
  166.             /*
  167.              * '\' can escape <carriage-return>/<newline>.  We
  168.              * don't discard the backslash because we need it
  169.              * to get the <newline> through the ex parser.
  170.              */
  171.             if (LF_ISSET(TXT_BACKSLASH) &&
  172.                 tp->len != 0 && tp->lb[tp->len - 1] == '\\')
  173.                 goto ins_ch;
  174.  
  175.             /*
  176.              * CR returns from the ex command line.
  177.              *
  178.              * XXX
  179.              * Terminate with a nul, needed by filter.
  180.              */
  181.             if (LF_ISSET(TXT_CR)) {
  182.                 tp->lb[tp->len] = '\0';
  183.                 goto done;
  184.             }
  185.  
  186.             /*
  187.              * '.' may terminate text input mode; free the current
  188.              * TEXT.
  189.              */
  190.             if (LF_ISSET(TXT_DOTTERM) && tp->len == tp->ai + 1 &&
  191.                 tp->lb[tp->len - 1] == '.') {
  192. notlast:            CIRCLEQ_REMOVE(tiqh, tp, q);
  193.                 text_free(tp);
  194.                 goto done;
  195.             }
  196.  
  197.             /* Set up bookkeeping for the new line. */
  198.             if ((ntp = text_init(sp, NULL, 0, 32)) == NULL)
  199.                 goto err;
  200.             ntp->lno = tp->lno + 1;
  201.  
  202.             /*
  203.              * Reset the autoindent line value.  0^D keeps the ai
  204.              * line from changing, ^D changes the level, even if
  205.              * there were no characters in the old line.  Note, if
  206.              * using the current tp structure, use the cursor as
  207.              * the length, the autoindent characters may have been
  208.              * erased.
  209.              */
  210.             if (LF_ISSET(TXT_AUTOINDENT)) {
  211.                 if (carat_st == C_NOCHANGE) {
  212.                     if (v_txt_auto(sp,
  213.                         OOBLNO, &ait, ait.ai, ntp))
  214.                         goto err;
  215.                     free(ait.lb);
  216.                 } else
  217.                     if (v_txt_auto(sp,
  218.                         OOBLNO, tp, tp->len, ntp))
  219.                         goto err;
  220.                 carat_st = C_NOTSET;
  221.             }
  222.             txt_prompt(sp, ntp, prompt, flags);
  223.  
  224.             /*
  225.              * Swap old and new TEXT's, and insert the new TEXT
  226.              * into the queue.
  227.              */
  228.             tp = ntp;
  229.             CIRCLEQ_INSERT_TAIL(tiqh, tp, q);
  230.             break;
  231.         case K_CARAT:            /* Delete autoindent chars. */
  232.             if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
  233.                 carat_st = C_CARATSET;
  234.             goto ins_ch;
  235.         case K_ZERO:            /* Delete autoindent chars. */
  236.             if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
  237.                 carat_st = C_ZEROSET;
  238.             goto ins_ch;
  239.         case K_CNTRLD:            /* Delete autoindent char. */
  240.             /*
  241.              * !!!
  242.              * Historically, the ^D command took (but then ignored)
  243.              * a count.  For simplicity, we don't return it unless
  244.              * it's the first character entered.  The check for len
  245.              * equal to 0 is okay, TXT_AUTOINDENT won't be set.
  246.              */
  247.             if (LF_ISSET(TXT_CNTRLD)) {
  248.                 for (cnt = 0; cnt < tp->len; ++cnt)
  249.                     if (!isblank(tp->lb[cnt]))
  250.                         break;
  251.                 if (cnt == tp->len) {
  252.                     tp->len = 1;
  253.                     tp->lb[0] = ev.e_c;
  254.                     tp->lb[1] = '\0';
  255.  
  256.                     /*
  257.                      * Put out a line separator, in case
  258.                      * the command fails.
  259.                      */
  260.                     (void)putchar('\n');
  261.                     goto done;
  262.                 }
  263.             }
  264.  
  265.             /*
  266.              * POSIX 1003.1b-1993, paragraph 7.1.1.9, states that
  267.              * the EOF characters are discarded if there are other
  268.              * characters to process in the line, i.e. if the EOF
  269.              * is not the first character in the line.  For this
  270.              * reason, historic ex discarded the EOF characters,
  271.              * even if occurring in the middle of the input line.
  272.              * We match that historic practice.
  273.              *
  274.              * !!!
  275.              * The test for discarding in the middle of the line is
  276.              * done in the switch, because the CARAT forms are N+1,
  277.              * not N.
  278.              *
  279.              * !!!
  280.              * There's considerable magic to make the terminal code
  281.              * return the EOF character at all.  See that code for
  282.              * details.
  283.              */
  284.             if (!LF_ISSET(TXT_AUTOINDENT) || tp->len == 0)
  285.                 continue;
  286.             switch (carat_st) {
  287.             case C_CARATSET:        /* ^^D */
  288.                 if (tp->len > tp->ai + 1)
  289.                     continue;
  290.  
  291.                 /* Save the ai string for later. */
  292.                 ait.lb = NULL;
  293.                 ait.lb_len = 0;
  294.                 BINC_GOTO(sp, ait.lb, ait.lb_len, tp->ai);
  295.                 memcpy(ait.lb, tp->lb, tp->ai);
  296.                 ait.ai = ait.len = tp->ai;
  297.  
  298.                 carat_st = C_NOCHANGE;
  299.                 goto leftmargin;
  300.             case C_ZEROSET:            /* 0^D */
  301.                 if (tp->len > tp->ai + 1)
  302.                     continue;
  303.  
  304.                 carat_st = C_NOTSET;
  305. leftmargin:            (void)gp->scr_ex_adjust(sp, EX_TERM_CE);
  306.                 tp->ai = tp->len = 0;
  307.                 break;
  308.             case C_NOTSET:            /* ^D */
  309.                 if (tp->len > tp->ai)
  310.                     continue;
  311.  
  312.                 if (txt_dent(sp, tp))
  313.                     goto err;
  314.                 break;
  315.             default:
  316.                 abort();
  317.             }
  318.  
  319.             /* Clear and redisplay the line. */
  320.             (void)gp->scr_ex_adjust(sp, EX_TERM_CE);
  321.             txt_prompt(sp, tp, prompt, flags);
  322.             break;
  323.         default:
  324.             /*
  325.              * See the TXT_BEAUTIFY comment in vi/v_txt_ev.c.
  326.              *
  327.              * Silently eliminate any iscntrl() character that was
  328.              * not already handled specially, except for <tab> and
  329.              * <ff>.
  330.              */
  331. ins_ch:            if (LF_ISSET(TXT_BEAUTIFY) && iscntrl(ev.e_c) &&
  332.                 ev.e_value != K_FORMFEED && ev.e_value != K_TAB)
  333.                 break;
  334.  
  335.             tp->lb[tp->len++] = ev.e_c;
  336.             break;
  337.         }
  338.     }
  339.     /* NOTREACHED */
  340.  
  341. done:    return (rval);
  342.  
  343. err:    
  344. alloc_err:
  345.     return (1);
  346. }
  347.  
  348. /*
  349.  * txt_prompt --
  350.  *    Display the ex prompt, line number, ai characters.  Characters had
  351.  *    better be printable by the terminal driver, but that's its problem,
  352.  *    not ours.
  353.  */
  354. static void
  355. txt_prompt(sp, tp, prompt, flags)
  356.     SCR *sp;
  357.     TEXT *tp;
  358.     ARG_CHAR_T prompt;
  359.     u_int32_t flags;
  360. {
  361.     /* Display the prompt. */
  362.     if (LF_ISSET(TXT_PROMPT))
  363.         (void)printf("%c", prompt);
  364.  
  365.     /* Display the line number. */
  366.     if (LF_ISSET(TXT_NUMBER) && O_ISSET(sp, O_NUMBER))
  367.         (void)printf("%6lu  ", (u_long)tp->lno);
  368.  
  369.     /* Print out autoindent string. */
  370.     if (LF_ISSET(TXT_AUTOINDENT))
  371.         (void)printf("%.*s", (int)tp->ai, tp->lb);
  372.     (void)fflush(stdout);
  373. }
  374.  
  375. /*
  376.  * txt_dent --
  377.  *    Handle ^D outdents.
  378.  *
  379.  * Ex version of vi/v_ntext.c:txt_dent().  See that code for the (usual)
  380.  * ranting and raving.  This is a fair bit simpler as ^T isn't special.
  381.  */
  382. static int
  383. txt_dent(sp, tp)
  384.     SCR *sp;
  385.     TEXT *tp;
  386. {
  387.     u_long sw, ts;
  388.     size_t cno, off, scno, spaces, tabs;
  389.  
  390.     ts = O_VAL(sp, O_TABSTOP);
  391.     sw = O_VAL(sp, O_SHIFTWIDTH);
  392.  
  393.     /* Get the current screen column. */
  394.     for (off = scno = 0; off < tp->len; ++off)
  395.         if (tp->lb[off] == '\t')
  396.             scno += COL_OFF(scno, ts);
  397.         else
  398.             ++scno;
  399.  
  400.     /* Get the previous shiftwidth column. */
  401.     cno = scno;
  402.     scno -= --scno % sw;
  403.  
  404.     /*
  405.      * Since we don't know what comes before the character(s) being
  406.      * deleted, we have to resolve the autoindent characters .  The
  407.      * example is a <tab>, which doesn't take up a full shiftwidth
  408.      * number of columns because it's preceded by <space>s.  This is
  409.      * easy to get if the user sets shiftwidth to a value less than
  410.      * tabstop, and then uses ^T to indent, and ^D to outdent.
  411.      *
  412.      * Count up spaces/tabs needed to get to the target.
  413.      */
  414.     for (cno = 0, tabs = 0; cno + COL_OFF(cno, ts) <= scno; ++tabs)
  415.         cno += COL_OFF(cno, ts);
  416.     spaces = scno - cno;
  417.  
  418.     /* Make sure there's enough room. */
  419.     BINC_RET(sp, tp->lb, tp->lb_len, tabs + spaces + 1);
  420.  
  421.     /* Adjust the final ai character count. */
  422.     tp->ai = tabs + spaces;
  423.  
  424.     /* Enter the replacement characters. */
  425.     for (tp->len = 0; tabs > 0; --tabs)
  426.         tp->lb[tp->len++] = '\t';
  427.     for (; spaces > 0; --spaces)
  428.         tp->lb[tp->len++] = ' ';
  429.     return (0);
  430. }
  431.