#include <sys/queue.h>
#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include "def.h"
#include "funmap.h"
#include "kbd.h"
extern int changemode(int, int, char *);
static int cc_strip_trailp = TRUE;
static int cc_basic_indent = 8;
static int cc_cont_indent = 4;
static int cc_colon_indent = -8;
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);
static PF cmode_brace[] = {
cc_brace,
};
static PF cmode_cCP[] = {
compile,
};
static PF cmode_cc[] = {
NULL,
rescan,
rescan,
rescan,
rescan,
rescan,
cc_tab,
rescan,
rescan,
rescan,
cc_lfindent,
};
static PF cmode_spec[] = {
cc_char,
};
static struct KEYMAPE (1) cmode_cmap = {
1,
1,
rescan,
{
{ 'P', 'P', cmode_cCP, NULL }
}
};
static struct KEYMAPE (3) cmodemap = {
3,
3,
rescan,
{
{ CCHR('C'), CCHR('M'), cmode_cc, (KEYMAP *) &cmode_cmap },
{ ':', ':', cmode_spec, NULL },
{ '}', '}', cmode_brace, NULL }
}
};
void
cmode_init(void)
{
funmap_add(cmode, "c-mode", 0);
funmap_add(cc_char, "c-handle-special-char", 0);
funmap_add(cc_brace, "c-handle-special-brace", 0);
funmap_add(cc_tab, "c-tab-or-indent", 0);
funmap_add(cc_indent, "c-indent", 0);
funmap_add(cc_lfindent, "c-indent-and-newline", 0);
maps_add((KEYMAP *)&cmodemap, "c");
}
int
cmode(int f, int n)
{
return(changemode(f, n, "c"));
}
int
cc_char(int f, int n)
{
if (n < 0)
return (FALSE);
if (selfinsert(FFRAND, n) == FALSE)
return (FALSE);
return (cc_indent(FFRAND, n));
}
int
cc_brace(int f, int n)
{
if (n < 0)
return (FALSE);
if (showmatch(FFRAND, 1) == FALSE)
return (FALSE);
return (cc_indent(FFRAND, n));
}
int
cc_tab(int f, int n)
{
int inwhitep = FALSE;
inwhitep = in_whitespace(curwp->w_dotp, llength(curwp->w_dotp));
if (llength(curwp->w_dotp) == 0 || inwhitep)
return (selfinsert(f, n));
return (cc_indent(FFRAND, 1));
}
int
cc_indent(int f, int n)
{
int pi, mi;
int ci;
struct line *lp;
int ret;
if (n < 0)
return (FALSE);
undo_boundary_enable(FFRAND, 0);
if (cc_strip_trailp)
deltrailwhite(FFRAND, 1);
lp = findnonblank(curwp->w_dotp);
pi = getindent(lp, &mi);
delleadwhite(FFRAND, 1);
(void)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);
}
int
cc_lfindent(int f, int n)
{
if (n < 0)
return (FALSE);
if (cc_strip_trailp)
(void)delwhite(FFRAND, 1);
if (enewline(FFRAND, 1) == FALSE)
return (FALSE);
return (cc_indent(FFRAND, n));
}
static int
getindent(const struct line *lp, int *curi)
{
int lo, co;
int nicol = 0;
int c = '\0';
int newind = 0;
int stringp = FALSE;
int escp = FALSE;
int lastc = '\0';
int nparen = 0;
int obrace = 0;
int cbrace = 0;
int firstnwsp = FALSE;
int colonp = FALSE;
int questionp = FALSE;
int slashp = FALSE;
int astp = FALSE;
int cpos = -1;
int cppp = FALSE;
*curi = 0;
for (lo = 0; lo < llength(lp); lo++) {
if (!isspace(c = lgetc(lp, lo)))
break;
if (c == '\t')
nicol = ntabstop(nicol, curbp->b_tabw);
else
nicol++;
}
if (lo == llength(lp))
nicol = 0;
newind = 0;
for (co = lo; co < llength(lp); co++) {
c = lgetc(lp, co);
if (!firstnwsp && !isspace(c)) {
if (c == '#')
cppp = TRUE;
firstnwsp = TRUE;
}
if (c == '\\')
escp = !escp;
else if (stringp) {
if (!escp && (c == '"' || c == '\'')) {
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;
} else if (c == '}') {
cbrace++;
} else if (c == '?') {
questionp = TRUE;
} else if (c == ':') {
if (!questionp)
colonp = TRUE;
} else if (c == '/') {
if (firstnwsp) {
if (astp)
cpos = -1;
else
slashp = TRUE;
}
} else if (c == '*') {
if (slashp)
cpos = co;
else
astp = TRUE;
} else if (firstnwsp) {
firstnwsp = FALSE;
}
if (c != '\\')
escp = FALSE;
if (c != '*')
astp = FALSE;
if (c != '/')
slashp = FALSE;
}
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;
if (cppp) {
newind = nicol;
*curi = 0;
} else {
newind += nicol;
}
if (cpos != -1)
newind = findcolpos(curbp, lp, cpos);
return (newind);
}
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);
}
static int
findcolpos(const struct buffer *bp, const struct line *lp, int lo)
{
int col, i, c;
char tmp[5];
col = 0;
for (i = 0; i < lo; ++i) {
c = lgetc(lp, i);
if (c == '\t') {
col = ntabstop(col, curbp->b_tabw);
} else if (ISCTRL(c) != FALSE)
col += 2;
else if (isprint(c)) {
col++;
} else {
col += snprintf(tmp, sizeof(tmp), "\\%o", c);
}
}
return (col);
}
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;
nonblankp = isnonblank(lp, llength(lp));
for (lo = llength(lp) - 1; lo >= 0; lo--) {
if (!isspace(c = lgetc(lp, lo))) {
if (commentp) {
if (c == '*')
astp = TRUE;
else if (astp && c == '/') {
commentp = FALSE;
nonblankp = isnonblank(lp, lo);
}
} else {
if (c == '/')
slashp = TRUE;
else if (slashp && c == '*')
commentp = TRUE;
}
}
}
}
if (lback(lp) == curbp->b_headp && !nonblankp)
return (curbp->b_headp);
return (lp);
}
static int
isnonblank(const struct line *lp, int omax)
{
int nonblankp = FALSE;
int slashp = FALSE;
int lo;
int c;
for (lo = 0; lo < omax; lo++) {
if (!isspace(c = lgetc(lp, lo))) {
nonblankp = TRUE;
if (c == '#' || (slashp && c == '/')) {
nonblankp = FALSE;
break;
} else if (!slashp && c == '/') {
slashp = TRUE;
continue;
}
}
slashp = FALSE;
}
return (nonblankp);
}