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