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