Home | History | Annotate | Line # | Download | only in vi
      1 /*	$NetBSD: v_replace.c,v 1.4 2018/08/07 08:05:47 rin Exp $ */
      2 /*-
      3  * Copyright (c) 1992, 1993, 1994
      4  *	The Regents of the University of California.  All rights reserved.
      5  * Copyright (c) 1992, 1993, 1994, 1995, 1996
      6  *	Keith Bostic.  All rights reserved.
      7  *
      8  * See the LICENSE file for redistribution information.
      9  */
     10 
     11 #include "config.h"
     12 
     13 #include <sys/cdefs.h>
     14 #if 0
     15 #ifndef lint
     16 static const char sccsid[] = "Id: v_replace.c,v 10.24 2001/06/25 15:19:34 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:34 ";
     17 #endif /* not lint */
     18 #else
     19 __RCSID("$NetBSD: v_replace.c,v 1.4 2018/08/07 08:05:47 rin Exp $");
     20 #endif
     21 
     22 #include <sys/types.h>
     23 #include <sys/queue.h>
     24 #include <sys/time.h>
     25 
     26 #include <bitstring.h>
     27 #include <ctype.h>
     28 #include <errno.h>
     29 #include <limits.h>
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <string.h>
     33 
     34 #include "../common/common.h"
     35 #include "vi.h"
     36 
     37 /*
     38  * v_replace -- [count]r<char>
     39  *
     40  * !!!
     41  * The r command in historic vi was almost beautiful in its badness.  For
     42  * example, "r<erase>" and "r<word erase>" beeped the terminal and deleted
     43  * a single character.  "Nr<carriage return>", where N was greater than 1,
     44  * inserted a single carriage return.  "r<escape>" did cancel the command,
     45  * but "r<literal><escape>" erased a single character.  To enter a literal
     46  * <literal> character, it required three <literal> characters after the
     47  * command.  This may not be right, but at least it's not insane.
     48  *
     49  * PUBLIC: int v_replace __P((SCR *, VICMD *));
     50  */
     51 int
     52 v_replace(SCR *sp, VICMD *vp)
     53 {
     54 	EVENT ev;
     55 	VI_PRIVATE *vip;
     56 	TEXT *tp;
     57 	size_t blen, len;
     58 	u_long cnt;
     59 	int quote, rval;
     60 	CHAR_T *bp;
     61 	CHAR_T *p;
     62 
     63 	vip = VIP(sp);
     64 
     65 	/*
     66 	 * If the line doesn't exist, or it's empty, replacement isn't
     67 	 * allowed.  It's not hard to implement, but:
     68 	 *
     69 	 *	1: It's historic practice (vi beeped before the replacement
     70 	 *	   character was even entered).
     71 	 *	2: For consistency, this change would require that the more
     72 	 *	   general case, "Nr", when the user is < N characters from
     73 	 *	   the end of the line, also work, which would be a bit odd.
     74 	 *	3: Replacing with a <newline> has somewhat odd semantics.
     75 	 */
     76 	if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len))
     77 		return (1);
     78 	if (len == 0) {
     79 		msgq(sp, M_BERR, "186|No characters to replace");
     80 		return (1);
     81 	}
     82 
     83 	/*
     84 	 * Figure out how many characters to be replace.  For no particular
     85 	 * reason (other than that the semantics of replacing the newline
     86 	 * are confusing) only permit the replacement of the characters in
     87 	 * the current line.  I suppose we could append replacement characters
     88 	 * to the line, but I see no compelling reason to do so.  Check this
     89 	 * before we get the character to match historic practice, where Nr
     90 	 * failed immediately if there were less than N characters from the
     91 	 * cursor to the end of the line.
     92 	 */
     93 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
     94 	vp->m_stop.lno = vp->m_start.lno;
     95 	vp->m_stop.cno = vp->m_start.cno + cnt - 1;
     96 	if (vp->m_stop.cno > len - 1) {
     97 		v_eol(sp, &vp->m_start);
     98 		return (1);
     99 	}
    100 
    101 	/*
    102 	 * If it's not a repeat, reset the current mode and get a replacement
    103 	 * character.
    104 	 */
    105 	quote = 0;
    106 	if (!F_ISSET(vp, VC_ISDOT)) {
    107 		sp->showmode = SM_REPLACE;
    108 		if (vs_refresh(sp, 0))
    109 			return (1);
    110 next:		if (v_event_get(sp, &ev, 0, 0))
    111 			return (1);
    112 
    113 		switch (ev.e_event) {
    114 		case E_CHARACTER:
    115 			/*
    116 			 * <literal_next> means escape the next character.
    117 			 * <escape> means they changed their minds.
    118 			 */
    119 			if (!quote) {
    120 				if (ev.e_value == K_VLNEXT) {
    121 					quote = 1;
    122 					goto next;
    123 				}
    124 				if (ev.e_value == K_ESCAPE)
    125 					return (0);
    126 			}
    127 			vip->rlast = ev.e_c;
    128 			vip->rvalue = ev.e_value;
    129 			break;
    130 		case E_ERR:
    131 		case E_EOF:
    132 			F_SET(sp, SC_EXIT_FORCE);
    133 			return (1);
    134 		case E_INTERRUPT:
    135 			/* <interrupt> means they changed their minds. */
    136 			return (0);
    137 		case E_WRESIZE:
    138 			/* <resize> interrupts the input mode. */
    139 #ifdef IMCTRL
    140 			sp->gp->scr_imctrl(sp, IMCTRL_OFF);
    141 #endif
    142 			v_emsg(sp, NULL, VIM_WRESIZE);
    143 			return (0);
    144 		case E_REPAINT:
    145 			if (v_erepaint(sp, &ev))
    146 				return (1);
    147 			goto next;
    148 		default:
    149 			v_event_err(sp, &ev);
    150 			return (0);
    151 		}
    152 	}
    153 
    154 	/* Copy the line. */
    155 	GET_SPACE_RETW(sp, bp, blen, len);
    156 	MEMMOVE(bp, p, len);
    157 	p = bp;
    158 
    159 	/*
    160 	 * Versions of nvi before 1.57 created N new lines when they replaced
    161 	 * N characters with <carriage-return> or <newline> characters.  This
    162 	 * is different from the historic vi, which replaced N characters with
    163 	 * a single new line.  Users complained, so we match historic practice.
    164 	 */
    165 	if ((!quote && vip->rvalue == K_CR) || vip->rvalue == K_NL) {
    166 		/* Set return line. */
    167 		vp->m_stop.lno = vp->m_start.lno + 1;
    168 		vp->m_stop.cno = 0;
    169 
    170 		/* The first part of the current line. */
    171 		if (db_set(sp, vp->m_start.lno, p, vp->m_start.cno))
    172 			goto err_ret;
    173 
    174 		/*
    175 		 * The rest of the current line.  And, of course, now it gets
    176 		 * tricky.  If there are characters left in the line and if
    177 		 * the autoindent edit option is set, white space after the
    178 		 * replaced character is discarded, autoindent is applied, and
    179 		 * the cursor moves to the last indent character.
    180 		 */
    181 		p += vp->m_start.cno + cnt;
    182 		len -= vp->m_start.cno + cnt;
    183 		if (len != 0 && O_ISSET(sp, O_AUTOINDENT))
    184 			for (; len && ISBLANK((UCHAR_T)*p); --len, ++p);
    185 
    186 		if ((tp = text_init(sp, p, len, len)) == NULL)
    187 			goto err_ret;
    188 
    189 		if (len != 0 && O_ISSET(sp, O_AUTOINDENT)) {
    190 			if (v_txt_auto(sp, vp->m_start.lno, NULL, 0, tp))
    191 				goto err_ret;
    192 			vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0;
    193 		} else
    194 			vp->m_stop.cno = 0;
    195 
    196 		vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0;
    197 		if (db_append(sp, 1, vp->m_start.lno, tp->lb, tp->len))
    198 err_ret:		rval = 1;
    199 		else {
    200 			text_free(tp);
    201 			rval = 0;
    202 		}
    203 	} else {
    204 		STRSET(bp + vp->m_start.cno, vip->rlast, cnt);
    205 		rval = db_set(sp, vp->m_start.lno, bp, len);
    206 	}
    207 	FREE_SPACEW(sp, bp, blen);
    208 
    209 	vp->m_final = vp->m_stop;
    210 	return (rval);
    211 }
    212