[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.41

1.41    ! lum         1: /*     $OpenBSD: util.c,v 1.40 2018/12/26 07:01:22 phessler 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.1       deraadt    18:
                     19: /*
1.6       mickey     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
1.41    ! lum        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
1.3       millert    27:  * position display; it does not truncate just because the screen does.
1.41    ! lum        28:  * This is normally bound to "C-x =".
1.1       deraadt    29:  */
1.2       millert    30: /* ARGSUSED */
1.3       millert    31: int
1.10      cloder     32: showcpos(int f, int n)
1.1       deraadt    33: {
1.15      deraadt    34:        struct line     *clp;
1.41    ! lum        35:        char            *msg;
1.11      db         36:        long     nchar, cchar;
1.3       millert    37:        int      nline, row;
                     38:        int      cline, cbyte;          /* Current line/char/byte */
                     39:        int      ratio;
                     40:
                     41:        /* collect the data */
1.22      kjell      42:        clp = bfirstlp(curbp);
1.41    ! lum        43:        msg = "Char:";
1.3       millert    44:        cchar = 0;
                     45:        cline = 0;
                     46:        cbyte = 0;
1.1       deraadt    47:        nchar = 0;
                     48:        nline = 0;
1.6       mickey     49:        for (;;) {
1.41    ! lum        50:                /* count lines and display total as (raw) 'lines' and
        !            51:                   compare with b_lines */
1.3       millert    52:                ++nline;
1.1       deraadt    53:                if (clp == curwp->w_dotp) {
1.41    ! lum        54:                        /* obtain (raw) dot line # and compare with w_dotline */
1.3       millert    55:                        cline = nline;
1.1       deraadt    56:                        cchar = nchar + curwp->w_doto;
                     57:                        if (curwp->w_doto == llength(clp))
1.41    ! lum        58:                                /* fake a \n at end of line */
1.1       deraadt    59:                                cbyte = '\n';
                     60:                        else
                     61:                                cbyte = lgetc(clp, curwp->w_doto);
                     62:                }
1.41    ! lum        63:                /* include # of chars in this line for point-thru-buff ratio */
1.3       millert    64:                nchar += llength(clp);
1.1       deraadt    65:                clp = lforw(clp);
1.41    ! lum        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:                        }
1.2       millert    72:                        break;
1.41    ! lum        73:                }
        !            74:                /* count the implied newline */
1.3       millert    75:                nchar++;
1.1       deraadt    76:        }
1.41    ! lum        77:        /* determine row # within current window */
1.3       millert    78:        row = curwp->w_toprow + 1;
1.1       deraadt    79:        clp = curwp->w_linep;
1.21      kjell      80:        while (clp != curbp->b_headp && clp != curwp->w_dotp) {
1.1       deraadt    81:                ++row;
                     82:                clp = lforw(clp);
                     83:        }
1.2       millert    84:        ratio = nchar ? (100L * cchar) / nchar : 100;
1.41    ! lum        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);
1.11      db         89:        return (TRUE);
1.1       deraadt    90: }
                     91:
