Logo Search packages:      
Sourcecode: vile version File versions

word.c

/*
 * The routines in this file implement commands that work word or a
 * paragraph at a time.  There are all sorts of word mode commands.  If I
 * do any sentence mode commands, they are likely to be put in this file.
 *
 * $Header: /usr/build/vile/vile/RCS/word.c,v 1.74 2002/04/30 23:24:11 tom Exp $
 *
 */

#include    "estruct.h"
#include    "edef.h"
#include    "nefunc.h"

/* Word wrap on n-spaces. Back-over whatever precedes the point on the current
 * line and stop on the first word-break or the beginning of the line. If we
 * reach the beginning of the line, jump back to the end of the word and start
 * a new line.    Otherwise, break the line at the word-break, eat it, and jump
 * back to the end of the word.
 * Returns TRUE on success, FALSE on errors.
 */
int
wrapword(int f, int n)
{
    register int cnt = 0;     /* size of word wrapped to next line */
    register int c;           /* character temporary */
    B_COUNT to_delete;
    C_NUM to_append = 0;

    /* Back up from the <NL> 1 char */
    if (!backchar(FALSE, 1))
      return (FALSE);

    /* If parameter given, delete the trailing white space.  This is done
     * to support a vi-like wrapmargin mode (see insert.c).  Unlike vi,
     * we're deleting all of the trailing whitespace; vi deletes only the
     * portion that was added during the current insertion.
     *
     * If there's no trailing whitespace, and we're not inserting blanks
     * (n!=0), try to split the line at the last embedded whitespace.
     */
    if (f) {
      register LINE *lp = DOT.l;
      to_delete = 0L;
      if (DOT.o >= 0 && !n && !isSpace(lgetc(lp, DOT.o))) {
          for (c = DOT.o; c >= 0; c--) {
            if (isSpace(lgetc(lp, c))) {
                to_append = n;
                cnt = (DOT.o - c);
                DOT.o = c;
                break;
            }
          }
      }
      for (c = DOT.o; c >= 0; c--) {
          if (isSpace(lgetc(lp, c))) {
            to_delete++;
            DOT.o = c;
          } else {
            break;
          }
      }
      if (to_delete == 0)
          DOT.o++;
    } else {
      /* Back up until we aren't in a word, make sure there is a
       * break in the line
       */
      while (c = char_at(DOT), !isSpace(c)) {
          cnt++;
          if (!backchar(FALSE, 1))
            return (FALSE);
          /* if we make it to the beginning, start a new line */
          if (DOT.o == 0) {
            (void) gotoeol(FALSE, 0);
            return (lnewline());
          }
      }
      to_delete = 1L;
    }

    /* delete the forward white space */
    if (!ldelete(to_delete, FALSE))
      return (FALSE);

    /* put in newline */
    if (!newline(FALSE, 1))
      return FALSE;

    /* and past the first word */
    while (cnt-- > 0) {
      if (forwchar(FALSE, 1) == FALSE)
          return (FALSE);
    }
    if (to_append > 0)
      linsert(to_append, ' ');
    return (TRUE);
}

/*
 * Implements the vi "w" command.
 *
 * Move the cursor forward by the specified number of words. All of the motion
 * is done by "forwchar". Error if you try and move beyond the buffer's end.
 *
 * Returns of SORTOFTRUE result if we're doing a non-delete operation.
 * Whitespace after a word is always included on deletes (and non-operations,
 * of course), but only on intermediate words for other operations, for
 * example.  The last word of non-delete ops does _not_ include its whitespace.
 */
int
forwviword(int f, int n)
{
    int s;

    if (n < 0)
      return (backword(f, -n));
    setchartype();
    if (forwchar(TRUE, 1) == FALSE)
      return (FALSE);
    while (n--) {
      int any = 0;
      while (((s = isnewviwordf()) == FALSE) ||
             (s == SORTOFTRUE && n != 0)) {
          if (forwchar(TRUE, 1) == FALSE)
            return (any != 0);
          any++;
      }
    }
    return TRUE;
}

