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