1.3       millert    92: int
1.32      florian    93: getcolpos(struct mgwin *wp)
1.2       millert    94: {
1.3       millert    95:        int     col, i, c;
1.20      kjell      96:        char tmp[5];
1.3       millert    97:
                     98:        /* determine column */
1.9       vincent    99:        col = 0;
1.1       deraadt   100:
1.32      florian   101:        for (i = 0; i < wp->w_doto; ++i) {
                    102:                c = lgetc(wp->w_dotp, i);
1.1       deraadt   103:                if (c == '\t'
1.3       millert   104: #ifdef NOTAB
1.32      florian   105:                    && !(wp->w_bufp->b_flag & BFNOTAB)
1.3       millert   106: #endif /* NOTAB */
1.1       deraadt   107:                        ) {
1.2       millert   108:                        col |= 0x07;
1.9       vincent   109:                        col++;
1.1       deraadt   110:                } else if (ISCTRL(c) != FALSE)
1.9       vincent   111:                        col += 2;
1.20      kjell     112:                else if (isprint(c)) {
1.9       vincent   113:                        col++;
1.20      kjell     114:                } else {
                    115:                        col += snprintf(tmp, sizeof(tmp), "\\%o", c);
1.9       vincent   116:                }
                    117:
1.1       deraadt   118:        }
1.11      db        119:        return (col);
1.1       deraadt   120: }
1.3       millert   121:
1.1       deraadt   122: /*
1.33      lum       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"
1.3       millert   126:  * is good enough.
1.1       deraadt   127:  */
1.2       millert   128: /* ARGSUSED */
1.3       millert   129: int
1.10      cloder    130: twiddle(int f, int n)
1.1       deraadt   131: {
1.15      deraadt   132:        struct line     *dotp;
1.3       millert   133:        int      doto, cr;
1.37      lum       134:
                    135:        if (n == 0)
                    136:                return (TRUE);
1.1       deraadt   137:
                    138:        dotp = curwp->w_dotp;
                    139:        doto = curwp->w_doto;
1.33      lum       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;
1.1       deraadt   156:        } else {
1.33      lum       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);
1.38      jasper    164:                } else {        /* twiddle is elsewhere in line */
1.33      lum       165:                        cr = lgetc(dotp, doto - 1);
                    166:                        (void)backdel(FFRAND, 1);
                    167:                        (void)forwchar(FFRAND, 1);
                    168:                        linsert(1, cr);
                    169:                }
1.1       deraadt   170:        }
1.26      kjell     171:        undo_boundary_enable(FFRAND, 1);
1.1       deraadt   172:        lchange(WFEDIT);
1.11      db        173:        return (TRUE);
1.1       deraadt   174: }
                    175:
                    176: /*
1.6       mickey    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
1.11      db        179:  * subcommand processors.  They even handle the looping.  Normally this
1.3       millert   180:  * is bound to "C-O".
1.1       deraadt   181:  */
1.2       millert   182: /* ARGSUSED */
1.3       millert   183: int
1.10      cloder    184: openline(int f, int n)
1.1       deraadt   185: {
1.11      db        186:        int     i, s;
1.1       deraadt   187:
                    188:        if (n < 0)
1.11      db        189:                return (FALSE);
1.1       deraadt   190:        if (n == 0)
1.11      db        191:                return (TRUE);
1.3       millert   192:
                    193:        /* insert newlines */
1.29      kjell     194:        undo_boundary_enable(FFRAND, 0);
1.3       millert   195:        i = n;
1.1       deraadt   196:        do {
                    197:                s = lnewline();
1.2       millert   198:        } while (s == TRUE && --i);
1.3       millert   199:
                    200:        /* then go back up overtop of them all */
                    201:        if (s == TRUE)
                    202:                s = backchar(f | FFRAND, n);
1.29      kjell     203:        undo_boundary_enable(FFRAND, 1);
1.11      db        204:        return (s);
1.1       deraadt   205: }
                    206:
                    207: /*
1.14      kjell     208:  * Insert a newline.
1.1       deraadt   209:  */
1.2       millert   210: /* ARGSUSED */
1.3       millert   211: int
1.34      bcallah   212: enewline(int f, int n)
1.1       deraadt   213: {
1.3       millert   214:        int      s;
1.1       deraadt   215:
1.2       millert   216:        if (n < 0)
1.11      db        217:                return (FALSE);
1.3       millert   218:
1.1       deraadt   219:        while (n--) {
1.2       millert   220:                if ((s = lnewline()) != TRUE)
1.11      db        221:                        return (s);
1.1       deraadt   222:        }
1.11      db        223:        return (TRUE);
1.1       deraadt   224: }
                    225:
                    226: /*
1.3       millert   227:  * Delete blank lines around dot. What this command does depends if dot is
1.6       mickey    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
1.3       millert   232:  * ignored.
1.1       deraadt   233:  */
1.2       millert   234: /* ARGSUSED */
1.3       millert   235: int
1.10      cloder    236: deblank(int f, int n)
1.1       deraadt   237: {
1.15      deraadt   238:        struct line     *lp1, *lp2;
1.3       millert   239:        RSIZE    nld;
1.1       deraadt   240:
                    241:        lp1 = curwp->w_dotp;
1.21      kjell     242:        while (llength(lp1) == 0 && (lp2 = lback(lp1)) != curbp->b_headp)
1.1       deraadt   243:                lp1 = lp2;
                    244:        lp2 = lp1;
1.3       millert   245:        nld = (RSIZE)0;
1.21      kjell     246:        while ((lp2 = lforw(lp2)) != curbp->b_headp && llength(lp2) == 0)
1.1       deraadt   247:                ++nld;
                    248:        if (nld == 0)
                    249:                return (TRUE);
                    250:        curwp->w_dotp = lforw(lp1);
                    251:        curwp->w_doto = 0;
1.11      db        252:        return (ldelete((RSIZE)nld, KNONE));
1.1       deraadt   253: }
                    254:
                    255: /*
                    256:  * Delete any whitespace around dot, then insert a space.
                    257:  */
