home *** CD-ROM | disk | FTP | other *** search
- /***************************************************************************
- * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne. JOVE *
- * is provided to you without charge, and with no warranty. You may give *
- * away copies of JOVE, including sources, provided that this notice is *
- * included in all the files. *
- ***************************************************************************/
-
- /* Contains commands for C mode. Paren matching routines are in here. */
-
- #include "jove.h"
- #include "re.h"
- #include "ctype.h"
- #include "disp.h"
-
- private int
- backslashed proto((char *, int));
- private void
- #if defined(CMT_FMT)
- FillComment proto((char *format)),
- #endif
- do_expr proto((int, int)),
- FindMatch proto((int)),
- parse_cmt_fmt proto((char *)),
- strip_c proto((char *, char *));
-
- extern void
- FSexpr(),
- FList(),
- BSexpr(),
- BList();
-
- private int
- backslashed(lp, cpos)
- register char *lp;
- register int cpos;
- {
- register int cnt = 0;
-
- while (cpos > 0 && lp[--cpos] == '\\')
- cnt += 1;
- return (cnt % 2);
- }
-
- private char *p_types = "(){}[]";
- private int mp_kind;
- #define MP_OKAY 0
- #define MP_MISMATCH 1
- #define MP_UNBALANCED 2
- #define MP_INCOMMENT 3
-
- void
- mp_error()
- {
- switch (mp_kind) {
- case MP_MISMATCH:
- message("[Mismatched parentheses]");
- break;
-
- case MP_UNBALANCED:
- message("[Unbalanced parenthesis]");
- break;
-
- case MP_INCOMMENT:
- message("[Inside a comment]");
- break;
-
- case MP_OKAY:
- default:
- return;
- }
- rbell();
- }
-
- /* Search from the current position for the paren that matches p_type.
- Search in the direction dir. If can_mismatch is YES then it is okay
- to have mismatched parens. If stop_early is YES then when an open
- paren is found at the beginning of a line, it is assumed that there
- is no point in backing up further. This is so when you hit tab or
- LineFeed outside, in-between procedure/function definitions, it won't
- sit there searching all the way to the beginning of the file for a
- match that doesn't exist. {forward,backward}-s-expression are the
- only ones that insist on getting the "true" story. */
-
- Bufpos *
- m_paren(p_type, dir, can_mismatch, can_stop)
- int p_type;
- register int dir;
- int can_mismatch;
- int can_stop;
- {
- static Bufpos ret;
- Bufpos savedot,
- *sp;
- struct RE_block re_blk;
- int count = 0;
- register char *lp,
- c;
- char p_match,
- re_str[128],
- *cp,
- quote_c = 0;
- register int c_char;
- int in_comment = -1,
- stopped = NO;
-
- swritef(re_str, "[(){}[\\]%s]", (MajorMode(CMODE)) ? "/\"'" : "\"");
- REcompile(re_str, 1, &re_blk);
- if ((cp = strchr(p_types, p_type)) != NIL)
- p_match = cp[dir];
- else
- complain("[Cannot match %c's]", p_type);
- DOTsave(&savedot);
-
- /* To make things a little faster I avoid copying lines into
- linebuf by setting curline and curchar by hand. Warning:
- this is slightly to very risky. When I did this there were
- lots of problems with procedures that expect the contents of
- curline to be in linebuf. */
- while (count >= 0) {
- sp = docompiled(dir, &re_blk);
- if (sp == 0)
- break;
- lp = lbptr(sp->p_line);
-
- curline = sp->p_line;
- curchar = sp->p_char; /* here's where I cheat */
-
- c_char = curchar;
- if (dir == FORWARD)
- c_char -= 1;
- if (backslashed(lp, c_char))
- continue;
- c = lp[c_char];
- /* check if this is a comment (if we're not inside quotes) */
- if (quote_c == 0 && c == '/') {
- int new_ic = in_comment;
-
- /* close comment */
- if ((c_char != 0) && lp[c_char - 1] == '*') {
- new_ic = (dir == FORWARD) ? NO : YES;
- if (new_ic == NO && in_comment == -1) {
- count = 0;
- quote_c = 0;
- }
- } else if (lp[c_char + 1] == '*') {
- new_ic = (dir == FORWARD) ? YES : NO;
- if (new_ic == NO && in_comment == -1) {
- count = 0;
- quote_c = 0;
- }
- }
- in_comment = new_ic;
- }
- if (in_comment == YES)
- continue;
- if (c == '"' || c == '\'') {
- if (quote_c == c)
- quote_c = 0;
- else if (quote_c == 0)
- quote_c = c;
- }
- if (quote_c != 0)
- continue;
- if (isopenp(c)) {
- count += dir;
- if (c_char == 0 && can_stop == YES && count >= 0) {
- stopped = YES;
- break;
- }
- } else if (isclosep(c))
- count -= dir;
- }
-
- ret.p_line = curline;
- ret.p_char = curchar;
-
- curline = savedot.p_line;
- curchar = savedot.p_char; /* here's where I undo it */
-
- if (count >= 0)
- mp_kind = MP_UNBALANCED;
- else if (c != p_match)
- mp_kind = MP_MISMATCH;
- else
- mp_kind = MP_OKAY;
-
- /* If we stopped (which means we were allowed to stop) and there
- was an error, we clear the error so no error message is printed.
- An error should be printed ONLY when we are sure about the fact,
- namely we didn't stop prematurely HOPING that it was the right
- answer. */
- if (stopped && mp_kind != MP_OKAY) {
- mp_kind = MP_OKAY;
- return 0;
- }
- if (mp_kind == MP_OKAY || (mp_kind == MP_MISMATCH && can_mismatch == YES))
- return &ret;
- return 0;
- }
-
- private void
- do_expr(dir, skip_words)
- register int dir;
- int skip_words;
- {
- register char c,
- syntax = (dir == FORWARD) ? _Op : _Cl;
-
- if (dir == BACKWARD)
- b_char(1);
- c = linebuf[curchar];
- for (;;) {
- if (!skip_words && ismword(c)) {
- WITH_TABLE(curbuf->b_major)
- if (dir == FORWARD)
- f_word(1);
- else
- b_word(1);
- END_TABLE();
- break;
- } else if (has_syntax(c, syntax)) {
- FindMatch(dir);
- break;
- }
- f_char(dir);
- if (eobp() || bobp())
- return;
- c = linebuf[curchar];
- }
- }
-
- void
- FSexpr()
- {
- register int num = arg_value();
-
- if (num < 0) {
- set_arg_value(-num);
- BSexpr();
- }
- while (--num >= 0)
- do_expr(FORWARD, NO);
- }
-
- void
- FList()
- {
- register int num = arg_value();
-
- if (num < 0) {
- set_arg_value(-num);
- BList();
- }
- while (--num >= 0)
- do_expr(FORWARD, YES);
- }
-
- void
- BSexpr()
- {
- register int num = arg_value();
-
- if (num < 0) {
- negate_arg_value();
- FSexpr();
- }
- while (--num >= 0)
- do_expr(BACKWARD, NO);
- }
-
- void
- BList()
- {
- register int num = arg_value();
-
- if (num < 0) {
- negate_arg_value();
- FList();
- }
- while (--num >= 0)
- do_expr(BACKWARD, YES);
- }
-
- void
- BUpList()
- {
- Bufpos *mp;
- char c = (MajorMode(CMODE) ? '}' : ')');
-
- mp = m_paren(c, BACKWARD, NO, YES);
- if (mp == 0)
- mp_error();
- else
- SetDot(mp);
- }
-
- void
- FDownList()
- {
- Bufpos *sp;
- char *sstr = (MajorMode(CMODE) ? "[{([\\])}]" : "[()]"),
- *lp;
-
- sp = dosearch(sstr, FORWARD, YES);
- if (sp != 0)
- lp = lcontents(sp->p_line);
- if (sp == 0 || has_syntax(lp[sp->p_char - 1], _Cl))
- complain("[No contained expression]");
- SetDot(sp);
- }
-
- /* Move to the matching brace or paren depending on the current position
- in the buffer. */
-
- private void
- FindMatch(dir)
- int dir;
- {
- register Bufpos *bp;
- register char c = linebuf[curchar];
-
- if ((strchr(p_types, c) == 0) ||
- (backslashed(linebuf, curchar)))
- complain((char *) 0);
- if (dir == FORWARD)
- f_char(1);
- bp = m_paren(c, dir, YES, NO);
- if (dir == FORWARD)
- b_char(1);
- if (bp != 0)
- SetDot(bp);
- mp_error(); /* if there is an error the user wants to
- know about it */
- }
-
- #define ALIGN_ARGS (-1)
-
- /* If CArgIndent == ALIGN_ARGS then the indentation routine will
- indent a continued line by lining it up with the first argument.
- Otherwise, it will indent CArgIndent characters past the indent
- of the first line of the procedure call. */
-
- int CArgIndent = ALIGN_ARGS;
-
- /* indent for C code */
- Bufpos *
- c_indent(brace)
- int brace;
- {
- Bufpos *bp;
- int new_indent = 0,
- current_indent,
- increment;
-
- if (brace == NO)
- increment = CIndIncrmt;
- else
- increment = 0;
- /* Find matching paren, which may be a mismatch now. If it
- is not a matching curly brace then it is a paren (most likely).
- In that case we try to line up the arguments to a procedure
- or inside an of statement. */
- if ((bp = m_paren('}', BACKWARD, YES, YES)) != NIL) {
- Bufpos save;
- int matching_indent;
-
- DOTsave(&save);
- SetDot(bp); /* go to matching paren */
- ToIndent();
- matching_indent = calc_pos(linebuf, curchar);
- SetDot(bp);
- switch (linebuf[curchar]) {
- case '{':
- new_indent = matching_indent;
- if (!bolp()) {
- b_char(1);
- /* If we're not within the indent then we
- can assume that there is either a C keyword
- line DO on the line before the brace, or
- there is a parenthesized expression. If
- that's the case we want to go backward
- over that to the beginning of the expression
- so that we can get the correct indent for
- this matching brace. This handles wrapped
- if statements, etc. */
- if (!within_indent()) {
- Bufpos savematch;
-
- savematch = *bp;
-
- do_expr(BACKWARD, NO);
- ToIndent();
- new_indent = calc_pos(linebuf, curchar);
-
- /* do_expr() calls b_paren, which
- returns a pointer to a structure,
- and that pointer is in BP so we
- have to save away the matching
- paren and restore it in the
- following line ... sigh */
- *bp = savematch;
- }
- }
- if (brace == NO)
- new_indent += (increment - (new_indent % increment));
- break;
-
- case '(':
- if (CArgIndent == ALIGN_ARGS) {
- f_char(1);
- new_indent = calc_pos(linebuf, curchar);
- } else
- new_indent = matching_indent + CArgIndent;
- break;
- }
- SetDot(&save);
- }
-
- /* new_indent is the "correct" place to indent. Now we check to
- see if what we consider as the correct place to indent is to
- the LEFT of where we already are. If it is, and we are NOT
- handling a brace, then we assume that the person wants to tab
- in further than what we think is right (for some reason) and
- so we allow that. */
-
- ToIndent();
- current_indent = calc_pos(linebuf, curchar);
- if (brace == NO && new_indent <= current_indent)
- new_indent = current_indent + (increment - (current_indent % increment));
- Bol();
- DelWtSpace(); /* nice uniform Tabs*Space* */
- n_indent(new_indent);
-
- return bp;
- }
-
- static void
- re_indent(incr)
- int incr;
- {
- Line *l1, *l2, *lp;
- int c1, c2;
- Mark *m = CurMark();
- Bufpos savedot;
-
- DOTsave(&savedot);
- l1 = curline;
- c1 = curchar;
- l2 = m->m_line;
- c2 = m->m_char;
- (void) fixorder(&l1, &c1, &l2, &c2);
- for (lp = l1; lp != l2->l_next; lp = lp->l_next) {
- int indent;
-
- SetLine(lp);
- ToIndent();
- indent = calc_pos(linebuf, curchar);
- if (indent != 0 || linebuf[0] != '\0')
- n_indent(indent + incr);
- }
- SetDot(&savedot);
- }
-
- void
- LRShift()
- {
- int amnt;
-
- if (is_an_arg())
- amnt = arg_value();
- else
- amnt = CIndIncrmt;
- re_indent(-amnt);
- }
-
- void
- RRShift()
- {
- int amnt;
-
- if (is_an_arg())
- amnt = arg_value();
- else
- amnt = CIndIncrmt;
- re_indent(amnt);
- }
-
- #if defined(CMT_FMT)
-
- char CmtFmt[80] = "/*%n%! * %c%!%n */";
-
- void
- Comment()
- {
- FillComment(CmtFmt);
- }
-
- /* Strip leading and trailing white space. Skip over any imbedded '\r's. */
-
- private void
- strip_c(from, to)
- char *from,
- *to;
- {
- register char *fr_p = from,
- *to_p = to,
- c;
-
- while ((c = *fr_p) != '\0') {
- if (c == ' ' || c == '\t' || c == '\r')
- fr_p += 1;
- else
- break;
- }
- while ((c = *fr_p) != '\0') {
- if (c != '\r')
- *to_p++ = c;
- fr_p += 1;
- }
- while (--to_p >= to)
- if (*to_p != ' ' && *to_p != '\t')
- break;
- *++to_p = '\0';
- }
-
- private char open_c[20], /* the open comment format string */
- open_pat[20], /* the search pattern for open comment */
- l_header[20], /* the prefix for each comment line */
- l_trailer[20], /* the suffix ... */
- close_c[20],
- close_pat[20];
-
- private char *const comment_body[] = {
- open_c,
- l_header,
- l_trailer,
- close_c
- };
-
- private int nlflags;
-
- /* Fill in the data structures above from the format string. Don't return
- if there's trouble. */
-
- private void
- parse_cmt_fmt(str)
- char *str;
- {
- register char *fmtp = str;
- register char *const *c_body = comment_body,
- *body_p = *c_body;
- int c,
- newlines = 1;
-
- /* pick apart the comment string */
- while ((c = *fmtp++) != '\0') {
- if (c != '%') {
- *body_p++ = c;
- continue;
- }
- switch(c = *fmtp++) {
- case 'n':
- if (newlines == 2 || newlines == 3)
- complain("%n not allowed in line header or trailer: %s",
- fmtp - 2);
- nlflags += newlines;
- *body_p++ = '\r';
- break;
- case 't':
- *body_p++ = '\t';
- break;
- case '%':
- *body_p++ = '%';
- break;
- case '!':
- case 'c':
- newlines += 1;
- *body_p++ = '\0';
- body_p = *++c_body;
- break;
- default:
- complain("[Unknown comment escape: %%%c]", c);
- break;
- }
- }
- *body_p = '\0';
- /* make search patterns */
- strip_c(open_c, open_pat);
- strip_c(close_c, close_pat);
- }
-
- #define NL_IN_OPEN_C ((nlflags % 4) == 1)
- #define NL_IN_CLOSE_C (nlflags >= 4)
-
- private void
- FillComment(format)
- char *format;
- {
- int saveRMargin,
- indent_pos,
- close_at_dot = NO;
- size_t header_len,
- trailer_len;
- register char *cp;
- static char inside_err[] = "[Must be between %s and %s to re-format]";
- Bufpos open_c_pt,
- close_c_pt,
- tmp_bp,
- *match_o,
- *match_c;
- Mark *entry_mark,
- *open_c_mark,
- *savedot;
-
- parse_cmt_fmt(format);
- /* figure out if we're "inside" a comment */
- if ((match_o = dosearch(open_pat, BACKWARD, 0)) == 0)
- complain("No opening %s to match to.", open_pat);
- open_c_pt = *match_o;
- if ((match_c = dosearch(close_pat, BACKWARD, NO)) != 0 &&
- inorder(open_c_pt.p_line, open_c_pt.p_char,
- match_c->p_line, match_c->p_char))
- complain(inside_err, open_pat, close_pat);
- if ((match_o = dosearch(open_pat, FORWARD, NO)) != 0) {
- tmp_bp = *match_o;
- match_o = &tmp_bp;
- }
- if ((match_c = dosearch(close_pat, FORWARD, 0)) != (Bufpos *) 0)
- close_c_pt = *match_c;
-
- /* Here's where we figure out whether to format from dot or from
- the close comment. Note that we've already searched backwards to
- find the open comment symbol for the comment we are formatting.
- The open symbol mentioned below refers to the possible existence
- of the next comment. There are 5 cases:
- 1) no open or close symbol ==> dot
- 2) open, but no close symbol ==> dot
- 3) close, but no open ==> close
- 4) open, close are inorder ==> dot
- 5) open, close are not inorder ==> close */
-
-
- if (match_o == (Bufpos *) 0) {
- if (match_c == (Bufpos *) 0)
- close_at_dot = YES;
- } else if (match_c == (Bufpos *) 0)
- close_at_dot = YES;
- else if (inorder(match_o->p_line, match_o->p_char,
- match_c->p_line, match_c->p_char))
- close_at_dot = YES;
- if (close_at_dot) {
- close_c_pt.p_line = curline;
- close_c_pt.p_char = curchar;
- } else {
- SetDot(match_c);
- }
- SetDot(&open_c_pt);
- open_c_mark = MakeMark(curline, curchar, M_FLOATER);
- indent_pos = calc_pos(linebuf, curchar);
- /* search for a close comment; delete it if it exits */
- SetDot(&close_c_pt);
- if (close_at_dot == 0)
- del_char(BACKWARD, (int)strlen(close_pat), NO);
- entry_mark = MakeMark(curline, curchar, M_FLOATER);
- ToMark(open_c_mark);
- /* always separate the comment body from anything preceeding it */
- LineInsert(1);
- DelWtSpace();
- Bol();
- for (cp = open_c; *cp; cp++) {
- if (*cp == '\r') {
- if (!eolp())
- LineInsert(1);
- else
- line_move(FORWARD, 1, NO);
- } else if (*cp == ' ' || *cp == '\t') {
- if (linebuf[curchar] != *cp)
- insert_c(*cp, 1);
- } else
- /* Since we matched the open comment string on this
- line, we don't need to worry about crossing line
- boundaries. */
- curchar += 1;
- }
- savedot = MakeMark(curline, curchar, M_FLOATER);
-
- /* We need to strip the line header pattern of leading white space
- since we need to match the line after all of its leading
- whitespace is gone. */
- for (cp = l_header; *cp && (isspace(*cp)); cp++)
- ;
- header_len = strlen(cp);
- trailer_len = strlen(l_trailer);
-
- /* Strip each comment line of the open and close comment strings
- before reformatting it. */
-
- do {
- Bol();
- DelWtSpace();
- if (header_len && strncmp(linebuf, cp, header_len)==0)
- del_char(FORWARD, (int)header_len, NO);
- if (trailer_len) {
- Eol();
- if (((size_t)curchar > trailer_len) &&
- (strncmp(&linebuf[curchar - trailer_len],
- l_trailer, trailer_len)==0))
- del_char(BACKWARD, (int)trailer_len, NO);
- }
- if (curline->l_next != 0)
- line_move(FORWARD, 1, NO);
- else
- break;
- } while (curline != entry_mark->m_line->l_next);
-
- do_set_mark(savedot->m_line, savedot->m_char);
- ToMark(entry_mark);
- saveRMargin = RMargin;
- RMargin = saveRMargin - strlen(l_header) -
- strlen(l_trailer) - indent_pos + 2;
- do_rfill(NO);
- RMargin = saveRMargin;
- /* get back to the start of the comment */
- PopMark();
- do {
- if (curline == open_c_mark->m_line->l_next) {
- ;
- } else {
- Bol();
- n_indent(indent_pos);
- ins_str(l_header, NO);
- }
- Eol();
- if (!NL_IN_CLOSE_C && (curline == entry_mark->m_line))
- ;
- else
- ins_str(l_trailer, NO);
- if (curline->l_next != 0)
- line_move(FORWARD, 1, NO);
- else
- break;
- } while (curline != entry_mark->m_line->l_next);
- /* handle the close comment symbol */
- if (curline == entry_mark->m_line->l_next) {
- line_move(BACKWARD, 1, NO);
- Eol();
- }
- DelWtSpace();
- /* if the addition of the close symbol would cause the line to be
- too long, put the close symbol on the next line. */
- if (!(NL_IN_CLOSE_C) &&
- (int)strlen(close_c) + calc_pos(linebuf, curchar) > RMargin) {
- LineInsert(1);
- n_indent(indent_pos);
- }
- for (cp = close_c; *cp; cp++) {
- if (*cp == '\r') {
- LineInsert(1);
- n_indent(indent_pos);
- } else
- insert_c(*cp, 1);
- }
- ToMark(open_c_mark);
- Eol();
- del_char(FORWARD, 1, NO);
- }
-
- #endif /* CMT_FMT */
-
-