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