home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / v_itxt.c < prev    next >
C/C++ Source or Header  |  1996-10-23  |  13KB  |  538 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[] = "@(#)v_itxt.c    10.16 (Berkeley) 10/23/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18. #include <sys/time.h>
  19.  
  20. #include <bitstring.h>
  21. #include <ctype.h>
  22. #include <errno.h>
  23. #include <limits.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27.  
  28. #include "../common/common.h"
  29. #include "vi.h"
  30.  
  31. /*
  32.  * !!!
  33.  * Repeated input in the historic vi is mostly wrong and this isn't very
  34.  * backward compatible.  For example, if the user entered "3Aab\ncd" in
  35.  * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then
  36.  * appended to the result.  There was also a hack which I don't remember
  37.  * right now, where "3o" would open 3 lines and then let the user fill them
  38.  * in, to make screen movements on 300 baud modems more tolerable.  I don't
  39.  * think it's going to be missed.
  40.  *
  41.  * !!!
  42.  * There's a problem with the way that we do logging for change commands with
  43.  * implied motions (e.g. A, I, O, cc, etc.).  Since the main vi loop logs the
  44.  * starting cursor position before the change command "moves" the cursor, the
  45.  * cursor position to which we return on undo will be where the user entered
  46.  * the change command, not the start of the change.  Several of the following
  47.  * routines re-log the cursor to make this work correctly.  Historic vi tried
  48.  * to do the same thing, and mostly got it right.  (The only spectacular way
  49.  * it fails is if the user entered 'o' from anywhere but the last character of
  50.  * the line, the undo returned the cursor to the start of the line.  If the
  51.  * user was on the last character of the line, the cursor returned to that
  52.  * position.)  We also check for mapped keys waiting, i.e. if we're in the
  53.  * middle of a map, don't bother logging the cursor.
  54.  */
  55. #define    LOG_CORRECT {                            \
  56.     if (!MAPPED_KEYS_WAITING(sp))                    \
  57.         (void)log_cursor(sp);                    \
  58. }
  59.  
  60. static u_int32_t set_txt_std __P((SCR *, VICMD *, u_int32_t));
  61.  
  62. /*
  63.  * v_iA -- [count]A
  64.  *    Append text to the end of the line.
  65.  *
  66.  * PUBLIC: int v_iA __P((SCR *, VICMD *));
  67.  */
  68. int
  69. v_iA(sp, vp)
  70.     SCR *sp;
  71.     VICMD *vp;
  72. {
  73.     size_t len;
  74.  
  75.     if (!db_get(sp, vp->m_start.lno, 0, NULL, &len))
  76.         sp->cno = len == 0 ? 0 : len - 1;
  77.  
  78.     LOG_CORRECT;
  79.  
  80.     return (v_ia(sp, vp));
  81. }
  82.  
  83. /*
  84.  * v_ia -- [count]a
  85.  *       [count]A
  86.  *    Append text to the cursor position.
  87.  *
  88.  * PUBLIC: int v_ia __P((SCR *, VICMD *));
  89.  */
  90. int
  91. v_ia(sp, vp)
  92.     SCR *sp;
  93.     VICMD *vp;
  94. {
  95.     size_t len;
  96.     u_int32_t flags;
  97.     int isempty;
  98.     char *p;
  99.  
  100.     flags = set_txt_std(sp, vp, 0);
  101.     sp->showmode = SM_APPEND;
  102.     sp->lno = vp->m_start.lno;
  103.  
  104.     /* Move the cursor one column to the right and repaint the screen. */
  105.     if (db_eget(sp, sp->lno, &p, &len, &isempty)) {
  106.         if (!isempty)
  107.             return (1);
  108.         len = 0;
  109.         LF_SET(TXT_APPENDEOL);
  110.     } else if (len) {
  111.         if (len == sp->cno + 1) {
  112.             sp->cno = len;
  113.             LF_SET(TXT_APPENDEOL);
  114.         } else
  115.             ++sp->cno;
  116.     } else
  117.         LF_SET(TXT_APPENDEOL);
  118.  
  119.     return (v_txt(sp, vp, NULL, p, len,
  120.         0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
  121. }
  122.  
  123. /*
  124.  * v_iI -- [count]I
  125.  *    Insert text at the first nonblank.
  126.  *
  127.  * PUBLIC: int v_iI __P((SCR *, VICMD *));
  128.  */
  129. int
  130. v_iI(sp, vp)
  131.     SCR *sp;
  132.     VICMD *vp;
  133. {
  134.     sp->cno = 0;
  135.     if (nonblank(sp, vp->m_start.lno, &sp->cno))
  136.         return (1);
  137.  
  138.     LOG_CORRECT;
  139.  
  140.     return (v_ii(sp, vp));
  141. }
  142.  
  143. /*
  144.  * v_ii -- [count]i
  145.  *       [count]I
  146.  *    Insert text at the cursor position.
  147.  *
  148.  * PUBLIC: int v_ii __P((SCR *, VICMD *));
  149.  */
  150. int
  151. v_ii(sp, vp)
  152.     SCR *sp;
  153.     VICMD *vp;
  154. {
  155.     size_t len;
  156.     u_int32_t flags;
  157.     int isempty;
  158.     char *p;
  159.  
  160.     flags = set_txt_std(sp, vp, 0);
  161.     sp->showmode = SM_INSERT;
  162.     sp->lno = vp->m_start.lno;
  163.  
  164.     if (db_eget(sp, sp->lno, &p, &len, &isempty)) {
  165.         if (!isempty)
  166.             return (1);
  167.         len = 0;
  168.     }
  169.  
  170.     if (len == 0)
  171.         LF_SET(TXT_APPENDEOL);
  172.     return (v_txt(sp, vp, NULL, p, len,
  173.         0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
  174. }
  175.  
  176. enum which { o_cmd, O_cmd };
  177. static int io __P((SCR *, VICMD *, enum which));
  178.  
  179. /*
  180.  * v_iO -- [count]O
  181.  *    Insert text above this line.
  182.  *
  183.  * PUBLIC: int v_iO __P((SCR *, VICMD *));
  184.  */
  185. int
  186. v_iO(sp, vp)
  187.     SCR *sp;
  188.     VICMD *vp;
  189. {
  190.     return (io(sp, vp, O_cmd));
  191. }
  192.  
  193. /*
  194.  * v_io -- [count]o
  195.  *    Insert text after this line.
  196.  *
  197.  * PUBLIC: int v_io __P((SCR *, VICMD *));
  198.  */
  199. int
  200. v_io(sp, vp)
  201.     SCR *sp;
  202.     VICMD *vp;
  203. {
  204.     return (io(sp, vp, o_cmd));
  205. }
  206.  
  207. static int
  208. io(sp, vp, cmd)
  209.     SCR *sp;
  210.     VICMD *vp;
  211.     enum which cmd;
  212. {
  213.     recno_t ai_line, lno;
  214.     size_t len;
  215.     u_int32_t flags;
  216.     char *p;
  217.  
  218.     flags = set_txt_std(sp, vp, TXT_ADDNEWLINE | TXT_APPENDEOL);
  219.     sp->showmode = SM_INSERT;
  220.  
  221.     if (sp->lno == 1) {
  222.         if (db_last(sp, &lno))
  223.             return (1);
  224.         if (lno != 0)
  225.             goto insert;
  226.         p = NULL;
  227.         len = 0;
  228.         ai_line = OOBLNO;
  229.     } else {
  230. insert:        p = "";
  231.         sp->cno = 0;
  232.         LOG_CORRECT;
  233.  
  234.         if (cmd == O_cmd) {
  235.             if (db_insert(sp, sp->lno, p, 0))
  236.                 return (1);
  237.             if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
  238.                 return (1);
  239.             ai_line = sp->lno + 1;
  240.         } else {
  241.             if (db_append(sp, 1, sp->lno, p, 0))
  242.                 return (1);
  243.             if (db_get(sp, ++sp->lno, DBG_FATAL, &p, &len))
  244.                 return (1);
  245.             ai_line = sp->lno - 1;
  246.         }
  247.     }
  248.     return (v_txt(sp, vp, NULL, p, len,
  249.         0, ai_line, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
  250. }
  251.  
  252. /*
  253.  * v_change -- [buffer][count]c[count]motion
  254.  *           [buffer][count]C
  255.  *           [buffer][count]S
  256.  *    Change command.
  257.  *
  258.  * PUBLIC: int v_change __P((SCR *, VICMD *));
  259.  */
  260. int
  261. v_change(sp, vp)
  262.     SCR *sp;
  263.     VICMD *vp;
  264. {
  265.     size_t blen, len;
  266.     u_int32_t flags;
  267.     int isempty, lmode, rval;
  268.     char *bp, *p;
  269.  
  270.     /*
  271.      * 'c' can be combined with motion commands that set the resulting
  272.      * cursor position, i.e. "cG".  Clear the VM_RCM flags and make the
  273.      * resulting cursor position stick, inserting text has its own rules
  274.      * for cursor positioning.
  275.      */
  276.     F_CLR(vp, VM_RCM_MASK);
  277.     F_SET(vp, VM_RCM_SET);
  278.  
  279.     /*
  280.      * Find out if the file is empty, it's easier to handle it as a
  281.      * special case.
  282.      */
  283.     if (vp->m_start.lno == vp->m_stop.lno &&
  284.         db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
  285.         if (!isempty)
  286.             return (1);
  287.         return (v_ia(sp, vp));
  288.     }
  289.  
  290.     flags = set_txt_std(sp, vp, 0);
  291.     sp->showmode = SM_CHANGE;
  292.  
  293.     /*
  294.      * Move the cursor to the start of the change.  Note, if autoindent
  295.      * is turned on, the cc command in line mode changes from the first
  296.      * *non-blank* character of the line, not the first character.  And,
  297.      * to make it just a bit more exciting, the initial space is handled
  298.      * as auto-indent characters.
  299.      */
  300.     lmode = F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0;
  301.     if (lmode) {
  302.         vp->m_start.cno = 0;
  303.         if (O_ISSET(sp, O_AUTOINDENT)) {
  304.             if (nonblank(sp, vp->m_start.lno, &vp->m_start.cno))
  305.                 return (1);
  306.             LF_SET(TXT_AICHARS);
  307.         }
  308.     }
  309.     sp->lno = vp->m_start.lno;
  310.     sp->cno = vp->m_start.cno;
  311.  
  312.     LOG_CORRECT;
  313.  
  314.     /*
  315.      * If not in line mode and changing within a single line, copy the
  316.      * text and overwrite it.
  317.      */
  318.     if (!lmode && vp->m_start.lno == vp->m_stop.lno) {
  319.         /*
  320.          * !!!
  321.          * Historic practice, c did not cut into the numeric buffers,
  322.          * only the unnamed one.
  323.          */
  324.         if (cut(sp,
  325.             F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
  326.             &vp->m_start, &vp->m_stop, lmode))
  327.             return (1);
  328.         if (len == 0)
  329.             LF_SET(TXT_APPENDEOL);
  330.         LF_SET(TXT_EMARK | TXT_OVERWRITE);
  331.         return (v_txt(sp, vp, &vp->m_stop, p, len,
  332.             0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
  333.     }
  334.  
  335.     /*
  336.      * It's trickier if in line mode or changing over multiple lines.  If
  337.      * we're in line mode delete all of the lines and insert a replacement
  338.      * line which the user edits.  If there was leading whitespace in the
  339.      * first line being changed, we copy it and use it as the replacement.
  340.      * If we're not in line mode, we delete the text and start inserting.
  341.      *
  342.      * !!!
  343.      * Copy the text.  Historic practice, c did not cut into the numeric
  344.      * buffers, only the unnamed one.
  345.      */
  346.     if (cut(sp,
  347.         F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
  348.         &vp->m_start, &vp->m_stop, lmode))
  349.         return (1);
  350.  
  351.     /* If replacing entire lines and there's leading text. */
  352.     if (lmode && vp->m_start.cno) {
  353.         /*
  354.          * Get a copy of the first line changed, and copy out the
  355.          * leading text.
  356.          */
  357.         if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len))
  358.             return (1);
  359.         GET_SPACE_RET(sp, bp, blen, vp->m_start.cno);
  360.         memmove(bp, p, vp->m_start.cno);
  361.     } else
  362.         bp = NULL;
  363.  
  364.     /* Delete the text. */
  365.     if (del(sp, &vp->m_start, &vp->m_stop, lmode))
  366.         return (1);
  367.  
  368.     /* If replacing entire lines, insert a replacement line. */
  369.     if (lmode) {
  370.         if (db_insert(sp, vp->m_start.lno, bp, vp->m_start.cno))
  371.             return (1);
  372.         sp->lno = vp->m_start.lno;
  373.         len = sp->cno = vp->m_start.cno;
  374.     }
  375.  
  376.     /* Get the line we're editing. */
  377.     if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
  378.         if (!isempty)
  379.             return (1);
  380.         len = 0;
  381.     }
  382.  
  383.     /* Check to see if we're appending to the line. */
  384.     if (vp->m_start.cno >= len)
  385.         LF_SET(TXT_APPENDEOL);
  386.  
  387.     rval = v_txt(sp, vp, NULL, p, len,
  388.         0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags);
  389.  
  390.     if (bp != NULL)
  391.         FREE_SPACE(sp, bp, blen);
  392.     return (rval);
  393. }
  394.  
  395. /*
  396.  * v_Replace -- [count]R
  397.  *    Overwrite multiple characters.
  398.  *
  399.  * PUBLIC: int v_Replace __P((SCR *, VICMD *));
  400.  */
  401. int
  402. v_Replace(sp, vp)
  403.     SCR *sp;
  404.     VICMD *vp;
  405. {
  406.     size_t len;
  407.     u_int32_t flags;
  408.     int isempty;
  409.     char *p;
  410.  
  411.     flags = set_txt_std(sp, vp, 0);
  412.     sp->showmode = SM_REPLACE;
  413.  
  414.     if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
  415.         if (!isempty)
  416.             return (1);
  417.         len = 0;
  418.         LF_SET(TXT_APPENDEOL);
  419.     } else {
  420.         if (len == 0)
  421.             LF_SET(TXT_APPENDEOL);
  422.         LF_SET(TXT_OVERWRITE | TXT_REPLACE);
  423.     }
  424.     vp->m_stop.lno = vp->m_start.lno;
  425.     vp->m_stop.cno = len ? len - 1 : 0;
  426.  
  427.     return (v_txt(sp, vp, &vp->m_stop, p, len,
  428.         0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
  429. }
  430.  
  431. /*
  432.  * v_subst -- [buffer][count]s
  433.  *    Substitute characters.
  434.  *
  435.  * PUBLIC: int v_subst __P((SCR *, VICMD *));
  436.  */
  437. int
  438. v_subst(sp, vp)
  439.     SCR *sp;
  440.     VICMD *vp;
  441. {
  442.     size_t len;
  443.     u_int32_t flags;
  444.     int isempty;
  445.     char *p;
  446.  
  447.     flags = set_txt_std(sp, vp, 0);
  448.     sp->showmode = SM_CHANGE;
  449.  
  450.     if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
  451.         if (!isempty)
  452.             return (1);
  453.         len = 0;
  454.         LF_SET(TXT_APPENDEOL);
  455.     } else {
  456.         if (len == 0)
  457.             LF_SET(TXT_APPENDEOL);
  458.         LF_SET(TXT_EMARK | TXT_OVERWRITE);
  459.     }
  460.  
  461.     vp->m_stop.lno = vp->m_start.lno;
  462.     vp->m_stop.cno =
  463.         vp->m_start.cno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
  464.     if (vp->m_stop.cno > len - 1)
  465.         vp->m_stop.cno = len - 1;
  466.  
  467.     if (p != NULL && cut(sp,
  468.         F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
  469.         &vp->m_start, &vp->m_stop, 0))
  470.         return (1);
  471.  
  472.     return (v_txt(sp, vp, &vp->m_stop, p, len, 0, OOBLNO, 1, flags));
  473. }
  474.  
  475. /*
  476.  * set_txt_std --
  477.  *    Initialize text processing flags.
  478.  */
  479. static u_int32_t
  480. set_txt_std(sp, vp, flags)
  481.     SCR *sp;
  482.     VICMD *vp;
  483.     u_int32_t flags;
  484. {
  485.     LF_SET(TXT_CNTRLT |
  486.         TXT_ESCAPE | TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE);
  487.  
  488.     if (F_ISSET(vp, VC_ISDOT))
  489.         LF_SET(TXT_REPLAY);
  490.  
  491.     if (O_ISSET(sp, O_ALTWERASE))
  492.         LF_SET(TXT_ALTWERASE);
  493.     if (O_ISSET(sp, O_AUTOINDENT))
  494.         LF_SET(TXT_AUTOINDENT);
  495.     if (O_ISSET(sp, O_BEAUTIFY))
  496.         LF_SET(TXT_BEAUTIFY);
  497.     if (O_ISSET(sp, O_SHOWMATCH))
  498.         LF_SET(TXT_SHOWMATCH);
  499.     if (F_ISSET(sp, SC_SCRIPT))
  500.         LF_SET(TXT_CR);
  501.     if (O_ISSET(sp, O_TTYWERASE))
  502.         LF_SET(TXT_TTYWERASE);
  503.  
  504.     /*
  505.      * !!!
  506.      * Mapped keys were sometimes unaffected by the wrapmargin option
  507.      * in the historic 4BSD vi.  Consider the following commands, where
  508.      * each is executed on an empty line, in an 80 column screen, with
  509.      * the wrapmargin value set to 60.
  510.      *
  511.      *    aABC DEF <ESC>....
  512.      *    :map K aABC DEF ^V<ESC><CR>KKKKK
  513.      *    :map K 5aABC DEF ^V<ESC><CR>K
  514.      *
  515.      * The first and second commands are affected by wrapmargin.  The
  516.      * third is not.  (If the inserted text is itself longer than the
  517.      * wrapmargin value, i.e. if the "ABC DEF " string is replaced by
  518.      * something that's longer than 60 columns from the beginning of
  519.      * the line, the first two commands behave as before, but the third
  520.      * command gets fairly strange.)  The problem is that people wrote
  521.      * macros that depended on the third command NOT being affected by
  522.      * wrapmargin, as in this gem which centers lines:
  523.      *
  524.      *    map #c $mq81a ^V^[81^V^V|D`qld0:s/  / /g^V^M$p
  525.      *
  526.      * For compatibility reasons, we try and make it all work here.  I
  527.      * offer no hope that this is right, but it's probably pretty close.
  528.      *
  529.      * XXX
  530.      * Once I work my courage up, this is all gonna go away.  It's too
  531.      * evil to survive.
  532.      */
  533.     if ((O_ISSET(sp, O_WRAPLEN) || O_ISSET(sp, O_WRAPMARGIN)) &&
  534.         (!MAPPED_KEYS_WAITING(sp) || !F_ISSET(vp, VC_C1SET)))
  535.         LF_SET(TXT_WRAPMARGIN);
  536.     return (flags);
  537. }
  538.