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