+++ /dev/null
-/* $OpenBSD: cmode.c,v 1.8 2012/05/18 02:13:44 lum Exp $ */
-/*
- * This file is in the public domain.
- *
- * Author: Kjell Wooding <kjell@openbsd.org>
- */
-
-/*
- * Implement an non-irritating KNF-compliant mode for editing
- * C code.
- */
-#include <ctype.h>
-
-#include "def.h"
-#include "kbd.h"
-#include "funmap.h"
-
-/* Pull in from modes.c */
-extern int changemode(int, int, char *);
-
-static int cc_strip_trailp = TRUE; /* Delete Trailing space? */
-static int cc_basic_indent = 8; /* Basic Indent multiple */
-static int cc_cont_indent = 4; /* Continued line indent */
-static int cc_colon_indent = -8; /* Label / case indent */
-
-static int getmatch(int, int);
-static int getindent(const struct line *, int *);
-static int in_whitespace(struct line *, int);
-static int findcolpos(const struct buffer *, const struct line *, int);
-static struct line *findnonblank(struct line *);
-static int isnonblank(const struct line *, int);
-
-void cmode_init(void);
-int cc_comment(int, int);
-
-/* Keymaps */
-
-static PF cmode_brace[] = {
- cc_brace, /* } */
-};
-
-static PF cmode_cCP[] = {
- compile, /* C-c P */
-};
-
-
-static PF cmode_cc[] = {
- NULL, /* ^C */
- rescan, /* ^D */
- rescan, /* ^E */
- rescan, /* ^F */
- rescan, /* ^G */
- rescan, /* ^H */
- cc_tab, /* ^I */
- rescan, /* ^J */
- rescan, /* ^K */
- rescan, /* ^L */
- cc_lfindent, /* ^M */
-};
-
-static PF cmode_spec[] = {
- cc_char, /* : */
-};
-
-static struct KEYMAPE (1 + IMAPEXT) cmode_cmap = {
- 1,
- 1 + IMAPEXT,
- rescan,
- {
- { 'P', 'P', cmode_cCP, NULL }
- }
-};
-
-static struct KEYMAPE (3 + IMAPEXT) cmodemap = {
- 3,
- 3 + IMAPEXT,
- rescan,
- {
- { CCHR('C'), CCHR('M'), cmode_cc, (KEYMAP *) &cmode_cmap },
- { ':', ':', cmode_spec, NULL },
- { '}', '}', cmode_brace, NULL }
- }
-};
-
-/* Funtion, Mode hooks */
-
-void
-cmode_init(void)
-{
- funmap_add(cmode, "c-mode");
- funmap_add(cc_char, "c-handle-special-char");
- funmap_add(cc_brace, "c-handle-special-brace");
- funmap_add(cc_tab, "c-tab-or-indent");
- funmap_add(cc_indent, "c-indent");
- funmap_add(cc_lfindent, "c-indent-and-newline");
- maps_add((KEYMAP *)&cmodemap, "c");
-}
-
-/*
- * Enable/toggle c-mode
- */
-int
-cmode(int f, int n)
-{
- return(changemode(f, n, "c"));
-}
-
-/*
- * Handle special C character - selfinsert then indent.
- */
-int
-cc_char(int f, int n)
-{
- if (n < 0)
- return (FALSE);
- if (selfinsert(FFRAND, n) == FALSE)
- return (FALSE);
- return (cc_indent(FFRAND, n));
-}
-
-/*
- * Handle special C character - selfinsert then indent.
- */
-int
-cc_brace(int f, int n)
-{
- if (n < 0)
- return (FALSE);
- if (showmatch(FFRAND, 1) == FALSE)
- return (FALSE);
- return (cc_indent(FFRAND, n));
-}
-
-
-/*
- * If we are in the whitespace at the beginning of the line,
- * simply act as a regular tab. If we are not, indent
- * current line according to whitespace rules.
- */
-int
-cc_tab(int f, int n)
-{
- int inwhitep = FALSE; /* In leading whitespace? */
-
- inwhitep = in_whitespace(curwp->w_dotp, llength(curwp->w_dotp));
-
- /* If empty line, or in whitespace */
- if (llength(curwp->w_dotp) == 0 || inwhitep)
- return (selfinsert(f, n));
-
- return (cc_indent(FFRAND, 1));
-}
-
-/*
- * Attempt to indent current line according to KNF rules.
- */
-int
-cc_indent(int f, int n)
-{
- int pi, mi; /* Previous indents */
- int ci, dci; /* current indent, don't care */
- struct line *lp;
- int ret;
-
- if (n < 0)
- return (FALSE);
-
- undo_boundary_enable(FFRAND, 0);
- if (cc_strip_trailp)
- deltrailwhite(FFRAND, 1);
-
- /*
- * Search backwards for a non-blank, non-preprocessor,
- * non-comment line
- */
-
- lp = findnonblank(curwp->w_dotp);
-
- pi = getindent(lp, &mi);
-
- /* Strip leading space on current line */
- delleadwhite(FFRAND, 1);
- /* current indent is computed only to current position */
- dci = getindent(curwp->w_dotp, &ci);
-
- if (pi + ci < 0)
- ret = indent(FFOTHARG, 0);
- else
- ret = indent(FFOTHARG, pi + ci);
-
- undo_boundary_enable(FFRAND, 1);
-
- return (ret);
-}
-
-/*
- * Indent-and-newline (technically, newline then indent)
- */
-int
-cc_lfindent(int f, int n)
-{
- if (n < 0)
- return (FALSE);
- if (newline(FFRAND, 1) == FALSE)
- return (FALSE);
- return (cc_indent(FFRAND, n));
-}
-
-/*
- * Get the level of indention after line lp is processed
- * Note getindent has two returns:
- * curi = value if indenting current line.
- * return value = value affecting subsequent lines.
- */
-static int
-getindent(const struct line *lp, int *curi)
-{
- int lo, co; /* leading space, current offset*/
- int nicol = 0; /* position count */
- int ccol = 0; /* current column */
- int c = '\0'; /* current char */
- int newind = 0; /* new index value */
- int stringp = FALSE; /* in string? */
- int escp = FALSE; /* Escape char? */
- int lastc = '\0'; /* Last matched string delimeter */
- int nparen = 0; /* paren count */
- int obrace = 0; /* open brace count */
- int cbrace = 0; /* close brace count */
- int contp = FALSE; /* Continue? */
- int firstnwsp = FALSE; /* First nonspace encountered? */
- int colonp = FALSE; /* Did we see a colon? */
- int questionp = FALSE; /* Did we see a question mark? */
- int slashp = FALSE; /* Slash? */
- int astp = FALSE; /* Asterisk? */
- int cpos = -1; /* comment position */
- int cppp = FALSE; /* Preprocessor command? */
-
- *curi = 0;
-
- /* Compute leading space */
- for (lo = 0; lo < llength(lp); lo++) {
- if (!isspace(c = lgetc(lp, lo)))
- break;
- if (c == '\t'
-#ifdef NOTAB
- && !(curbp->b_flag & BFNOTAB)
-#endif /* NOTAB */
- ) {
- nicol |= 0x07;
- }
- nicol++;
- }
-
- /* If last line was blank, choose 0 */
- if (lo == llength(lp))
- nicol = 0;
-
- newind = 0;
- ccol = nicol; /* current column */
- /* Compute modifiers */
- for (co = lo; co < llength(lp); co++) {
- c = lgetc(lp, co);
- /* We have a non-whitespace char */
- if (!firstnwsp && !isspace(c)) {
- contp = TRUE;
- if (c == '#')
- cppp = TRUE;
- firstnwsp = TRUE;
- }
- if (c == '\\')
- escp = !escp;
- else if (stringp) {
- if (!escp && (c == '"' || c == '\'')) {
- /* unescaped string char */
- if (getmatch(c, lastc))
- stringp = FALSE;
- }
- } else if (c == '"' || c == '\'') {
- stringp = TRUE;
- lastc = c;
- } else if (c == '(') {
- nparen++;
- } else if (c == ')') {
- nparen--;
- } else if (c == '{') {
- obrace++;
- firstnwsp = FALSE;
- contp = FALSE;
- } else if (c == '}') {
- cbrace++;
- } else if (c == '?') {
- questionp = TRUE;
- } else if (c == ':') {
- /* ignore (foo ? bar : baz) construct */
- if (!questionp)
- colonp = TRUE;
- } else if (c == ';') {
- if (nparen > 0)
- contp = FALSE;
- } else if (c == '/') {
- /* first nonwhitespace? -> indent */
- if (firstnwsp) {
- /* If previous char asterisk -> close */
- if (astp)
- cpos = -1;
- else
- slashp = TRUE;
- }
- } else if (c == '*') {
- /* If previous char slash -> open */
- if (slashp)
- cpos = co;
- else
- astp = TRUE;
- } else if (firstnwsp) {
- firstnwsp = FALSE;
- }
-
- /* Reset matches that apply to next character only */
- if (c != '\\')
- escp = FALSE;
- if (c != '*')
- astp = FALSE;
- if (c != '/')
- slashp = FALSE;
- }
- /*
- * If not terminated with a semicolon, and brace or paren open.
- * we continue
- */
- if (colonp) {
- *curi += cc_colon_indent;
- newind -= cc_colon_indent;
- }
-
- *curi -= (cbrace) * cc_basic_indent;
- newind += obrace * cc_basic_indent;
-
- if (nparen < 0)
- newind -= cc_cont_indent;
- else if (nparen > 0)
- newind += cc_cont_indent;
-
- *curi += nicol;
-
- /* Ignore preprocessor. Otherwise, add current column */
- if (cppp) {
- newind = nicol;
- *curi = 0;
- } else {
- newind += nicol;
- }
-
- if (cpos != -1)
- newind = findcolpos(curbp, lp, cpos);
-
- return (newind);
-}
-
-/*
- * Given a delimeter and its purported mate, tell us if they
- * match.
- */
-static int
-getmatch(int c, int mc)
-{
- int match = FALSE;
-
- switch (c) {
- case '"':
- match = (mc == '"');
- break;
- case '\'':
- match = (mc == '\'');
- break;
- case '(':
- match = (mc == ')');
- break;
- case '[':
- match = (mc == ']');
- break;
- case '{':
- match = (mc == '}');
- break;
- }
-
- return (match);
-}
-
-static int
-in_whitespace(struct line *lp, int len)
-{
- int lo;
- int inwhitep = FALSE;
-
- for (lo = 0; lo < len; lo++) {
- if (!isspace(lgetc(lp, lo)))
- break;
- if (lo == len - 1)
- inwhitep = TRUE;
- }
-
- return (inwhitep);
-}
-
-
-/* convert a line/offset pair to a column position (for indenting) */
-static int
-findcolpos(const struct buffer *bp, const struct line *lp, int lo)
-{
- int col, i, c;
- char tmp[5];
-
- /* determine column */
- col = 0;
-
- for (i = 0; i < lo; ++i) {
- c = lgetc(lp, i);
- if (c == '\t'
-#ifdef NOTAB
- && !(bp->b_flag & BFNOTAB)
-#endif /* NOTAB */
- ) {
- col |= 0x07;
- col++;
- } else if (ISCTRL(c) != FALSE)
- col += 2;
- else if (isprint(c)) {
- col++;
- } else {
- col += snprintf(tmp, sizeof(tmp), "\\%o", c);
- }
-
- }
- return (col);
-}
-
-/*
- * Find a non-blank line, searching backwards from the supplied line pointer.
- * For C, nonblank is non-preprocessor, non C++, and accounts
- * for complete C-style comments.
- */
-static struct line *
-findnonblank(struct line *lp)
-{
- int lo;
- int nonblankp = FALSE;
- int commentp = FALSE;
- int slashp;
- int astp;
- int c;
-
- while (lback(lp) != curbp->b_headp && (commentp || !nonblankp)) {
- lp = lback(lp);
- slashp = FALSE;
- astp = FALSE;
-
- /* Potential nonblank? */
- nonblankp = isnonblank(lp, llength(lp));
-
- /*
- * Search from end, removing complete C-style
- * comments. If one is found, ignore it and
- * test for nonblankness from where it starts.
- */
- slashp = FALSE;
- /* Scan backwards from end to find C-style comment */
- for (lo = llength(lp) - 1; lo >= 0; lo--) {
- if (!isspace(c = lgetc(lp, lo))) {
- if (commentp) { /* find comment "open" */
- if (c == '*')
- astp = TRUE;
- else if (astp && c == '/') {
- commentp = FALSE;
- /* whitespace to here? */
- nonblankp = isnonblank(lp, lo);
- }
- } else { /* find comment "close" */
- if (c == '/')
- slashp = TRUE;
- else if (slashp && c == '*')
- /* found a comment */
- commentp = TRUE;
- }
- }
- }
- }
-
- /* Rewound to start of file? */
- if (lback(lp) == curbp->b_headp && !nonblankp)
- return (curbp->b_headp);
-
- return (lp);
-}
-
-/*
- * Given a line, scan forward to 'omax' and determine if we
- * are all C whitespace.
- * Note that preprocessor directives and C++-style comments
- * count as whitespace. C-style comments do not, and must
- * be handled elsewhere.
- */
-static int
-isnonblank(const struct line *lp, int omax)
-{
- int nonblankp = FALSE; /* Return value */
- int slashp = FALSE; /* Encountered slash */
- int lo; /* Loop index */
- int c; /* char being read */
-
- /* Scan from front for preprocessor, C++ comments */
- for (lo = 0; lo < omax; lo++) {
- if (!isspace(c = lgetc(lp, lo))) {
- /* Possible nonblank line */
- nonblankp = TRUE;
- /* skip // and # starts */
- if (c == '#' || (slashp && c == '/')) {
- nonblankp = FALSE;
- break;
- } else if (!slashp && c == '/') {
- slashp = TRUE;
- continue;
- }
- }
- slashp = FALSE;
- }
- return (nonblankp);
-}