1 /* $OpenBSD: v_section.c,v 1.3 2001/01/29 01:58:52 niklas Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * Copyright (c) 1992, 1993, 1994, 1995, 1996 7 * Keith Bostic. All rights reserved. 8 * 9 * See the LICENSE file for redistribution information. 10 */ 11 12 #include "config.h" 13 14 #ifndef lint 15 static const char sccsid[] = "@(#)v_section.c 10.7 (Berkeley) 3/6/96"; 16 #endif /* not lint */ 17 18 #include <sys/types.h> 19 #include <sys/queue.h> 20 #include <sys/time.h> 21 22 #include <bitstring.h> 23 #include <limits.h> 24 #include <stdio.h> 25 #include <string.h> 26 27 #include "../common/common.h" 28 #include "vi.h" 29 30 /* 31 * !!! 32 * In historic vi, the section commands ignored empty lines, unlike the 33 * paragraph commands, which was probably okay. However, they also moved 34 * to the start of the last line when there where no more sections instead 35 * of the end of the last line like the paragraph commands. I've changed 36 * the latter behavior to match the paragraph commands. 37 * 38 * In historic vi, a section was defined as the first character(s) of the 39 * line matching, which could be followed by anything. This implementation 40 * follows that historic practice. 41 * 42 * !!! 43 * The historic vi documentation (USD:15-10) claimed: 44 * The section commands interpret a preceding count as a different 45 * window size in which to redraw the screen at the new location, 46 * and this window size is the base size for newly drawn windows 47 * until another size is specified. This is very useful if you are 48 * on a slow terminal ... 49 * 50 * I can't get the 4BSD vi to do this, it just beeps at me. For now, a 51 * count to the section commands simply repeats the command. 52 */ 53 54 /* 55 * v_sectionf -- [count]]] 56 * Move forward count sections/functions. 57 * 58 * !!! 59 * Using ]] as a motion command was a bit special, historically. It could 60 * match } as well as the usual { and section values. If it matched a { or 61 * a section, it did NOT include the matched line. If it matched a }, it 62 * did include the line. No clue why. 63 * 64 * PUBLIC: int v_sectionf __P((SCR *, VICMD *)); 65 */ 66 int 67 v_sectionf(sp, vp) 68 SCR *sp; 69 VICMD *vp; 70 { 71 recno_t cnt, lno; 72 size_t len; 73 char *p, *list, *lp; 74 75 /* Get the macro list. */ 76 if ((list = O_STR(sp, O_SECTIONS)) == NULL) 77 return (1); 78 79 /* 80 * !!! 81 * If the starting cursor position is at or before any non-blank 82 * characters in the line, i.e. the movement is cutting all of the 83 * line's text, the buffer is in line mode. It's a lot easier to 84 * check here, because we know that the end is going to be the start 85 * or end of a line. 86 */ 87 if (ISMOTION(vp)) 88 if (vp->m_start.cno == 0) 89 F_SET(vp, VM_LMODE); 90 else { 91 vp->m_stop = vp->m_start; 92 vp->m_stop.cno = 0; 93 if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno)) 94 return (1); 95 if (vp->m_start.cno <= vp->m_stop.cno) 96 F_SET(vp, VM_LMODE); 97 } 98 99 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 100 for (lno = vp->m_start.lno; !db_get(sp, ++lno, 0, &p, &len);) { 101 if (len == 0) 102 continue; 103 if (p[0] == '{' || ISMOTION(vp) && p[0] == '}') { 104 if (!--cnt) { 105 if (p[0] == '{') 106 goto adjust1; 107 goto adjust2; 108 } 109 continue; 110 } 111 /* 112 * !!! 113 * Historic documentation (USD:15-11, 4.2) said that formfeed 114 * characters (^L) in the first column delimited sections. 115 * The historic code mentions formfeed characters, but never 116 * implements them. Seems reasonable, do it. 117 */ 118 if (p[0] == '\014') { 119 if (!--cnt) 120 goto adjust1; 121 continue; 122 } 123 if (p[0] != '.' || len < 2) 124 continue; 125 for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp)) 126 if (lp[0] == p[1] && 127 (lp[1] == ' ' && len == 2 || lp[1] == p[2]) && 128 !--cnt) { 129 /* 130 * !!! 131 * If not cutting this line, adjust to the end 132 * of the previous one. Otherwise, position to 133 * column 0. 134 */ 135 adjust1: if (ISMOTION(vp)) 136 goto ret1; 137 138 adjust2: vp->m_stop.lno = lno; 139 vp->m_stop.cno = 0; 140 goto ret2; 141 } 142 } 143 144 /* If moving forward, reached EOF, check to see if we started there. */ 145 if (vp->m_start.lno == lno - 1) { 146 v_eof(sp, NULL); 147 return (1); 148 } 149 150 ret1: if (db_get(sp, --lno, DBG_FATAL, NULL, &len)) 151 return (1); 152 vp->m_stop.lno = lno; 153 vp->m_stop.cno = len ? len - 1 : 0; 154 155 /* 156 * Non-motion commands go to the end of the range. Delete and 157 * yank stay at the start of the range. Ignore others. 158 */ 159 ret2: if (ISMOTION(vp)) { 160 vp->m_final = vp->m_start; 161 if (F_ISSET(vp, VM_LMODE)) 162 vp->m_final.cno = 0; 163 } else 164 vp->m_final = vp->m_stop; 165 return (0); 166 } 167 168 /* 169 * v_sectionb -- [count][[ 170 * Move backward count sections/functions. 171 * 172 * PUBLIC: int v_sectionb __P((SCR *, VICMD *)); 173 */ 174 int 175 v_sectionb(sp, vp) 176 SCR *sp; 177 VICMD *vp; 178 { 179 size_t len; 180 recno_t cnt, lno; 181 char *p, *list, *lp; 182 183 /* An empty file or starting from line 1 is always illegal. */ 184 if (vp->m_start.lno <= 1) { 185 v_sof(sp, NULL); 186 return (1); 187 } 188 189 /* Get the macro list. */ 190 if ((list = O_STR(sp, O_SECTIONS)) == NULL) 191 return (1); 192 193 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 194 for (lno = vp->m_start.lno; !db_get(sp, --lno, 0, &p, &len);) { 195 if (len == 0) 196 continue; 197 if (p[0] == '{') { 198 if (!--cnt) 199 goto adjust1; 200 continue; 201 } 202 /* 203 * !!! 204 * Historic documentation (USD:15-11, 4.2) said that formfeed 205 * characters (^L) in the first column delimited sections. 206 * The historic code mentions formfeed characters, but never 207 * implements them. Seems reasonable, do it. 208 */ 209 if (p[0] == '\014') { 210 if (!--cnt) 211 goto adjust1; 212 continue; 213 } 214 if (p[0] != '.' || len < 2) 215 continue; 216 for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp)) 217 if (lp[0] == p[1] && 218 (lp[1] == ' ' && len == 2 || lp[1] == p[2]) && 219 !--cnt) { 220 adjust1: vp->m_stop.lno = lno; 221 vp->m_stop.cno = 0; 222 goto ret1; 223 } 224 } 225 226 /* 227 * If moving backward, reached SOF, which is a movement sink. 228 * We already checked for starting there. 229 */ 230 vp->m_stop.lno = 1; 231 vp->m_stop.cno = 0; 232 233 /* 234 * All commands move to the end of the range. 235 * 236 * !!! 237 * Historic practice is the section cut was in line mode if it started 238 * from column 0 and was in the backward direction. Otherwise, left 239 * motion commands adjust the starting point to the character before 240 * the current one. What makes this worse is that if it cut to line 241 * mode it also went to the first non-<blank>. 242 */ 243 ret1: if (vp->m_start.cno == 0) { 244 F_CLR(vp, VM_RCM_MASK); 245 F_SET(vp, VM_RCM_SETFNB); 246 247 --vp->m_start.lno; 248 F_SET(vp, VM_LMODE); 249 } else 250 --vp->m_start.cno; 251 252 vp->m_final = vp->m_stop; 253 return (0); 254 } 255