|
|
1.1 root 1: /***************************************************************************
2: * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne. JOVE *
3: * is provided to you without charge, and with no warranty. You may give *
4: * away copies of JOVE, including sources, provided that this notice is *
5: * included in all the files. *
6: ***************************************************************************/
7:
8: /* Contains commands for C mode. Paren matching routines are in here. */
9:
10: #include "jove.h"
11: #include "re.h"
12: #include "ctype.h"
13: #include "disp.h"
14:
15: private int
16: backslashed proto((char *, int));
17: private void
18: #if defined(CMT_FMT)
19: FillComment proto((char *format)),
20: #endif
21: do_expr proto((int, int)),
22: FindMatch proto((int)),
23: parse_cmt_fmt proto((char *)),
24: strip_c proto((char *, char *));
25:
26: extern void
27: FSexpr(),
28: FList(),
29: BSexpr(),
30: BList();
31:
32: private int
33: backslashed(lp, cpos)
34: register char *lp;
35: register int cpos;
36: {
37: register int cnt = 0;
38:
39: while (cpos > 0 && lp[--cpos] == '\\')
40: cnt += 1;
41: return (cnt % 2);
42: }
43:
44: private char *p_types = "(){}[]";
45: private int mp_kind;
46: #define MP_OKAY 0
47: #define MP_MISMATCH 1
48: #define MP_UNBALANCED 2
49: #define MP_INCOMMENT 3
50:
51: void
52: mp_error()
53: {
54: switch (mp_kind) {
55: case MP_MISMATCH:
56: message("[Mismatched parentheses]");
57: break;
58:
59: case MP_UNBALANCED:
60: message("[Unbalanced parenthesis]");
61: break;
62:
63: case MP_INCOMMENT:
64: message("[Inside a comment]");
65: break;
66:
67: case MP_OKAY:
68: default:
69: return;
70: }
71: rbell();
72: }
73:
74: /* Search from the current position for the paren that matches p_type.
75: Search in the direction dir. If can_mismatch is YES then it is okay
76: to have mismatched parens. If stop_early is YES then when an open
77: paren is found at the beginning of a line, it is assumed that there
78: is no point in backing up further. This is so when you hit tab or
79: LineFeed outside, in-between procedure/function definitions, it won't
80: sit there searching all the way to the beginning of the file for a
81: match that doesn't exist. {forward,backward}-s-expression are the
82: only ones that insist on getting the "true" story. */
83:
84: Bufpos *
85: m_paren(p_type, dir, can_mismatch, can_stop)
86: int p_type;
87: register int dir;
88: int can_mismatch;
89: int can_stop;
90: {
91: static Bufpos ret;
92: Bufpos savedot,
93: *sp;
94: struct RE_block re_blk;
95: int count = 0;
96: register char *lp,
97: c;
98: char p_match,
99: re_str[128],
100: *cp,
101: quote_c = 0;
102: register int c_char;
103: int in_comment = -1,
104: stopped = NO;
105:
106: swritef(re_str, "[(){}[\\]%s]", (MajorMode(CMODE)) ? "/\"'" : "\"");
107: REcompile(re_str, 1, &re_blk);
108: if ((cp = strchr(p_types, p_type)) != NIL)
109: p_match = cp[dir];
110: else
111: complain("[Cannot match %c's]", p_type);
112: DOTsave(&savedot);
113:
114: /* To make things a little faster I avoid copying lines into
115: linebuf by setting curline and curchar by hand. Warning:
116: this is slightly to very risky. When I did this there were
117: lots of problems with procedures that expect the contents of
118: curline to be in linebuf. */
119: while (count >= 0) {
120: sp = docompiled(dir, &re_blk);
121: if (sp == 0)
122: break;
123: lp = lbptr(sp->p_line);
124:
125: curline = sp->p_line;
126: curchar = sp->p_char; /* here's where I cheat */
127:
128: c_char = curchar;
129: if (dir == FORWARD)
130: c_char -= 1;
131: if (backslashed(lp, c_char))
132: continue;
133: c = lp[c_char];
134: /* check if this is a comment (if we're not inside quotes) */
135: if (quote_c == 0 && c == '/') {
136: int new_ic = in_comment;
137:
138: /* close comment */
139: if ((c_char != 0) && lp[c_char - 1] == '*') {
140: new_ic = (dir == FORWARD) ? NO : YES;
141: if (new_ic == NO && in_comment == -1) {
142: count = 0;
143: quote_c = 0;
144: }
145: } else if (lp[c_char + 1] == '*') {
146: new_ic = (dir == FORWARD) ? YES : NO;
147: if (new_ic == NO && in_comment == -1) {
148: count = 0;
149: quote_c = 0;
150: }
151: }
152: in_comment = new_ic;
153: }
154: if (in_comment == YES)
155: continue;
156: if (c == '"' || c == '\'') {
157: if (quote_c == c)
158: quote_c = 0;
159: else if (quote_c == 0)
160: quote_c = c;
161: }
162: if (quote_c != 0)
163: continue;
164: if (isopenp(c)) {
165: count += dir;
166: if (c_char == 0 && can_stop == YES && count >= 0) {
167: stopped = YES;
168: break;
169: }
170: } else if (isclosep(c))
171: count -= dir;
172: }
173:
174: ret.p_line = curline;
175: ret.p_char = curchar;
176:
177: curline = savedot.p_line;
178: curchar = savedot.p_char; /* here's where I undo it */
179:
180: if (count >= 0)
181: mp_kind = MP_UNBALANCED;
182: else if (c != p_match)
183: mp_kind = MP_MISMATCH;
184: else
185: mp_kind = MP_OKAY;
186:
187: /* If we stopped (which means we were allowed to stop) and there
188: was an error, we clear the error so no error message is printed.
189: An error should be printed ONLY when we are sure about the fact,
190: namely we didn't stop prematurely HOPING that it was the right
191: answer. */
192: if (stopped && mp_kind != MP_OKAY) {
193: mp_kind = MP_OKAY;
194: return 0;
195: }
196: if (mp_kind == MP_OKAY || (mp_kind == MP_MISMATCH && can_mismatch == YES))
197: return &ret;
198: return 0;
199: }
200:
201: private void
202: do_expr(dir, skip_words)
203: register int dir;
204: int skip_words;
205: {
206: register char c,
207: syntax = (dir == FORWARD) ? _Op : _Cl;
208:
209: if (dir == BACKWARD)
210: b_char(1);
211: c = linebuf[curchar];
212: for (;;) {
213: if (!skip_words && ismword(c)) {
214: WITH_TABLE(curbuf->b_major)
215: if (dir == FORWARD)
216: f_word(1);
217: else
218: b_word(1);
219: END_TABLE();
220: break;
221: } else if (has_syntax(c, syntax)) {
222: FindMatch(dir);
223: break;
224: }
225: f_char(dir);
226: if (eobp() || bobp())
227: return;
228: c = linebuf[curchar];
229: }
230: }
231:
232: void
233: FSexpr()
234: {
235: register int num = arg_value();
236:
237: if (num < 0) {
238: set_arg_value(-num);
239: BSexpr();
240: }
241: while (--num >= 0)
242: do_expr(FORWARD, NO);
243: }
244:
245: void
246: FList()
247: {
248: register int num = arg_value();
249:
250: if (num < 0) {
251: set_arg_value(-num);
252: BList();
253: }
254: while (--num >= 0)
255: do_expr(FORWARD, YES);
256: }
257:
258: void
259: BSexpr()
260: {
261: register int num = arg_value();
262:
263: if (num < 0) {
264: negate_arg_value();
265: FSexpr();
266: }
267: while (--num >= 0)
268: do_expr(BACKWARD, NO);
269: }
270:
271: void
272: BList()
273: {
274: register int num = arg_value();
275:
276: if (num < 0) {
277: negate_arg_value();
278: FList();
279: }
280: while (--num >= 0)
281: do_expr(BACKWARD, YES);
282: }
283:
284: void
285: BUpList()
286: {
287: Bufpos *mp;
288: char c = (MajorMode(CMODE) ? '}' : ')');
289:
290: mp = m_paren(c, BACKWARD, NO, YES);
291: if (mp == 0)
292: mp_error();
293: else
294: SetDot(mp);
295: }
296:
297: void
298: FDownList()
299: {
300: Bufpos *sp;
301: char *sstr = (MajorMode(CMODE) ? "[{([\\])}]" : "[()]"),
302: *lp;
303:
304: sp = dosearch(sstr, FORWARD, YES);
305: if (sp != 0)
306: lp = lcontents(sp->p_line);
307: if (sp == 0 || has_syntax(lp[sp->p_char - 1], _Cl))
308: complain("[No contained expression]");
309: SetDot(sp);
310: }
311:
312: /* Move to the matching brace or paren depending on the current position
313: in the buffer. */
314:
315: private void
316: FindMatch(dir)
317: int dir;
318: {
319: register Bufpos *bp;
320: register char c = linebuf[curchar];
321:
322: if ((strchr(p_types, c) == 0) ||
323: (backslashed(linebuf, curchar)))
324: complain((char *) 0);
325: if (dir == FORWARD)
326: f_char(1);
327: bp = m_paren(c, dir, YES, NO);
328: if (dir == FORWARD)
329: b_char(1);
330: if (bp != 0)
331: SetDot(bp);
332: mp_error(); /* if there is an error the user wants to
333: know about it */
334: }
335:
336: #define ALIGN_ARGS (-1)
337:
338: /* If CArgIndent == ALIGN_ARGS then the indentation routine will
339: indent a continued line by lining it up with the first argument.
340: Otherwise, it will indent CArgIndent characters past the indent
341: of the first line of the procedure call. */
342:
343: int CArgIndent = ALIGN_ARGS;
344:
345: /* indent for C code */
346: Bufpos *
347: c_indent(brace)
348: int brace;
349: {
350: Bufpos *bp;
351: int new_indent = 0,
352: current_indent,
353: increment;
354:
355: if (brace == NO)
356: increment = CIndIncrmt;
357: else
358: increment = 0;
359: /* Find matching paren, which may be a mismatch now. If it
360: is not a matching curly brace then it is a paren (most likely).
361: In that case we try to line up the arguments to a procedure
362: or inside an of statement. */
363: if ((bp = m_paren('}', BACKWARD, YES, YES)) != NIL) {
364: Bufpos save;
365: int matching_indent;
366:
367: DOTsave(&save);
368: SetDot(bp); /* go to matching paren */
369: ToIndent();
370: matching_indent = calc_pos(linebuf, curchar);
371: SetDot(bp);
372: switch (linebuf[curchar]) {
373: case '{':
374: new_indent = matching_indent;
375: if (!bolp()) {
376: b_char(1);
377: /* If we're not within the indent then we
378: can assume that there is either a C keyword
379: line DO on the line before the brace, or
380: there is a parenthesized expression. If
381: that's the case we want to go backward
382: over that to the beginning of the expression
383: so that we can get the correct indent for
384: this matching brace. This handles wrapped
385: if statements, etc. */
386: if (!within_indent()) {
387: Bufpos savematch;
388:
389: savematch = *bp;
390:
391: do_expr(BACKWARD, NO);
392: ToIndent();
393: new_indent = calc_pos(linebuf, curchar);
394:
395: /* do_expr() calls b_paren, which
396: returns a pointer to a structure,
397: and that pointer is in BP so we
398: have to save away the matching
399: paren and restore it in the
400: following line ... sigh */
401: *bp = savematch;
402: }
403: }
404: if (brace == NO)
405: new_indent += (increment - (new_indent % increment));
406: break;
407:
408: case '(':
409: if (CArgIndent == ALIGN_ARGS) {
410: f_char(1);
411: new_indent = calc_pos(linebuf, curchar);
412: } else
413: new_indent = matching_indent + CArgIndent;
414: break;
415: }
416: SetDot(&save);
417: }
418:
419: /* new_indent is the "correct" place to indent. Now we check to
420: see if what we consider as the correct place to indent is to
421: the LEFT of where we already are. If it is, and we are NOT
422: handling a brace, then we assume that the person wants to tab
423: in further than what we think is right (for some reason) and
424: so we allow that. */
425:
426: ToIndent();
427: current_indent = calc_pos(linebuf, curchar);
428: if (brace == NO && new_indent <= current_indent)
429: new_indent = current_indent + (increment - (current_indent % increment));
430: Bol();
431: DelWtSpace(); /* nice uniform Tabs*Space* */
432: n_indent(new_indent);
433:
434: return bp;
435: }
436:
437: static void
438: re_indent(incr)
439: int incr;
440: {
441: Line *l1, *l2, *lp;
442: int c1, c2;
443: Mark *m = CurMark();
444: Bufpos savedot;
445:
446: DOTsave(&savedot);
447: l1 = curline;
448: c1 = curchar;
449: l2 = m->m_line;
450: c2 = m->m_char;
451: (void) fixorder(&l1, &c1, &l2, &c2);
452: for (lp = l1; lp != l2->l_next; lp = lp->l_next) {
453: int indent;
454:
455: SetLine(lp);
456: ToIndent();
457: indent = calc_pos(linebuf, curchar);
458: if (indent != 0 || linebuf[0] != '\0')
459: n_indent(indent + incr);
460: }
461: SetDot(&savedot);
462: }
463:
464: void
465: LRShift()
466: {
467: int amnt;
468:
469: if (is_an_arg())
470: amnt = arg_value();
471: else
472: amnt = CIndIncrmt;
473: re_indent(-amnt);
474: }
475:
476: void
477: RRShift()
478: {
479: int amnt;
480:
481: if (is_an_arg())
482: amnt = arg_value();
483: else
484: amnt = CIndIncrmt;
485: re_indent(amnt);
486: }
487:
488: #if defined(CMT_FMT)
489:
490: char CmtFmt[80] = "/*%n%! * %c%!%n */";
491:
492: void
493: Comment()
494: {
495: FillComment(CmtFmt);
496: }
497:
498: /* Strip leading and trailing white space. Skip over any imbedded '\r's. */
499:
500: private void
501: strip_c(from, to)
502: char *from,
503: *to;
504: {
505: register char *fr_p = from,
506: *to_p = to,
507: c;
508:
509: while ((c = *fr_p) != '\0') {
510: if (c == ' ' || c == '\t' || c == '\r')
511: fr_p += 1;
512: else
513: break;
514: }
515: while ((c = *fr_p) != '\0') {
516: if (c != '\r')
517: *to_p++ = c;
518: fr_p += 1;
519: }
520: while (--to_p >= to)
521: if (*to_p != ' ' && *to_p != '\t')
522: break;
523: *++to_p = '\0';
524: }
525:
526: private char open_c[20], /* the open comment format string */
527: open_pat[20], /* the search pattern for open comment */
528: l_header[20], /* the prefix for each comment line */
529: l_trailer[20], /* the suffix ... */
530: close_c[20],
531: close_pat[20];
532:
533: private char *const comment_body[] = {
534: open_c,
535: l_header,
536: l_trailer,
537: close_c
538: };
539:
540: private int nlflags;
541:
542: /* Fill in the data structures above from the format string. Don't return
543: if there's trouble. */
544:
545: private void
546: parse_cmt_fmt(str)
547: char *str;
548: {
549: register char *fmtp = str;
550: register char *const *c_body = comment_body,
551: *body_p = *c_body;
552: int c,
553: newlines = 1;
554:
555: /* pick apart the comment string */
556: while ((c = *fmtp++) != '\0') {
557: if (c != '%') {
558: *body_p++ = c;
559: continue;
560: }
561: switch(c = *fmtp++) {
562: case 'n':
563: if (newlines == 2 || newlines == 3)
564: complain("%n not allowed in line header or trailer: %s",
565: fmtp - 2);
566: nlflags += newlines;
567: *body_p++ = '\r';
568: break;
569: case 't':
570: *body_p++ = '\t';
571: break;
572: case '%':
573: *body_p++ = '%';
574: break;
575: case '!':
576: case 'c':
577: newlines += 1;
578: *body_p++ = '\0';
579: body_p = *++c_body;
580: break;
581: default:
582: complain("[Unknown comment escape: %%%c]", c);
583: break;
584: }
585: }
586: *body_p = '\0';
587: /* make search patterns */
588: strip_c(open_c, open_pat);
589: strip_c(close_c, close_pat);
590: }
591:
592: #define NL_IN_OPEN_C ((nlflags % 4) == 1)
593: #define NL_IN_CLOSE_C (nlflags >= 4)
594:
595: private void
596: FillComment(format)
597: char *format;
598: {
599: int saveRMargin,
600: indent_pos,
601: close_at_dot = NO;
602: size_t header_len,
603: trailer_len;
604: register char *cp;
605: static char inside_err[] = "[Must be between %s and %s to re-format]";
606: Bufpos open_c_pt,
607: close_c_pt,
608: tmp_bp,
609: *match_o,
610: *match_c;
611: Mark *entry_mark,
612: *open_c_mark,
613: *savedot;
614:
615: parse_cmt_fmt(format);
616: /* figure out if we're "inside" a comment */
617: if ((match_o = dosearch(open_pat, BACKWARD, 0)) == 0)
618: complain("No opening %s to match to.", open_pat);
619: open_c_pt = *match_o;
620: if ((match_c = dosearch(close_pat, BACKWARD, NO)) != 0 &&
621: inorder(open_c_pt.p_line, open_c_pt.p_char,
622: match_c->p_line, match_c->p_char))
623: complain(inside_err, open_pat, close_pat);
624: if ((match_o = dosearch(open_pat, FORWARD, NO)) != 0) {
625: tmp_bp = *match_o;
626: match_o = &tmp_bp;
627: }
628: if ((match_c = dosearch(close_pat, FORWARD, 0)) != (Bufpos *) 0)
629: close_c_pt = *match_c;
630:
631: /* Here's where we figure out whether to format from dot or from
632: the close comment. Note that we've already searched backwards to
633: find the open comment symbol for the comment we are formatting.
634: The open symbol mentioned below refers to the possible existence
635: of the next comment. There are 5 cases:
636: 1) no open or close symbol ==> dot
637: 2) open, but no close symbol ==> dot
638: 3) close, but no open ==> close
639: 4) open, close are inorder ==> dot
640: 5) open, close are not inorder ==> close */
641:
642:
643: if (match_o == (Bufpos *) 0) {
644: if (match_c == (Bufpos *) 0)
645: close_at_dot = YES;
646: } else if (match_c == (Bufpos *) 0)
647: close_at_dot = YES;
648: else if (inorder(match_o->p_line, match_o->p_char,
649: match_c->p_line, match_c->p_char))
650: close_at_dot = YES;
651: if (close_at_dot) {
652: close_c_pt.p_line = curline;
653: close_c_pt.p_char = curchar;
654: } else {
655: SetDot(match_c);
656: }
657: SetDot(&open_c_pt);
658: open_c_mark = MakeMark(curline, curchar, M_FLOATER);
659: indent_pos = calc_pos(linebuf, curchar);
660: /* search for a close comment; delete it if it exits */
661: SetDot(&close_c_pt);
662: if (close_at_dot == 0)
663: del_char(BACKWARD, (int)strlen(close_pat), NO);
664: entry_mark = MakeMark(curline, curchar, M_FLOATER);
665: ToMark(open_c_mark);
666: /* always separate the comment body from anything preceeding it */
667: LineInsert(1);
668: DelWtSpace();
669: Bol();
670: for (cp = open_c; *cp; cp++) {
671: if (*cp == '\r') {
672: if (!eolp())
673: LineInsert(1);
674: else
675: line_move(FORWARD, 1, NO);
676: } else if (*cp == ' ' || *cp == '\t') {
677: if (linebuf[curchar] != *cp)
678: insert_c(*cp, 1);
679: } else
680: /* Since we matched the open comment string on this
681: line, we don't need to worry about crossing line
682: boundaries. */
683: curchar += 1;
684: }
685: savedot = MakeMark(curline, curchar, M_FLOATER);
686:
687: /* We need to strip the line header pattern of leading white space
688: since we need to match the line after all of its leading
689: whitespace is gone. */
690: for (cp = l_header; *cp && (isspace(*cp)); cp++)
691: ;
692: header_len = strlen(cp);
693: trailer_len = strlen(l_trailer);
694:
695: /* Strip each comment line of the open and close comment strings
696: before reformatting it. */
697:
698: do {
699: Bol();
700: DelWtSpace();
701: if (header_len && strncmp(linebuf, cp, header_len)==0)
702: del_char(FORWARD, (int)header_len, NO);
703: if (trailer_len) {
704: Eol();
705: if (((size_t)curchar > trailer_len) &&
706: (strncmp(&linebuf[curchar - trailer_len],
707: l_trailer, trailer_len)==0))
708: del_char(BACKWARD, (int)trailer_len, NO);
709: }
710: if (curline->l_next != 0)
711: line_move(FORWARD, 1, NO);
712: else
713: break;
714: } while (curline != entry_mark->m_line->l_next);
715:
716: do_set_mark(savedot->m_line, savedot->m_char);
717: ToMark(entry_mark);
718: saveRMargin = RMargin;
719: RMargin = saveRMargin - strlen(l_header) -
720: strlen(l_trailer) - indent_pos + 2;
721: do_rfill(NO);
722: RMargin = saveRMargin;
723: /* get back to the start of the comment */
724: PopMark();
725: do {
726: if (curline == open_c_mark->m_line->l_next) {
727: ;
728: } else {
729: Bol();
730: n_indent(indent_pos);
731: ins_str(l_header, NO);
732: }
733: Eol();
734: if (!NL_IN_CLOSE_C && (curline == entry_mark->m_line))
735: ;
736: else
737: ins_str(l_trailer, NO);
738: if (curline->l_next != 0)
739: line_move(FORWARD, 1, NO);
740: else
741: break;
742: } while (curline != entry_mark->m_line->l_next);
743: /* handle the close comment symbol */
744: if (curline == entry_mark->m_line->l_next) {
745: line_move(BACKWARD, 1, NO);
746: Eol();
747: }
748: DelWtSpace();
749: /* if the addition of the close symbol would cause the line to be
750: too long, put the close symbol on the next line. */
751: if (!(NL_IN_CLOSE_C) &&
752: (int)strlen(close_c) + calc_pos(linebuf, curchar) > RMargin) {
753: LineInsert(1);
754: n_indent(indent_pos);
755: }
756: for (cp = close_c; *cp; cp++) {
757: if (*cp == '\r') {
758: LineInsert(1);
759: n_indent(indent_pos);
760: } else
761: insert_c(*cp, 1);
762: }
763: ToMark(open_c_mark);
764: Eol();
765: del_char(FORWARD, 1, NO);
766: }
767:
768: #endif /* CMT_FMT */
769:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.