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

Defined functions

BSexpr defined in line 204; used 3 times
Comment defined in line 263; used 2 times
FSexpr defined in line 192; used 5 times
FillComment defined in line 366; used 1 times
FindMatch defined in line 219; used 1 times
backslashed defined in line 16; used 2 times
c_indent defined in line 239; used 3 times
do_expr defined in line 164; used 2 times
m_paren defined in line 62; used 5 times
mp_error defined in line 34; used 2 times
parse_cmt_fmt defined in line 315; used 1 times
strip_c defined in line 270; used 2 times

Defined variables

CmtFmt defined in line 261; used 2 times
close_c defined in line 300; used 4 times
close_pat defined in line 301; used 5 times
comment_body defined in line 303; used 1 times
l_header defined in line 298; used 4 times
l_trailer defined in line 299; used 5 times
mp_kind defined in line 29; used 8 times
nlflags defined in line 310; used 3 times
open_c defined in line 296; used 3 times
open_pat defined in line 297; used 5 times
p_types defined in line 28; used 2 times

Defined macros

MP_MISMATCH defined in line 31; used 2 times
MP_OKAY defined in line 30; used 4 times
MP_UNBALANCED defined in line 32; used 1 times
NL_IN_CLOSE_C defined in line 364; used 1 times
NL_IN_OPEN_C defined in line 363; never used
Last modified: 1986-04-02
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1644
Valid CSS Valid XHTML 1.0 Strict