Home | History | Annotate | Line # | Download | only in vi
      1 /*	$NetBSD: v_paragraph.c,v 1.6 2017/01/22 05:11:22 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_paragraph.c,v 10.10 2001/06/25 15:19:32 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:32 ";
     17 #endif /* not lint */
     18 #else
     19 __RCSID("$NetBSD: v_paragraph.c,v 1.6 2017/01/22 05:11:22 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 <errno.h>
     28 #include <limits.h>
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <string.h>
     32 
     33 #include "../common/common.h"
     34 #include "vi.h"
     35 
     36 #define	INTEXT_CHECK {							\
     37 	if (len == 0 || v_isempty(p, len)) {				\
     38 		if (!--cnt)						\
     39 			goto found;					\
     40 		pstate = P_INBLANK;					\
     41 	}								\
     42 	/*								\
     43 	 * !!!								\
     44 	 * Historic documentation (USD:15-11, 4.2) said that formfeed	\
     45 	 * characters (^L) in the first column delimited paragraphs.	\
     46 	 * The historic vi code mentions formfeed characters, but never	\
     47 	 * implements them.  It seems reasonable, do it.		\
     48 	 */								\
     49 	if (p[0] == '\014') {						\
     50 		if (!--cnt)						\
     51 			goto found;					\
     52 		continue;						\
     53 	}								\
     54 	if (p[0] != '.' || len < 2)					\
     55 		continue;						\
     56 	for (lp = VIP(sp)->ps; *lp != '\0'; lp += 2)			\
     57 		if (lp[0] == p[1] &&					\
     58 		    ((lp[1] == ' ' && len == 2) || lp[1] == p[2]) &&	\
     59 		    !--cnt)						\
     60 			goto found;					\
     61 }
     62 
     63 /*
     64  * v_paragraphf -- [count]}
     65  *	Move forward count paragraphs.
     66  *
     67  * Paragraphs are empty lines after text, formfeed characters, or values
     68  * from the paragraph or section options.
     69  *
     70  * PUBLIC: int v_paragraphf __P((SCR *, VICMD *));
     71  */
     72 int
     73 v_paragraphf(SCR *sp, VICMD *vp)
     74 {
     75 	enum { P_INTEXT, P_INBLANK } pstate;
     76 	size_t lastcno, cno, prevlen, len;
     77 	db_recno_t cnt, lastlno, prevlno, lno;
     78 	int isempty;
     79 	CHAR_T *p;
     80 	char *lp;
     81 
     82 	/*
     83 	 * !!!
     84 	 * If the starting cursor position is at or before any non-blank
     85 	 * characters in the line, i.e. the movement is cutting all of the
     86 	 * line's text, the buffer is in line mode.  It's a lot easier to
     87 	 * check here, because we know that the end is going to be the start
     88 	 * or end of a line.
     89 	 *
     90 	 * This was historical practice in vi, with a single exception.  If
     91 	 * the paragraph movement was from the start of the last line to EOF,
     92 	 * then all the characters were deleted from the last line, but the
     93 	 * line itself remained.  If somebody complains, don't pause, don't
     94 	 * hesitate, just hit them.
     95 	 */
     96 	if (db_last(sp, &lastlno))
     97 		return (1);
     98 	lno = vp->m_start.lno;
     99 	if (ISMOTION(vp) && lno != lastlno) {
    100 		if ((cno = vp->m_start.cno) == 0)
    101 			F_SET(vp, VM_LMODE);
    102 		else {
    103 			if (nonblank(sp, lno, &len))
    104 				return (1);
    105 			if (cno <= len)
    106 				F_SET(vp, VM_LMODE);
    107 		}
    108 	}
    109 
    110 	/*
    111 	 * Figure out what state we're currently in.  It also historically
    112 	 * worked on empty files, so we have to make it okay.
    113 	 */
    114 	if (db_eget(sp, lno, &p, &len, &isempty)) {
    115 		if (isempty) {
    116 			vp->m_stop = vp->m_final = vp->m_start;
    117 			return (0);
    118 		} else
    119 			return (1);
    120 	}
    121 
    122 	/*
    123 	 * If we start in text, we want to switch states
    124 	 * (2 * N - 1) times, in non-text, (2 * N) times.
    125 	 */
    126 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
    127 	cnt *= 2;
    128 	if (len == 0 || v_isempty(p, len))
    129 		pstate = P_INBLANK;
    130 	else {
    131 		--cnt;
    132 		pstate = P_INTEXT;
    133 	}
    134 
    135 	for (;;) {
    136 		prevlno = lno;
    137 		prevlen = len;
    138 		if (++lno > lastlno)
    139 			goto eof;
    140 		if (db_get(sp, lno, 0, &p, &len))
    141 			return (1);
    142 		switch (pstate) {
    143 		case P_INTEXT:
    144 			INTEXT_CHECK;
    145 			break;
    146 		case P_INBLANK:
    147 			if (len == 0 || v_isempty(p, len))
    148 				break;
    149 			if (--cnt) {
    150 				pstate = P_INTEXT;
    151 				break;
    152 			}
    153 			/*
    154 			 * !!!
    155 			 * Non-motion commands move to the end of the range,
    156 			 * delete and yank stay at the start.  Ignore others.
    157 			 * Adjust the end of the range for motion commands;
    158 			 * historically, a motion component was to the end of
    159 			 * the previous line, whereas the movement command was
    160 			 * to the start of the new "paragraph".
    161 			 */
    162 found:			if (ISMOTION(vp)) {
    163 				vp->m_stop.lno = prevlno;
    164 				vp->m_stop.cno = prevlen ? prevlen - 1 : 0;
    165 				vp->m_final = vp->m_start;
    166 			} else {
    167 				vp->m_stop.lno = lno;
    168 				vp->m_stop.cno = 0;
    169 				vp->m_final = vp->m_stop;
    170 			}
    171 			return (0);
    172 		default:
    173 			abort();
    174 		}
    175 	}
    176 
    177 	/*
    178 	 * !!!
    179 	 * Adjust end of the range for motion commands; EOF is a movement
    180 	 * sink.  The } command historically moved to the end of the last
    181 	 * line, not the beginning, from any position before the end of the
    182 	 * last line.
    183 	 */
    184 eof:	lastcno = len ? len - 1 : 0;
    185 	if (vp->m_start.lno == lastlno && vp->m_start.cno == lastcno) {
    186 		v_eof(sp, NULL);
    187 		return (1);
    188 	}
    189 	/*
    190 	 * !!!
    191 	 * Non-motion commands move to the end of the range, delete
    192 	 * and yank stay at the start.  Ignore others.
    193 	 *
    194 	 * If deleting the line (which happens if deleting to EOF), then
    195 	 * cursor movement is to the first nonblank.
    196 	 */
    197 	if (ISMOTION(vp) && ISCMD(vp->rkp, 'd')) {
    198 		F_CLR(vp, VM_RCM_MASK);
    199 		F_SET(vp, VM_RCM_SETFNB);
    200 	}
    201 	vp->m_stop.lno = lastlno;
    202 	vp->m_stop.cno = lastcno;
    203 	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
    204 	return (0);
    205 }
    206 
    207 /*
    208  * v_paragraphb -- [count]{
    209  *	Move backward count paragraphs.
    210  *
    211  * PUBLIC: int v_paragraphb __P((SCR *, VICMD *));
    212  */
    213 int
    214 v_paragraphb(SCR *sp, VICMD *vp)
    215 {
    216 	enum { P_INTEXT, P_INBLANK } pstate;
    217 	size_t len;
    218 	db_recno_t cnt, lno;
    219 	CHAR_T *p;
    220 	char *lp;
    221 
    222 	/*
    223 	 * !!!
    224 	 * Check for SOF.  The historic vi didn't complain if users hit SOF
    225 	 * repeatedly, unless it was part of a motion command.  There is no
    226 	 * question but that Emerson's editor of choice was vi.
    227 	 *
    228 	 * The { command historically moved to the beginning of the first
    229 	 * line if invoked on the first line.
    230 	 *
    231 	 * !!!
    232 	 * If the starting cursor position is in the first column (backward
    233 	 * paragraph movements did NOT historically pay attention to non-blank
    234 	 * characters) i.e. the movement is cutting the entire line, the buffer
    235 	 * is in line mode.  Cuts from the beginning of the line also did not
    236 	 * cut the current line, but started at the previous EOL.
    237 	 *
    238 	 * Correct for a left motion component while we're thinking about it.
    239 	 */
    240 	lno = vp->m_start.lno;
    241 
    242 	if (ISMOTION(vp)) {
    243 		if (vp->m_start.cno == 0) {
    244 			if (vp->m_start.lno == 1) {
    245 				v_sof(sp, &vp->m_start);
    246 				return (1);
    247 			} else
    248 				--vp->m_start.lno;
    249 			F_SET(vp, VM_LMODE);
    250 		} else
    251 			--vp->m_start.cno;
    252 	}
    253 
    254 	if (vp->m_start.lno <= 1)
    255 		goto sof;
    256 
    257 	/* Figure out what state we're currently in. */
    258 	if (db_get(sp, lno, 0, &p, &len))
    259 		goto sof;
    260 
    261 	/*
    262 	 * If we start in text, we want to switch states
    263 	 * (2 * N - 1) times, in non-text, (2 * N) times.
    264 	 */
    265 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
    266 	cnt *= 2;
    267 	if (len == 0 || v_isempty(p, len))
    268 		pstate = P_INBLANK;
    269 	else {
    270 		--cnt;
    271 		pstate = P_INTEXT;
    272 
    273 		/*
    274 		 * !!!
    275 		 * If the starting cursor is past the first column,
    276 		 * the current line is checked for a paragraph.
    277 		 */
    278 		if (vp->m_start.cno > 0)
    279 			++lno;
    280 	}
    281 
    282 	for (;;) {
    283 		if (db_get(sp, --lno, 0, &p, &len))
    284 			goto sof;
    285 		switch (pstate) {
    286 		case P_INTEXT:
    287 			INTEXT_CHECK;
    288 			break;
    289 		case P_INBLANK:
    290 			if (len != 0 && !v_isempty(p, len)) {
    291 				if (!--cnt)
    292 					goto found;
    293 				pstate = P_INTEXT;
    294 			}
    295 			break;
    296 		default:
    297 			abort();
    298 		}
    299 	}
    300 
    301 	/* SOF is a movement sink. */
    302 sof:	lno = 1;
    303 
    304 found:	vp->m_stop.lno = lno;
    305 	vp->m_stop.cno = 0;
    306 
    307 	/*
    308 	 * All commands move to the end of the range.  (We already
    309 	 * adjusted the start of the range for motion commands).
    310 	 */
    311 	vp->m_final = vp->m_stop;
    312 	return (0);
    313 }
    314 
    315 /*
    316  * v_buildps --
    317  *	Build the paragraph command search pattern.
    318  *
    319  * PUBLIC: int v_buildps __P((SCR *, const char *, const char *));
    320  */
    321 int
    322 v_buildps(SCR *sp, const char *p_p, const char *s_p)
    323 {
    324 	VI_PRIVATE *vip;
    325 	size_t p_len, s_len;
    326 	char *p;
    327 
    328 	/*
    329 	 * The vi paragraph command searches for either a paragraph or
    330 	 * section option macro.
    331 	 */
    332 	p_len = p_p == NULL ? 0 : strlen(p_p);
    333 	s_len = s_p == NULL ? 0 : strlen(s_p);
    334 
    335 	if (p_len == 0 && s_len == 0)
    336 		return (0);
    337 
    338 	MALLOC_RET(sp, p, char *, p_len + s_len + 1);
    339 
    340 	vip = VIP(sp);
    341 	if (vip->ps != NULL)
    342 		free(vip->ps);
    343 
    344 	if (p_p != NULL)
    345 		memmove(p, p_p, p_len + 1);
    346 	if (s_p != NULL)
    347 		memmove(p + p_len, s_p, s_len + 1);
    348 	vip->ps = p;
    349 	return (0);
    350 }
    351