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