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