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

Annotation of src/usr.bin/mg/cmode.c, Revision 1.21

1.21    ! op          1: /* $OpenBSD: cmode.c,v 1.20 2022/12/26 19:16:02 jmc Exp $ */
1.1       kjell       2: /*
                      3:  * This file is in the public domain.
                      4:  *
                      5:  * Author: Kjell Wooding <kjell@openbsd.org>
                      6:  */
                      7:
                      8: /*
                      9:  * Implement an non-irritating KNF-compliant mode for editing
                     10:  * C code.
                     11:  */
1.14      bcallah    12:
                     13: #include <sys/queue.h>
1.1       kjell      14: #include <ctype.h>
1.14      bcallah    15: #include <signal.h>
                     16: #include <stdio.h>
1.1       kjell      17:
                     18: #include "def.h"
1.14      bcallah    19: #include "funmap.h"
1.1       kjell      20: #include "kbd.h"
                     21:
                     22: /* Pull in from modes.c */
                     23: extern int changemode(int, int, char *);
                     24:
                     25: static int cc_strip_trailp = TRUE;     /* Delete Trailing space? */
                     26: static int cc_basic_indent = 8;                /* Basic Indent multiple */
                     27: static int cc_cont_indent = 4;         /* Continued line indent */
                     28: static int cc_colon_indent = -8;       /* Label / case indent */
                     29:
                     30: static int getmatch(int, int);
                     31: static int getindent(const struct line *, int *);
1.2       kjell      32: static int in_whitespace(struct line *, int);
                     33: static int findcolpos(const struct buffer *, const struct line *, int);
                     34: static struct line *findnonblank(struct line *);
                     35: static int isnonblank(const struct line *, int);
                     36:
1.7       lum        37: void cmode_init(void);
1.2       kjell      38: int cc_comment(int, int);
1.1       kjell      39:
                     40: /* Keymaps */
                     41:
                     42: static PF cmode_brace[] = {
1.3       kjell      43:        cc_brace,       /* } */
1.1       kjell      44: };
                     45:
1.4       kjell      46: static PF cmode_cCP[] = {
                     47:        compile,                /* C-c P */
                     48: };
                     49:
                     50:
                     51: static PF cmode_cc[] = {
                     52:        NULL,           /* ^C */
                     53:        rescan,         /* ^D */
                     54:        rescan,         /* ^E */
                     55:        rescan,         /* ^F */
                     56:        rescan,         /* ^G */
                     57:        rescan,         /* ^H */
1.1       kjell      58:        cc_tab,         /* ^I */
                     59:        rescan,         /* ^J */
                     60:        rescan,         /* ^K */
                     61:        rescan,         /* ^L */
                     62:        cc_lfindent,    /* ^M */
                     63: };
                     64:
1.2       kjell      65: static PF cmode_spec[] = {
1.1       kjell      66:        cc_char,        /* : */
                     67: };
                     68:
1.15      bcallah    69: static struct KEYMAPE (1) cmode_cmap = {
                     70:        1,
1.4       kjell      71:        1,
                     72:        rescan,
                     73:        {
                     74:                { 'P', 'P', cmode_cCP, NULL }
                     75:        }
                     76: };
                     77:
1.15      bcallah    78: static struct KEYMAPE (3) cmodemap = {
                     79:        3,
1.1       kjell      80:        3,
                     81:        rescan,
                     82:        {
1.4       kjell      83:                { CCHR('C'), CCHR('M'), cmode_cc, (KEYMAP *) &cmode_cmap },
1.2       kjell      84:                { ':', ':', cmode_spec, NULL },
1.1       kjell      85:                { '}', '}', cmode_brace, NULL }
                     86:        }
                     87: };
                     88:
