--- /dev/null
+/* $OpenBSD: paragraph.c,v 1.22 2011/11/29 05:59:54 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Code for dealing with paragraphs and filling. Adapted from MicroEMACS 3.6
+ * and GNU-ified by mwm@ucbvax. Several bug fixes by blarson@usc-oberon.
+ */
+
+#include "def.h"
+
+static int fillcol = 70;
+
+#define MAXWORD 256
+
+/*
+ * Move to start of paragraph. Go back to the beginning of the current
+ * paragraph here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
+ * combination to delimit the beginning of a paragraph.
+ */
+/* ARGSUSED */
+int
+gotobop(int f, int n)
+{
+ /* the other way... */
+ if (n < 0)
+ return (gotoeop(f, -n));
+
+ while (n-- > 0) {
+ /* first scan back until we are in a word */
+ while (backchar(FFRAND, 1) && inword() == 0);
+
+ /* and go to the B-O-Line */
+ curwp->w_doto = 0;
+
+ /*
+ * and scan back until we hit a <NL><SP> <NL><TAB> or
+ * <NL><NL>
+ */
+ while (lback(curwp->w_dotp) != curbp->b_headp)
+ if (llength(lback(curwp->w_dotp)) &&
+ lgetc(curwp->w_dotp, 0) != ' ' &&
+ lgetc(curwp->w_dotp, 0) != '.' &&
+ lgetc(curwp->w_dotp, 0) != '\t')
+ curwp->w_dotp = lback(curwp->w_dotp);
+ else {
+ if (llength(lback(curwp->w_dotp)) &&
+ lgetc(curwp->w_dotp, 0) == '.') {
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ if (curwp->w_dotp == curbp->b_headp) {
+ /*
+ * beyond end of buffer,
+ * cleanup time
+ */
+ curwp->w_dotp =
+ lback(curwp->w_dotp);
+ curwp->w_doto =
+ llength(curwp->w_dotp);
+ }
+ }
+ break;
+ }
+ }
+ /* force screen update */
+ curwp->w_rflag |= WFMOVE;
+ return (TRUE);
+}
+
+/*
+ * Move to end of paragraph. Go forward to the end of the current paragraph
+ * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE> combination to
+ * delimit the beginning of a paragraph.
+ */
+/* ARGSUSED */
+int
+gotoeop(int f, int n)
+{
+ /* the other way... */
+ if (n < 0)
+ return (gotobop(f, -n));
+
+ /* for each one asked for */
+ while (n-- > 0) {
+ /* Find the first word on/after the current line */
+ curwp->w_doto = 0;
+ while (forwchar(FFRAND, 1) && inword() == 0);
+
+ curwp->w_doto = 0;
+ curwp->w_dotp = lforw(curwp->w_dotp);
+
+ /* and scan forword until we hit a <NL><SP> or ... */
+ while (curwp->w_dotp != curbp->b_headp) {
+ if (llength(curwp->w_dotp) &&
+ lgetc(curwp->w_dotp, 0) != ' ' &&
+ lgetc(curwp->w_dotp, 0) != '.' &&
+ lgetc(curwp->w_dotp, 0) != '\t')
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ else
+ break;
+ }
+ if (curwp->w_dotp == curbp->b_headp) {
+ /* beyond end of buffer, cleanup time */
+ curwp->w_dotp = lback(curwp->w_dotp);
+ curwp->w_doto = llength(curwp->w_dotp);
+ break;
+ }
+ }
+ /* force screen update */
+ curwp->w_rflag |= WFMOVE;
+ return (TRUE);
+}
+
+/*
+ * Justify a paragraph. Fill the current paragraph according to the current
+ * fill column.
+ */
+/* ARGSUSED */
+int
+fillpara(int f, int n)
+{
+ int c; /* current char during scan */
+ int wordlen; /* length of current word */
+ int clength; /* position on line during fill */
+ int i; /* index during word copy */
+ int eopflag; /* Are we at the End-Of-Paragraph? */
+ int firstflag; /* first word? (needs no space) */
+ int newlength; /* tentative new line length */
+ int eolflag; /* was at end of line */
+ int retval; /* return value */
+ struct line *eopline; /* pointer to line just past EOP */
+ char wbuf[MAXWORD]; /* buffer for current word */
+
+ undo_boundary_enable(FFRAND, 0);
+
+ /* record the pointer to the line just past the EOP */
+ (void)gotoeop(FFRAND, 1);
+ if (curwp->w_doto != 0) {
+ /* paragraph ends at end of buffer */
+ (void)lnewline();
+ eopline = lforw(curwp->w_dotp);
+ } else
+ eopline = curwp->w_dotp;
+
+ /* and back top the begining of the paragraph */
+ (void)gotobop(FFRAND, 1);
+
+ /* initialize various info */
+ while (inword() == 0 && forwchar(FFRAND, 1));
+
+ clength = curwp->w_doto;
+ wordlen = 0;
+
+ /* scan through lines, filling words */
+ firstflag = TRUE;
+ eopflag = FALSE;
+ while (!eopflag) {
+
+ /* get the next character in the paragraph */
+ if ((eolflag = (curwp->w_doto == llength(curwp->w_dotp)))) {
+ c = ' ';
+ if (lforw(curwp->w_dotp) == eopline)
+ eopflag = TRUE;
+ } else
+ c = lgetc(curwp->w_dotp, curwp->w_doto);
+
+ /* and then delete it */
+ if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag) {
+ retval = FALSE;
+ goto cleanup;
+ }
+
+ /* if not a separator, just add it in */
+ if (c != ' ' && c != '\t') {
+ if (wordlen < MAXWORD - 1)
+ wbuf[wordlen++] = c;
+ else {
+ /*
+ * You lose chars beyond MAXWORD if the word
+ * is too long. I'm too lazy to fix it now; it
+ * just silently truncated the word before,
+ * so I get to feel smug.
+ */
+ ewprintf("Word too long!");
+ }
+ } else if (wordlen) {
+
+ /* calculate tentative new length with word added */
+ newlength = clength + 1 + wordlen;
+
+ /*
+ * if at end of line or at doublespace and previous
+ * character was one of '.','?','!' doublespace here.
+ * behave the same way if a ')' is preceded by a
+ * [.?!] and followed by a doublespace.
+ */
+ if ((eolflag ||
+ curwp->w_doto == llength(curwp->w_dotp) ||
+ (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' '
+ || c == '\t') && (ISEOSP(wbuf[wordlen - 1]) ||
+ (wbuf[wordlen - 1] == ')' && wordlen >= 2 &&
+ ISEOSP(wbuf[wordlen - 2]))) &&
+ wordlen < MAXWORD - 1)
+ wbuf[wordlen++] = ' ';
+
+ /* at a word break with a word waiting */
+ if (newlength <= fillcol) {
+ /* add word to current line */
+ if (!firstflag) {
+ (void)linsert(1, ' ');
+ ++clength;
+ }
+ firstflag = FALSE;
+ } else {
+ if (curwp->w_doto > 0 &&
+ lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') {
+ curwp->w_doto -= 1;
+ (void)ldelete((RSIZE) 1, KNONE);
+ }
+ /* start a new line */
+ (void)lnewline();
+ clength = 0;
+ }
+
+ /* and add the word in in either case */
+ for (i = 0; i < wordlen; i++) {
+ (void)linsert(1, wbuf[i]);
+ ++clength;
+ }
+ wordlen = 0;
+ }
+ }
+ /* and add a last newline for the end of our new paragraph */
+ (void)lnewline();
+
+ /*
+ * We really should wind up where we started, (which is hard to keep
+ * track of) but I think the end of the last line is better than the
+ * beginning of the blank line.
+ */
+ (void)backchar(FFRAND, 1);
+ retval = TRUE;
+cleanup:
+ undo_boundary_enable(FFRAND, 1);
+ return (retval);
+}
+
+/*
+ * Delete a paragraph. Delete n paragraphs starting with the current one.
+ */
+/* ARGSUSED */
+int
+killpara(int f, int n)
+{
+ int status; /* returned status of functions */
+
+ /* for each paragraph to delete */
+ while (n--) {
+
+ /* mark out the end and beginning of the para to delete */
+ (void)gotoeop(FFRAND, 1);
+
+ /* set the mark here */
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+
+ /* go to the beginning of the paragraph */
+ (void)gotobop(FFRAND, 1);
+
+ /* force us to the beginning of line */
+ curwp->w_doto = 0;
+
+ /* and delete it */
+ if ((status = killregion(FFRAND, 1)) != TRUE)
+ return (status);
+
+ /* and clean up the 2 extra lines */
+ (void)ldelete((RSIZE) 1, KFORW);
+ }
+ return (TRUE);
+}
+
+/*
+ * Insert char with work wrap. Check to see if we're past fillcol, and if so,
+ * justify this line. As a last step, justify the line.
+ */
+/* ARGSUSED */
+int
+fillword(int f, int n)
+{
+ char c;
+ int col, i, nce;
+
+ for (i = col = 0; col <= fillcol; ++i, ++col) {
+ if (i == curwp->w_doto)
+ return selfinsert(f, n);
+ c = lgetc(curwp->w_dotp, i);
+ if (c == '\t'
+#ifdef NOTAB
+ && !(curbp->b_flag & BFNOTAB)
+#endif
+ )
+ col |= 0x07;
+ else if (ISCTRL(c) != FALSE)
+ ++col;
+ }
+ if (curwp->w_doto != llength(curwp->w_dotp)) {
+ (void)selfinsert(f, n);
+ nce = llength(curwp->w_dotp) - curwp->w_doto;
+ } else
+ nce = 0;
+ curwp->w_doto = i;
+
+ if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t')
+ do {
+ (void)backchar(FFRAND, 1);
+ } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
+ c != '\t' && curwp->w_doto > 0);
+
+ if (curwp->w_doto == 0)
+ do {
+ (void)forwchar(FFRAND, 1);
+ } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
+ c != '\t' && curwp->w_doto < llength(curwp->w_dotp));
+
+ (void)delwhite(FFRAND, 1);
+ (void)lnewline();
+ i = llength(curwp->w_dotp) - nce;
+ curwp->w_doto = i > 0 ? i : 0;
+ curwp->w_rflag |= WFMOVE;
+ if (nce == 0 && curwp->w_doto != 0)
+ return (fillword(f, n));
+ return (TRUE);
+}
+
+/*
+ * Set fill column to n for justify.
+ */
+int
+setfillcol(int f, int n)
+{
+ char buf[32], *rep;
+ const char *es;
+ int nfill;
+
+ if ((f & FFARG) != 0) {
+ fillcol = n;
+ } else {
+ if ((rep = eread("Set fill-column: ", buf, sizeof(buf),
+ EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (rep[0] == '\0')
+ return (FALSE);
+ nfill = strtonum(rep, 0, INT_MAX, &es);
+ if (es != NULL) {
+ ewprintf("Invalid fill column: %s", rep);
+ return (FALSE);
+ }
+ fillcol = nfill;
+ ewprintf("Fill column set to %d", fillcol);
+ }
+ return (TRUE);
+}