/*
 * Implements the vi "W" command.
 *
 * Move the cursor forward by the specified number of words. All of the motion
 * is done by "forwchar". Error if you try and move beyond the buffer's end.
 */
int
forwword(int f, int n)
{
    int s;

    if (n < 0)
      return (backword(f, -n));
    setchartype();
    if (forwchar(TRUE, 1) == FALSE)
      return (FALSE);
    while (n--) {
      int any = 0;
      while (((s = isnewwordf()) == FALSE) ||
             (s == SORTOFTRUE && n != 0)) {
          if (forwchar(TRUE, 1) == FALSE)
            return (any != 0);
          any++;
      }
    }
    return (TRUE);
}

/*
 * Implements the vi "e" command.
 *
 * Move the cursor forward by the specified number of words. All of the motion
 * is done by "forwchar". Error if you try and move beyond the buffer's end.
 */
int
forwviendw(int f, int n)
{
    int s = FALSE;
    if (!f)
      n = 1;
    else if (n <= 0)
      return (FALSE);
    if (forwchar(TRUE, 1) == FALSE)
      return (FALSE);
    setchartype();
    while (n--) {
      int any = 0;
      while ((s = isendviwordf()) == FALSE) {
          if (forwchar(TRUE, 1) == FALSE)
            return (any != 0);
          any++;
      }

    }
    if (s == SORTOFTRUE)
      return TRUE;
    else
      return backchar(FALSE, 1);
}

/*
 * Implements the vi "E" command.
 *
 * Move the cursor forward by the specified number of words. All of the motion
 * is done by "forwchar". Error if you try and move beyond the buffer's end.
 */
int
forwendw(int f, int n)
{
    int s = FALSE;
    if (!f)
      n = 1;
    else if (n <= 0)
      return (FALSE);
    if (forwchar(TRUE, 1) == FALSE)
      return (FALSE);
    setchartype();
    while (n--) {
      int any = 0;
      while ((s = isendwordf()) == FALSE) {
          if (forwchar(TRUE, 1) == FALSE)
            return (any != 0);
          any++;
      }

    }
    if (s == SORTOFTRUE)
      return TRUE;
    else
      return backchar(FALSE, 1);
}

/*
 * Implements the vi "b" command.
 *
 * Move the cursor backward by "n" words. All of the details of motion are
 * performed by the "backchar" and "forwchar" routines. Error if you try to
 * move beyond the buffers.
 */
int
backviword(int f, int n)
{
    if (n < 0)
      return (forwword(f, -n));
    if (backchar(FALSE, 1) == FALSE)
      return (FALSE);
    setchartype();
    while (n--) {
      int any = 0;
      while (isnewviwordb() == FALSE) {
          any++;
          if (backchar(FALSE, 1) == FALSE)
            return (any != 0);
      }
    }
    return (forwchar(TRUE, 1));
}

/*
 * Implements the vi "B" command.
 *
 * Move the cursor backward by "n" words. All of the details of motion are
 * performed by the "backchar" and "forwchar" routines. Error if you try to
 * move beyond the buffers.
 */
int
backword(int f, int n)
{
    if (n < 0)
      return (forwword(f, -n));
    if (backchar(FALSE, 1) == FALSE)
      return (FALSE);
    setchartype();
    while (n--) {
      int any = 0;
      while (isnewwordb() == FALSE) {
          any++;
          if (backchar(FALSE, 1) == FALSE)
            return (any != 0);
      }
    }
    return (forwchar(TRUE, 1));
}

