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 */

Defined functions

BList defined in line 278; used 5 times
BSexpr defined in line 265; used 5 times
BUpList defined in line 291; used 4 times
Comment defined in line 371; used 4 times
FDownList defined in line 304; used 4 times
FList defined in line 252; used 5 times
FillComment defined in line 475; used 3 times
FindMatch defined in line 322; used 3 times
backslashed defined in line 43; used 4 times
c_indent defined in line 342; used 4 times
do_expr defined in line 209; used 6 times
m_paren defined in line 90; used 7 times
mp_error defined in line 61; used 5 times
parse_cmt_fmt defined in line 424; used 3 times
strip_c defined in line 379; used 4 times

Defined variables

CmtFmt defined in line 369; used 2 times
close_c defined in line 409; used 4 times
close_pat defined in line 410; used 5 times
comment_body defined in line 412; used 1 times
l_header defined in line 407; used 4 times
l_trailer defined in line 408; used 5 times
mp_kind defined in line 56; used 8 times
nlflags defined in line 419; used 3 times
open_c defined in line 405; used 3 times
open_pat defined in line 406; used 5 times
p_types defined in line 55; used 2 times
private defined in line 379; never used

Defined macros

MP_MISMATCH defined in line 58; used 2 times
MP_OKAY defined in line 57; used 4 times
MP_UNBALANCED defined in line 59; used 1 times
NL_IN_CLOSE_C defined in line 473; used 2 times
NL_IN_OPEN_C defined in line 472; never used
private defined in line 39; used 14 times
Last modified: 1988-04-06
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 749
Valid CSS Valid XHTML 1.0 Strict