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