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

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