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