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

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