1.19      op         89: /* Function, Mode hooks */
1.1       kjell      90:
                     91: void
                     92: cmode_init(void)
                     93: {
1.17      lum        94:        funmap_add(cmode, "c-mode", 0);
                     95:        funmap_add(cc_char, "c-handle-special-char", 0);
                     96:        funmap_add(cc_brace, "c-handle-special-brace", 0);
                     97:        funmap_add(cc_tab, "c-tab-or-indent", 0);
                     98:        funmap_add(cc_indent, "c-indent", 0);
                     99:        funmap_add(cc_lfindent, "c-indent-and-newline", 0);
1.6       sobrado   100:        maps_add((KEYMAP *)&cmodemap, "c");
1.1       kjell     101: }
                    102:
                    103: /*
                    104:  * Enable/toggle c-mode
                    105:  */
                    106: int
                    107: cmode(int f, int n)
                    108: {
1.6       sobrado   109:        return(changemode(f, n, "c"));
1.1       kjell     110: }
                    111:
                    112: /*
                    113:  * Handle special C character - selfinsert then indent.
                    114:  */
                    115: int
                    116: cc_char(int f, int n)
                    117: {
                    118:        if (n < 0)
                    119:                return (FALSE);
                    120:        if (selfinsert(FFRAND, n) == FALSE)
                    121:                return (FALSE);
                    122:        return (cc_indent(FFRAND, n));
                    123: }
1.3       kjell     124:
                    125: /*
                    126:  * Handle special C character - selfinsert then indent.
                    127:  */
                    128: int
                    129: cc_brace(int f, int n)
                    130: {
                    131:        if (n < 0)
                    132:                return (FALSE);
                    133:        if (showmatch(FFRAND, 1) == FALSE)
                    134:                return (FALSE);
                    135:        return (cc_indent(FFRAND, n));
                    136: }
                    137:
