1 /* $OpenBSD: basic.c,v 1.14 2003/05/16 19:28:59 vincent 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 col += snprintf(tmp, sizeof tmp, "\\%o", 243 c); 244 } 245 if (col > curgoal) 246 break; 247 } 248 return (i); 249 } 250 251 /* 252 * Scroll forward by a specified number 253 * of lines, or by a full page if no argument. 254 * The "2" is the window overlap (this is the default 255 * value from ITS EMACS). Because the top line in 256 * the window is zapped, we have to do a hard 257 * update and get it back. 258 */ 259 /* ARGSUSED */ 260 int 261 forwpage(int f, int n) 262 { 263 LINE *lp; 264 265 if (!(f & FFARG)) { 266 n = curwp->w_ntrows - 2; /* Default scroll. */ 267 if (n <= 0) /* Forget the overlap */ 268 n = 1; /* if tiny window. */ 269 } else if (n < 0) 270 return backpage(f | FFRAND, -n); 271 #ifdef CVMVAS 272 else /* Convert from pages */ 273 n *= curwp->w_ntrows; /* to lines. */ 274 #endif 275 lp = curwp->w_linep; 276 while (n-- && lforw(lp) != curbp->b_linep) 277 lp = lforw(lp); 278 curwp->w_linep = lp; 279 curwp->w_flag |= WFHARD; 280 /* if in current window, don't move dot */ 281 for (n = curwp->w_ntrows; n-- && lp != curbp->b_linep; lp = lforw(lp)) 282 if (lp == curwp->w_dotp) 283 return TRUE; 284 curwp->w_dotp = curwp->w_linep; 285 curwp->w_doto = 0; 286 return TRUE; 287 } 288 289 /* 290 * This command is like "forwpage", 291 * but it goes backwards. The "2", like above, 292 * is the overlap between the two windows. The 293 * value is from the ITS EMACS manual. The 294 * hard update is done because the top line in 295 * the window is zapped. 296 */ 297 /* ARGSUSED */ 298 int 299 backpage(int f, int n) 300 { 301 LINE *lp; 302 303 if (!(f & FFARG)) { 304 n = curwp->w_ntrows - 2; /* Default scroll. */ 305 if (n <= 0) /* Don't blow up if the */ 306 n = 1; /* window is tiny. */ 307 } else if (n < 0) 308 return forwpage(f | FFRAND, -n); 309 #ifdef CVMVAS 310 else /* Convert from pages */ 311 n *= curwp->w_ntrows; /* to lines. */ 312 #endif 313 lp = curwp->w_linep; 314 while (n-- && lback(lp) != curbp->b_linep) 315 lp = lback(lp); 316 curwp->w_linep = lp; 317 curwp->w_flag |= WFHARD; 318 /* if in current window, don't move dot */ 319 for (n = curwp->w_ntrows; n-- && lp != curbp->b_linep; lp = lforw(lp)) 320 if (lp == curwp->w_dotp) 321 return TRUE; 322 curwp->w_dotp = curwp->w_linep; 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 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 } 383 384 /* 385 * Set the mark in the current window 386 * to the value of dot. A message is written to 387 * the echo line. (ewprintf knows about macros) 388 */ 389 /* ARGSUSED */ 390 int 391 setmark(int f, int n) 392 { 393 394 isetmark(); 395 ewprintf("Mark set"); 396 return TRUE; 397 } 398 399 /* 400 * Swap the values of "dot" and "mark" in 401 * the current window. This is pretty easy, because 402 * all of the hard work gets done by the standard routine 403 * that moves the mark about. The only possible 404 * error is "no mark". 405 */ 406 /* ARGSUSED */ 407 int 408 swapmark(int f, int n) 409 { 410 LINE *odotp; 411 int odoto; 412 413 if (curwp->w_markp == NULL) { 414 ewprintf("No mark in this window"); 415 return FALSE; 416 } 417 odotp = curwp->w_dotp; 418 odoto = curwp->w_doto; 419 curwp->w_dotp = curwp->w_markp; 420 curwp->w_doto = curwp->w_marko; 421 curwp->w_markp = odotp; 422 curwp->w_marko = odoto; 423 curwp->w_flag |= WFMOVE; 424 return TRUE; 425 } 426 427 /* 428 * Go to a specific line, mostly for 429 * looking up errors in C programs, which give the 430 * error a line number. If an argument is present, then 431 * it is the line number, else prompt for a line number 432 * to use. 433 */ 434 /* ARGSUSED */ 435 int 436 gotoline(int f, int n) 437 { 438 LINE *clp; 439 int s; 440 char buf[32], *tmp; 441 long nl; 442 443 if (!(f & FFARG)) { 444 if ((s = ereply("Goto line: ", buf, sizeof(buf))) != TRUE) 445 return s; 446 447 nl = strtol(buf, &tmp, 10); 448 if (buf[0] == '\0' || *tmp != '\0') { 449 ewprintf("Invalid number"); 450 return FALSE; 451 } 452 if (nl >= INT_MAX || nl <= INT_MIN) { 453 ewprintf("Out of range"); 454 return FALSE; 455 } 456 n = (int)nl; 457 } 458 if (n >= 0) { 459 clp = lforw(curbp->b_linep); /* "clp" is first line */ 460 while (--n > 0) { 461 if (lforw(clp) == curbp->b_linep) 462 break; 463 clp = lforw(clp); 464 } 465 } else { 466 clp = lback(curbp->b_linep); /* "clp" is last line */ 467 while (n < 0) { 468 if (lback(clp) == curbp->b_linep) 469 break; 470 clp = lback(clp); 471 n++; 472 } 473 } 474 curwp->w_dotp = clp; 475 curwp->w_doto = 0; 476 curwp->w_flag |= WFMOVE; 477 return TRUE; 478 } 479