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