1.3       millert   258: int
1.10      cloder    259: justone(int f, int n)
1.2       millert   260: {
1.29      kjell     261:        undo_boundary_enable(FFRAND, 0);
1.5       art       262:        (void)delwhite(f, n);
1.29      kjell     263:        linsert(1, ' ');
                    264:        undo_boundary_enable(FFRAND, 1);
                    265:        return (TRUE);
1.1       deraadt   266: }
1.3       millert   267:
1.1       deraadt   268: /*
                    269:  * Delete any whitespace around dot.
                    270:  */
1.2       millert   271: /* ARGSUSED */
1.3       millert   272: int
1.10      cloder    273: delwhite(int f, int n)
1.1       deraadt   274: {
1.25      kjell     275:        int     col, s;
1.1       deraadt   276:
                    277:        col = curwp->w_doto;
1.3       millert   278:
1.8       vincent   279:        while (col < llength(curwp->w_dotp) &&
1.25      kjell     280:            (isspace(lgetc(curwp->w_dotp, col))))
1.1       deraadt   281:                ++col;
                    282:        do {
                    283:                if (curwp->w_doto == 0) {
                    284:                        s = FALSE;
                    285:                        break;
                    286:                }
1.2       millert   287:                if ((s = backchar(FFRAND, 1)) != TRUE)
                    288:                        break;
1.25      kjell     289:        } while (isspace(lgetc(curwp->w_dotp, curwp->w_doto)));
1.1       deraadt   290:
1.2       millert   291:        if (s == TRUE)
1.5       art       292:                (void)forwchar(FFRAND, 1);
                    293:        (void)ldelete((RSIZE)(col - curwp->w_doto), KNONE);
1.11      db        294:        return (TRUE);
1.1       deraadt   295: }
1.3       millert   296:
1.1       deraadt   297: /*
1.25      kjell     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: /*
1.3       millert   347:  * Insert a newline, then enough tabs and spaces to duplicate the indentation
1.6       mickey    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
1.24      kjell     352:  * one of the subcommands failed. Normally bound to "C-M".
1.1       deraadt   353:  */
1.2       millert   354: /* ARGSUSED */
1.3       millert   355: int
1.25      kjell     356: lfindent(int f, int n)
1.1       deraadt   357: {
1.11      db        358:        int     c, i, nicol;
1.29      kjell     359:        int     s = TRUE;
1.1       deraadt   360:
1.2       millert   361:        if (n < 0)
                    362:                return (FALSE);
1.3       millert   363:
1.29      kjell     364:        undo_boundary_enable(FFRAND, 0);
1.1       deraadt   365:        while (n--) {
                    366:                nicol = 0;
1.2       millert   367:                for (i = 0; i < llength(curwp->w_dotp); ++i) {
1.1       deraadt   368:                        c = lgetc(curwp->w_dotp, i);
1.2       millert   369:                        if (c != ' ' && c != '\t')
1.1       deraadt   370:                                break;
                    371:                        if (c == '\t')
                    372:                                nicol |= 0x07;
                    373:                        ++nicol;
                    374:                }
                    375:                if (lnewline() == FALSE || ((
                    376: #ifdef NOTAB
1.3       millert   377:                    curbp->b_flag & BFNOTAB) ? linsert(nicol, ' ') == FALSE : (
                    378: #endif /* NOTAB */
                    379:                    ((i = nicol / 8) != 0 && linsert(i, '\t') == FALSE) ||
1.29      kjell     380:                    ((i = nicol % 8) != 0 && linsert(i, ' ') == FALSE)))) {
                    381:                        s = FALSE;
                    382:                        break;
                    383:                }
1.1       deraadt   384:        }
1.29      kjell     385:        undo_boundary_enable(FFRAND, 1);
                    386:        return (s);
1.1       deraadt   387: }
1.25      kjell     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
1.31      lum       413:            (curbp->b_flag & BFNOTAB) ? linsert(n, ' ') == FALSE :
1.25      kjell     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:
1.1       deraadt   424:
                    425: /*
1.3       millert   426:  * Delete forward.  This is real easy, because the basic delete routine does
1.6       mickey    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
1.40      phessler  429:  * of text if typed with a big argument.  Normally bound to "C-D".
1.1       deraadt   430:  */
1.2       millert   431: /* ARGSUSED */
1.3       millert   432: int
1.10      cloder    433: forwdel(int f, int n)
1.1       deraadt   434: {
                    435:        if (n < 0)
1.11      db        436:                return (backdel(f | FFRAND, -n));
1.3       millert   437:
                    438:        /* really a kill */
                    439:        if (f & FFARG) {
1.2       millert   440:                if ((lastflag & CFKILL) == 0)
1.1       deraadt   441:                        kdelete();
                    442:                thisflag |= CFKILL;
                    443:        }
1.3       millert   444:
1.11      db        445:        return (ldelete((RSIZE) n, (f & FFARG) ? KFORW : KNONE));
1.1       deraadt   446: }
                    447:
                    448: /*
1.6       mickey    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
1.3       millert   451:  * delete forward, this actually does a kill if presented with an argument.
1.1       deraadt   452:  */
1.2       millert   453: /* ARGSUSED */
1.3       millert   454: int
1.10      cloder    455: backdel(int f, int n)
1.1       deraadt   456: {
1.3       millert   457:        int     s;
1.1       deraadt   458:
                    459:        if (n < 0)
1.11      db        460:                return (forwdel(f | FFRAND, -n));
1.3       millert   461:
                    462:        /* really a kill */
                    463:        if (f & FFARG) {
1.2       millert   464:                if ((lastflag & CFKILL) == 0)
1.1       deraadt   465:                        kdelete();
                    466:                thisflag |= CFKILL;
                    467:        }
1.2       millert   468:        if ((s = backchar(f | FFRAND, n)) == TRUE)
1.3       millert   469:                s = ldelete((RSIZE)n, (f & FFARG) ? KFORW : KNONE);
                    470:
1.11      db        471:        return (s);
1.1       deraadt   472: }
                    473:
                    474: #ifdef NOTAB
1.2       millert   475: /* ARGSUSED */
1.3       millert   476: int
1.10      cloder    477: space_to_tabstop(int f, int n)
1.1       deraadt   478: {
1.2       millert   479:        if (n < 0)
1.11      db        480:                return (FALSE);
1.2       millert   481:        if (n == 0)
1.11      db        482:                return (TRUE);
                    483:        return (linsert((n << 3) - (curwp->w_doto & 7), ' '));
1.1       deraadt   484: }
1.3       millert   485: #endif /* NOTAB */
1.27      kjell     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;
1.28      kjell     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:
1.27      kjell     527:        return (TRUE);
                    528: }