119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: v_paragraph.c,v 10.10 2001/06/25 15:19:32 skimo Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <errno.h> 2219304Speter#include <limits.h> 2319304Speter#include <stdio.h> 2419304Speter#include <stdlib.h> 2519304Speter#include <string.h> 2619304Speter 2719304Speter#include "../common/common.h" 2819304Speter#include "vi.h" 2919304Speter 3019304Speter#define INTEXT_CHECK { \ 3119304Speter if (len == 0 || v_isempty(p, len)) { \ 3219304Speter if (!--cnt) \ 3319304Speter goto found; \ 3419304Speter pstate = P_INBLANK; \ 3519304Speter } \ 3619304Speter /* \ 3719304Speter * !!! \ 3819304Speter * Historic documentation (USD:15-11, 4.2) said that formfeed \ 3919304Speter * characters (^L) in the first column delimited paragraphs. \ 4019304Speter * The historic vi code mentions formfeed characters, but never \ 4119304Speter * implements them. It seems reasonable, do it. \ 4219304Speter */ \ 4319304Speter if (p[0] == '\014') { \ 4419304Speter if (!--cnt) \ 4519304Speter goto found; \ 4619304Speter continue; \ 4719304Speter } \ 4819304Speter if (p[0] != '.' || len < 2) \ 4919304Speter continue; \ 5019304Speter for (lp = VIP(sp)->ps; *lp != '\0'; lp += 2) \ 5119304Speter if (lp[0] == p[1] && \ 5219304Speter (lp[1] == ' ' && len == 2 || lp[1] == p[2]) && \ 5319304Speter !--cnt) \ 5419304Speter goto found; \ 5519304Speter} 5619304Speter 5719304Speter/* 5819304Speter * v_paragraphf -- [count]} 5919304Speter * Move forward count paragraphs. 6019304Speter * 6119304Speter * Paragraphs are empty lines after text, formfeed characters, or values 6219304Speter * from the paragraph or section options. 6319304Speter * 6419304Speter * PUBLIC: int v_paragraphf __P((SCR *, VICMD *)); 6519304Speter */ 6619304Speterint 67254225Speterv_paragraphf(SCR *sp, VICMD *vp) 6819304Speter{ 6919304Speter enum { P_INTEXT, P_INBLANK } pstate; 7019304Speter size_t lastlen, len; 7119304Speter recno_t cnt, lastlno, lno; 7219304Speter int isempty; 73254225Speter CHAR_T *p; 74254225Speter char *lp; 7519304Speter 7619304Speter /* 7719304Speter * !!! 7819304Speter * If the starting cursor position is at or before any non-blank 7919304Speter * characters in the line, i.e. the movement is cutting all of the 8019304Speter * line's text, the buffer is in line mode. It's a lot easier to 8119304Speter * check here, because we know that the end is going to be the start 8219304Speter * or end of a line. 8319304Speter * 8419304Speter * This was historical practice in vi, with a single exception. If 8519304Speter * the paragraph movement was from the start of the last line to EOF, 8619304Speter * then all the characters were deleted from the last line, but the 8719304Speter * line itself remained. If somebody complains, don't pause, don't 8819304Speter * hesitate, just hit them. 8919304Speter */ 9019304Speter if (ISMOTION(vp)) 9119304Speter if (vp->m_start.cno == 0) 9219304Speter F_SET(vp, VM_LMODE); 9319304Speter else { 9419304Speter vp->m_stop = vp->m_start; 9519304Speter vp->m_stop.cno = 0; 9619304Speter if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno)) 9719304Speter return (1); 9819304Speter if (vp->m_start.cno <= vp->m_stop.cno) 9919304Speter F_SET(vp, VM_LMODE); 10019304Speter } 10119304Speter 10219304Speter /* Figure out what state we're currently in. */ 10319304Speter lno = vp->m_start.lno; 10419304Speter if (db_get(sp, lno, 0, &p, &len)) 10519304Speter goto eof; 10619304Speter 10719304Speter /* 10819304Speter * If we start in text, we want to switch states 10919304Speter * (2 * N - 1) times, in non-text, (2 * N) times. 11019304Speter */ 11119304Speter cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 11219304Speter cnt *= 2; 11319304Speter if (len == 0 || v_isempty(p, len)) 11419304Speter pstate = P_INBLANK; 11519304Speter else { 11619304Speter --cnt; 11719304Speter pstate = P_INTEXT; 11819304Speter } 11919304Speter 12019304Speter for (;;) { 12119304Speter lastlno = lno; 12219304Speter lastlen = len; 12319304Speter if (db_get(sp, ++lno, 0, &p, &len)) 12419304Speter goto eof; 12519304Speter switch (pstate) { 12619304Speter case P_INTEXT: 12719304Speter INTEXT_CHECK; 12819304Speter break; 12919304Speter case P_INBLANK: 13019304Speter if (len == 0 || v_isempty(p, len)) 13119304Speter break; 13219304Speter if (--cnt) { 13319304Speter pstate = P_INTEXT; 13419304Speter break; 13519304Speter } 13619304Speter /* 13719304Speter * !!! 13819304Speter * Non-motion commands move to the end of the range, 13919304Speter * delete and yank stay at the start. Ignore others. 14019304Speter * Adjust the end of the range for motion commands; 14119304Speter * historically, a motion component was to the end of 14219304Speter * the previous line, whereas the movement command was 14319304Speter * to the start of the new "paragraph". 14419304Speter */ 14519304Speterfound: if (ISMOTION(vp)) { 14619304Speter vp->m_stop.lno = lastlno; 14719304Speter vp->m_stop.cno = lastlen ? lastlen - 1 : 0; 14819304Speter vp->m_final = vp->m_start; 14919304Speter } else { 15019304Speter vp->m_stop.lno = lno; 15119304Speter vp->m_stop.cno = 0; 15219304Speter vp->m_final = vp->m_stop; 15319304Speter } 15419304Speter return (0); 15519304Speter default: 15619304Speter abort(); 15719304Speter } 15819304Speter } 15919304Speter 16019304Speter /* 16119304Speter * !!! 16219304Speter * Adjust end of the range for motion commands; EOF is a movement 16319304Speter * sink. The } command historically moved to the end of the last 16419304Speter * line, not the beginning, from any position before the end of the 16519304Speter * last line. It also historically worked on empty files, so we 16619304Speter * have to make it okay. 16719304Speter */ 16819304Spetereof: if (vp->m_start.lno == lno || vp->m_start.lno == lno - 1) { 16919304Speter if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 17019304Speter if (!isempty) 17119304Speter return (1); 17219304Speter vp->m_start.cno = 0; 17319304Speter return (0); 17419304Speter } 17519304Speter if (vp->m_start.cno == (len ? len - 1 : 0)) { 17619304Speter v_eof(sp, NULL); 17719304Speter return (1); 17819304Speter } 17919304Speter } 18019304Speter /* 18119304Speter * !!! 18219304Speter * Non-motion commands move to the end of the range, delete 18319304Speter * and yank stay at the start. Ignore others. 18419304Speter * 18519304Speter * If deleting the line (which happens if deleting to EOF), then 18619304Speter * cursor movement is to the first nonblank. 18719304Speter */ 18819304Speter if (ISMOTION(vp) && ISCMD(vp->rkp, 'd')) { 18919304Speter F_CLR(vp, VM_RCM_MASK); 19019304Speter F_SET(vp, VM_RCM_SETFNB); 19119304Speter } 19219304Speter vp->m_stop.lno = lno - 1; 19319304Speter vp->m_stop.cno = len ? len - 1 : 0; 19419304Speter vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop; 19519304Speter return (0); 19619304Speter} 19719304Speter 19819304Speter/* 19919304Speter * v_paragraphb -- [count]{ 20019304Speter * Move backward count paragraphs. 20119304Speter * 20219304Speter * PUBLIC: int v_paragraphb __P((SCR *, VICMD *)); 20319304Speter */ 20419304Speterint 205254225Speterv_paragraphb(SCR *sp, VICMD *vp) 20619304Speter{ 20719304Speter enum { P_INTEXT, P_INBLANK } pstate; 20819304Speter size_t len; 20919304Speter recno_t cnt, lno; 210254225Speter CHAR_T *p; 211254225Speter char *lp; 21219304Speter 21319304Speter /* 21419304Speter * !!! 21519304Speter * Check for SOF. The historic vi didn't complain if users hit SOF 21619304Speter * repeatedly, unless it was part of a motion command. There is no 21719304Speter * question but that Emerson's editor of choice was vi. 21819304Speter * 21919304Speter * The { command historically moved to the beginning of the first 22019304Speter * line if invoked on the first line. 22119304Speter * 22219304Speter * !!! 22319304Speter * If the starting cursor position is in the first column (backward 22419304Speter * paragraph movements did NOT historically pay attention to non-blank 22519304Speter * characters) i.e. the movement is cutting the entire line, the buffer 22619304Speter * is in line mode. Cuts from the beginning of the line also did not 22719304Speter * cut the current line, but started at the previous EOL. 22819304Speter * 22919304Speter * Correct for a left motion component while we're thinking about it. 23019304Speter */ 23119304Speter lno = vp->m_start.lno; 23219304Speter 23319304Speter if (ISMOTION(vp)) 23419304Speter if (vp->m_start.cno == 0) { 23519304Speter if (vp->m_start.lno == 1) { 23619304Speter v_sof(sp, &vp->m_start); 23719304Speter return (1); 23819304Speter } else 23919304Speter --vp->m_start.lno; 24019304Speter F_SET(vp, VM_LMODE); 24119304Speter } else 24219304Speter --vp->m_start.cno; 24319304Speter 24419304Speter if (vp->m_start.lno <= 1) 24519304Speter goto sof; 24619304Speter 24719304Speter /* Figure out what state we're currently in. */ 24819304Speter if (db_get(sp, lno, 0, &p, &len)) 24919304Speter goto sof; 25019304Speter 25119304Speter /* 25219304Speter * If we start in text, we want to switch states 25319304Speter * (2 * N - 1) times, in non-text, (2 * N) times. 25419304Speter */ 25519304Speter cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 25619304Speter cnt *= 2; 25719304Speter if (len == 0 || v_isempty(p, len)) 25819304Speter pstate = P_INBLANK; 25919304Speter else { 26019304Speter --cnt; 26119304Speter pstate = P_INTEXT; 26219304Speter 26319304Speter /* 26419304Speter * !!! 26519304Speter * If the starting cursor is past the first column, 26619304Speter * the current line is checked for a paragraph. 26719304Speter */ 26819304Speter if (vp->m_start.cno > 0) 26919304Speter ++lno; 27019304Speter } 27119304Speter 27219304Speter for (;;) { 27319304Speter if (db_get(sp, --lno, 0, &p, &len)) 27419304Speter goto sof; 27519304Speter switch (pstate) { 27619304Speter case P_INTEXT: 27719304Speter INTEXT_CHECK; 27819304Speter break; 27919304Speter case P_INBLANK: 28019304Speter if (len != 0 && !v_isempty(p, len)) { 28119304Speter if (!--cnt) 28219304Speter goto found; 28319304Speter pstate = P_INTEXT; 28419304Speter } 28519304Speter break; 28619304Speter default: 28719304Speter abort(); 28819304Speter } 28919304Speter } 29019304Speter 29119304Speter /* SOF is a movement sink. */ 29219304Spetersof: lno = 1; 29319304Speter 29419304Speterfound: vp->m_stop.lno = lno; 29519304Speter vp->m_stop.cno = 0; 29619304Speter 29719304Speter /* 29819304Speter * All commands move to the end of the range. (We already 29919304Speter * adjusted the start of the range for motion commands). 30019304Speter */ 30119304Speter vp->m_final = vp->m_stop; 30219304Speter return (0); 30319304Speter} 30419304Speter 30519304Speter/* 30619304Speter * v_buildps -- 30719304Speter * Build the paragraph command search pattern. 30819304Speter * 30919304Speter * PUBLIC: int v_buildps __P((SCR *, char *, char *)); 31019304Speter */ 31119304Speterint 312254225Speterv_buildps(SCR *sp, char *p_p, char *s_p) 31319304Speter{ 31419304Speter VI_PRIVATE *vip; 31519304Speter size_t p_len, s_len; 31619304Speter char *p; 31719304Speter 31819304Speter /* 31919304Speter * The vi paragraph command searches for either a paragraph or 32019304Speter * section option macro. 32119304Speter */ 32219304Speter p_len = p_p == NULL ? 0 : strlen(p_p); 32319304Speter s_len = s_p == NULL ? 0 : strlen(s_p); 32419304Speter 32519304Speter if (p_len == 0 && s_len == 0) 32619304Speter return (0); 32719304Speter 32819304Speter MALLOC_RET(sp, p, char *, p_len + s_len + 1); 32919304Speter 33019304Speter vip = VIP(sp); 33119304Speter if (vip->ps != NULL) 33219304Speter free(vip->ps); 33319304Speter 33419304Speter if (p_p != NULL) 33519304Speter memmove(p, p_p, p_len + 1); 33619304Speter if (s_p != NULL) 33719304Speter memmove(p + p_len, s_p, s_len + 1); 33819304Speter vip->ps = p; 33919304Speter return (0); 34019304Speter} 341