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