1 /* $OpenBSD: basic.c,v 1.49 2019/06/17 11:39:26 lum Exp $ */ 2 3 /* This file is in the public domain */ 4 5 /* 6 * Basic cursor motion commands. 7 * 8 * The routines in this file are the basic 9 * command functions for moving the cursor around on 10 * the screen, setting mark, and swapping dot with 11 * mark. Only moves between lines, which might make the 12 * current buffer framing bad, are hard. 13 */ 14 15 #include <sys/queue.h> 16 #include <ctype.h> 17 #include <limits.h> 18 #include <signal.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 22 #include "def.h" 23 24 #define percint(n1, n2) ((n1 * (int) n2) * 0.1) 25 26 /* 27 * Go to beginning of line. 28 */ 29 /* ARGSUSED */ 30 int 31 gotobol(int f, int n) 32 { 33 if (n == 0) 34 return (TRUE); 35 36 curwp->w_doto = 0; 37 return (TRUE); 38 } 39 40 /* 41 * Move cursor backwards. Do the 42 * right thing if the count is less than 43 * 0. Error if you try to move back from 44 * the beginning of the buffer. 45 */ 46 /* ARGSUSED */ 47 int 48 backchar(int f, int n) 49 { 50 struct line *lp; 51 52 if (n < 0) 53 return (forwchar(f, -n)); 54 while (n--) { 55 if (curwp->w_doto == 0) { 56 if ((lp = lback(curwp->w_dotp)) == curbp->b_headp) { 57 if (!(f & FFRAND)) { 58 dobeep(); 59 ewprintf("Beginning of buffer"); 60 } 61 return (FALSE); 62 } 63 curwp->w_dotp = lp; 64 curwp->w_doto = llength(lp); 65 curwp->w_rflag |= WFMOVE; 66 curwp->w_dotline--; 67 } else 68 curwp->w_doto--; 69 } 70 return (TRUE); 71 } 72 73 /* 74 * Go to end of line. 75 */ 76 /* ARGSUSED */ 77 int 78 gotoeol(int f, int n) 79 { 80 if (n == 0) 81 return (TRUE); 82 83 curwp->w_doto = llength(curwp->w_dotp); 84 return (TRUE); 85 } 86 87 /* 88 * Move cursor forwards. Do the 89 * right thing if the count is less than 90 * 0. Error if you try to move forward 91 * from the end of the buffer. 92 */ 93 /* ARGSUSED */ 94 int 95 forwchar(int f, int n) 96 { 97 if (n < 0) 98 return (backchar(f, -n)); 99 while (n--) { 100 if (curwp->w_doto == llength(curwp->w_dotp)) { 101 curwp->w_dotp = lforw(curwp->w_dotp); 102 if (curwp->w_dotp == curbp->b_headp) { 103 curwp->w_dotp = lback(curwp->w_dotp); 104 if (!(f & FFRAND)) { 105 dobeep(); 106 ewprintf("End of buffer"); 107 } 108 return (FALSE); 109 } 110 curwp->w_doto = 0; 111 curwp->w_dotline++; 112 curwp->w_rflag |= WFMOVE; 113 } else 114 curwp->w_doto++; 115 } 116 return (TRUE); 117 } 118 119 /* 120 * Go to the beginning of the buffer. Setting WFFULL is conservative, 121 * but almost always the case. A universal argument of higher than 9 122 * puts the cursor back to the end of buffer. 123 */ 124 int 125 gotobob(int f, int n) 126 { 127 if (!curwp->w_markp) 128 (void) setmark(f, n); 129 curwp->w_dotp = bfirstlp(curbp); 130 curwp->w_doto = 0; 131 curwp->w_rflag |= WFFULL; 132 curwp->w_dotline = 1; 133 if (f & FFOTHARG && n > 0) { 134 if (n > 9) 135 gotoeob(0, 0); 136 else 137 forwline(f, percint(curwp->w_bufp->b_lines, n) - 1); 138 } 139 return (TRUE); 140 } 141 142 /* 143 * Go to the end of the buffer. Leave dot 3 lines from the bottom of the 144 * window if buffer length is longer than window length; same as emacs. 145 * Setting WFFULL is conservative, but almost always the case. A universal 146 * argument of higher than 9 puts the cursor back to the start of buffer. 147 */ 148 int 149 gotoeob(int f, int n) 150 { 151 int ln; 152 struct line *lp; 153 154 if (!curwp->w_markp) 155 (void) setmark(f, n); 156 curwp->w_dotp = blastlp(curbp); 157 curwp->w_doto = llength(curwp->w_dotp); 158 curwp->w_dotline = curwp->w_bufp->b_lines; 159 160 lp = curwp->w_dotp; 161 ln = curwp->w_ntrows - 3; 162 163 if (ln < curwp->w_bufp->b_lines && ln >= 3) { 164 while (ln--) 165 curwp->w_dotp = lback(curwp->w_dotp); 166 167 curwp->w_linep = curwp->w_dotp; 168 curwp->w_dotp = lp; 169 } 170 if (f & FFOTHARG && n > 0) { 171 if (n > 9) 172 gotobob(0, 0); 173 else 174 backline(f, percint(curwp->w_bufp->b_lines, n)); 175 } 176 177 curwp->w_rflag |= WFFULL; 178 return (TRUE); 179 } 180 181 /* 182 * Move forward by full lines. 183 * If the number of lines to move is less 184 * than zero, call the backward line function to 185 * actually do it. The last command controls how 186 * the goal column is set. 187 */ 188 /* ARGSUSED */ 189 int 190 forwline(int f, int n) 191 { 192 struct line *dlp; 193 194 if (n < 0) 195 return (backline(f | FFRAND, -n)); 196 if ((dlp = curwp->w_dotp) == curbp->b_headp) { 197 if (!(f & FFRAND)) { 198 dobeep(); 199 ewprintf("End of buffer"); 200 } 201 return(TRUE); 202 } 203 if ((lastflag & CFCPCN) == 0) /* Fix goal. */ 204 setgoal(); 205 thisflag |= CFCPCN; 206 if (n == 0) 207 return (TRUE); 208 while (n--) { 209 dlp = lforw(dlp); 210 if (dlp == curbp->b_headp) { 211 curwp->w_dotp = lback(dlp); 212 curwp->w_doto = llength(curwp->w_dotp); 213 curwp->w_rflag |= WFMOVE; 214 if (!(f & FFRAND)) { 215 dobeep(); 216 ewprintf("End of buffer"); 217 } 218 return (TRUE); 219 } 220 curwp->w_dotline++; 221 } 222 curwp->w_rflag |= WFMOVE; 223 curwp->w_dotp = dlp; 224 curwp->w_doto = getgoal(dlp); 225 226 return (TRUE); 227 } 228 229 /* 230 * This function is like "forwline", but 231 * goes backwards. The scheme is exactly the same. 232 * Check for arguments that are less than zero and 233 * call your alternate. Figure out the new line and 234 * call "movedot" to perform the motion. 235 */ 236 /* ARGSUSED */ 237 int 238 backline(int f, int n) 239 { 240 struct line *dlp; 241 242 if (n < 0) 243 return (forwline(f | FFRAND, -n)); 244 if ((lastflag & CFCPCN) == 0) /* Fix goal. */ 245 setgoal(); 246 thisflag |= CFCPCN; 247 dlp = curwp->w_dotp; 248 if (lback(dlp) == curbp->b_headp) { 249 if (!(f & FFRAND)) { 250 dobeep(); 251 ewprintf("Beginning of buffer"); 252 } 253 return(TRUE); 254 } 255 while (n-- && lback(dlp) != curbp->b_headp) { 256 dlp = lback(dlp); 257 curwp->w_dotline--; 258 } 259 if (n > 0 && !(f & FFRAND)) { 260 dobeep(); 261 ewprintf("Beginning of buffer"); 262 } 263 curwp->w_dotp = dlp; 264 curwp->w_doto = getgoal(dlp); 265 curwp->w_rflag |= WFMOVE; 266 return (TRUE); 267 } 268 269 /* 270 * Set the current goal column, which is saved in the external variable 271 * "curgoal", to the current cursor column. The column is never off 272 * the edge of the screen; it's more like display then show position. 273 */ 274 void 275 setgoal(void) 276 { 277 curgoal = getcolpos(curwp); /* Get the position. */ 278 /* we can now display past end of display, don't chop! */ 279 } 280 281 /* 282 * This routine looks at a line (pointed 283 * to by the LINE pointer "dlp") and the current 284 * vertical motion goal column (set by the "setgoal" 285 * routine above) and returns the best offset to use 286 * when a vertical motion is made into the line. 287 */ 288 int 289 getgoal(struct line *dlp) 290 { 291 int c, i, col = 0; 292 char tmp[5]; 293 294 295 for (i = 0; i < llength(dlp); i++) { 296 c = lgetc(dlp, i); 297 if (c == '\t' 298 #ifdef NOTAB 299 && !(curbp->b_flag & BFNOTAB) 300 #endif 301 ) { 302 col |= 0x07; 303 col++; 304 } else if (ISCTRL(c) != FALSE) { 305 col += 2; 306 } else if (isprint(c)) 307 col++; 308 else { 309 col += snprintf(tmp, sizeof(tmp), "\\%o", c); 310 } 311 if (col > curgoal) 312 break; 313 } 314 return (i); 315 } 316 317 /* 318 * Scroll forward by a specified number 319 * of lines, or by a full page if no argument. 320 * The "2" is the window overlap (this is the default 321 * value from ITS EMACS). Because the top line in 322 * the window is zapped, we have to do a hard 323 * update and get it back. 324 */ 325 /* ARGSUSED */ 326 int 327 forwpage(int f, int n) 328 { 329 struct line *lp; 330 331 if (!(f & FFARG)) { 332 n = curwp->w_ntrows - 2; /* Default scroll. */ 333 if (n <= 0) /* Forget the overlap */ 334 n = 1; /* if tiny window. */ 335 } else if (n < 0) 336 return (backpage(f | FFRAND, -n)); 337 338 lp = curwp->w_linep; 339 while (n--) 340 if ((lp = lforw(lp)) == curbp->b_headp) { 341 dobeep(); 342 ewprintf("End of buffer"); 343 return(TRUE); 344 } 345 346 curwp->w_linep = lp; 347 curwp->w_rflag |= WFFULL; 348 349 /* if in current window, don't move dot */ 350 for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp)) 351 if (lp == curwp->w_dotp) 352 return (TRUE); 353 354 /* Advance the dot the slow way, for line nos */ 355 while (curwp->w_dotp != curwp->w_linep) { 356 curwp->w_dotp = lforw(curwp->w_dotp); 357 curwp->w_dotline++; 358 } 359 curwp->w_doto = 0; 360 return (TRUE); 361 } 362 363 /* 364 * This command is like "forwpage", 365 * but it goes backwards. The "2", like above, 366 * is the overlap between the two windows. The 367 * value is from the ITS EMACS manual. The 368 * hard update is done because the top line in 369 * the window is zapped. 370 */ 371 /* ARGSUSED */ 372 int 373 backpage(int f, int n) 374 { 375 struct line *lp, *lp2; 376 377 if (!(f & FFARG)) { 378 n = curwp->w_ntrows - 2; /* Default scroll. */ 379 if (n <= 0) /* Don't blow up if the */ 380 return (backline(f, 1));/* window is tiny. */ 381 } else if (n < 0) 382 return (forwpage(f | FFRAND, -n)); 383 384 lp = lp2 = curwp->w_linep; 385 386 while (n-- && lback(lp) != curbp->b_headp) { 387 lp = lback(lp); 388 } 389 if (lp == curwp->w_linep) { 390 dobeep(); 391 ewprintf("Beginning of buffer"); 392 } 393 curwp->w_linep = lp; 394 curwp->w_rflag |= WFFULL; 395 396 /* if in current window, don't move dot */ 397 for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp)) 398 if (lp == curwp->w_dotp) 399 return (TRUE); 400 401 lp2 = lforw(lp2); 402 403 /* Move the dot the slow way, for line nos */ 404 while (curwp->w_dotp != lp2) { 405 if (curwp->w_dotline <= curwp->w_ntrows) 406 goto out; 407 curwp->w_dotp = lback(curwp->w_dotp); 408 curwp->w_dotline--; 409 } 410 out: 411 curwp->w_doto = 0; 412 return (TRUE); 413 } 414 415 /* 416 * These functions are provided for compatibility with Gosling's Emacs. They 417 * are used to scroll the display up (or down) one line at a time. 418 */ 419 int 420 forw1page(int f, int n) 421 { 422 if (!(f & FFARG)) { 423 n = 1; 424 f = FFUNIV; 425 } 426 forwpage(f | FFRAND, n); 427 return (TRUE); 428 } 429 430 int 431 back1page(int f, int n) 432 { 433 if (!(f & FFARG)) { 434 n = 1; 435 f = FFUNIV; 436 } 437 backpage(f | FFRAND, n); 438 return (TRUE); 439 } 440 441 /* 442 * Page the other window. Check to make sure it exists, then 443 * nextwind, forwpage and restore window pointers. 444 */ 445 int 446 pagenext(int f, int n) 447 { 448 struct mgwin *wp; 449 450 if (wheadp->w_wndp == NULL) { 451 dobeep(); 452 ewprintf("No other window"); 453 return (FALSE); 454 } 455 wp = curwp; 456 (void) nextwind(f, n); 457 (void) forwpage(f, n); 458 curwp = wp; 459 curbp = wp->w_bufp; 460 return (TRUE); 461 } 462 463 /* 464 * Internal set mark routine, used by other functions (daveb). 465 */ 466 void 467 isetmark(void) 468 { 469 curwp->w_markp = curwp->w_dotp; 470 curwp->w_marko = curwp->w_doto; 471 curwp->w_markline = curwp->w_dotline; 472 } 473 474 /* 475 * Set the mark in the current window 476 * to the value of dot. A message is written to 477 * the echo line. (ewprintf knows about macros) 478 */ 479 /* ARGSUSED */ 480 int 481 setmark(int f, int n) 482 { 483 isetmark(); 484 ewprintf("Mark set"); 485 return (TRUE); 486 } 487 488 /* Clear the mark, if set. */ 489 /* ARGSUSED */ 490 int 491 clearmark(int f, int n) 492 { 493 if (!curwp->w_markp) 494 return (FALSE); 495 496 curwp->w_markp = NULL; 497 curwp->w_marko = 0; 498 curwp->w_markline = 0; 499 500 return (TRUE); 501 } 502 503 /* 504 * Swap the values of "dot" and "mark" in 505 * the current window. This is pretty easy, because 506 * all of the hard work gets done by the standard routine 507 * that moves the mark about. The only possible 508 * error is "no mark". 509 */ 510 /* ARGSUSED */ 511 int 512 swapmark(int f, int n) 513 { 514 struct line *odotp; 515 int odoto, odotline; 516 517 if (curwp->w_markp == NULL) { 518 dobeep(); 519 ewprintf("No mark in this window"); 520 return (FALSE); 521 } 522 odotp = curwp->w_dotp; 523 odoto = curwp->w_doto; 524 odotline = curwp->w_dotline; 525 curwp->w_dotp = curwp->w_markp; 526 curwp->w_doto = curwp->w_marko; 527 curwp->w_dotline = curwp->w_markline; 528 curwp->w_markp = odotp; 529 curwp->w_marko = odoto; 530 curwp->w_markline = odotline; 531 curwp->w_rflag |= WFMOVE; 532 return (TRUE); 533 } 534 535 /* 536 * Go to a specific line, mostly for 537 * looking up errors in C programs, which give the 538 * error a line number. If an argument is present, then 539 * it is the line number, else prompt for a line number 540 * to use. 541 */ 542 /* ARGSUSED */ 543 int 544 gotoline(int f, int n) 545 { 546 char buf[32], *bufp; 547 const char *err; 548 549 if (!(f & FFARG)) { 550 if ((bufp = eread("Goto line: ", buf, sizeof(buf), 551 EFNUL | EFNEW | EFCR)) == NULL) 552 return (ABORT); 553 if (bufp[0] == '\0') 554 return (ABORT); 555 n = (int)strtonum(buf, INT_MIN, INT_MAX, &err); 556 if (err) { 557 dobeep(); 558 ewprintf("Line number %s", err); 559 return (FALSE); 560 } 561 } 562 return(setlineno(n)); 563 } 564 565 /* 566 * Set the line number and switch to it. 567 */ 568 int 569 setlineno(int n) 570 { 571 struct line *clp; 572 573 if (n >= 0) { 574 if (n == 0) 575 n++; 576 curwp->w_dotline = n; 577 clp = lforw(curbp->b_headp); /* "clp" is first line */ 578 while (--n > 0) { 579 if (lforw(clp) == curbp->b_headp) { 580 curwp->w_dotline = curwp->w_bufp->b_lines; 581 break; 582 } 583 clp = lforw(clp); 584 } 585 } else { 586 curwp->w_dotline = curwp->w_bufp->b_lines + n; 587 clp = lback(curbp->b_headp); /* "clp" is last line */ 588 while (n < 0) { 589 if (lback(clp) == curbp->b_headp) { 590 curwp->w_dotline = 1; 591 break; 592 } 593 clp = lback(clp); 594 n++; 595 } 596 } 597 curwp->w_dotp = clp; 598 curwp->w_doto = 0; 599 curwp->w_rflag |= WFMOVE; 600 return (TRUE); 601 } 602