|
|
1.1 ! root 1: /************************************************************************* ! 2: * This program is copyright (C) 1985, 1986 by Jonathan Payne. It is * ! 3: * provided to you without charge for use only on a licensed Unix * ! 4: * system. You may copy JOVE provided that this notice is included with * ! 5: * the copy. You may not sell copies of this program or versions * ! 6: * modified for use on microcomputer systems, unless the copies are * ! 7: * included with a Unix system distribution and the source is provided. * ! 8: *************************************************************************/ ! 9: ! 10: /* Contains commands for C mode. Paren matching routines are in here. */ ! 11: ! 12: #include "jove.h" ! 13: #include "re.h" ! 14: #include "ctype.h" ! 15: ! 16: private ! 17: backslashed(lp, cpos) ! 18: register char *lp; ! 19: register int cpos; ! 20: { ! 21: register int cnt = 0; ! 22: ! 23: while (cpos > 0 && lp[--cpos] == '\\') ! 24: cnt++; ! 25: return (cnt % 2); ! 26: } ! 27: ! 28: private char *p_types = "(){}[]"; ! 29: private int mp_kind; ! 30: #define MP_OKAY 0 ! 31: #define MP_MISMATCH 1 ! 32: #define MP_UNBALANCED 2 ! 33: ! 34: mp_error() ! 35: { ! 36: switch (mp_kind) { ! 37: case MP_MISMATCH: ! 38: message("[Mismatched parentheses]"); ! 39: break; ! 40: ! 41: case MP_UNBALANCED: ! 42: message("[Unbalanced parenthesis]"); ! 43: break; ! 44: ! 45: case MP_OKAY: ! 46: default: ! 47: return; ! 48: } ! 49: rbell(); ! 50: } ! 51: ! 52: /* Search from the current position for the paren that matches p_type. ! 53: Search in the direction dir. If can_mismatch is YES then it is okay ! 54: to have mismatched parens. If stop_early is YES then when a close ! 55: paren is found at the beginning of a line, it is assumed that there ! 56: is no point in backing up further. This is so when you hit tab or ! 57: LineFeed outside, in-between procedure/function definitions, it won't ! 58: sit there searching all the way to the beginning of the file for a ! 59: match that doesn't exist. {forward,backward}-s-expression are the ! 60: only ones that insist on getting the "true" story. */ ! 61: ! 62: Bufpos * ! 63: m_paren(p_type, dir, can_mismatch, can_stop) ! 64: char p_type; ! 65: register int dir; ! 66: { ! 67: static Bufpos ret; ! 68: Bufpos savedot, ! 69: *sp; ! 70: static char re_buf[100], ! 71: *re_alts[NALTS]; ! 72: int count = 0; ! 73: register char *lp, ! 74: c; ! 75: char p_match, ! 76: re_str[128], ! 77: *cp, ! 78: quote_c = 0; ! 79: register int c_char; ! 80: int in_comment = NO, ! 81: stopped = NO; ! 82: ! 83: sprintf(re_str, "[(){}[\\]%s]", (MajorMode(CMODE)) ? "/\"'" : "\""); ! 84: REcompile(re_str, 1, re_buf, re_alts); ! 85: if (cp = index(p_types, p_type)) ! 86: p_match = cp[dir]; ! 87: else ! 88: complain("[Cannot match %c's]", p_type); ! 89: DOTsave(&savedot); ! 90: ! 91: /* To make things a little faster I avoid copying lines into ! 92: linebuf by setting curline and curchar by hand. Warning: ! 93: this is slightly to very risky. When I did this there were ! 94: lots of problems with procedures that expect the contents of ! 95: curline to be in linebuf. */ ! 96: while (count >= 0) { ! 97: sp = docompiled(dir, re_buf, re_alts); ! 98: if (sp == 0) ! 99: break; ! 100: lp = lbptr(sp->p_line); ! 101: ! 102: curline = sp->p_line; ! 103: curchar = sp->p_char; /* here's where I cheat */ ! 104: c_char = curchar; ! 105: if (dir == FORWARD) ! 106: c_char--; ! 107: ! 108: if (backslashed(lp, c_char)) ! 109: continue; ! 110: c = lp[c_char]; ! 111: if (c == '/') { /* check if this is a comment */ ! 112: if ((c_char != 0) && lp[c_char - 1] == '*') ! 113: in_comment = (dir == FORWARD) ? NO : YES; ! 114: else if (lp[c_char + 1] == '*') ! 115: in_comment = (dir == FORWARD) ? YES : NO; ! 116: } ! 117: if (in_comment) ! 118: continue; ! 119: if (c == '"' || c == '\'') { ! 120: if (quote_c == c) ! 121: quote_c = 0; ! 122: else if (quote_c == 0) ! 123: quote_c = c; ! 124: } ! 125: if (quote_c != 0) ! 126: continue; ! 127: if (isopenp(c)) { ! 128: count += dir; ! 129: if (c_char == 0 && can_stop == YES && count >= 0) { ! 130: stopped = YES; ! 131: break; ! 132: } ! 133: } else if (isclosep(c)) ! 134: count -= dir; ! 135: } ! 136: ! 137: ret.p_line = curline; ! 138: ret.p_char = curchar; ! 139: ! 140: curline = savedot.p_line; ! 141: curchar = savedot.p_char; /* here's where I undo it */ ! 142: ! 143: if (count >= 0) ! 144: mp_kind = MP_UNBALANCED; ! 145: else if (c != p_match) ! 146: mp_kind = MP_MISMATCH; ! 147: else ! 148: mp_kind = MP_OKAY; ! 149: ! 150: /* If we stopped (which means we were allowed to stop) and there ! 151: was an error, we clear the error so no error message is printed. ! 152: An error should be printed ONLY when we are sure about the fact, ! 153: namely we didn't stop prematurely HOPING that it was the right ! 154: answer. */ ! 155: if (stopped && mp_kind != MP_OKAY) { ! 156: mp_kind = MP_OKAY; ! 157: return 0; ! 158: } ! 159: if (mp_kind == MP_OKAY || (mp_kind == MP_MISMATCH && can_mismatch == YES)) ! 160: return &ret; ! 161: return 0; ! 162: } ! 163: ! 164: private ! 165: do_expr(dir) ! 166: register int dir; ! 167: { ! 168: register char c, ! 169: syntax = (dir == FORWARD) ? _Op : _Cl; ! 170: ! 171: exp = 1; ! 172: if (dir == BACKWARD) ! 173: BackChar(); ! 174: c = linebuf[curchar]; ! 175: for (;;) { ! 176: if (ismword(c)) { ! 177: WITH_TABLE(curbuf->b_major) ! 178: (dir == FORWARD) ? ForWord() : BackWord(); ! 179: END_TABLE(); ! 180: break; ! 181: } else if (has_syntax(c, syntax)) { ! 182: FindMatch(dir); ! 183: break; ! 184: } ! 185: DoTimes(ForChar(), dir); ! 186: if (eobp() || bobp()) ! 187: return; ! 188: c = linebuf[curchar]; ! 189: } ! 190: } ! 191: ! 192: FSexpr() ! 193: { ! 194: register int num = exp; ! 195: ! 196: if (exp < 0) { ! 197: exp = -exp; ! 198: BSexpr(); ! 199: } ! 200: while (--num >= 0) ! 201: do_expr(FORWARD); ! 202: } ! 203: ! 204: BSexpr() ! 205: { ! 206: register int num = exp; ! 207: ! 208: if (exp < 0) { ! 209: exp = -exp; ! 210: FSexpr(); ! 211: } ! 212: while (--num >= 0) ! 213: do_expr(BACKWARD); ! 214: } ! 215: ! 216: /* Move to the matching brace or paren depending on the current position ! 217: in the buffer. */ ! 218: ! 219: private ! 220: FindMatch(dir) ! 221: { ! 222: register Bufpos *bp; ! 223: register char c = linebuf[curchar]; ! 224: ! 225: if ((index(p_types, c) == 0) || ! 226: (backslashed(curline, curchar))) ! 227: complain((char *) 0); ! 228: if (dir == FORWARD) ! 229: ForChar(); ! 230: bp = m_paren(c, dir, YES, NO); ! 231: if (dir == FORWARD) ! 232: BackChar(); ! 233: if (bp != 0) ! 234: SetDot(bp); ! 235: mp_error(); /* if there is an error the user wants to ! 236: know about it */ ! 237: } ! 238: ! 239: Bufpos * ! 240: c_indent(incrmt) ! 241: { ! 242: Bufpos *bp; ! 243: int indent = 0; ! 244: ! 245: if (bp = m_paren('}', BACKWARD, NO, YES)) { ! 246: Bufpos save; ! 247: ! 248: DOTsave(&save); ! 249: SetDot(bp); ! 250: ToIndent(); ! 251: indent = calc_pos(linebuf, curchar); ! 252: SetDot(&save); ! 253: } ! 254: n_indent(indent + incrmt); ! 255: ! 256: return bp; ! 257: } ! 258: ! 259: #ifdef CMT_FMT ! 260: ! 261: char CmtFmt[80] = "/*%n%! * %c%!%n */"; ! 262: ! 263: Comment() ! 264: { ! 265: FillComment(CmtFmt); ! 266: } ! 267: ! 268: /* Strip leading and trailing white space. Skip over any imbedded '\r's. */ ! 269: ! 270: private ! 271: strip_c(from, to) ! 272: char *from, ! 273: *to; ! 274: { ! 275: register char *fr_p = from, ! 276: *to_p = to, ! 277: c; ! 278: ! 279: while (c = *fr_p) { ! 280: if (c == ' ' || c == '\t' || c == '\r') ! 281: fr_p++; ! 282: else ! 283: break; ! 284: } ! 285: while (c = *fr_p) { ! 286: if (c != '\r') ! 287: *to_p++ = c; ! 288: fr_p++; ! 289: } ! 290: while (--to_p >= to) ! 291: if (*to_p != ' ' && *to_p != '\t') ! 292: break; ! 293: *++to_p = '\0'; ! 294: } ! 295: ! 296: private char open_c[20], /* the open comment format string */ ! 297: open_pat[20], /* the search pattern for open comment */ ! 298: l_header[20], /* the prefix for each comment line */ ! 299: l_trailer[20], /* the suffix ... */ ! 300: close_c[20], ! 301: close_pat[20]; ! 302: ! 303: private char *comment_body[] = { ! 304: open_c, ! 305: l_header, ! 306: l_trailer, ! 307: close_c ! 308: }; ! 309: ! 310: private int nlflags; ! 311: ! 312: /* Fill in the data structures above from the format string. Don't return ! 313: if there's trouble. */ ! 314: ! 315: private ! 316: parse_cmt_fmt(str) ! 317: char *str; ! 318: { ! 319: register char *fmtp = str; ! 320: register char **c_body = comment_body, ! 321: *body_p = *c_body; ! 322: int c, ! 323: newlines = 1; ! 324: ! 325: /* pick apart the comment string */ ! 326: while (c = *fmtp++) { ! 327: if (c != '%') { ! 328: *body_p++ = c; ! 329: continue; ! 330: } ! 331: switch(c = *fmtp++) { ! 332: case 'n': ! 333: if (newlines == 2 || newlines == 3) ! 334: complain("%n not allowed in line header or trailer: %s", ! 335: fmtp - 2); ! 336: nlflags += newlines; ! 337: *body_p++ = '\r'; ! 338: break; ! 339: case 't': ! 340: *body_p++ = '\t'; ! 341: break; ! 342: case '%': ! 343: *body_p++ = '%'; ! 344: break; ! 345: case '!': ! 346: case 'c': ! 347: newlines++; ! 348: *body_p++ = '\0'; ! 349: body_p = *++c_body; ! 350: break; ! 351: default: ! 352: complain("[Unknown comment escape: %%%c]", c); ! 353: /* VARARGS */ ! 354: break; ! 355: } ! 356: } ! 357: *body_p = '\0'; ! 358: /* make search patterns */ ! 359: strip_c(open_c, open_pat); ! 360: strip_c(close_c, close_pat); ! 361: } ! 362: ! 363: #define NL_IN_OPEN_C ((nlflags % 4) == 1) ! 364: #define NL_IN_CLOSE_C (nlflags >= 4) ! 365: ! 366: FillComment(format) ! 367: char *format; ! 368: { ! 369: int saveRMargin, ! 370: indent_pos, ! 371: close_at_dot = 0, ! 372: slen, ! 373: header_len, ! 374: trailer_len; ! 375: register char *cp; ! 376: static char inside_err[] = "[Must be between %s and %s to re-format]"; ! 377: Bufpos open_c_pt, ! 378: close_c_pt, ! 379: tmp_bp, ! 380: *match_o, ! 381: *match_c; ! 382: Mark *entry_mark, ! 383: *open_c_mark, ! 384: *savedot; ! 385: ! 386: parse_cmt_fmt(format); ! 387: /* figure out if we're "inside" a comment */ ! 388: if ((match_o = dosearch(open_pat, BACKWARD, 0)) == 0) ! 389: /* VARARGS */ ! 390: complain("No opening %s to match to.", open_pat); ! 391: open_c_pt = *match_o; ! 392: if ((match_c = dosearch(close_pat, BACKWARD, NO)) != 0 && ! 393: inorder(open_c_pt.p_line, open_c_pt.p_char, ! 394: match_c->p_line, match_c->p_char)) ! 395: complain(inside_err, open_pat, close_pat); ! 396: if ((match_o = dosearch(open_pat, FORWARD, NO)) != 0) { ! 397: tmp_bp = *match_o; ! 398: match_o = &tmp_bp; ! 399: } ! 400: if ((match_c = dosearch(close_pat, FORWARD, 0)) != (Bufpos *) 0) ! 401: close_c_pt = *match_c; ! 402: ! 403: /* Here's where we figure out whether to format from dot or from ! 404: the close comment. Note that we've already searched backwards to ! 405: find the open comment symbol for the comment we are formatting. ! 406: The open symbol mentioned below refers to the possible existence ! 407: of the next comment. There are 5 cases: ! 408: 1) no open or close symbol ==> dot ! 409: 2) open, but no close symbol ==> dot ! 410: 3) close, but no open ==> close ! 411: 4) open, close are inorder ==> dot ! 412: 5) open, close are not inorder ==> close */ ! 413: ! 414: ! 415: if (match_o == (Bufpos *) 0) { ! 416: if (match_c == (Bufpos *) 0) ! 417: close_at_dot++; ! 418: } else if (match_c == (Bufpos *) 0) ! 419: close_at_dot++; ! 420: else if (inorder(match_o->p_line, match_o->p_char, ! 421: match_c->p_line, match_c->p_char)) ! 422: close_at_dot++; ! 423: ! 424: if (close_at_dot) { ! 425: close_c_pt.p_line = curline; ! 426: close_c_pt.p_char = curchar; ! 427: } else { ! 428: SetDot(match_c); ! 429: } ! 430: SetDot(&open_c_pt); ! 431: open_c_mark = MakeMark(curline, curchar, FLOATER); ! 432: indent_pos = calc_pos(linebuf, curchar); ! 433: /* search for a close comment; delete it if it exits */ ! 434: SetDot(&close_c_pt); ! 435: if (close_at_dot == 0) { ! 436: slen = strlen(close_pat); ! 437: while (slen--) ! 438: DelPChar(); ! 439: } ! 440: entry_mark = MakeMark(curline, curchar, FLOATER); ! 441: ToMark(open_c_mark); ! 442: /* always separate the comment body from anything preceeding it */ ! 443: LineInsert(); ! 444: DelWtSpace(); ! 445: Bol(); ! 446: for (cp = open_c; *cp; cp++) { ! 447: if (*cp == '\r') { ! 448: if (!eolp()) ! 449: LineInsert(); ! 450: else ! 451: line_move(FORWARD, NO); ! 452: } else if (*cp == ' ' || *cp == '\t') { ! 453: if (linebuf[curchar] != *cp) ! 454: Insert(*cp); ! 455: } else ! 456: /* Since we matched the open comment string on this ! 457: line, we don't need to worry about crossing line ! 458: boundaries. */ ! 459: curchar++; ! 460: } ! 461: savedot = MakeMark(curline, curchar, FLOATER); ! 462: ! 463: /* We need to strip the line header pattern of leading white space ! 464: since we need to match the line after all of its leading ! 465: whitespace is gone. */ ! 466: for (cp = l_header; *cp && (isspace(*cp)); cp++) ! 467: ; ! 468: header_len = strlen(cp); ! 469: trailer_len = strlen(l_trailer); ! 470: ! 471: /* Strip each comment line of the open and close comment strings ! 472: before reformatting it. */ ! 473: ! 474: do { ! 475: Bol(); ! 476: DelWtSpace(); ! 477: if (header_len && !strncmp(linebuf, cp, header_len)) ! 478: DoTimes(DelNChar(), header_len); ! 479: if (trailer_len) { ! 480: Eol(); ! 481: if ((curchar > trailer_len) && ! 482: (!strncmp(&linebuf[curchar - trailer_len], ! 483: l_trailer, trailer_len))) ! 484: DoTimes(DelPChar(), trailer_len); ! 485: } ! 486: if (curline->l_next != 0) ! 487: line_move(FORWARD, NO); ! 488: else ! 489: break; ! 490: } while (curline != entry_mark->m_line->l_next); ! 491: ! 492: DoSetMark(savedot->m_line, savedot->m_char); ! 493: ToMark(entry_mark); ! 494: saveRMargin = RMargin; ! 495: RMargin = saveRMargin - strlen(l_header) - ! 496: strlen(l_trailer) - indent_pos + 2; ! 497: /* do not use the left margin */ ! 498: exp_p = 0; ! 499: do_rfill(); ! 500: RMargin = saveRMargin; ! 501: /* get back to the start of the comment */ ! 502: PopMark(); ! 503: do { ! 504: if (curline == open_c_mark->m_line->l_next) { ! 505: ; ! 506: } else { ! 507: Bol(); ! 508: n_indent(indent_pos); ! 509: ins_str(l_header, NO); ! 510: } ! 511: Eol(); ! 512: if (!NL_IN_CLOSE_C && (curline == entry_mark->m_line)) ! 513: ; ! 514: else ! 515: ins_str(l_trailer, NO); ! 516: if (curline->l_next != 0) ! 517: line_move(FORWARD, NO); ! 518: else ! 519: break; ! 520: } while (curline != entry_mark->m_line->l_next); ! 521: /* handle the close comment symbol */ ! 522: if (curline == entry_mark->m_line->l_next) { ! 523: line_move(BACKWARD, NO); ! 524: Eol(); ! 525: } ! 526: DelWtSpace(); ! 527: /* if the addition of the close symbol would cause the line to be ! 528: too long, put the close symbol on the next line. */ ! 529: if (strlen(close_c) + calc_pos(linebuf, curchar) > RMargin) { ! 530: LineInsert(); ! 531: n_indent(indent_pos); ! 532: } ! 533: for (cp = close_c; *cp; cp++) { ! 534: if (*cp == '\r') { ! 535: LineInsert(); ! 536: n_indent(indent_pos); ! 537: } else ! 538: Insert(*cp); ! 539: } ! 540: ToMark(open_c_mark); ! 541: Eol(); ! 542: exp_p = 0; ! 543: DelNChar(); ! 544: } ! 545: ! 546: #endif CMT_FMT ! 547:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.