int
joinregion(void)
{
    register int status;
    register int doto, c;
    LINE *end;
    REGION region;
    int done = FALSE;

    if ((status = getregion(&region)) == TRUE
      && (status = !is_last_line(region.r_orig, curbp)) == TRUE) {

      DOT = region.r_orig;
      end = region.r_end.l;
      if (DOT.l == end) {
          /* Degenerate case -- nothing to join */

          return (TRUE);
      }
      regionshape = EXACT;

      while (!done && status == TRUE) {
          c = EOS;
          (void) gotoeol(FALSE, 1);
          if (DOT.o > 0)
            c = lgetc(DOT.l, DOT.o - 1);
          (void) setmark();
          if (forwline(FALSE, 1) != TRUE)
            break;
          (void) firstnonwhite(FALSE, 1);

          done = ((DOT.l == end) || (lforw(DOT.l) == end));
          if (killregionmaybesave(FALSE) != TRUE)
            break;

          doto = DOT.o;
          if (doto == 0) {
            /*EMPTY */ ;
          }
          /* join at column 0 to empty line */
          else if (doto < llength(DOT.l)) {
            if (lgetc(DOT.l, doto) == ')') {
                /*EMPTY */ ;
            }
            /* join after parentheses */
            else if (lgetc(DOT.l, doto - 1) == '.') {
                status = linsert(2, ' ');
            } else if (!isSpace(c)) {
                status = linsert(1, ' ');
            }
          }
      }
    }
    return status;
}

int
joinlines(int f, int n)
{
    havemotion = &f_godotplus;
    return (operjoin(f, n));
}

#if OPT_FORMAT
static int
dot_at_section_break(void)
{
    regexp *expP = b_val_rexp(curbp, VAL_PARAGRAPHS)->reg;
    regexp *expC = b_val_rexp(curbp, VAL_COMMENTS)->reg;

    return (lregexec(expP, DOT.l, 0, llength(DOT.l)) ||
          lregexec(expC, DOT.l, 0, llength(DOT.l)));
}

/* returns the length of the comment-prefix, if it matches, otherwise -1 */
static int
comment_prefix(void)
{
    regexp *expP = b_val_rexp(curbp, VAL_CMT_PREFIX)->reg;
    int result = -1;
    if (lregexec(expP, DOT.l, 0, llength(DOT.l))) {
      result = (int) (expP->endp[0] - DOT.l->l_text);
    }
    return result;
}

#define cplus_comment_start(c) \
            ((c == '/') \
            && DOT.o+1 < llength(DOT.l) \
            && lgetc(DOT.l,DOT.o+1) == '/')

#define c_comment_start(c) \
            ((c == '/') \
            && DOT.o+1 < llength(DOT.l) \
            && lgetc(DOT.l,DOT.o+1) == '*')

#define fmt_insert(count,chr) \
            if ((s = linsert(count,chr)) != TRUE) \
                  return s; \
            else \
                  clength += count

#define fmt_c_preprocessor(cp) \
            (tb_length(*cp) == 1 \
            && *tb_values(*cp) == '#' \
            && is_c_mode(curbp))

#define word_finishes_c_comment(wp) \
            (tb_length(*wp) >= 2 \
            && memcmp("/*", tb_values(*wp), 2) == 0)

