1 /* $OpenBSD: util.c,v 1.6 2001/05/24 03:05:25 mickey Exp $ */ 2 3 /* 4 * Assorted commands. 5 * This file contains the command processors for a large assortment of 6 * unrelated commands. The only thing they have in common is that they 7 * are all command processors. 8 */ 9 10 #include "def.h" 11 12 /* 13 * Display a bunch of useful information about the current location of dot. 14 * The character under the cursor (in octal), the current line, row, and 15 * column, and approximate position of the cursor in the file (as a 16 * percentage) is displayed. The column position assumes an infinite 17 * position display; it does not truncate just because the screen does. 18 * This is normally bound to "C-X =". 19 */ 20 /* ARGSUSED */ 21 int 22 showcpos(f, n) 23 int f, n; 24 { 25 LINE *clp; 26 long nchar; 27 long cchar; 28 int nline, row; 29 int cline, cbyte; /* Current line/char/byte */ 30 int ratio; 31 32 /* collect the data */ 33 clp = lforw(curbp->b_linep); 34 cchar = 0; 35 cline = 0; 36 cbyte = 0; 37 nchar = 0; 38 nline = 0; 39 for (;;) { 40 /* count this line */ 41 ++nline; 42 if (clp == curwp->w_dotp) { 43 /* mark line */ 44 cline = nline; 45 cchar = nchar + curwp->w_doto; 46 if (curwp->w_doto == llength(clp)) 47 cbyte = '\n'; 48 else 49 cbyte = lgetc(clp, curwp->w_doto); 50 } 51 /* now count the chars */ 52 nchar += llength(clp); 53 clp = lforw(clp); 54 if (clp == curbp->b_linep) 55 break; 56 /* count the newline */ 57 nchar++; 58 } 59 /* determine row */ 60 row = curwp->w_toprow + 1; 61 clp = curwp->w_linep; 62 while (clp != curbp->b_linep && clp != curwp->w_dotp) { 63 ++row; 64 clp = lforw(clp); 65 } 66 /* NOSTRICT */ 67 ratio = nchar ? (100L * cchar) / nchar : 100; 68 ewprintf("Char: %c (0%o) point=%ld(%d%%) line=%d row=%d col=%d", 69 cbyte, cbyte, cchar, ratio, cline, row, getcolpos()); 70 return TRUE; 71 } 72 73 int 74 getcolpos() 75 { 76 int col, i, c; 77 78 /* determine column */ 79 col = 1; 80 81 for (i = 0; i < curwp->w_doto; ++i) { 82 c = lgetc(curwp->w_dotp, i); 83 if (c == '\t' 84 #ifdef NOTAB 85 && !(curbp->b_flag & BFNOTAB) 86 #endif /* NOTAB */ 87 ) { 88 col |= 0x07; 89 ++col; 90 } else if (ISCTRL(c) != FALSE) 91 ++col; 92 ++col; 93 } 94 return col; 95 } 96 97 /* 98 * Twiddle the two characters on either side of dot. If dot is at the end 99 * of the line twiddle the two characters before it. Return with an error 100 * if dot is at the beginning of line; it seems to be a bit pointless to 101 * make this work. This fixes up a very common typo with a single stroke. 102 * Normally bound to "C-T". This always works within a line, so "WFEDIT" 103 * is good enough. 104 */ 105 /* ARGSUSED */ 106 int 107 twiddle(f, n) 108 int f, n; 109 { 110 LINE *dotp; 111 int doto, cr; 112 113 dotp = curwp->w_dotp; 114 doto = curwp->w_doto; 115 if (doto == llength(dotp)) { 116 if (--doto <= 0) 117 return FALSE; 118 } else { 119 if (doto == 0) 120 return FALSE; 121 ++curwp->w_doto; 122 } 123 cr = lgetc(dotp, doto--); 124 lputc(dotp, doto + 1, lgetc(dotp, doto)); 125 lputc(dotp, doto, cr); 126 lchange(WFEDIT); 127 return TRUE; 128 } 129 130 /* 131 * Open up some blank space. The basic plan is to insert a bunch of 132 * newlines, and then back up over them. Everything is done by the 133 * subcommand procerssors. They even handle the looping. Normally this 134 * is bound to "C-O". 135 */ 136 /* ARGSUSED */ 137 int 138 openline(f, n) 139 int f, n; 140 { 141 int i; 142 int s; 143 144 if (n < 0) 145 return FALSE; 146 if (n == 0) 147 return TRUE; 148 149 /* insert newlines */ 150 i = n; 151 do { 152 s = lnewline(); 153 } while (s == TRUE && --i); 154 155 /* then go back up overtop of them all */ 156 if (s == TRUE) 157 s = backchar(f | FFRAND, n); 158 return s; 159 } 160 161 /* 162 * Insert a newline. [following "feature" not present in current version of 163 * Gnu, and now disabled here too] If you are at the end of the line and the 164 * next line is a blank line, just move into the blank line. This makes 165 * "C-O" and "C-X C-O" work nicely, and reduces the ammount of screen update 166 * that has to be done. This would not be as critical if screen update were a 167 * lot more efficient. 168 */ 169 /* ARGSUSED */ 170 int 171 newline(f, n) 172 int f, n; 173 { 174 LINE *lp; 175 int s; 176 177 if (n < 0) 178 return FALSE; 179 180 while (n--) { 181 lp = curwp->w_dotp; 182 #ifdef undef 183 if (llength(lp) == curwp->w_doto 184 && lforw(lp) != curbp->b_linep 185 && llength(lforw(lp)) == 0) { 186 if ((s = forwchar(FFRAND, 1)) != TRUE) 187 return s; 188 } else 189 #endif /* undef */ 190 if ((s = lnewline()) != TRUE) 191 return s; 192 } 193 return TRUE; 194 } 195 196 /* 197 * Delete blank lines around dot. What this command does depends if dot is 198 * sitting on a blank line. If dot is sitting on a blank line, this command 199 * deletes all the blank lines above and below the current line. If it is 200 * sitting on a non blank line then it deletes all of the blank lines after 201 * the line. Normally this command is bound to "C-X C-O". Any argument is 202 * ignored. 203 */ 204 /* ARGSUSED */ 205 int 206 deblank(f, n) 207 int f, n; 208 { 209 LINE *lp1, *lp2; 210 RSIZE nld; 211 212 lp1 = curwp->w_dotp; 213 while (llength(lp1) == 0 && (lp2 = lback(lp1)) != curbp->b_linep) 214 lp1 = lp2; 215 lp2 = lp1; 216 nld = (RSIZE)0; 217 while ((lp2 = lforw(lp2)) != curbp->b_linep && llength(lp2) == 0) 218 ++nld; 219 if (nld == 0) 220 return (TRUE); 221 curwp->w_dotp = lforw(lp1); 222 curwp->w_doto = 0; 223 return ldelete((RSIZE)nld, KNONE); 224 } 225 226 /* 227 * Delete any whitespace around dot, then insert a space. 228 */ 229 int 230 justone(f, n) 231 int f, n; 232 { 233 (void)delwhite(f, n); 234 return linsert(1, ' '); 235 } 236 237 /* 238 * Delete any whitespace around dot. 239 */ 240 /* ARGSUSED */ 241 int 242 delwhite(f, n) 243 int f, n; 244 { 245 int col, c, s; 246 247 col = curwp->w_doto; 248 249 while (((c = lgetc(curwp->w_dotp, col)) == ' ' || c == '\t') 250 && col < llength(curwp->w_dotp)) 251 ++col; 252 do { 253 if (curwp->w_doto == 0) { 254 s = FALSE; 255 break; 256 } 257 if ((s = backchar(FFRAND, 1)) != TRUE) 258 break; 259 } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' ' || c == '\t'); 260 261 if (s == TRUE) 262 (void)forwchar(FFRAND, 1); 263 (void)ldelete((RSIZE)(col - curwp->w_doto), KNONE); 264 return TRUE; 265 } 266 267 /* 268 * Insert a newline, then enough tabs and spaces to duplicate the indentation 269 * of the previous line. Assumes tabs are every eight characters. Quite 270 * simple. Figure out the indentation of the current line. Insert a newline 271 * by calling the standard routine. Insert the indentation by inserting the 272 * right number of tabs and spaces. Return TRUE if all ok. Return FALSE if 273 * one of the subcomands failed. Normally bound to "C-J". 274 */ 275 /* ARGSUSED */ 276 int 277 indent(f, n) 278 int f, n; 279 { 280 int nicol; 281 int c; 282 int i; 283 284 if (n < 0) 285 return (FALSE); 286 287 while (n--) { 288 nicol = 0; 289 for (i = 0; i < llength(curwp->w_dotp); ++i) { 290 c = lgetc(curwp->w_dotp, i); 291 if (c != ' ' && c != '\t') 292 break; 293 if (c == '\t') 294 nicol |= 0x07; 295 ++nicol; 296 } 297 if (lnewline() == FALSE || (( 298 #ifdef NOTAB 299 curbp->b_flag & BFNOTAB) ? linsert(nicol, ' ') == FALSE : ( 300 #endif /* NOTAB */ 301 ((i = nicol / 8) != 0 && linsert(i, '\t') == FALSE) || 302 ((i = nicol % 8) != 0 && linsert(i, ' ') == FALSE)))) 303 return FALSE; 304 } 305 return TRUE; 306 } 307 308 /* 309 * Delete forward. This is real easy, because the basic delete routine does 310 * all of the work. Watches for negative arguments, and does the right thing. 311 * If any argument is present, it kills rather than deletes, to prevent loss 312 * of text if typed with a big argument. Normally bound to "C-D". 313 */ 314 /* ARGSUSED */ 315 int 316 forwdel(f, n) 317 int f, n; 318 { 319 if (n < 0) 320 return backdel(f | FFRAND, -n); 321 322 /* really a kill */ 323 if (f & FFARG) { 324 if ((lastflag & CFKILL) == 0) 325 kdelete(); 326 thisflag |= CFKILL; 327 } 328 329 return ldelete((RSIZE) n, (f & FFARG) ? KFORW : KNONE); 330 } 331 332 /* 333 * Delete backwards. This is quite easy too, because it's all done with 334 * other functions. Just move the cursor back, and delete forwards. Like 335 * delete forward, this actually does a kill if presented with an argument. 336 */ 337 /* ARGSUSED */ 338 int 339 backdel(f, n) 340 int f, n; 341 { 342 int s; 343 344 if (n < 0) 345 return forwdel(f | FFRAND, -n); 346 347 /* really a kill */ 348 if (f & FFARG) { 349 if ((lastflag & CFKILL) == 0) 350 kdelete(); 351 thisflag |= CFKILL; 352 } 353 if ((s = backchar(f | FFRAND, n)) == TRUE) 354 s = ldelete((RSIZE)n, (f & FFARG) ? KFORW : KNONE); 355 356 return s; 357 } 358 359 /* 360 * Kill line. If called without an argument, it kills from dot to the end 361 * of the line, unless it is at the end of the line, when it kills the 362 * newline. If called with an argument of 0, it kills from the start of the 363 * line to dot. If called with a positive argument, it kills from dot 364 * forward over that number of newlines. If called with a negative argument 365 * it kills any text before dot on the current line, then it kills back 366 * abs(arg) lines. 367 */ 368 /* ARGSUSED */ 369 int 370 killline(f, n) 371 int f, n; 372 { 373 LINE *nextp; 374 RSIZE chunk; 375 int i, c; 376 377 /* clear kill buffer if last wasn't a kill */ 378 if ((lastflag & CFKILL) == 0) 379 kdelete(); 380 thisflag |= CFKILL; 381 if (!(f & FFARG)) { 382 for (i = curwp->w_doto; i < llength(curwp->w_dotp); ++i) 383 if ((c = lgetc(curwp->w_dotp, i)) != ' ' && c != '\t') 384 break; 385 if (i == llength(curwp->w_dotp)) 386 chunk = llength(curwp->w_dotp) - curwp->w_doto + 1; 387 else { 388 chunk = llength(curwp->w_dotp) - curwp->w_doto; 389 if (chunk == 0) 390 chunk = 1; 391 } 392 } else if (n > 0) { 393 chunk = llength(curwp->w_dotp) - curwp->w_doto + 1; 394 nextp = lforw(curwp->w_dotp); 395 i = n; 396 while (--i) { 397 if (nextp == curbp->b_linep) 398 break; 399 chunk += llength(nextp) + 1; 400 nextp = lforw(nextp); 401 } 402 } else { 403 /* n <= 0 */ 404 chunk = curwp->w_doto; 405 curwp->w_doto = 0; 406 i = n; 407 while (i++) { 408 if (lback(curwp->w_dotp) == curbp->b_linep) 409 break; 410 curwp->w_dotp = lback(curwp->w_dotp); 411 curwp->w_flag |= WFMOVE; 412 chunk += llength(curwp->w_dotp) + 1; 413 } 414 } 415 /* 416 * KFORW here is a bug. Should be KBACK/KFORW, but we need to 417 * rewrite the ldelete code (later)? 418 */ 419 return (ldelete(chunk, KFORW)); 420 } 421 422 /* 423 * Yank text back from the kill buffer. This is really easy. All of the work 424 * is done by the standard insert routines. All you do is run the loop, and 425 * check for errors. The blank lines are inserted with a call to "newline" 426 * instead of a call to "lnewline" so that the magic stuff that happens when 427 * you type a carriage return also happens when a carriage return is yanked 428 * back from the kill buffer. An attempt has been made to fix the cosmetic 429 * bug associated with a yank when dot is on the top line of the window 430 * (nothing moves, because all of the new text landed off screen). 431 */ 432 /* ARGSUSED */ 433 int 434 yank(f, n) 435 int f, n; 436 { 437 LINE *lp; 438 int c, i, nline; 439 440 if (n < 0) 441 return FALSE; 442 443 /* newline counting */ 444 nline = 0; 445 446 while (n--) { 447 /* mark around last yank */ 448 isetmark(); 449 i = 0; 450 while ((c = kremove(i)) >= 0) { 451 if (c == '\n') { 452 if (newline(FFRAND, 1) == FALSE) 453 return FALSE; 454 ++nline; 455 } else { 456 if (linsert(1, c) == FALSE) 457 return FALSE; 458 } 459 ++i; 460 } 461 } 462 /* cosmetic adjustment */ 463 lp = curwp->w_linep; 464 465 /* if offscreen insert */ 466 if (curwp->w_dotp == lp) { 467 while (nline-- && lback(lp) != curbp->b_linep) 468 lp = lback(lp); 469 /* adjust framing */ 470 curwp->w_linep = lp; 471 curwp->w_flag |= WFHARD; 472 } 473 return TRUE; 474 } 475 476 #ifdef NOTAB 477 /* ARGSUSED */ 478 int 479 space_to_tabstop(f, n) 480 int f, n; 481 { 482 if (n < 0) 483 return FALSE; 484 if (n == 0) 485 return TRUE; 486 return linsert((n << 3) - (curwp->w_doto & 7), ' '); 487 } 488 #endif /* NOTAB */ 489