home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume8 / jove / part02 / c.c next >
Encoding:
C/C++ Source or Header  |  1987-02-02  |  12.9 KB  |  601 lines

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