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