[BACK]Return to util.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mg

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: }