1.1       kjell     138:
                    139: /*
                    140:  * If we are in the whitespace at the beginning of the line,
                    141:  * simply act as a regular tab. If we are not, indent
                    142:  * current line according to whitespace rules.
                    143:  */
                    144: int
                    145: cc_tab(int f, int n)
                    146: {
                    147:        int inwhitep = FALSE;   /* In leading whitespace? */
1.16      jasper    148:
1.2       kjell     149:        inwhitep = in_whitespace(curwp->w_dotp, llength(curwp->w_dotp));
1.1       kjell     150:
                    151:        /* If empty line, or in whitespace */
                    152:        if (llength(curwp->w_dotp) == 0 || inwhitep)
                    153:                return (selfinsert(f, n));
                    154:
                    155:        return (cc_indent(FFRAND, 1));
                    156: }
                    157:
                    158: /*
                    159:  * Attempt to indent current line according to KNF rules.
                    160:  */
                    161: int
                    162: cc_indent(int f, int n)
                    163: {
1.10      guenther  164:        int pi, mi;                     /* Previous indents (mi is ignored) */
                    165:        int ci;                         /* current indent */
1.1       kjell     166:        struct line *lp;
1.4       kjell     167:        int ret;
1.16      jasper    168:
1.1       kjell     169:        if (n < 0)
                    170:                return (FALSE);
                    171:
1.5       kjell     172:        undo_boundary_enable(FFRAND, 0);
1.1       kjell     173:        if (cc_strip_trailp)
                    174:                deltrailwhite(FFRAND, 1);
                    175:
1.2       kjell     176:        /*
                    177:         * Search backwards for a non-blank, non-preprocessor,
                    178:         * non-comment line
                    179:         */
                    180:
                    181:        lp = findnonblank(curwp->w_dotp);
1.1       kjell     182:
                    183:        pi = getindent(lp, &mi);
                    184:
                    185:        /* Strip leading space on current line */
                    186:        delleadwhite(FFRAND, 1);
                    187:        /* current indent is computed only to current position */
1.10      guenther  188:        (void)getindent(curwp->w_dotp, &ci);
1.16      jasper    189:
1.1       kjell     190:        if (pi + ci < 0)
1.4       kjell     191:                ret = indent(FFOTHARG, 0);
1.1       kjell     192:        else
1.4       kjell     193:                ret = indent(FFOTHARG, pi + ci);
1.16      jasper    194:
1.5       kjell     195:        undo_boundary_enable(FFRAND, 1);
1.16      jasper    196:
1.4       kjell     197:        return (ret);
1.1       kjell     198: }
                    199:
                    200: /*
                    201:  * Indent-and-newline (technically, newline then indent)
                    202:  */
                    203: int
                    204: cc_lfindent(int f, int n)
                    205: {
                    206:        if (n < 0)
                    207:                return (FALSE);
1.18      op        208:        if (cc_strip_trailp)
                    209:                (void)delwhite(FFRAND, 1);
1.13      bcallah   210:        if (enewline(FFRAND, 1) == FALSE)
1.1       kjell     211:                return (FALSE);
                    212:        return (cc_indent(FFRAND, n));
                    213: }
                    214:
                    215: /*
1.20      jmc       216:  * Get the level of indentation after line lp is processed
1.2       kjell     217:  * Note getindent has two returns:
1.1       kjell     218:  * curi = value if indenting current line.
                    219:  * return value = value affecting subsequent lines.
                    220:  */
                    221: static int
                    222: getindent(const struct line *lp, int *curi)
                    223: {
                    224:        int lo, co;             /* leading space,  current offset*/
                    225:        int nicol = 0;          /* position count */
                    226:        int c = '\0';           /* current char */
                    227:        int newind = 0;         /* new index value */
                    228:        int stringp = FALSE;    /* in string? */
                    229:        int escp = FALSE;       /* Escape char? */
1.20      jmc       230:        int lastc = '\0';       /* Last matched string delimiter */
1.1       kjell     231:        int nparen = 0;         /* paren count */
                    232:        int obrace = 0;         /* open brace count */
                    233:        int cbrace = 0;         /* close brace count */
1.2       kjell     234:        int firstnwsp = FALSE;  /* First nonspace encountered? */
1.1       kjell     235:        int colonp = FALSE;     /* Did we see a colon? */
                    236:        int questionp = FALSE;  /* Did we see a question mark? */
1.2       kjell     237:        int slashp = FALSE;     /* Slash? */
                    238:        int astp = FALSE;       /* Asterisk? */
                    239:        int cpos = -1;          /* comment position */
                    240:        int cppp  = FALSE;      /* Preprocessor command? */
1.16      jasper    241:
1.1       kjell     242:        *curi = 0;
                    243:
                    244:        /* Compute leading space */
                    245:        for (lo = 0; lo < llength(lp); lo++) {
                    246:                if (!isspace(c = lgetc(lp, lo)))
                    247:                        break;
1.21    ! op        248:                if (c == '\t') {
1.1       kjell     249:                        nicol |= 0x07;
                    250:                }
                    251:                nicol++;
                    252:        }
                    253:
                    254:        /* If last line was blank, choose 0 */
                    255:        if (lo == llength(lp))
                    256:                nicol = 0;
                    257:
                    258:        newind = 0;
                    259:        /* Compute modifiers */
                    260:        for (co = lo; co < llength(lp); co++) {
                    261:                c = lgetc(lp, co);
                    262:                /* We have a non-whitespace char */
1.2       kjell     263:                if (!firstnwsp && !isspace(c)) {
                    264:                        if (c == '#')
                    265:                                cppp = TRUE;
                    266:                        firstnwsp = TRUE;
1.1       kjell     267:                }
                    268:                if (c == '\\')
                    269:                        escp = !escp;
                    270:                else if (stringp) {
                    271:                        if (!escp && (c == '"' || c == '\'')) {
                    272:                                /* unescaped string char */
                    273:                                if (getmatch(c, lastc))
                    274:                                        stringp = FALSE;
                    275:                        }
                    276:                } else if (c == '"' || c == '\'') {
                    277:                        stringp = TRUE;
                    278:                        lastc = c;
                    279:                } else if (c == '(') {
                    280:                        nparen++;
                    281:                } else if (c == ')') {
                    282:                        nparen--;
                    283:                } else if (c == '{') {
                    284:                        obrace++;
1.2       kjell     285:                        firstnwsp = FALSE;
1.1       kjell     286:                } else if (c == '}') {
                    287:                        cbrace++;
                    288:                } else if (c == '?') {
                    289:                        questionp = TRUE;
                    290:                } else if (c == ':') {
                    291:                        /* ignore (foo ? bar : baz) construct */
                    292:                        if (!questionp)
                    293:                                colonp = TRUE;
1.2       kjell     294:                } else if (c == '/') {
                    295:                        /* first nonwhitespace? -> indent */
                    296:                        if (firstnwsp) {
                    297:                                /* If previous char asterisk -> close */
                    298:                                if (astp)
                    299:                                        cpos = -1;
                    300:                                else
                    301:                                        slashp = TRUE;
                    302:                        }
                    303:                } else if (c == '*') {
                    304:                        /* If previous char slash -> open */
                    305:                        if (slashp)
                    306:                                cpos = co;
                    307:                        else
                    308:                                astp = TRUE;
                    309:                } else if (firstnwsp) {
                    310:                        firstnwsp = FALSE;
1.1       kjell     311:                }
                    312:
1.2       kjell     313:                /* Reset matches that apply to next character only */
1.1       kjell     314:                if (c != '\\')
                    315:                        escp = FALSE;
1.2       kjell     316:                if (c != '*')
                    317:                        astp = FALSE;
                    318:                if (c != '/')
                    319:                        slashp = FALSE;
1.1       kjell     320:        }
                    321:        /*
                    322:         * If not terminated with a semicolon, and brace or paren open.
                    323:         * we continue
                    324:         */
                    325:        if (colonp) {
                    326:                *curi += cc_colon_indent;
                    327:                newind -= cc_colon_indent;
                    328:        }
                    329:
                    330:        *curi -= (cbrace) * cc_basic_indent;
                    331:        newind += obrace * cc_basic_indent;
                    332:
                    333:        if (nparen < 0)
                    334:                newind -= cc_cont_indent;
                    335:        else if (nparen > 0)
                    336:                newind += cc_cont_indent;
1.2       kjell     337:
1.1       kjell     338:        *curi += nicol;
                    339:
1.2       kjell     340:        /* Ignore preprocessor. Otherwise, add current column */
                    341:        if (cppp) {
                    342:                newind = nicol;
                    343:                *curi = 0;
                    344:        } else {
                    345:                newind += nicol;
                    346:        }
                    347:
                    348:        if (cpos != -1)
                    349:                newind = findcolpos(curbp, lp, cpos);
                    350:
1.1       kjell     351:        return (newind);
                    352: }
                    353:
                    354: /*
1.20      jmc       355:  * Given a delimiter and its purported mate, tell us if they
1.1       kjell     356:  * match.
                    357:  */
                    358: static int
                    359: getmatch(int c, int mc)
                    360: {
                    361:        int match = FALSE;
                    362:
                    363:        switch (c) {
                    364:        case '"':
                    365:                match = (mc == '"');
                    366:                break;
                    367:        case '\'':
                    368:                match = (mc == '\'');
                    369:                break;
                    370:        case '(':
                    371:                match = (mc == ')');
                    372:                break;
                    373:        case '[':
                    374:                match = (mc == ']');
                    375:                break;
                    376:        case '{':
                    377:                match = (mc == '}');
                    378:                break;
                    379:        }
                    380:
                    381:        return (match);
1.2       kjell     382: }
                    383:
                    384: static int
                    385: in_whitespace(struct line *lp, int len)
                    386: {
                    387:        int lo;
                    388:        int inwhitep = FALSE;
                    389:
                    390:        for (lo = 0; lo < len; lo++) {
                    391:                if (!isspace(lgetc(lp, lo)))
                    392:                        break;
                    393:                if (lo == len - 1)
                    394:                        inwhitep = TRUE;
                    395:        }
                    396:
                    397:        return (inwhitep);
                    398: }
                    399:
                    400:
                    401: /* convert a line/offset pair to a column position (for indenting) */
                    402: static int
                    403: findcolpos(const struct buffer *bp, const struct line *lp, int lo)
                    404: {
                    405:        int     col, i, c;
                    406:        char tmp[5];
                    407:
                    408:        /* determine column */
                    409:        col = 0;
                    410:
                    411:        for (i = 0; i < lo; ++i) {
                    412:                c = lgetc(lp, i);
1.21    ! op        413:                if (c == '\t') {
1.2       kjell     414:                        col |= 0x07;
                    415:                        col++;
                    416:                } else if (ISCTRL(c) != FALSE)
                    417:                        col += 2;
                    418:                else if (isprint(c)) {
                    419:                        col++;
                    420:                } else {
                    421:                        col += snprintf(tmp, sizeof(tmp), "\\%o", c);
                    422:                }
                    423:
                    424:        }
                    425:        return (col);
                    426: }
                    427:
                    428: /*
                    429:  * Find a non-blank line, searching backwards from the supplied line pointer.
                    430:  * For C, nonblank is non-preprocessor, non C++, and accounts
                    431:  * for complete C-style comments.
                    432:  */
                    433: static struct line *
                    434: findnonblank(struct line *lp)
                    435: {
                    436:        int lo;
                    437:        int nonblankp = FALSE;
                    438:        int commentp = FALSE;
                    439:        int slashp;
                    440:        int astp;
                    441:        int c;
                    442:
                    443:        while (lback(lp) != curbp->b_headp && (commentp || !nonblankp)) {
                    444:                lp = lback(lp);
                    445:                slashp = FALSE;
                    446:                astp = FALSE;
                    447:
                    448:                /* Potential nonblank? */
                    449:                nonblankp = isnonblank(lp, llength(lp));
                    450:
                    451:                /*
                    452:                 * Search from end, removing complete C-style
                    453:                 * comments. If one is found, ignore it and
                    454:                 * test for nonblankness from where it starts.
                    455:                 */
                    456:                for (lo = llength(lp) - 1; lo >= 0; lo--) {
                    457:                        if (!isspace(c = lgetc(lp, lo))) {
                    458:                                if (commentp) { /* find comment "open" */
                    459:                                        if (c == '*')
                    460:                                                astp = TRUE;
                    461:                                        else if (astp && c == '/') {
                    462:                                                commentp = FALSE;
                    463:                                                /* whitespace to here? */
                    464:                                                nonblankp = isnonblank(lp, lo);
                    465:                                        }
                    466:                                } else { /* find comment "close" */
                    467:                                        if (c == '/')
                    468:                                                slashp = TRUE;
                    469:                                        else if (slashp && c == '*')
                    470:                                                /* found a comment */
                    471:                                                commentp = TRUE;
                    472:                                }
                    473:                        }
                    474:                }
                    475:        }
                    476:
                    477:        /* Rewound to start of file? */
                    478:        if (lback(lp) == curbp->b_headp && !nonblankp)
                    479:                return (curbp->b_headp);
                    480:
                    481:        return (lp);
                    482: }
                    483:
                    484: /*
                    485:  * Given a line, scan forward to 'omax' and determine if we
                    486:  * are all C whitespace.
                    487:  * Note that preprocessor directives and C++-style comments
                    488:  * count as whitespace. C-style comments do not, and must
                    489:  * be handled elsewhere.
                    490:  */
                    491: static int
                    492: isnonblank(const struct line *lp, int omax)
                    493: {
                    494:        int nonblankp = FALSE;          /* Return value */
                    495:        int slashp = FALSE;             /* Encountered slash */
                    496:        int lo;                         /* Loop index */
                    497:        int c;                          /* char being read */
                    498:
                    499:        /* Scan from front for preprocessor, C++ comments */
                    500:        for (lo = 0; lo < omax; lo++) {
                    501:                if (!isspace(c = lgetc(lp, lo))) {
                    502:                        /* Possible nonblank line */
                    503:                        nonblankp = TRUE;
                    504:                        /* skip // and # starts */
                    505:                        if (c == '#' || (slashp && c == '/')) {
                    506:                                nonblankp = FALSE;
                    507:                                break;
                    508:                        } else if (!slashp && c == '/') {
                    509:                                slashp = TRUE;
                    510:                                continue;
                    511:                        }
                    512:                }
                    513:                slashp = FALSE;
                    514:        }
                    515:        return (nonblankp);
1.1       kjell     516: }