1 /* $NetBSD: v_paragraph.c,v 1.3 2014/01/26 21:43:45 christos 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.3 2014/01/26 21:43:45 christos 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 lastlen, len; 77 db_recno_t cnt, lastlno, 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 (ISMOTION(vp)) { 97 if (vp->m_start.cno == 0) 98 F_SET(vp, VM_LMODE); 99 else { 100 vp->m_stop = vp->m_start; 101 vp->m_stop.cno = 0; 102 if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno)) 103 return (1); 104 if (vp->m_start.cno <= vp->m_stop.cno) 105 F_SET(vp, VM_LMODE); 106 } 107 } 108 109 /* Figure out what state we're currently in. */ 110 lno = vp->m_start.lno; 111 if (db_get(sp, lno, 0, &p, &len)) 112 goto eof; 113 114 /* 115 * If we start in text, we want to switch states 116 * (2 * N - 1) times, in non-text, (2 * N) times. 117 */ 118 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 119 cnt *= 2; 120 if (len == 0 || v_isempty(p, len)) 121 pstate = P_INBLANK; 122 else { 123 --cnt; 124 pstate = P_INTEXT; 125 } 126 127 for (;;) { 128 lastlno = lno; 129 lastlen = len; 130 if (db_get(sp, ++lno, 0, &p, &len)) 131 goto eof; 132 switch (pstate) { 133 case P_INTEXT: 134 INTEXT_CHECK; 135 break; 136 case P_INBLANK: 137 if (len == 0 || v_isempty(p, len)) 138 break; 139 if (--cnt) { 140 pstate = P_INTEXT; 141 break; 142 } 143 /* 144 * !!! 145 * Non-motion commands move to the end of the range, 146 * delete and yank stay at the start. Ignore others. 147 * Adjust the end of the range for motion commands; 148 * historically, a motion component was to the end of 149 * the previous line, whereas the movement command was 150 * to the start of the new "paragraph". 151 */ 152 found: if (ISMOTION(vp)) { 153 vp->m_stop.lno = lastlno; 154 vp->m_stop.cno = lastlen ? lastlen - 1 : 0; 155 vp->m_final = vp->m_start; 156 } else { 157 vp->m_stop.lno = lno; 158 vp->m_stop.cno = 0; 159 vp->m_final = vp->m_stop; 160 } 161 return (0); 162 default: 163 abort(); 164 } 165 } 166 167 /* 168 * !!! 169 * Adjust end of the range for motion commands; EOF is a movement 170 * sink. The } command historically moved to the end of the last 171 * line, not the beginning, from any position before the end of the 172 * last line. It also historically worked on empty files, so we 173 * have to make it okay. 174 */ 175 eof: if (vp->m_start.lno == lno || vp->m_start.lno == lno - 1) { 176 if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 177 if (!isempty) 178 return (1); 179 vp->m_start.cno = 0; 180 return (0); 181 } 182 if (vp->m_start.cno == (len ? len - 1 : 0)) { 183 v_eof(sp, NULL); 184 return (1); 185 } 186 } 187 /* 188 * !!! 189 * Non-motion commands move to the end of the range, delete 190 * and yank stay at the start. Ignore others. 191 * 192 * If deleting the line (which happens if deleting to EOF), then 193 * cursor movement is to the first nonblank. 194 */ 195 if (ISMOTION(vp) && ISCMD(vp->rkp, 'd')) { 196 F_CLR(vp, VM_RCM_MASK); 197 F_SET(vp, VM_RCM_SETFNB); 198 } 199 vp->m_stop.lno = lno - 1; 200 vp->m_stop.cno = len ? len - 1 : 0; 201 vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop; 202 return (0); 203 } 204 205 /* 206 * v_paragraphb -- [count]{ 207 * Move backward count paragraphs. 208 * 209 * PUBLIC: int v_paragraphb __P((SCR *, VICMD *)); 210 */ 211 int 212 v_paragraphb(SCR *sp, VICMD *vp) 213 { 214 enum { P_INTEXT, P_INBLANK } pstate; 215 size_t len; 216 db_recno_t cnt, lno; 217 CHAR_T *p; 218 char *lp; 219 220 /* 221 * !!! 222 * Check for SOF. The historic vi didn't complain if users hit SOF 223 * repeatedly, unless it was part of a motion command. There is no 224 * question but that Emerson's editor of choice was vi. 225 * 226 * The { command historically moved to the beginning of the first 227 * line if invoked on the first line. 228 * 229 * !!! 230 * If the starting cursor position is in the first column (backward 231 * paragraph movements did NOT historically pay attention to non-blank 232 * characters) i.e. the movement is cutting the entire line, the buffer 233 * is in line mode. Cuts from the beginning of the line also did not 234 * cut the current line, but started at the previous EOL. 235 * 236 * Correct for a left motion component while we're thinking about it. 237 */ 238 lno = vp->m_start.lno; 239 240 if (ISMOTION(vp)) { 241 if (vp->m_start.cno == 0) { 242 if (vp->m_start.lno == 1) { 243 v_sof(sp, &vp->m_start); 244 return (1); 245 } else 246 --vp->m_start.lno; 247 F_SET(vp, VM_LMODE); 248 } else 249 --vp->m_start.cno; 250 } 251 252 if (vp->m_start.lno <= 1) 253 goto sof; 254 255 /* Figure out what state we're currently in. */ 256 if (db_get(sp, lno, 0, &p, &len)) 257 goto sof; 258 259 /* 260 * If we start in text, we want to switch states 261 * (2 * N - 1) times, in non-text, (2 * N) times. 262 */ 263 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 264 cnt *= 2; 265 if (len == 0 || v_isempty(p, len)) 266 pstate = P_INBLANK; 267 else { 268 --cnt; 269 pstate = P_INTEXT; 270 271 /* 272 * !!! 273 * If the starting cursor is past the first column, 274 * the current line is checked for a paragraph. 275 */ 276 if (vp->m_start.cno > 0) 277 ++lno; 278 } 279 280 for (;;) { 281 if (db_get(sp, --lno, 0, &p, &len)) 282 goto sof; 283 switch (pstate) { 284 case P_INTEXT: 285 INTEXT_CHECK; 286 break; 287 case P_INBLANK: 288 if (len != 0 && !v_isempty(p, len)) { 289 if (!--cnt) 290 goto found; 291 pstate = P_INTEXT; 292 } 293 break; 294 default: 295 abort(); 296 } 297 } 298 299 /* SOF is a movement sink. */ 300 sof: lno = 1; 301 302 found: vp->m_stop.lno = lno; 303 vp->m_stop.cno = 0; 304 305 /* 306 * All commands move to the end of the range. (We already 307 * adjusted the start of the range for motion commands). 308 */ 309 vp->m_final = vp->m_stop; 310 return (0); 311 } 312 313 /* 314 * v_buildps -- 315 * Build the paragraph command search pattern. 316 * 317 * PUBLIC: int v_buildps __P((SCR *, const char *, const char *)); 318 */ 319 int 320 v_buildps(SCR *sp, const char *p_p, const char *s_p) 321 { 322 VI_PRIVATE *vip; 323 size_t p_len, s_len; 324 char *p; 325 326 /* 327 * The vi paragraph command searches for either a paragraph or 328 * section option macro. 329 */ 330 p_len = p_p == NULL ? 0 : strlen(p_p); 331 s_len = s_p == NULL ? 0 : strlen(s_p); 332 333 if (p_len == 0 && s_len == 0) 334 return (0); 335 336 MALLOC_RET(sp, p, char *, p_len + s_len + 1); 337 338 vip = VIP(sp); 339 if (vip->ps != NULL) 340 free(vip->ps); 341 342 if (p_p != NULL) 343 memmove(p, p_p, p_len + 1); 344 if (s_p != NULL) 345 memmove(p + p_len, s_p, s_len + 1); 346 vip->ps = p; 347 return (0); 348 } 349