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_section.c,v 10.10 2001/06/25 15:19:35 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 <limits.h> 2219304Speter#include <stdio.h> 2319304Speter#include <string.h> 2419304Speter 2519304Speter#include "../common/common.h" 2619304Speter#include "vi.h" 2719304Speter 2819304Speter/* 2919304Speter * !!! 3019304Speter * In historic vi, the section commands ignored empty lines, unlike the 3119304Speter * paragraph commands, which was probably okay. However, they also moved 3219304Speter * to the start of the last line when there where no more sections instead 3319304Speter * of the end of the last line like the paragraph commands. I've changed 3419304Speter * the latter behavior to match the paragraph commands. 3519304Speter * 3619304Speter * In historic vi, a section was defined as the first character(s) of the 3719304Speter * line matching, which could be followed by anything. This implementation 3819304Speter * follows that historic practice. 3919304Speter * 4019304Speter * !!! 4119304Speter * The historic vi documentation (USD:15-10) claimed: 4219304Speter * The section commands interpret a preceding count as a different 4319304Speter * window size in which to redraw the screen at the new location, 4419304Speter * and this window size is the base size for newly drawn windows 4519304Speter * until another size is specified. This is very useful if you are 4619304Speter * on a slow terminal ... 4719304Speter * 4819304Speter * I can't get the 4BSD vi to do this, it just beeps at me. For now, a 4919304Speter * count to the section commands simply repeats the command. 5019304Speter */ 5119304Speter 5219304Speter/* 5319304Speter * v_sectionf -- [count]]] 5419304Speter * Move forward count sections/functions. 5519304Speter * 5619304Speter * !!! 5719304Speter * Using ]] as a motion command was a bit special, historically. It could 5819304Speter * match } as well as the usual { and section values. If it matched a { or 5919304Speter * a section, it did NOT include the matched line. If it matched a }, it 6019304Speter * did include the line. No clue why. 6119304Speter * 6219304Speter * PUBLIC: int v_sectionf __P((SCR *, VICMD *)); 6319304Speter */ 6419304Speterint 65254225Speterv_sectionf(SCR *sp, VICMD *vp) 6619304Speter{ 6719304Speter recno_t cnt, lno; 6819304Speter size_t len; 69254225Speter CHAR_T *p; 70254225Speter char *list, *lp; 7119304Speter 7219304Speter /* Get the macro list. */ 7319304Speter if ((list = O_STR(sp, O_SECTIONS)) == NULL) 7419304Speter return (1); 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 if (ISMOTION(vp)) 8519304Speter if (vp->m_start.cno == 0) 8619304Speter F_SET(vp, VM_LMODE); 8719304Speter else { 8819304Speter vp->m_stop = vp->m_start; 8919304Speter vp->m_stop.cno = 0; 9019304Speter if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno)) 9119304Speter return (1); 9219304Speter if (vp->m_start.cno <= vp->m_stop.cno) 9319304Speter F_SET(vp, VM_LMODE); 9419304Speter } 9519304Speter 9619304Speter cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 9719304Speter for (lno = vp->m_start.lno; !db_get(sp, ++lno, 0, &p, &len);) { 9819304Speter if (len == 0) 9919304Speter continue; 100254225Speter if (p[0] == '{' || (ISMOTION(vp) && p[0] == '}')) { 10119304Speter if (!--cnt) { 10219304Speter if (p[0] == '{') 10319304Speter goto adjust1; 10419304Speter goto adjust2; 10519304Speter } 10619304Speter continue; 10719304Speter } 10819304Speter /* 10919304Speter * !!! 11019304Speter * Historic documentation (USD:15-11, 4.2) said that formfeed 11119304Speter * characters (^L) in the first column delimited sections. 11219304Speter * The historic code mentions formfeed characters, but never 11319304Speter * implements them. Seems reasonable, do it. 11419304Speter */ 11519304Speter if (p[0] == '\014') { 11619304Speter if (!--cnt) 11719304Speter goto adjust1; 11819304Speter continue; 11919304Speter } 12019304Speter if (p[0] != '.' || len < 2) 12119304Speter continue; 12219304Speter for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp)) 12319304Speter if (lp[0] == p[1] && 124254225Speter ((lp[1] == ' ' && len == 2) || lp[1] == p[2]) && 12519304Speter !--cnt) { 12619304Speter /* 12719304Speter * !!! 12819304Speter * If not cutting this line, adjust to the end 12919304Speter * of the previous one. Otherwise, position to 13019304Speter * column 0. 13119304Speter */ 13219304Speteradjust1: if (ISMOTION(vp)) 13319304Speter goto ret1; 13419304Speter 13519304Speteradjust2: vp->m_stop.lno = lno; 13619304Speter vp->m_stop.cno = 0; 13719304Speter goto ret2; 13819304Speter } 13919304Speter } 14019304Speter 14119304Speter /* If moving forward, reached EOF, check to see if we started there. */ 14219304Speter if (vp->m_start.lno == lno - 1) { 14319304Speter v_eof(sp, NULL); 14419304Speter return (1); 14519304Speter } 14619304Speter 14719304Speterret1: if (db_get(sp, --lno, DBG_FATAL, NULL, &len)) 14819304Speter return (1); 14919304Speter vp->m_stop.lno = lno; 15019304Speter vp->m_stop.cno = len ? len - 1 : 0; 15119304Speter 15219304Speter /* 15319304Speter * Non-motion commands go to the end of the range. Delete and 15419304Speter * yank stay at the start of the range. Ignore others. 15519304Speter */ 15619304Speterret2: if (ISMOTION(vp)) { 15719304Speter vp->m_final = vp->m_start; 15819304Speter if (F_ISSET(vp, VM_LMODE)) 15919304Speter vp->m_final.cno = 0; 16019304Speter } else 16119304Speter vp->m_final = vp->m_stop; 16219304Speter return (0); 16319304Speter} 16419304Speter 16519304Speter/* 16619304Speter * v_sectionb -- [count][[ 16719304Speter * Move backward count sections/functions. 16819304Speter * 16919304Speter * PUBLIC: int v_sectionb __P((SCR *, VICMD *)); 17019304Speter */ 17119304Speterint 172254225Speterv_sectionb(SCR *sp, VICMD *vp) 17319304Speter{ 17419304Speter size_t len; 17519304Speter recno_t cnt, lno; 176254225Speter CHAR_T *p; 177254225Speter char *list, *lp; 17819304Speter 17919304Speter /* An empty file or starting from line 1 is always illegal. */ 18019304Speter if (vp->m_start.lno <= 1) { 18119304Speter v_sof(sp, NULL); 18219304Speter return (1); 18319304Speter } 18419304Speter 18519304Speter /* Get the macro list. */ 18619304Speter if ((list = O_STR(sp, O_SECTIONS)) == NULL) 18719304Speter return (1); 18819304Speter 18919304Speter cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 19019304Speter for (lno = vp->m_start.lno; !db_get(sp, --lno, 0, &p, &len);) { 19119304Speter if (len == 0) 19219304Speter continue; 19319304Speter if (p[0] == '{') { 19419304Speter if (!--cnt) 19519304Speter goto adjust1; 19619304Speter continue; 19719304Speter } 19819304Speter /* 19919304Speter * !!! 20019304Speter * Historic documentation (USD:15-11, 4.2) said that formfeed 20119304Speter * characters (^L) in the first column delimited sections. 20219304Speter * The historic code mentions formfeed characters, but never 20319304Speter * implements them. Seems reasonable, do it. 20419304Speter */ 20519304Speter if (p[0] == '\014') { 20619304Speter if (!--cnt) 20719304Speter goto adjust1; 20819304Speter continue; 20919304Speter } 21019304Speter if (p[0] != '.' || len < 2) 21119304Speter continue; 21219304Speter for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp)) 21319304Speter if (lp[0] == p[1] && 214254225Speter ((lp[1] == ' ' && len == 2) || lp[1] == p[2]) && 21519304Speter !--cnt) { 21619304Speteradjust1: vp->m_stop.lno = lno; 21719304Speter vp->m_stop.cno = 0; 21819304Speter goto ret1; 21919304Speter } 22019304Speter } 22119304Speter 22219304Speter /* 22319304Speter * If moving backward, reached SOF, which is a movement sink. 22419304Speter * We already checked for starting there. 22519304Speter */ 22619304Speter vp->m_stop.lno = 1; 22719304Speter vp->m_stop.cno = 0; 22819304Speter 22919304Speter /* 23019304Speter * All commands move to the end of the range. 23119304Speter * 23219304Speter * !!! 23319304Speter * Historic practice is the section cut was in line mode if it started 23419304Speter * from column 0 and was in the backward direction. Otherwise, left 23519304Speter * motion commands adjust the starting point to the character before 23619304Speter * the current one. What makes this worse is that if it cut to line 23719304Speter * mode it also went to the first non-<blank>. 23819304Speter */ 23919304Speterret1: if (vp->m_start.cno == 0) { 24019304Speter F_CLR(vp, VM_RCM_MASK); 24119304Speter F_SET(vp, VM_RCM_SETFNB); 24219304Speter 24319304Speter --vp->m_start.lno; 24419304Speter F_SET(vp, VM_LMODE); 24519304Speter } else 24619304Speter --vp->m_start.cno; 24719304Speter 24819304Speter vp->m_final = vp->m_stop; 24919304Speter return (0); 25019304Speter} 251