1 /* $OpenBSD: basic.c,v 1.6 2001/05/23 22:20:34 art Exp $ */ 2 3 /* 4 * Basic cursor motion commands. 5 * 6 * The routines in this file are the basic 7 * command functions for moving the cursor around on 8 * the screen, setting mark, and swapping dot with 9 * mark. Only moves between lines, which might make the 10 * current buffer framing bad, are hard. 11 */ 12 #include "def.h" 13 14 void setgoal __P((void)); 15 16 /* 17 * Go to beginning of line. 18 */ 19 /* ARGSUSED */ 20 int 21 gotobol(f, n) 22 int f, n; 23 { 24 curwp->w_doto = 0; 25 return (TRUE); 26 } 27 28 /* 29 * Move cursor backwards. Do the 30 * right thing if the count is less than 31 * 0. Error if you try to move back from 32 * the beginning of the buffer. 33 */ 34 /* ARGSUSED */ 35 int 36 backchar(f, n) 37 int n; 38 { 39 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_linep) { 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 } else 54 curwp->w_doto--; 55 } 56 return TRUE; 57 } 58 59 /* 60 * Go to end of line. 61 */ 62 /* ARGSUSED */ 63 int 64 gotoeol(f, n) 65 int f, 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(f, n) 80 int f, n; 81 { 82 83 if (n < 0) 84 return backchar(f, -n); 85 while (n--) { 86 if (curwp->w_doto == llength(curwp->w_dotp)) { 87 curwp->w_dotp = lforw(curwp->w_dotp); 88 if (curwp->w_dotp == curbp->b_linep) { 89 curwp->w_dotp = lback(curwp->w_dotp); 90 if (!(f & FFRAND)) 91 ewprintf("End of buffer"); 92 return FALSE; 93 } 94 curwp->w_doto = 0; 95 curwp->w_flag |= WFMOVE; 96 } else 97 curwp->w_doto++; 98 } 99 return TRUE; 100 } 101 102 /* 103 * Go to the beginning of the 104 * buffer. Setting WFHARD is conservative, 105 * but almost always the case. 106 */ 107 int 108 gotobob(f, n) 109 int f, n; 110 { 111 112 (void) setmark(f, n); 113 curwp->w_dotp = lforw(curbp->b_linep); 114 curwp->w_doto = 0; 115 curwp->w_flag |= WFHARD; 116 return TRUE; 117 } 118 119 /* 120 * Go to the end of the buffer. 121 * Setting WFHARD is conservative, but 122 * almost always the case. 123 */ 124 int 125 gotoeob(f, n) 126 int f, n; 127 { 128 129 (void) setmark(f, n); 130 curwp->w_dotp = lback(curbp->b_linep); 131 curwp->w_doto = llength(curwp->w_dotp); 132 curwp->w_flag |= WFHARD; 133 return TRUE; 134 } 135 136 /* 137 * Move forward by full lines. 138 * If the number of lines to move is less 139 * than zero, call the backward line function to 140 * actually do it. The last command controls how 141 * the goal column is set. 142 */ 143 /* ARGSUSED */ 144 int 145 forwline(f, n) 146 int f, n; 147 { 148 LINE *dlp; 149 150 if (n < 0) 151 return backline(f | FFRAND, -n); 152 if ((lastflag & CFCPCN) == 0) /* Fix goal. */ 153 setgoal(); 154 thisflag |= CFCPCN; 155 if (n == 0) 156 return TRUE; 157 dlp = curwp->w_dotp; 158 while (dlp != curbp->b_linep && n--) 159 dlp = lforw(dlp); 160 curwp->w_flag |= WFMOVE; 161 if (dlp == curbp->b_linep) { /* ^N at end of buffer creates lines 162 * (like gnu) */ 163 if (!(curbp->b_flag & BFCHG)) { /* first change */ 164 curbp->b_flag |= BFCHG; 165 curwp->w_flag |= WFMODE; 166 } 167 curwp->w_doto = 0; 168 while (n-- >= 0) { 169 if ((dlp = lallocx(0)) == NULL) 170 return FALSE; 171 dlp->l_fp = curbp->b_linep; 172 dlp->l_bp = lback(dlp->l_fp); 173 dlp->l_bp->l_fp = dlp->l_fp->l_bp = dlp; 174 } 175 curwp->w_dotp = lback(curbp->b_linep); 176 } else { 177 curwp->w_dotp = dlp; 178 curwp->w_doto = getgoal(dlp); 179 } 180 return TRUE; 181 } 182 183 /* 184 * This function is like "forwline", but 185 * goes backwards. The scheme is exactly the same. 186 * Check for arguments that are less than zero and 187 * call your alternate. Figure out the new line and 188 * call "movedot" to perform the motion. 189 */ 190 /* ARGSUSED */ 191 int 192 backline(f, n) 193 int f, n; 194 { 195 LINE *dlp; 196 197 if (n < 0) 198 return forwline(f | FFRAND, -n); 199 if ((lastflag & CFCPCN) == 0) /* Fix goal. */ 200 setgoal(); 201 thisflag |= CFCPCN; 202 dlp = curwp->w_dotp; 203 while (n-- && lback(dlp) != curbp->b_linep) 204 dlp = lback(dlp); 205 curwp->w_dotp = dlp; 206 curwp->w_doto = getgoal(dlp); 207 curwp->w_flag |= WFMOVE; 208 return TRUE; 209 } 210 211 /* 212 * Set the current goal column, which is saved in the external variable 213 * "curgoal", to the current cursor column. The column is never off 214 * the edge of the screen; it's more like display then show position. 215 */ 216 void 217 setgoal() 218 { 219 220 curgoal = getcolpos() - 1; /* Get the position. */ 221 /* we can now display past end of display, don't chop! */ 222 } 223 224 /* 225 * This routine looks at a line (pointed 226 * to by the LINE pointer "dlp") and the current 227 * vertical motion goal column (set by the "setgoal" 228 * routine above) and returns the best offset to use 229 * when a vertical motion is made into the line. 230 */ 231 int 232 getgoal(dlp) 233 LINE *dlp; 234 { 235 int c; 236 int col; 237 int newcol; 238 int dbo; 239 240 col = 0; 241 dbo = 0; 242 while (dbo != llength(dlp)) { 243 c = lgetc(dlp, dbo); 244 newcol = col; 245 if (c == '\t' 246 #ifdef NOTAB 247 && !(curbp->b_flag & BFNOTAB) 248 #endif 249 ) 250 newcol |= 0x07; 251 else if (ISCTRL(c) != FALSE) 252 ++newcol; 253 ++newcol; 254 if (newcol > curgoal) 255 break; 256 col = newcol; 257 ++dbo; 258 } 259 return (dbo); 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(f, n) 273 int f, n; 274 { 275 LINE *lp; 276 277 if (!(f & FFARG)) { 278 n = curwp->w_ntrows - 2; /* Default scroll. */ 279 if (n <= 0) /* Forget the overlap */ 280 n = 1; /* if tiny window. */ 281 } else if (n < 0) 282 return backpage(f | FFRAND, -n); 283 #ifdef CVMVAS 284 else /* Convert from pages */ 285 n *= curwp->w_ntrows; /* to lines. */ 286 #endif 287 lp = curwp->w_linep; 288 while (n-- && lforw(lp) != curbp->b_linep) 289 lp = lforw(lp); 290 curwp->w_linep = lp; 291 curwp->w_flag |= WFHARD; 292 /* if in current window, don't move dot */ 293 for (n = curwp->w_ntrows; n-- && lp != curbp->b_linep; lp = lforw(lp)) 294 if (lp == curwp->w_dotp) 295 return TRUE; 296 curwp->w_dotp = curwp->w_linep; 297 curwp->w_doto = 0; 298 return TRUE; 299 } 300 301 /* 302 * This command is like "forwpage", 303 * but it goes backwards. The "2", like above, 304 * is the overlap between the two windows. The 305 * value is from the ITS EMACS manual. The 306 * hard update is done because the top line in 307 * the window is zapped. 308 */ 309 /* ARGSUSED */ 310 int 311 backpage(f, n) 312 int f, n; 313 { 314 LINE *lp; 315 316 if (!(f & FFARG)) { 317 n = curwp->w_ntrows - 2; /* Default scroll. */ 318 if (n <= 0) /* Don't blow up if the */ 319 n = 1; /* window is tiny. */ 320 } else if (n < 0) 321 return forwpage(f | FFRAND, -n); 322 #ifdef CVMVAS 323 else /* Convert from pages */ 324 n *= curwp->w_ntrows; /* to lines. */ 325 #endif 326 lp = curwp->w_linep; 327 while (n-- && lback(lp) != curbp->b_linep) 328 lp = lback(lp); 329 curwp->w_linep = lp; 330 curwp->w_flag |= WFHARD; 331 /* if in current window, don't move dot */ 332 for (n = curwp->w_ntrows; n-- && lp != curbp->b_linep; lp = lforw(lp)) 333 if (lp == curwp->w_dotp) 334 return TRUE; 335 curwp->w_dotp = curwp->w_linep; 336 curwp->w_doto = 0; 337 return TRUE; 338 } 339 340 /* 341 * These functions are provided for compatibility with Gosling's Emacs. They 342 * are used to scroll the display up (or down) one line at a time. 343 */ 344 #ifdef GOSMACS 345 void 346 forw1page(f, n) 347 int f, n; 348 { 349 350 if (!(f & FFARG)) { 351 n = 1; 352 f = FFUNIV; 353 } 354 forwpage(f | FFRAND, n); 355 } 356 357 void 358 back1page(f, n) 359 int f, n; 360 { 361 362 if (!(f & FFARG)) { 363 n = 1; 364 f = FFUNIV; 365 } 366 backpage(f | FFRAND, n); 367 } 368 #endif 369 370 /* 371 * Page the other window. Check to make sure it exists, then 372 * nextwind, forwpage and restore window pointers. 373 */ 374 int 375 pagenext(f, n) 376 int f, n; 377 { 378 MGWIN *wp; 379 380 if (wheadp->w_wndp == NULL) { 381 ewprintf("No other window"); 382 return FALSE; 383 } 384 wp = curwp; 385 (void) nextwind(f, n); 386 (void) forwpage(f, n); 387 curwp = wp; 388 curbp = wp->w_bufp; 389 return TRUE; 390 } 391 392 /* 393 * Internal set mark routine, used by other functions (daveb). 394 */ 395 void 396 isetmark() 397 { 398 399 curwp->w_markp = curwp->w_dotp; 400 curwp->w_marko = curwp->w_doto; 401 } 402 403 /* 404 * Set the mark in the current window 405 * to the value of dot. A message is written to 406 * the echo line. (ewprintf knows about macros) 407 */ 408 /* ARGSUSED */ 409 int 410 setmark(f, n) 411 int f, n; 412 { 413 414 isetmark(); 415 ewprintf("Mark set"); 416 return TRUE; 417 } 418 419 /* 420 * Swap the values of "dot" and "mark" in 421 * the current window. This is pretty easy, because 422 * all of the hard work gets done by the standard routine 423 * that moves the mark about. The only possible 424 * error is "no mark". 425 */ 426 /* ARGSUSED */ 427 int 428 swapmark(f, n) 429 int f, n; 430 { 431 LINE *odotp; 432 int odoto; 433 434 if (curwp->w_markp == NULL) { 435 ewprintf("No mark in this window"); 436 return FALSE; 437 } 438 odotp = curwp->w_dotp; 439 odoto = curwp->w_doto; 440 curwp->w_dotp = curwp->w_markp; 441 curwp->w_doto = curwp->w_marko; 442 curwp->w_markp = odotp; 443 curwp->w_marko = odoto; 444 curwp->w_flag |= WFMOVE; 445 return TRUE; 446 } 447 448 /* 449 * Go to a specific line, mostly for 450 * looking up errors in C programs, which give the 451 * error a line number. If an argument is present, then 452 * it is the line number, else prompt for a line number 453 * to use. 454 */ 455 /* ARGSUSED */ 456 int 457 gotoline(f, n) 458 int f, n; 459 { 460 LINE *clp; 461 int s; 462 char buf[32]; 463 464 if (!(f & FFARG)) { 465 if ((s = ereply("Goto line: ", buf, sizeof(buf))) != TRUE) 466 return s; 467 n = atoi(buf); 468 } 469 if (n >= 0) { 470 clp = lforw(curbp->b_linep); /* "clp" is first line */ 471 while (--n > 0) { 472 if (lforw(clp) == curbp->b_linep) 473 break; 474 clp = lforw(clp); 475 } 476 } else { 477 clp = lback(curbp->b_linep); /* "clp" is last line */ 478 while (n < 0) { 479 if (lback(clp) == curbp->b_linep) 480 break; 481 clp = lback(clp); 482 n++; 483 } 484 } 485 curwp->w_dotp = clp; 486 curwp->w_doto = 0; 487 curwp->w_flag |= WFMOVE; 488 return TRUE; 489 } 490