static int
do_formatting(TBUFF ** wp, TBUFF ** cp)
{
    register int c;           /* current char during scan     */
    register int clength;     /* position on line during fill */
    register int plength;     /* prefix to ignore during fill */
    register size_t i;        /* index during word copy       */
    register int newlen;      /* tentative new line length    */
    register int finished;    /* Are we at the End-Of-Paragraph? */
    register int firstflag;   /* first word? (needs no space) */
    register int is_comment;  /* doing a comment block?       */
    register int at_nl = TRUE;      /* just saw a newline?          */
    register LINEPTR pastline;      /* pointer to line just past EOP */
    register int sentence;    /* was the last char a period?  */
    int secondindent;
    int s;
    int fillcolumn = getfillcol(curbp);

    if (!sameline(MK, DOT)) {
      REGION region;

      if (getregion(&region) != TRUE)
          return FALSE;
      if (sameline(region.r_orig, MK))
          swapmark();
    }
    pastline = MK.l;
    if (pastline != buf_head(curbp))
      pastline = lforw(pastline);

    finished = FALSE;
    while (finished != TRUE) {      /* i.e. is FALSE or SORTOFTRUE */
      if (DOT.l == pastline)  /* FIXME */
          return setmark();
      while (dot_at_section_break()) {
          DOT.l = lforw(DOT.l);
          if (DOT.l == pastline)
            return setmark();
      }

      secondindent = indentlen(DOT.l);

      /* go forward to get the indent for the second
         and following lines */
      DOT.l = lforw(DOT.l);

      if (DOT.l != pastline) {
          secondindent = indentlen(DOT.l);
      }

      /* and back where we should be */
      DOT.l = lback(DOT.l);
      (void) firstnonwhite(FALSE, 1);

      clength = indentlen(DOT.l);
      tb_init(wp, EOS);
      tb_init(cp, EOS);
      sentence = FALSE;

      c = char_at(DOT);
      is_comment = FALSE;
      if ((plength = comment_prefix()) >= 0) {
          is_comment = TRUE;
          tb_bappend(cp,
                   DOT.l->l_text + DOT.o,
                   (size_t) (plength - DOT.o));
      } else if (cplus_comment_start(c)) {
          is_comment = TRUE;
          tb_bappend(cp, "//", 2);
      } else if (c_comment_start(c)) {
          is_comment = TRUE;
          tb_bappend(cp, "*", 1);
      }

      /* scan through lines, filling words */
      firstflag = TRUE;
      finished = FALSE;
      plength -= DOT.o;
      while (finished == FALSE) {   /* i.e. is not TRUE  */
          /* or SORTOFTRUE */
          if (interrupted())
            return ABORT;

          /* get the next character */
          if (is_at_end_of_line(DOT)) {
            c = ' ';
            DOT.l = lforw(DOT.l);
            if (DOT.l == pastline) {
                finished = TRUE;
            } else if (dot_at_section_break()) {
                finished = SORTOFTRUE;
            }

            if ((s = comment_prefix()) >= 0) {
                int save = DOT.o;

                (void) firstnonwhite(FALSE, 1);
                s -= DOT.o;

                if (s != (int) tb_length(*cp)
                  || (s > 0
                      && memcmp(tb_values(*cp),
                              DOT.l->l_text + DOT.o,
                              s))) {
                  finished = SORTOFTRUE;
                }

                if (finished == FALSE) {
                  plength = s;
                  tb_init(cp, EOS);
                  if (plength > 0) {
                      tb_bappend(cp,
                               DOT.l->l_text + DOT.o,
                               (size_t) (plength));
                  }
                  if (DOT.l != pastline
                      && !dot_at_section_break()) {
                      int spcs = DOT.o;
                      DOT.o = 0;
                      s = ldelete(spcs, FALSE);
                      if (s != TRUE)
                        return s;
                  }
                }
                DOT.o = save;
            }
            DOT.l = lback(DOT.l);
            at_nl = TRUE;
          } else {
            c = char_at(DOT);
            if (at_nl && ((plength-- > 0) || isSpace(c)))
                c = ' ';
            else
                at_nl = FALSE;
          }

          /* and then delete it */
          if (finished == FALSE) {
            s = ldelete(1L, FALSE);
            if (s != TRUE)
                return s;
          }

          /* if not a separator, just add it in */
          if (!isBlank(c)) {
            tb_append(wp, c);
            sentence = FALSE;
            if (global_g_val(GMDSPACESENT)) {
                /* was it the end of a "sentence"? */
                if (c == '?' || c == ':' || c == '!') {
                  sentence = TRUE;
                } else if (c == '.') {
                  unsigned len = tb_length(*wp);
                  if (len == 4) {
                      char *ptr = tb_values(*wp);
                      /*
                       * These "never" end a sentence.  Some Europeans
                       * drop the comma which Americans add, which makes
                       * formatting sentences a little odd.  I would
                       * add "etc.", but that is a valid sentence ending,
                       * and would be unclear without a comma otherwise.
                       */
                      sentence = memcmp(ptr, "e.g.", 4)
                        && memcmp(ptr, "i.e.", 4);
                  } else {
                      sentence = TRUE;
                  }
                }
            }
          } else if (tb_length(*wp)) {
            /* at a word break with a word waiting */
            /* calculate tentative new length
               with word added */
            newlen = clength + 1 + tb_length(*wp);
            if (newlen <= fillcolumn) {
                /* add word to current line */
                if (!firstflag) {
                  fmt_insert(1, ' ');
                }
            } else {
                /* fix the leading indent now, if
                   some spaces should be tabs */
                if (b_val(curbp, MDTABINSERT))
                  entabline((void *) TRUE, 0, 0);
                if (lnewline() == FALSE)
                  return FALSE;
                clength = 0;
                fmt_insert(secondindent, ' ');
                firstflag = TRUE;
            }

            if (firstflag
                && is_comment
                && !word_finishes_c_comment(wp)) {
                for (i = 0; i < tb_length(*cp); i++) {
                  fmt_insert(1, tb_values(*cp)[i]);
                }
                if (!fmt_c_preprocessor(cp)) {
                  fmt_insert(1, ' ');
                }
            }
            firstflag = FALSE;

            /* and add the word in in either case */
            for (i = 0; i < tb_length(*wp); i++) {
                fmt_insert(1, tb_values(*wp)[i]);
            }
            if (finished == FALSE && sentence) {
                fmt_insert(1, ' ');
            }
            tb_init(wp, EOS);
          }
      }
      /* catch the case where we're done with a line not because
         there's no more room, but because we're done processing a
         section or the region */
      if (b_val(curbp, MDTABINSERT))
          entabline((void *) TRUE, 0, 0);
      DOT.l = lforw(DOT.l);
    }
    return setmark();
}

