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 #ifndef lint 13 static const char sccsid[] = "$Id: v_itxt.c,v 10.21 2001/06/25 15:19:32 skimo Exp $"; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/queue.h> 18 #include <sys/time.h> 19 20 #include <bitstring.h> 21 #include <ctype.h> 22 #include <errno.h> 23 #include <limits.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #include "../common/common.h" 29 #include "vi.h" 30 31 /* 32 * !!! 33 * Repeated input in the historic vi is mostly wrong and this isn't very 34 * backward compatible. For example, if the user entered "3Aab\ncd" in 35 * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then 36 * appended to the result. There was also a hack which I don't remember 37 * right now, where "3o" would open 3 lines and then let the user fill them 38 * in, to make screen movements on 300 baud modems more tolerable. I don't 39 * think it's going to be missed. 40 * 41 * !!! 42 * There's a problem with the way that we do logging for change commands with 43 * implied motions (e.g. A, I, O, cc, etc.). Since the main vi loop logs the 44 * starting cursor position before the change command "moves" the cursor, the 45 * cursor position to which we return on undo will be where the user entered 46 * the change command, not the start of the change. Several of the following 47 * routines re-log the cursor to make this work correctly. Historic vi tried 48 * to do the same thing, and mostly got it right. (The only spectacular way 49 * it fails is if the user entered 'o' from anywhere but the last character of 50 * the line, the undo returned the cursor to the start of the line. If the 51 * user was on the last character of the line, the cursor returned to that 52 * position.) We also check for mapped keys waiting, i.e. if we're in the 53 * middle of a map, don't bother logging the cursor. 54 */ 55 #define LOG_CORRECT { \ 56 if (!MAPPED_KEYS_WAITING(sp)) \ 57 (void)log_cursor(sp); \ 58 } 59 60 static u_int32_t set_txt_std __P((SCR *, VICMD *, u_int32_t)); 61 62 /* 63 * v_iA -- [count]A 64 * Append text to the end of the line. 65 * 66 * PUBLIC: int v_iA __P((SCR *, VICMD *)); 67 */ 68 int 69 v_iA(SCR *sp, VICMD *vp) 70 { 71 size_t len; 72 73 if (!db_get(sp, vp->m_start.lno, 0, NULL, &len)) 74 sp->cno = len == 0 ? 0 : len - 1; 75 76 LOG_CORRECT; 77 78 return (v_ia(sp, vp)); 79 } 80 81 /* 82 * v_ia -- [count]a 83 * [count]A 84 * Append text to the cursor position. 85 * 86 * PUBLIC: int v_ia __P((SCR *, VICMD *)); 87 */ 88 int 89 v_ia(SCR *sp, VICMD *vp) 90 { 91 size_t len; 92 u_int32_t flags; 93 int isempty; 94 CHAR_T *p; 95 96 flags = set_txt_std(sp, vp, 0); 97 sp->showmode = SM_APPEND; 98 sp->lno = vp->m_start.lno; 99 100 /* Move the cursor one column to the right and repaint the screen. */ 101 if (db_eget(sp, sp->lno, &p, &len, &isempty)) { 102 if (!isempty) 103 return (1); 104 len = 0; 105 LF_SET(TXT_APPENDEOL); 106 } else if (len) { 107 if (len == sp->cno + 1) { 108 sp->cno = len; 109 LF_SET(TXT_APPENDEOL); 110 } else 111 ++sp->cno; 112 } else 113 LF_SET(TXT_APPENDEOL); 114 115 return (v_txt(sp, vp, NULL, p, len, 116 0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags)); 117 } 118 119 /* 120 * v_iI -- [count]I 121 * Insert text at the first nonblank. 122 * 123 * PUBLIC: int v_iI __P((SCR *, VICMD *)); 124 */ 125 int 126 v_iI(SCR *sp, VICMD *vp) 127 { 128 sp->cno = 0; 129 if (nonblank(sp, vp->m_start.lno, &sp->cno)) 130 return (1); 131 132 LOG_CORRECT; 133 134 return (v_ii(sp, vp)); 135 } 136 137 /* 138 * v_ii -- [count]i 139 * [count]I 140 * Insert text at the cursor position. 141 * 142 * PUBLIC: int v_ii __P((SCR *, VICMD *)); 143 */ 144 int 145 v_ii(SCR *sp, VICMD *vp) 146 { 147 size_t len; 148 u_int32_t flags; 149 int isempty; 150 CHAR_T *p; 151 152 flags = set_txt_std(sp, vp, 0); 153 sp->showmode = SM_INSERT; 154 sp->lno = vp->m_start.lno; 155 156 if (db_eget(sp, sp->lno, &p, &len, &isempty)) { 157 if (!isempty) 158 return (1); 159 len = 0; 160 } 161 162 if (len == 0) 163 LF_SET(TXT_APPENDEOL); 164 return (v_txt(sp, vp, NULL, p, len, 165 0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags)); 166 } 167 168 enum which { o_cmd, O_cmd }; 169 static int io __P((SCR *, VICMD *, enum which)); 170 171 /* 172 * v_iO -- [count]O 173 * Insert text above this line. 174 * 175 * PUBLIC: int v_iO __P((SCR *, VICMD *)); 176 */ 177 int 178 v_iO(SCR *sp, VICMD *vp) 179 { 180 return (io(sp, vp, O_cmd)); 181 } 182 183 /* 184 * v_io -- [count]o 185 * Insert text after this line. 186 * 187 * PUBLIC: int v_io __P((SCR *, VICMD *)); 188 */ 189 int 190 v_io(SCR *sp, VICMD *vp) 191 { 192 return (io(sp, vp, o_cmd)); 193 } 194 195 static int 196 io(SCR *sp, VICMD *vp, enum which cmd) 197 { 198 recno_t ai_line, lno; 199 size_t len; 200 u_int32_t flags; 201 CHAR_T *p; 202 203 flags = set_txt_std(sp, vp, TXT_ADDNEWLINE | TXT_APPENDEOL); 204 sp->showmode = SM_INSERT; 205 206 if (sp->lno == 1) { 207 if (db_last(sp, &lno)) 208 return (1); 209 if (lno != 0) 210 goto insert; 211 p = NULL; 212 len = 0; 213 ai_line = OOBLNO; 214 } else { 215 insert: p = L(""); 216 sp->cno = 0; 217 LOG_CORRECT; 218 219 if (cmd == O_cmd) { 220 if (db_insert(sp, sp->lno, p, 0)) 221 return (1); 222 if (db_get(sp, sp->lno, DBG_FATAL, &p, &len)) 223 return (1); 224 ai_line = sp->lno + 1; 225 } else { 226 if (db_append(sp, 1, sp->lno, p, 0)) 227 return (1); 228 if (db_get(sp, ++sp->lno, DBG_FATAL, &p, &len)) 229 return (1); 230 ai_line = sp->lno - 1; 231 } 232 } 233 return (v_txt(sp, vp, NULL, p, len, 234 0, ai_line, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags)); 235 } 236 237 /* 238 * v_change -- [buffer][count]c[count]motion 239 * [buffer][count]C 240 * [buffer][count]S 241 * Change command. 242 * 243 * PUBLIC: int v_change __P((SCR *, VICMD *)); 244 */ 245 int 246 v_change(SCR *sp, VICMD *vp) 247 { 248 size_t blen, len; 249 u_int32_t flags; 250 int isempty, lmode, rval; 251 CHAR_T *bp; 252 CHAR_T *p; 253 254 /* 255 * 'c' can be combined with motion commands that set the resulting 256 * cursor position, i.e. "cG". Clear the VM_RCM flags and make the 257 * resulting cursor position stick, inserting text has its own rules 258 * for cursor positioning. 259 */ 260 F_CLR(vp, VM_RCM_MASK); 261 F_SET(vp, VM_RCM_SET); 262 263 /* 264 * Find out if the file is empty, it's easier to handle it as a 265 * special case. 266 */ 267 if (vp->m_start.lno == vp->m_stop.lno && 268 db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 269 if (!isempty) 270 return (1); 271 return (v_ia(sp, vp)); 272 } 273 274 flags = set_txt_std(sp, vp, 0); 275 sp->showmode = SM_CHANGE; 276 277 /* 278 * Move the cursor to the start of the change. Note, if autoindent 279 * is turned on, the cc command in line mode changes from the first 280 * *non-blank* character of the line, not the first character. And, 281 * to make it just a bit more exciting, the initial space is handled 282 * as auto-indent characters. 283 */ 284 lmode = F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0; 285 if (lmode) { 286 vp->m_start.cno = 0; 287 if (O_ISSET(sp, O_AUTOINDENT)) { 288 if (nonblank(sp, vp->m_start.lno, &vp->m_start.cno)) 289 return (1); 290 LF_SET(TXT_AICHARS); 291 } 292 } 293 sp->lno = vp->m_start.lno; 294 sp->cno = vp->m_start.cno; 295 296 LOG_CORRECT; 297 298 /* 299 * If not in line mode and changing within a single line, copy the 300 * text and overwrite it. 301 */ 302 if (!lmode && vp->m_start.lno == vp->m_stop.lno) { 303 /* 304 * !!! 305 * Historic practice, c did not cut into the numeric buffers, 306 * only the unnamed one. 307 */ 308 if (cut(sp, 309 F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, 310 &vp->m_start, &vp->m_stop, lmode)) 311 return (1); 312 if (len == 0) 313 LF_SET(TXT_APPENDEOL); 314 LF_SET(TXT_EMARK | TXT_OVERWRITE); 315 return (v_txt(sp, vp, &vp->m_stop, p, len, 316 0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags)); 317 } 318 319 /* 320 * It's trickier if in line mode or changing over multiple lines. If 321 * we're in line mode delete all of the lines and insert a replacement 322 * line which the user edits. If there was leading whitespace in the 323 * first line being changed, we copy it and use it as the replacement. 324 * If we're not in line mode, we delete the text and start inserting. 325 * 326 * !!! 327 * Copy the text. Historic practice, c did not cut into the numeric 328 * buffers, only the unnamed one. 329 */ 330 if (cut(sp, 331 F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, 332 &vp->m_start, &vp->m_stop, lmode)) 333 return (1); 334 335 /* If replacing entire lines and there's leading text. */ 336 if (lmode && vp->m_start.cno) { 337 /* 338 * Get a copy of the first line changed, and copy out the 339 * leading text. 340 */ 341 if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len)) 342 return (1); 343 GET_SPACE_RETW(sp, bp, blen, vp->m_start.cno); 344 MEMMOVE(bp, p, vp->m_start.cno); 345 } else 346 bp = NULL; 347 348 /* Delete the text. */ 349 if (del(sp, &vp->m_start, &vp->m_stop, lmode)) 350 return (1); 351 352 /* If replacing entire lines, insert a replacement line. */ 353 if (lmode) { 354 if (db_insert(sp, vp->m_start.lno, bp, vp->m_start.cno)) 355 return (1); 356 sp->lno = vp->m_start.lno; 357 len = sp->cno = vp->m_start.cno; 358 } 359 360 /* Get the line we're editing. */ 361 if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 362 if (!isempty) 363 return (1); 364 len = 0; 365 } 366 367 /* Check to see if we're appending to the line. */ 368 if (vp->m_start.cno >= len) 369 LF_SET(TXT_APPENDEOL); 370 371 rval = v_txt(sp, vp, NULL, p, len, 372 0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags); 373 374 if (bp != NULL) 375 FREE_SPACEW(sp, bp, blen); 376 return (rval); 377 } 378 379 /* 380 * v_Replace -- [count]R 381 * Overwrite multiple characters. 382 * 383 * PUBLIC: int v_Replace __P((SCR *, VICMD *)); 384 */ 385 int 386 v_Replace(SCR *sp, VICMD *vp) 387 { 388 size_t len; 389 u_int32_t flags; 390 int isempty; 391 CHAR_T *p; 392 393 flags = set_txt_std(sp, vp, 0); 394 sp->showmode = SM_REPLACE; 395 396 if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 397 if (!isempty) 398 return (1); 399 len = 0; 400 LF_SET(TXT_APPENDEOL); 401 } else { 402 if (len == 0) 403 LF_SET(TXT_APPENDEOL); 404 LF_SET(TXT_OVERWRITE | TXT_REPLACE); 405 } 406 vp->m_stop.lno = vp->m_start.lno; 407 vp->m_stop.cno = len ? len - 1 : 0; 408 409 return (v_txt(sp, vp, &vp->m_stop, p, len, 410 0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags)); 411 } 412 413 /* 414 * v_subst -- [buffer][count]s 415 * Substitute characters. 416 * 417 * PUBLIC: int v_subst __P((SCR *, VICMD *)); 418 */ 419 int 420 v_subst(SCR *sp, VICMD *vp) 421 { 422 size_t len; 423 u_int32_t flags; 424 int isempty; 425 CHAR_T *p; 426 427 flags = set_txt_std(sp, vp, 0); 428 sp->showmode = SM_CHANGE; 429 430 if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) { 431 if (!isempty) 432 return (1); 433 len = 0; 434 LF_SET(TXT_APPENDEOL); 435 } else { 436 if (len == 0) 437 LF_SET(TXT_APPENDEOL); 438 LF_SET(TXT_EMARK | TXT_OVERWRITE); 439 } 440 441 vp->m_stop.lno = vp->m_start.lno; 442 vp->m_stop.cno = 443 vp->m_start.cno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0); 444 if (vp->m_stop.cno > len - 1) 445 vp->m_stop.cno = len - 1; 446 447 if (p != NULL && cut(sp, 448 F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, 449 &vp->m_start, &vp->m_stop, 0)) 450 return (1); 451 452 return (v_txt(sp, vp, &vp->m_stop, p, len, 0, OOBLNO, 1, flags)); 453 } 454 455 /* 456 * set_txt_std -- 457 * Initialize text processing flags. 458 */ 459 static u_int32_t 460 set_txt_std(SCR *sp, VICMD *vp, u_int32_t flags) 461 { 462 LF_SET(TXT_CNTRLT | 463 TXT_ESCAPE | TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE); 464 465 if (F_ISSET(vp, VC_ISDOT)) 466 LF_SET(TXT_REPLAY); 467 468 if (O_ISSET(sp, O_ALTWERASE)) 469 LF_SET(TXT_ALTWERASE); 470 if (O_ISSET(sp, O_AUTOINDENT)) 471 LF_SET(TXT_AUTOINDENT); 472 if (O_ISSET(sp, O_BEAUTIFY)) 473 LF_SET(TXT_BEAUTIFY); 474 if (O_ISSET(sp, O_SHOWMATCH)) 475 LF_SET(TXT_SHOWMATCH); 476 if (F_ISSET(sp, SC_SCRIPT)) 477 LF_SET(TXT_CR); 478 if (O_ISSET(sp, O_TTYWERASE)) 479 LF_SET(TXT_TTYWERASE); 480 481 /* 482 * !!! 483 * Mapped keys were sometimes unaffected by the wrapmargin option 484 * in the historic 4BSD vi. Consider the following commands, where 485 * each is executed on an empty line, in an 80 column screen, with 486 * the wrapmargin value set to 60. 487 * 488 * aABC DEF <ESC>.... 489 * :map K aABC DEF ^V<ESC><CR>KKKKK 490 * :map K 5aABC DEF ^V<ESC><CR>K 491 * 492 * The first and second commands are affected by wrapmargin. The 493 * third is not. (If the inserted text is itself longer than the 494 * wrapmargin value, i.e. if the "ABC DEF " string is replaced by 495 * something that's longer than 60 columns from the beginning of 496 * the line, the first two commands behave as before, but the third 497 * command gets fairly strange.) The problem is that people wrote 498 * macros that depended on the third command NOT being affected by 499 * wrapmargin, as in this gem which centers lines: 500 * 501 * map #c $mq81a ^V^[81^V^V|D`qld0:s/ / /g^V^M$p 502 * 503 * For compatibility reasons, we try and make it all work here. I 504 * offer no hope that this is right, but it's probably pretty close. 505 * 506 * XXX 507 * Once I work my courage up, this is all gonna go away. It's too 508 * evil to survive. 509 */ 510 if ((O_ISSET(sp, O_WRAPLEN) || O_ISSET(sp, O_WRAPMARGIN)) && 511 (!MAPPED_KEYS_WAITING(sp) || !F_ISSET(vp, VC_C1SET))) 512 LF_SET(TXT_WRAPMARGIN); 513 return (flags); 514 } 515