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 1319304Speterstatic const char sccsid[] = "@(#)v_paragraph.c 10.7 (Berkeley) 3/6/96"; 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 6719304Speterv_paragraphf(sp, vp) 6819304Speter SCR *sp; 6919304Speter VICMD *vp; 7019304Speter{ 7119304Speter enum { P_INTEXT, P_INBLANK } pstate; 7219304Speter size_t lastlen, len; 7319304Speter recno_t cnt, lastlno, lno; 7419304Speter int isempty; 7519304Speter char *p, *lp; 7619304Speter 7719304Speter /* 7819304Speter * !!! 7919304Speter * If the starting cursor position is at or before any non-blank 8019304Speter * characters in the line, i.e. the movement is cutting all of the 8119304Speter * line's text, the buffer is in line mode. It's a lot easier to 8219304Speter * check here, because we know that the end is going to be the start 8319304Speter * or end of a line. 8419304Speter * 8519304Speter * This was historical practice in vi, with a single exception. If 8619304Speter * the paragraph movement was from the start of the last line to EOF, 8719304Speter * then all the characters were deleted from the last line, but the 8819304Speter * line itself remained. If somebody complains, don't pause, don't 8919304Speter * hesitate, just hit them. 9019304Speter */ 9119304Speter if (ISMOTION(vp)) 9219304Speter if (vp->m_start.cno == 0) 9319304Speter F_SET(vp, VM_LMODE); 9419304Speter else { 9519304Speter vp->m_stop = vp->m_start; 9619304Speter vp->m_stop.cno = 0; 9719304Speter if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno)) 9819304Speter return (1); 9919304Speter if (vp->m_start.cno <= vp->m_stop.cno) 10019304Speter F_SET(vp, VM_LMODE); 10119304Speter } 10219304Speter 10319304Speter /* Figure out what state we're currently in. */ 10419304Speter lno = vp->m_start.lno; 10519304Speter if (db_get(sp, lno, 0, &p, &len)) 10619304Speter goto eof; 10719304Speter 10819304Speter /* 10919304Speter * If we start in text, we want to switch states 11019304Speter * (2 * N - 1) times, in non-text, (2 * N) times. 11119304Speter */ 11219304Speter cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 11319304Speter cnt *= 2; 11419304Speter if (len == 0 || v_isempty(p, len)) 11519304Speter pstate = P_INBLANK; 11619304Speter else { 11719304Speter --cnt; 11819304Speter pstate = P_INTEXT; 11919304Speter } 12019304Speter 12119304Speter for (;;) { 12219304Speter lastlno = lno; 12319304Speter lastlen = len; 12419304Speter if (db_get(sp, ++lno, 0, &p, &len)) 12519304Speter goto eof; 12619304Speter switch (pstate) { 12719304Speter case P_INTEXT: 12819304Speter INTEXT_CHECK; 12919304Speter break; 13019304Speter case P_INBLANK: 13119304Speter if (len == 0 || v_isempty(p, len)) 13219304Speter break; 13319304Speter if (--cnt) { 13419304Speter pstate = P_INTEXT; 13519304Speter break; 13619304Speter } 13719304Speter /* 13819304Speter * !!! 13919304Speter * Non-motion commands move to the end of the range, 14019304Speter * delete and yank stay at the start. Ignore others. 14119304Speter * Adjust the end of the range for motion commands; 14219304Speter * historically, a motion component was to the end of 14319304Speter * the previous line, whereas the movement command was 14419304Speter * to the start of the new "paragraph". 14519304Speter */ 14619304Speterfound: if (ISMOTION(vp)) { 14719304Speter vp->m_stop.lno = lastlno; 14819304Speter vp->m_stop.cno = lastlen ? lastlen - 1 : 0; 14919304Speter vp->m_final = vp->m_start; 15019304Speter } else { 15119304Speter vp->m_stop.lno = lno; 15219304Speter vp->m_stop.cno = 0; 15319304Speter vp->m_final = vp->m_stop; 15419304Speter } 15519304Speter return (0); 15619304Speter default: 15719304Speter abort(); 15819304Speter } 15919304Speter } 16019304Speter 16119304Speter /* 16219304Speter * !!! 16319304Speter * Adjust end of the range for motion commands; EOF is a movement 16419304Speter * sink. The } command historically moved to the end of the last 16519304Speter * line, not the beginning, from any position before the end of the 16619304Speter * last line. It also historically worked on empty files, so we 16719304Speter * have to make it okay. 16819304Speter */ 16919304Spetereof: if (vp->m_start.lno == lno || vp->m_start.lno == lno - 1) { 17019304Speter if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 17119304Speter if (!isempty) 17219304Speter return (1); 17319304Speter vp->m_start.cno = 0; 17419304Speter return (0); 17519304Speter } 17619304Speter if (vp->m_start.cno == (len ? len - 1 : 0)) { 17719304Speter v_eof(sp, NULL); 17819304Speter return (1); 17919304Speter } 18019304Speter } 18119304Speter /* 18219304Speter * !!! 18319304Speter * Non-motion commands move to the end of the range, delete 18419304Speter * and yank stay at the start. Ignore others. 18519304Speter * 18619304Speter * If deleting the line (which happens if deleting to EOF), then 18719304Speter * cursor movement is to the first nonblank. 18819304Speter */ 18919304Speter if (ISMOTION(vp) && ISCMD(vp->rkp, 'd')) { 19019304Speter F_CLR(vp, VM_RCM_MASK); 19119304Speter F_SET(vp, VM_RCM_SETFNB); 19219304Speter } 19319304Speter vp->m_stop.lno = lno - 1; 19419304Speter vp->m_stop.cno = len ? len - 1 : 0; 19519304Speter vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop; 19619304Speter return (0); 19719304Speter} 19819304Speter 19919304Speter/* 20019304Speter * v_paragraphb -- [count]{ 20119304Speter * Move backward count paragraphs. 20219304Speter * 20319304Speter * PUBLIC: int v_paragraphb __P((SCR *, VICMD *)); 20419304Speter */ 20519304Speterint 20619304Speterv_paragraphb(sp, vp) 20719304Speter SCR *sp; 20819304Speter VICMD *vp; 20919304Speter{ 21019304Speter enum { P_INTEXT, P_INBLANK } pstate; 21119304Speter size_t len; 21219304Speter recno_t cnt, lno; 21319304Speter char *p, *lp; 21419304Speter 21519304Speter /* 21619304Speter * !!! 21719304Speter * Check for SOF. The historic vi didn't complain if users hit SOF 21819304Speter * repeatedly, unless it was part of a motion command. There is no 21919304Speter * question but that Emerson's editor of choice was vi. 22019304Speter * 22119304Speter * The { command historically moved to the beginning of the first 22219304Speter * line if invoked on the first line. 22319304Speter * 22419304Speter * !!! 22519304Speter * If the starting cursor position is in the first column (backward 22619304Speter * paragraph movements did NOT historically pay attention to non-blank 22719304Speter * characters) i.e. the movement is cutting the entire line, the buffer 22819304Speter * is in line mode. Cuts from the beginning of the line also did not 22919304Speter * cut the current line, but started at the previous EOL. 23019304Speter * 23119304Speter * Correct for a left motion component while we're thinking about it. 23219304Speter */ 23319304Speter lno = vp->m_start.lno; 23419304Speter 23519304Speter if (ISMOTION(vp)) 23619304Speter if (vp->m_start.cno == 0) { 23719304Speter if (vp->m_start.lno == 1) { 23819304Speter v_sof(sp, &vp->m_start); 23919304Speter return (1); 24019304Speter } else 24119304Speter --vp->m_start.lno; 24219304Speter F_SET(vp, VM_LMODE); 24319304Speter } else 24419304Speter --vp->m_start.cno; 24519304Speter 24619304Speter if (vp->m_start.lno <= 1) 24719304Speter goto sof; 24819304Speter 24919304Speter /* Figure out what state we're currently in. */ 25019304Speter if (db_get(sp, lno, 0, &p, &len)) 25119304Speter goto sof; 25219304Speter 25319304Speter /* 25419304Speter * If we start in text, we want to switch states 25519304Speter * (2 * N - 1) times, in non-text, (2 * N) times. 25619304Speter */ 25719304Speter cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 25819304Speter cnt *= 2; 25919304Speter if (len == 0 || v_isempty(p, len)) 26019304Speter pstate = P_INBLANK; 26119304Speter else { 26219304Speter --cnt; 26319304Speter pstate = P_INTEXT; 26419304Speter 26519304Speter /* 26619304Speter * !!! 26719304Speter * If the starting cursor is past the first column, 26819304Speter * the current line is checked for a paragraph. 26919304Speter */ 27019304Speter if (vp->m_start.cno > 0) 27119304Speter ++lno; 27219304Speter } 27319304Speter 27419304Speter for (;;) { 27519304Speter if (db_get(sp, --lno, 0, &p, &len)) 27619304Speter goto sof; 27719304Speter switch (pstate) { 27819304Speter case P_INTEXT: 27919304Speter INTEXT_CHECK; 28019304Speter break; 28119304Speter case P_INBLANK: 28219304Speter if (len != 0 && !v_isempty(p, len)) { 28319304Speter if (!--cnt) 28419304Speter goto found; 28519304Speter pstate = P_INTEXT; 28619304Speter } 28719304Speter break; 28819304Speter default: 28919304Speter abort(); 29019304Speter } 29119304Speter } 29219304Speter 29319304Speter /* SOF is a movement sink. */ 29419304Spetersof: lno = 1; 29519304Speter 29619304Speterfound: vp->m_stop.lno = lno; 29719304Speter vp->m_stop.cno = 0; 29819304Speter 29919304Speter /* 30019304Speter * All commands move to the end of the range. (We already 30119304Speter * adjusted the start of the range for motion commands). 30219304Speter */ 30319304Speter vp->m_final = vp->m_stop; 30419304Speter return (0); 30519304Speter} 30619304Speter 30719304Speter/* 30819304Speter * v_buildps -- 30919304Speter * Build the paragraph command search pattern. 31019304Speter * 31119304Speter * PUBLIC: int v_buildps __P((SCR *, char *, char *)); 31219304Speter */ 31319304Speterint 31419304Speterv_buildps(sp, p_p, s_p) 31519304Speter SCR *sp; 31619304Speter char *p_p, *s_p; 31719304Speter{ 31819304Speter VI_PRIVATE *vip; 31919304Speter size_t p_len, s_len; 32019304Speter char *p; 32119304Speter 32219304Speter /* 32319304Speter * The vi paragraph command searches for either a paragraph or 32419304Speter * section option macro. 32519304Speter */ 32619304Speter p_len = p_p == NULL ? 0 : strlen(p_p); 32719304Speter s_len = s_p == NULL ? 0 : strlen(s_p); 32819304Speter 32919304Speter if (p_len == 0 && s_len == 0) 33019304Speter return (0); 33119304Speter 33219304Speter MALLOC_RET(sp, p, char *, p_len + s_len + 1); 33319304Speter 33419304Speter vip = VIP(sp); 33519304Speter if (vip->ps != NULL) 33619304Speter free(vip->ps); 33719304Speter 33819304Speter if (p_p != NULL) 33919304Speter memmove(p, p_p, p_len + 1); 34019304Speter if (s_p != NULL) 34119304Speter memmove(p + p_len, s_p, s_len + 1); 34219304Speter vip->ps = p; 34319304Speter return (0); 34419304Speter} 345