int
formatregion(void)
{
    int s = FALSE;
    TBUFF *wp = 0;            /* word buffer */
    TBUFF *cp = 0;            /* comment-prefix buffer */

    if ((wp = tb_init(&wp, EOS)) != 0
      && (wp = tb_init(&wp, EOS)) != 0) {
      s = do_formatting(&wp, &cp);
      tb_free(&wp);
      tb_free(&cp);
    }
    return s;
}
#endif /* OPT_FORMAT */

#if OPT_WORDCOUNT
/*    wordcount:  count the # of words in the marked region,
                  along with average word sizes, # of chars, etc,
                  and report on them.                 */
/*ARGSUSED*/
int
wordcount(int f GCC_UNUSED, int n GCC_UNUSED)
{
    register LINE *lp;        /* current line to scan */
    register int offset;      /* current char to scan */
    long size;                /* size of region left to count */
    register int ch;          /* current character to scan */
    register int wordflag;    /* are we in a word now? */
    register int lastflag;    /* were we just in a word? */
    long nwhite;        /* total # of blanks */
    long nwords;        /* total # of words */
    long nchars;        /* total number of chars */
    int nlines;               /* total number of lines in region */
    double avgch;       /* average number of chars/word */
    int status;               /* status return code */
    REGION region;            /* region to look at */
    const char *ending = get_record_sep(curbp);
    int len_rs = strlen(ending);

    /* make sure we have a region to count */
    if ((status = getregion(&region)) != TRUE) {
      return (status);
    }
    lp = region.r_orig.l;
    offset = region.r_orig.o;
    size = region.r_size;

    /* count up things */
    lastflag = FALSE;
    nwhite = 0L;
    nchars = 0L;
    nwords = 0L;
    nlines = 0;
    while (size--) {

      /* get the current character */
      if (offset == llength(lp)) {  /* end of line */
          ch = *ending;
          lp = lforw(lp);
          offset = 0;
          ++nlines;
          if (len_rs > 1) {
            nwhite += (len_rs - 1);
            size -= (len_rs - 1);
          }
      } else {
          ch = lgetc(lp, offset);
          ++offset;
      }

      /* and tabulate it */
      if (((wordflag = isident(ch)) != 0) && !lastflag)
          ++nwords;
      lastflag = wordflag;
      if (isSpace(ch))
          ++nwhite;
      else
          ++nchars;
    }

    /* and report on the info */
    if (nwords > 0L)
      avgch = ((1.0 * nchars) / nwords);
    else
      avgch = 0.0;

    mlforce("lines %d, words %ld, chars %ld, avg chars/word %f",
          nlines, nwords, nchars + nwhite, avgch);

    return (TRUE);
}
#endif

Generated by  Doxygen 1.6.0   Back to index