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