1 /* $OpenBSD: util.c,v 1.33 2014/03/26 22:02:06 lum Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Assorted commands. 7 * This file contains the command processors for a large assortment of 8 * unrelated commands. The only thing they have in common is that they 9 * are all command processors. 10 */ 11 12 #include "def.h" 13 14 #include <ctype.h> 15 16 /* 17 * Display a bunch of useful information about the current location of dot. 18 * The character under the cursor (in octal), the current line, row, and 19 * column, and approximate position of the cursor in the file (as a 20 * percentage) is displayed. The column position assumes an infinite 21 * position display; it does not truncate just because the screen does. 22 * This is normally bound to "C-X =". 23 */ 24 /* ARGSUSED */ 25 int 26 showcpos(int f, int n) 27 { 28 struct line *clp; 29 long nchar, cchar; 30 int nline, row; 31 int cline, cbyte; /* Current line/char/byte */ 32 int ratio; 33 34 /* collect the data */ 35 clp = bfirstlp(curbp); 36 cchar = 0; 37 cline = 0; 38 cbyte = 0; 39 nchar = 0; 40 nline = 0; 41 for (;;) { 42 /* count this line */ 43 ++nline; 44 if (clp == curwp->w_dotp) { 45 /* mark line */ 46 cline = nline; 47 cchar = nchar + curwp->w_doto; 48 if (curwp->w_doto == llength(clp)) 49 cbyte = '\n'; 50 else 51 cbyte = lgetc(clp, curwp->w_doto); 52 } 53 /* now count the chars */ 54 nchar += llength(clp); 55 clp = lforw(clp); 56 if (clp == curbp->b_headp) 57 break; 58 /* count the newline */ 59 nchar++; 60 } 61 /* determine row */ 62 row = curwp->w_toprow + 1; 63 clp = curwp->w_linep; 64 while (clp != curbp->b_headp && clp != curwp->w_dotp) { 65 ++row; 66 clp = lforw(clp); 67 } 68 /* NOSTRICT */ 69 ratio = nchar ? (100L * cchar) / nchar : 100; 70 ewprintf("Char: %c (0%o) point=%ld(%d%%) line=%d row=%d col=%d", 71 cbyte, cbyte, cchar, ratio, cline, row, getcolpos(curwp)); 72 return (TRUE); 73 } 74 75 int 76 getcolpos(struct mgwin *wp) 77 { 78 int col, i, c; 79 char tmp[5]; 80 81 /* determine column */ 82 col = 0; 83 84 for (i = 0; i < wp->w_doto; ++i) { 85 c = lgetc(wp->w_dotp, i); 86 if (c == '\t' 87 #ifdef NOTAB 88 && !(wp->w_bufp->b_flag & BFNOTAB) 89 #endif /* NOTAB */ 90 ) { 91 col |= 0x07; 92 col++; 93 } else if (ISCTRL(c) != FALSE) 94 col += 2; 95 else if (isprint(c)) { 96 col++; 97 } else { 98 col += snprintf(tmp, sizeof(tmp), "\\%o", c); 99 } 100 101 } 102 return (col); 103 } 104 105 /* 106 * Twiddle the two characters in front of and under dot, then move forward 107 * one character. Treat new-line characters the same as any other. 108 * Normally bound to "C-t". This always works within a line, so "WFEDIT" 109 * is good enough. 110 */ 111 /* ARGSUSED */ 112 int 113 twiddle(int f, int n) 114 { 115 struct line *dotp; 116 int doto, cr; 117 118 dotp = curwp->w_dotp; 119 doto = curwp->w_doto; 120 121 /* Don't twiddle if the dot is on the first char of buffer */ 122 if (doto == 0 && lback(dotp) == curbp->b_headp) { 123 dobeep(); 124 ewprintf("Beginning of buffer"); 125 return(FALSE); 126 } 127 /* Don't twiddle if the dot is on the last char of buffer */ 128 if (doto == llength(dotp) && lforw(dotp) == curbp->b_headp) { 129 dobeep(); 130 return(FALSE); 131 } 132 undo_boundary_enable(FFRAND, 0); 133 if (doto == 0 && doto == llength(dotp)) { /* only '\n' on this line */ 134 (void)forwline(FFRAND, 1); 135 curwp->w_doto = 0; 136 } else { 137 if (doto == 0) { /* 1st twiddle is on 1st character of a line */ 138 cr = lgetc(dotp, doto); 139 (void)backdel(FFRAND, 1); 140 (void)forwchar(FFRAND, 1); 141 lnewline(); 142 linsert(1, cr); 143 (void)backdel(FFRAND, 1); 144 } else { /* twiddle is elsewhere in line */ 145 cr = lgetc(dotp, doto - 1); 146 (void)backdel(FFRAND, 1); 147 (void)forwchar(FFRAND, 1); 148 linsert(1, cr); 149 } 150 } 151 undo_boundary_enable(FFRAND, 1); 152 lchange(WFEDIT); 153 return (TRUE); 154 } 155 156 /* 157 * Open up some blank space. The basic plan is to insert a bunch of 158 * newlines, and then back up over them. Everything is done by the 159 * subcommand processors. They even handle the looping. Normally this 160 * is bound to "C-O". 161 */ 162 /* ARGSUSED */ 163 int 164 openline(int f, int n) 165 { 166 int i, s; 167 168 if (n < 0) 169 return (FALSE); 170 if (n == 0) 171 return (TRUE); 172 173 /* insert newlines */ 174 undo_boundary_enable(FFRAND, 0); 175 i = n; 176 do { 177 s = lnewline(); 178 } while (s == TRUE && --i); 179 180 /* then go back up overtop of them all */ 181 if (s == TRUE) 182 s = backchar(f | FFRAND, n); 183 undo_boundary_enable(FFRAND, 1); 184 return (s); 185 } 186 187 /* 188 * Insert a newline. 189 */ 190 /* ARGSUSED */ 191 int 192 newline(int f, int n) 193 { 194 int s; 195 196 if (n < 0) 197 return (FALSE); 198 199 while (n--) { 200 if ((s = lnewline()) != TRUE) 201 return (s); 202 } 203 return (TRUE); 204 } 205 206 /* 207 * Delete blank lines around dot. What this command does depends if dot is 208 * sitting on a blank line. If dot is sitting on a blank line, this command 209 * deletes all the blank lines above and below the current line. If it is 210 * sitting on a non blank line then it deletes all of the blank lines after 211 * the line. Normally this command is bound to "C-X C-O". Any argument is 212 * ignored. 213 */ 214 /* ARGSUSED */ 215 int 216 deblank(int f, int n) 217 { 218 struct line *lp1, *lp2; 219 RSIZE nld; 220 221 lp1 = curwp->w_dotp; 222 while (llength(lp1) == 0 && (lp2 = lback(lp1)) != curbp->b_headp) 223 lp1 = lp2; 224 lp2 = lp1; 225 nld = (RSIZE)0; 226 while ((lp2 = lforw(lp2)) != curbp->b_headp && llength(lp2) == 0) 227 ++nld; 228 if (nld == 0) 229 return (TRUE); 230 curwp->w_dotp = lforw(lp1); 231 curwp->w_doto = 0; 232 return (ldelete((RSIZE)nld, KNONE)); 233 } 234 235 /* 236 * Delete any whitespace around dot, then insert a space. 237 */ 238 int 239 justone(int f, int n) 240 { 241 undo_boundary_enable(FFRAND, 0); 242 (void)delwhite(f, n); 243 linsert(1, ' '); 244 undo_boundary_enable(FFRAND, 1); 245 return (TRUE); 246 } 247 248 /* 249 * Delete any whitespace around dot. 250 */ 251 /* ARGSUSED */ 252 int 253 delwhite(int f, int n) 254 { 255 int col, s; 256 257 col = curwp->w_doto; 258 259 while (col < llength(curwp->w_dotp) && 260 (isspace(lgetc(curwp->w_dotp, col)))) 261 ++col; 262 do { 263 if (curwp->w_doto == 0) { 264 s = FALSE; 265 break; 266 } 267 if ((s = backchar(FFRAND, 1)) != TRUE) 268 break; 269 } while (isspace(lgetc(curwp->w_dotp, curwp->w_doto))); 270 271 if (s == TRUE) 272 (void)forwchar(FFRAND, 1); 273 (void)ldelete((RSIZE)(col - curwp->w_doto), KNONE); 274 return (TRUE); 275 } 276 277 /* 278 * Delete any leading whitespace on the current line 279 */ 280 int 281 delleadwhite(int f, int n) 282 { 283 int soff, ls; 284 struct line *slp; 285 286 /* Save current position */ 287 slp = curwp->w_dotp; 288 soff = curwp->w_doto; 289 290 for (ls = 0; ls < llength(slp); ls++) 291 if (!isspace(lgetc(slp, ls))) 292 break; 293 gotobol(FFRAND, 1); 294 forwdel(FFRAND, ls); 295 soff -= ls; 296 if (soff < 0) 297 soff = 0; 298 forwchar(FFRAND, soff); 299 300 return (TRUE); 301 } 302 303 /* 304 * Delete any trailing whitespace on the current line 305 */ 306 int 307 deltrailwhite(int f, int n) 308 { 309 int soff; 310 311 /* Save current position */ 312 soff = curwp->w_doto; 313 314 gotoeol(FFRAND, 1); 315 delwhite(FFRAND, 1); 316 317 /* restore original position, if possible */ 318 if (soff < curwp->w_doto) 319 curwp->w_doto = soff; 320 321 return (TRUE); 322 } 323 324 325 326 /* 327 * Insert a newline, then enough tabs and spaces to duplicate the indentation 328 * of the previous line. Assumes tabs are every eight characters. Quite 329 * simple. Figure out the indentation of the current line. Insert a newline 330 * by calling the standard routine. Insert the indentation by inserting the 331 * right number of tabs and spaces. Return TRUE if all ok. Return FALSE if 332 * one of the subcommands failed. Normally bound to "C-M". 333 */ 334 /* ARGSUSED */ 335 int 336 lfindent(int f, int n) 337 { 338 int c, i, nicol; 339 int s = TRUE; 340 341 if (n < 0) 342 return (FALSE); 343 344 undo_boundary_enable(FFRAND, 0); 345 while (n--) { 346 nicol = 0; 347 for (i = 0; i < llength(curwp->w_dotp); ++i) { 348 c = lgetc(curwp->w_dotp, i); 349 if (c != ' ' && c != '\t') 350 break; 351 if (c == '\t') 352 nicol |= 0x07; 353 ++nicol; 354 } 355 if (lnewline() == FALSE || (( 356 #ifdef NOTAB 357 curbp->b_flag & BFNOTAB) ? linsert(nicol, ' ') == FALSE : ( 358 #endif /* NOTAB */ 359 ((i = nicol / 8) != 0 && linsert(i, '\t') == FALSE) || 360 ((i = nicol % 8) != 0 && linsert(i, ' ') == FALSE)))) { 361 s = FALSE; 362 break; 363 } 364 } 365 undo_boundary_enable(FFRAND, 1); 366 return (s); 367 } 368 369 /* 370 * Indent the current line. Delete existing leading whitespace, 371 * and use tabs/spaces to achieve correct indentation. Try 372 * to leave dot where it started. 373 */ 374 int 375 indent(int f, int n) 376 { 377 int soff, i; 378 379 if (n < 0) 380 return (FALSE); 381 382 delleadwhite(FFRAND, 1); 383 384 /* If not invoked with a numerical argument, done */ 385 if (!(f & FFARG)) 386 return (TRUE); 387 388 /* insert appropriate whitespace */ 389 soff = curwp->w_doto; 390 (void)gotobol(FFRAND, 1); 391 if ( 392 #ifdef NOTAB 393 (curbp->b_flag & BFNOTAB) ? linsert(n, ' ') == FALSE : 394 #endif /* NOTAB */ 395 (((i = n / 8) != 0 && linsert(i, '\t') == FALSE) || 396 ((i = n % 8) != 0 && linsert(i, ' ') == FALSE))) 397 return (FALSE); 398 399 forwchar(FFRAND, soff); 400 401 return (TRUE); 402 } 403 404 405 /* 406 * Delete forward. This is real easy, because the basic delete routine does 407 * all of the work. Watches for negative arguments, and does the right thing. 408 * If any argument is present, it kills rather than deletes, to prevent loss 409 * of text if typed with a big argument. Normally bound to "C-D". 410 */ 411 /* ARGSUSED */ 412 int 413 forwdel(int f, int n) 414 { 415 if (n < 0) 416 return (backdel(f | FFRAND, -n)); 417 418 /* really a kill */ 419 if (f & FFARG) { 420 if ((lastflag & CFKILL) == 0) 421 kdelete(); 422 thisflag |= CFKILL; 423 } 424 425 return (ldelete((RSIZE) n, (f & FFARG) ? KFORW : KNONE)); 426 } 427 428 /* 429 * Delete backwards. This is quite easy too, because it's all done with 430 * other functions. Just move the cursor back, and delete forwards. Like 431 * delete forward, this actually does a kill if presented with an argument. 432 */ 433 /* ARGSUSED */ 434 int 435 backdel(int f, int n) 436 { 437 int s; 438 439 if (n < 0) 440 return (forwdel(f | FFRAND, -n)); 441 442 /* really a kill */ 443 if (f & FFARG) { 444 if ((lastflag & CFKILL) == 0) 445 kdelete(); 446 thisflag |= CFKILL; 447 } 448 if ((s = backchar(f | FFRAND, n)) == TRUE) 449 s = ldelete((RSIZE)n, (f & FFARG) ? KFORW : KNONE); 450 451 return (s); 452 } 453 454 #ifdef NOTAB 455 /* ARGSUSED */ 456 int 457 space_to_tabstop(int f, int n) 458 { 459 if (n < 0) 460 return (FALSE); 461 if (n == 0) 462 return (TRUE); 463 return (linsert((n << 3) - (curwp->w_doto & 7), ' ')); 464 } 465 #endif /* NOTAB */ 466 467 /* 468 * Move the dot to the first non-whitespace character of the current line. 469 */ 470 int 471 backtoindent(int f, int n) 472 { 473 gotobol(FFRAND, 1); 474 while (curwp->w_doto < llength(curwp->w_dotp) && 475 (isspace(lgetc(curwp->w_dotp, curwp->w_doto)))) 476 ++curwp->w_doto; 477 return (TRUE); 478 } 479 480 /* 481 * Join the current line to the previous, or with arg, the next line 482 * to the current one. If the former line is not empty, leave exactly 483 * one space at the joint. Otherwise, leave no whitespace. 484 */ 485 int 486 joinline(int f, int n) 487 { 488 int doto; 489 490 undo_boundary_enable(FFRAND, 0); 491 if (f & FFARG) { 492 gotoeol(FFRAND, 1); 493 forwdel(FFRAND, 1); 494 } else { 495 gotobol(FFRAND, 1); 496 backdel(FFRAND, 1); 497 } 498 499 delwhite(FFRAND, 1); 500 501 if ((doto = curwp->w_doto) > 0) { 502 linsert(1, ' '); 503 curwp->w_doto = doto; 504 } 505 undo_boundary_enable(FFRAND, 1); 506 507 return (TRUE); 508 } 509