1 /* $OpenBSD: util.c,v 1.32 2013/03/25 11:41:44 florian 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 on either side of dot. If dot is at the end 107 * of the line twiddle the two characters before it. Return with an error 108 * if dot is at the beginning of line; it seems to be a bit pointless to 109 * make this work. This fixes up a very common typo with a single stroke. 110 * Normally bound to "C-T". This always works within a line, so "WFEDIT" 111 * is good enough. 112 */ 113 /* ARGSUSED */ 114 int 115 twiddle(int f, int n) 116 { 117 struct line *dotp; 118 int doto, cr; 119 int fudge = FALSE; 120 121 dotp = curwp->w_dotp; 122 doto = curwp->w_doto; 123 if (doto == llength(dotp)) { 124 if (--doto <= 0) 125 return (FALSE); 126 (void)backchar(FFRAND, 1); 127 fudge = TRUE; 128 } else { 129 if (doto == 0) 130 return (FALSE); 131 } 132 undo_boundary_enable(FFRAND, 0); 133 cr = lgetc(dotp, doto - 1); 134 (void)backdel(FFRAND, 1); 135 (void)forwchar(FFRAND, 1); 136 linsert(1, cr); 137 if (fudge != TRUE) 138 (void)backchar(FFRAND, 1); 139 undo_boundary_enable(FFRAND, 1); 140 lchange(WFEDIT); 141 return (TRUE); 142 } 143 144 /* 145 * Open up some blank space. The basic plan is to insert a bunch of 146 * newlines, and then back up over them. Everything is done by the 147 * subcommand processors. They even handle the looping. Normally this 148 * is bound to "C-O". 149 */ 150 /* ARGSUSED */ 151 int 152 openline(int f, int n) 153 { 154 int i, s; 155 156 if (n < 0) 157 return (FALSE); 158 if (n == 0) 159 return (TRUE); 160 161 /* insert newlines */ 162 undo_boundary_enable(FFRAND, 0); 163 i = n; 164 do { 165 s = lnewline(); 166 } while (s == TRUE && --i); 167 168 /* then go back up overtop of them all */ 169 if (s == TRUE) 170 s = backchar(f | FFRAND, n); 171 undo_boundary_enable(FFRAND, 1); 172 return (s); 173 } 174 175 /* 176 * Insert a newline. 177 */ 178 /* ARGSUSED */ 179 int 180 newline(int f, int n) 181 { 182 int s; 183 184 if (n < 0) 185 return (FALSE); 186 187 while (n--) { 188 if ((s = lnewline()) != TRUE) 189 return (s); 190 } 191 return (TRUE); 192 } 193 194 /* 195 * Delete blank lines around dot. What this command does depends if dot is 196 * sitting on a blank line. If dot is sitting on a blank line, this command 197 * deletes all the blank lines above and below the current line. If it is 198 * sitting on a non blank line then it deletes all of the blank lines after 199 * the line. Normally this command is bound to "C-X C-O". Any argument is 200 * ignored. 201 */ 202 /* ARGSUSED */ 203 int 204 deblank(int f, int n) 205 { 206 struct line *lp1, *lp2; 207 RSIZE nld; 208 209 lp1 = curwp->w_dotp; 210 while (llength(lp1) == 0 && (lp2 = lback(lp1)) != curbp->b_headp) 211 lp1 = lp2; 212 lp2 = lp1; 213 nld = (RSIZE)0; 214 while ((lp2 = lforw(lp2)) != curbp->b_headp && llength(lp2) == 0) 215 ++nld; 216 if (nld == 0) 217 return (TRUE); 218 curwp->w_dotp = lforw(lp1); 219 curwp->w_doto = 0; 220 return (ldelete((RSIZE)nld, KNONE)); 221 } 222 223 /* 224 * Delete any whitespace around dot, then insert a space. 225 */ 226 int 227 justone(int f, int n) 228 { 229 undo_boundary_enable(FFRAND, 0); 230 (void)delwhite(f, n); 231 linsert(1, ' '); 232 undo_boundary_enable(FFRAND, 1); 233 return (TRUE); 234 } 235 236 /* 237 * Delete any whitespace around dot. 238 */ 239 /* ARGSUSED */ 240 int 241 delwhite(int f, int n) 242 { 243 int col, s; 244 245 col = curwp->w_doto; 246 247 while (col < llength(curwp->w_dotp) && 248 (isspace(lgetc(curwp->w_dotp, col)))) 249 ++col; 250 do { 251 if (curwp->w_doto == 0) { 252 s = FALSE; 253 break; 254 } 255 if ((s = backchar(FFRAND, 1)) != TRUE) 256 break; 257 } while (isspace(lgetc(curwp->w_dotp, curwp->w_doto))); 258 259 if (s == TRUE) 260 (void)forwchar(FFRAND, 1); 261 (void)ldelete((RSIZE)(col - curwp->w_doto), KNONE); 262 return (TRUE); 263 } 264 265 /* 266 * Delete any leading whitespace on the current line 267 */ 268 int 269 delleadwhite(int f, int n) 270 { 271 int soff, ls; 272 struct line *slp; 273 274 /* Save current position */ 275 slp = curwp->w_dotp; 276 soff = curwp->w_doto; 277 278 for (ls = 0; ls < llength(slp); ls++) 279 if (!isspace(lgetc(slp, ls))) 280 break; 281 gotobol(FFRAND, 1); 282 forwdel(FFRAND, ls); 283 soff -= ls; 284 if (soff < 0) 285 soff = 0; 286 forwchar(FFRAND, soff); 287 288 return (TRUE); 289 } 290 291 /* 292 * Delete any trailing whitespace on the current line 293 */ 294 int 295 deltrailwhite(int f, int n) 296 { 297 int soff; 298 299 /* Save current position */ 300 soff = curwp->w_doto; 301 302 gotoeol(FFRAND, 1); 303 delwhite(FFRAND, 1); 304 305 /* restore original position, if possible */ 306 if (soff < curwp->w_doto) 307 curwp->w_doto = soff; 308 309 return (TRUE); 310 } 311 312 313 314 /* 315 * Insert a newline, then enough tabs and spaces to duplicate the indentation 316 * of the previous line. Assumes tabs are every eight characters. Quite 317 * simple. Figure out the indentation of the current line. Insert a newline 318 * by calling the standard routine. Insert the indentation by inserting the 319 * right number of tabs and spaces. Return TRUE if all ok. Return FALSE if 320 * one of the subcommands failed. Normally bound to "C-M". 321 */ 322 /* ARGSUSED */ 323 int 324 lfindent(int f, int n) 325 { 326 int c, i, nicol; 327 int s = TRUE; 328 329 if (n < 0) 330 return (FALSE); 331 332 undo_boundary_enable(FFRAND, 0); 333 while (n--) { 334 nicol = 0; 335 for (i = 0; i < llength(curwp->w_dotp); ++i) { 336 c = lgetc(curwp->w_dotp, i); 337 if (c != ' ' && c != '\t') 338 break; 339 if (c == '\t') 340 nicol |= 0x07; 341 ++nicol; 342 } 343 if (lnewline() == FALSE || (( 344 #ifdef NOTAB 345 curbp->b_flag & BFNOTAB) ? linsert(nicol, ' ') == FALSE : ( 346 #endif /* NOTAB */ 347 ((i = nicol / 8) != 0 && linsert(i, '\t') == FALSE) || 348 ((i = nicol % 8) != 0 && linsert(i, ' ') == FALSE)))) { 349 s = FALSE; 350 break; 351 } 352 } 353 undo_boundary_enable(FFRAND, 1); 354 return (s); 355 } 356 357 /* 358 * Indent the current line. Delete existing leading whitespace, 359 * and use tabs/spaces to achieve correct indentation. Try 360 * to leave dot where it started. 361 */ 362 int 363 indent(int f, int n) 364 { 365 int soff, i; 366 367 if (n < 0) 368 return (FALSE); 369 370 delleadwhite(FFRAND, 1); 371 372 /* If not invoked with a numerical argument, done */ 373 if (!(f & FFARG)) 374 return (TRUE); 375 376 /* insert appropriate whitespace */ 377 soff = curwp->w_doto; 378 (void)gotobol(FFRAND, 1); 379 if ( 380 #ifdef NOTAB 381 (curbp->b_flag & BFNOTAB) ? linsert(n, ' ') == FALSE : 382 #endif /* NOTAB */ 383 (((i = n / 8) != 0 && linsert(i, '\t') == FALSE) || 384 ((i = n % 8) != 0 && linsert(i, ' ') == FALSE))) 385 return (FALSE); 386 387 forwchar(FFRAND, soff); 388 389 return (TRUE); 390 } 391 392 393 /* 394 * Delete forward. This is real easy, because the basic delete routine does 395 * all of the work. Watches for negative arguments, and does the right thing. 396 * If any argument is present, it kills rather than deletes, to prevent loss 397 * of text if typed with a big argument. Normally bound to "C-D". 398 */ 399 /* ARGSUSED */ 400 int 401 forwdel(int f, int n) 402 { 403 if (n < 0) 404 return (backdel(f | FFRAND, -n)); 405 406 /* really a kill */ 407 if (f & FFARG) { 408 if ((lastflag & CFKILL) == 0) 409 kdelete(); 410 thisflag |= CFKILL; 411 } 412 413 return (ldelete((RSIZE) n, (f & FFARG) ? KFORW : KNONE)); 414 } 415 416 /* 417 * Delete backwards. This is quite easy too, because it's all done with 418 * other functions. Just move the cursor back, and delete forwards. Like 419 * delete forward, this actually does a kill if presented with an argument. 420 */ 421 /* ARGSUSED */ 422 int 423 backdel(int f, int n) 424 { 425 int s; 426 427 if (n < 0) 428 return (forwdel(f | FFRAND, -n)); 429 430 /* really a kill */ 431 if (f & FFARG) { 432 if ((lastflag & CFKILL) == 0) 433 kdelete(); 434 thisflag |= CFKILL; 435 } 436 if ((s = backchar(f | FFRAND, n)) == TRUE) 437 s = ldelete((RSIZE)n, (f & FFARG) ? KFORW : KNONE); 438 439 return (s); 440 } 441 442 #ifdef NOTAB 443 /* ARGSUSED */ 444 int 445 space_to_tabstop(int f, int n) 446 { 447 if (n < 0) 448 return (FALSE); 449 if (n == 0) 450 return (TRUE); 451 return (linsert((n << 3) - (curwp->w_doto & 7), ' ')); 452 } 453 #endif /* NOTAB */ 454 455 /* 456 * Move the dot to the first non-whitespace character of the current line. 457 */ 458 int 459 backtoindent(int f, int n) 460 { 461 gotobol(FFRAND, 1); 462 while (curwp->w_doto < llength(curwp->w_dotp) && 463 (isspace(lgetc(curwp->w_dotp, curwp->w_doto)))) 464 ++curwp->w_doto; 465 return (TRUE); 466 } 467 468 /* 469 * Join the current line to the previous, or with arg, the next line 470 * to the current one. If the former line is not empty, leave exactly 471 * one space at the joint. Otherwise, leave no whitespace. 472 */ 473 int 474 joinline(int f, int n) 475 { 476 int doto; 477 478 undo_boundary_enable(FFRAND, 0); 479 if (f & FFARG) { 480 gotoeol(FFRAND, 1); 481 forwdel(FFRAND, 1); 482 } else { 483 gotobol(FFRAND, 1); 484 backdel(FFRAND, 1); 485 } 486 487 delwhite(FFRAND, 1); 488 489 if ((doto = curwp->w_doto) > 0) { 490 linsert(1, ' '); 491 curwp->w_doto = doto; 492 } 493 undo_boundary_enable(FFRAND, 1); 494 495 return (TRUE); 496 } 497