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