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