1 /*- 2 * Copyright (c) 1992, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 1992, 1993, 1994, 1995, 1996 5 * Keith Bostic. All rights reserved. 6 * 7 * See the LICENSE file for redistribution information. 8 */ 9 10 #include "config.h" 11 12 #include <sys/types.h> 13 #include <sys/queue.h> 14 #include <sys/time.h> 15 16 #include <bitstring.h> 17 #include <errno.h> 18 #include <limits.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 23 #include "../common/common.h" 24 #include "vi.h" 25 26 #define INTEXT_CHECK do { \ 27 if (len == 0 || v_isempty(p, len)) { \ 28 if (!--cnt) \ 29 goto found; \ 30 pstate = P_INBLANK; \ 31 } \ 32 /* \ 33 * !!! \ 34 * Historic documentation (USD:15-11, 4.2) said that formfeed \ 35 * characters (^L) in the first column delimited paragraphs. \ 36 * The historic vi code mentions formfeed characters, but never \ 37 * implements them. It seems reasonable, do it. \ 38 */ \ 39 if (p[0] == '\014') { \ 40 if (!--cnt) \ 41 goto found; \ 42 if (pstate == P_INTEXT && !--cnt) \ 43 goto found; \ 44 continue; \ 45 } \ 46 if (p[0] != '.' || len < 2) \ 47 continue; \ 48 for (lp = VIP(sp)->ps; *lp != '\0'; lp += 2) \ 49 if (lp[0] == p[1] && \ 50 (lp[1] == ' ' && len == 2 || lp[1] == p[2])) { \ 51 if (!--cnt) \ 52 goto found; \ 53 if (pstate == P_INTEXT && !--cnt) \ 54 goto found; \ 55 } \ 56 } while (0) 57 58 /* 59 * v_paragraphf -- [count]} 60 * Move forward count paragraphs. 61 * 62 * Paragraphs are empty lines after text, formfeed characters, or values 63 * from the paragraph or section options. 64 * 65 * PUBLIC: int v_paragraphf(SCR *, VICMD *); 66 */ 67 int 68 v_paragraphf(SCR *sp, VICMD *vp) 69 { 70 enum { P_INTEXT, P_INBLANK } pstate; 71 size_t lastlen, len; 72 recno_t cnt, lastlno, lno; 73 int isempty; 74 CHAR_T *p; 75 char *lp; 76 77 /* 78 * !!! 79 * If the starting cursor position is at or before any non-blank 80 * characters in the line, i.e. the movement is cutting all of the 81 * line's text, the buffer is in line mode. It's a lot easier to 82 * check here, because we know that the end is going to be the start 83 * or end of a line. 84 * 85 * This was historical practice in vi, with a single exception. If 86 * the paragraph movement was from the start of the last line to EOF, 87 * then all the characters were deleted from the last line, but the 88 * line itself remained. If somebody complains, don't pause, don't 89 * hesitate, just hit them. 90 */ 91 if (ISMOTION(vp)) { 92 if (vp->m_start.cno == 0) 93 F_SET(vp, VM_LMODE); 94 else { 95 vp->m_stop = vp->m_start; 96 vp->m_stop.cno = 0; 97 if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno)) 98 return (1); 99 if (vp->m_start.cno <= vp->m_stop.cno) 100 F_SET(vp, VM_LMODE); 101 } 102 } 103 104 /* Figure out what state we're currently in. */ 105 lno = vp->m_start.lno; 106 if (db_get(sp, lno, 0, &p, &len)) 107 goto eof; 108 109 /* 110 * If we start in text, we want to switch states 111 * (2 * N - 1) times, in non-text, (2 * N) times. 112 */ 113 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 114 cnt *= 2; 115 if (len == 0 || v_isempty(p, len)) 116 pstate = P_INBLANK; 117 else { 118 --cnt; 119 pstate = P_INTEXT; 120 } 121 122 for (;;) { 123 lastlno = lno; 124 lastlen = len; 125 if (db_get(sp, ++lno, 0, &p, &len)) 126 goto eof; 127 switch (pstate) { 128 case P_INTEXT: 129 INTEXT_CHECK; 130 break; 131 case P_INBLANK: 132 if (len == 0 || v_isempty(p, len)) 133 break; 134 if (--cnt) { 135 pstate = P_INTEXT; 136 break; 137 } 138 /* 139 * !!! 140 * Non-motion commands move to the end of the range, 141 * delete and yank stay at the start. Ignore others. 142 * Adjust the end of the range for motion commands; 143 * historically, a motion component was to the end of 144 * the previous line, whereas the movement command was 145 * to the start of the new "paragraph". 146 */ 147 found: if (ISMOTION(vp)) { 148 vp->m_stop.lno = lastlno; 149 vp->m_stop.cno = lastlen ? lastlen - 1 : 0; 150 vp->m_final = vp->m_start; 151 } else { 152 vp->m_stop.lno = lno; 153 vp->m_stop.cno = 0; 154 vp->m_final = vp->m_stop; 155 } 156 return (0); 157 default: 158 abort(); 159 } 160 } 161 162 /* 163 * !!! 164 * Adjust end of the range for motion commands; EOF is a movement 165 * sink. The } command historically moved to the end of the last 166 * line, not the beginning, from any position before the end of the 167 * last line. It also historically worked on empty files, so we 168 * have to make it okay. 169 */ 170 eof: if (vp->m_start.lno == lno || vp->m_start.lno == lno - 1) { 171 if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 172 if (!isempty) 173 return (1); 174 vp->m_start.cno = 0; 175 return (0); 176 } 177 if (vp->m_start.cno == (len ? len - 1 : 0)) { 178 v_eof(sp, NULL); 179 return (1); 180 } 181 } 182 /* 183 * !!! 184 * Non-motion commands move to the end of the range, delete 185 * and yank stay at the start. Ignore others. 186 * 187 * If deleting the line (which happens if deleting to EOF), then 188 * cursor movement is to the first nonblank. 189 */ 190 if (ISMOTION(vp) && ISCMD(vp->rkp, 'd')) { 191 F_CLR(vp, VM_RCM_MASK); 192 F_SET(vp, VM_RCM_SETFNB); 193 } 194 vp->m_stop.lno = lno - 1; 195 vp->m_stop.cno = len ? len - 1 : 0; 196 vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop; 197 return (0); 198 } 199 200 /* 201 * v_paragraphb -- [count]{ 202 * Move backward count paragraphs. 203 * 204 * PUBLIC: int v_paragraphb(SCR *, VICMD *); 205 */ 206 int 207 v_paragraphb(SCR *sp, VICMD *vp) 208 { 209 enum { P_INTEXT, P_INBLANK } pstate; 210 size_t len; 211 recno_t cnt, lno; 212 CHAR_T *p; 213 char *lp; 214 215 /* 216 * !!! 217 * Check for SOF. The historic vi didn't complain if users hit SOF 218 * repeatedly, unless it was part of a motion command. There is no 219 * question but that Emerson's editor of choice was vi. 220 * 221 * The { command historically moved to the beginning of the first 222 * line if invoked on the first line. 223 * 224 * !!! 225 * If the starting cursor position is in the first column (backward 226 * paragraph movements did NOT historically pay attention to non-blank 227 * characters) i.e. the movement is cutting the entire line, the buffer 228 * is in line mode. Cuts from the beginning of the line also did not 229 * cut the current line, but started at the previous EOL. 230 * 231 * Correct for a left motion component while we're thinking about it. 232 */ 233 lno = vp->m_start.lno; 234 235 if (ISMOTION(vp)) { 236 if (vp->m_start.cno == 0) { 237 if (vp->m_start.lno == 1) { 238 v_sof(sp, &vp->m_start); 239 return (1); 240 } else 241 --vp->m_start.lno; 242 F_SET(vp, VM_LMODE); 243 } else 244 --vp->m_start.cno; 245 } 246 247 if (vp->m_start.lno <= 1) 248 goto sof; 249 250 /* Figure out what state we're currently in. */ 251 if (db_get(sp, lno, 0, &p, &len)) 252 goto sof; 253 254 /* 255 * If we start in text, we want to switch states 256 * (2 * N - 1) times, in non-text, (2 * N) times. 257 */ 258 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 259 cnt *= 2; 260 if (len == 0 || v_isempty(p, len)) 261 pstate = P_INBLANK; 262 else { 263 --cnt; 264 pstate = P_INTEXT; 265 266 /* 267 * !!! 268 * If the starting cursor is past the first column, 269 * the current line is checked for a paragraph. 270 */ 271 if (vp->m_start.cno > 0) 272 ++lno; 273 } 274 275 for (;;) { 276 if (db_get(sp, --lno, 0, &p, &len)) 277 goto sof; 278 switch (pstate) { 279 case P_INTEXT: 280 INTEXT_CHECK; 281 break; 282 case P_INBLANK: 283 if (len != 0 && !v_isempty(p, len)) { 284 if (!--cnt) 285 goto found; 286 pstate = P_INTEXT; 287 } 288 break; 289 default: 290 abort(); 291 } 292 } 293 294 /* SOF is a movement sink. */ 295 sof: lno = 1; 296 297 found: vp->m_stop.lno = lno; 298 vp->m_stop.cno = 0; 299 300 /* 301 * All commands move to the end of the range. (We already 302 * adjusted the start of the range for motion commands). 303 */ 304 vp->m_final = vp->m_stop; 305 return (0); 306 } 307 308 /* 309 * v_buildps -- 310 * Build the paragraph command search pattern. 311 * 312 * PUBLIC: int v_buildps(SCR *, char *, char *); 313 */ 314 int 315 v_buildps(SCR *sp, char *p_p, char *s_p) 316 { 317 VI_PRIVATE *vip; 318 size_t p_len, s_len; 319 char *p; 320 321 /* 322 * The vi paragraph command searches for either a paragraph or 323 * section option macro. 324 */ 325 p_len = p_p == NULL ? 0 : strlen(p_p); 326 s_len = s_p == NULL ? 0 : strlen(s_p); 327 328 if (p_len == 0 && s_len == 0) 329 return (0); 330 331 MALLOC_RET(sp, p, p_len + s_len + 1); 332 333 vip = VIP(sp); 334 free(vip->ps); 335 336 if (p_p != NULL) 337 memmove(p, p_p, p_len + 1); 338 if (s_p != NULL) 339 memmove(p + p_len, s_p, s_len + 1); 340 vip->ps = p; 341 return (0); 342 } 343