Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/paragraph.c
- This file was added.
/* $OpenBSD: paragraph.c,v 1.46 2018/11/17 09:52:34 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 <sys/queue.h> | |||||
#include <ctype.h> | |||||
#include <limits.h> | |||||
#include <signal.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include "def.h" | |||||
static int fillcol = 70; | |||||
#define MAXWORD 256 | |||||
static int findpara(void); | |||||
static int do_gotoeop(int, int, int *); | |||||
/* | |||||
* Move to start of paragraph. | |||||
* Move backwards by line, checking from the 1st character forwards for the | |||||
* existence a non-space. If a non-space character is found, move to the | |||||
* preceding line. Keep doing this until a line with only spaces is found or | |||||
* the start of buffer. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
gotobop(int f, int n) | |||||
{ | |||||
int col, nospace; | |||||
/* the other way... */ | |||||
if (n < 0) | |||||
return (gotoeop(f, -n)); | |||||
while (n-- > 0) { | |||||
nospace = 0; | |||||
while (lback(curwp->w_dotp) != curbp->b_headp) { | |||||
curwp->w_doto = 0; | |||||
col = 0; | |||||
while (col < llength(curwp->w_dotp) && | |||||
(isspace(lgetc(curwp->w_dotp, col)))) | |||||
col++; | |||||
if (col >= llength(curwp->w_dotp)) { | |||||
if (nospace) | |||||
break; | |||||
} else | |||||
nospace = 1; | |||||
curwp->w_dotline--; | |||||
curwp->w_dotp = lback(curwp->w_dotp); | |||||
} | |||||
} | |||||
/* force screen update */ | |||||
curwp->w_rflag |= WFMOVE; | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Move to end of paragraph. | |||||
* See comments for gotobop(). Same, but moving forwards. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
gotoeop(int f, int n) | |||||
{ | |||||
int i; | |||||
return(do_gotoeop(f, n, &i)); | |||||
} | |||||
int | |||||
do_gotoeop(int f, int n, int *i) | |||||
{ | |||||
int col, nospace, j = 0; | |||||
/* the other way... */ | |||||
if (n < 0) | |||||
return (gotobop(f, -n)); | |||||
/* for each one asked for */ | |||||
while (n-- > 0) { | |||||
*i = ++j; | |||||
nospace = 0; | |||||
while (lforw(curwp->w_dotp) != curbp->b_headp) { | |||||
col = 0; | |||||
curwp->w_doto = 0; | |||||
while (col < llength(curwp->w_dotp) && | |||||
(isspace(lgetc(curwp->w_dotp, col)))) | |||||
col++; | |||||
if (col >= llength(curwp->w_dotp)) { | |||||
if (nospace) | |||||
break; | |||||
} else | |||||
nospace = 1; | |||||
curwp->w_dotp = lforw(curwp->w_dotp); | |||||
curwp->w_dotline++; | |||||
} | |||||
} | |||||
/* do not continue after end of buffer */ | |||||
if (lforw(curwp->w_dotp) == curbp->b_headp) { | |||||
gotoeol(FFRAND, 1); | |||||
curwp->w_rflag |= WFMOVE; | |||||
return (FALSE); | |||||
} | |||||
/* 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 */ | |||||
if (n == 0) | |||||
return (TRUE); | |||||
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 beginning 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 (dblspace && (!eopflag && ((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 n paragraphs. Move to the beginning of the current paragraph, or if | |||||
* the cursor is on an empty line, move down the buffer to the first line with | |||||
* non-space characters. Then mark n paragraphs and delete. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
killpara(int f, int n) | |||||
{ | |||||
int lineno, status; | |||||
if (n == 0) | |||||
return (TRUE); | |||||
if (findpara() == FALSE) | |||||
return (TRUE); | |||||
/* go to the beginning of the paragraph */ | |||||
(void)gotobop(FFRAND, 1); | |||||
/* take a note of the line number for after deletions and set mark */ | |||||
lineno = curwp->w_dotline; | |||||
curwp->w_markp = curwp->w_dotp; | |||||
curwp->w_marko = curwp->w_doto; | |||||
(void)gotoeop(FFRAND, n); | |||||
if ((status = killregion(FFRAND, 1)) != TRUE) | |||||
return (status); | |||||
curwp->w_dotline = lineno; | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Mark n paragraphs starting with the n'th and working our way backwards. | |||||
* This leaves the cursor at the beginning of the paragraph where markpara() | |||||
* was invoked. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
markpara(int f, int n) | |||||
{ | |||||
int i = 0; | |||||
if (n == 0) | |||||
return (TRUE); | |||||
clearmark(FFARG, 0); | |||||
if (findpara() == FALSE) | |||||
return (TRUE); | |||||
(void)do_gotoeop(FFRAND, n, &i); | |||||
/* set the mark here */ | |||||
curwp->w_markp = curwp->w_dotp; | |||||
curwp->w_marko = curwp->w_doto; | |||||
(void)gotobop(FFRAND, i); | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Transpose the current paragraph with the following paragraph. If invoked | |||||
* multiple times, transpose to the n'th paragraph. If invoked between | |||||
* paragraphs, move to the previous paragraph, then continue. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
transposepara(int f, int n) | |||||
{ | |||||
int i = 0, status; | |||||
char flg; | |||||
if (n == 0) | |||||
return (TRUE); | |||||
undo_boundary_enable(FFRAND, 0); | |||||
/* find a paragraph, set mark, then goto the end */ | |||||
gotobop(FFRAND, 1); | |||||
curwp->w_markp = curwp->w_dotp; | |||||
curwp->w_marko = curwp->w_doto; | |||||
(void)gotoeop(FFRAND, 1); | |||||
/* take a note of buffer flags - we may need them */ | |||||
flg = curbp->b_flag; | |||||
/* clean out kill buffer then kill region */ | |||||
kdelete(); | |||||
if ((status = killregion(FFRAND, 1)) != TRUE) | |||||
return (status); | |||||
/* | |||||
* Now step through n paragraphs. If we reach the end of buffer, | |||||
* stop and paste the killed region back, then display a message. | |||||
*/ | |||||
if (do_gotoeop(FFRAND, n, &i) == FALSE) { | |||||
ewprintf("Cannot transpose paragraph, end of buffer reached."); | |||||
(void)gotobop(FFRAND, i); | |||||
(void)yank(FFRAND, 1); | |||||
curbp->b_flag = flg; | |||||
return (FALSE); | |||||
} | |||||
(void)yank(FFRAND, 1); | |||||
undo_boundary_enable(FFRAND, 1); | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Go down the buffer until we find a line with non-space characters. | |||||
*/ | |||||
int | |||||
findpara(void) | |||||
{ | |||||
int col, nospace = 0; | |||||
/* we move forward to find a para to mark */ | |||||
do { | |||||
curwp->w_doto = 0; | |||||
col = 0; | |||||
/* check if we are on a blank line */ | |||||
while (col < llength(curwp->w_dotp)) { | |||||
if (!isspace(lgetc(curwp->w_dotp, col))) | |||||
nospace = 1; | |||||
col++; | |||||
} | |||||
if (nospace) | |||||
break; | |||||
if (lforw(curwp->w_dotp) == curbp->b_headp) | |||||
return (FALSE); | |||||
curwp->w_dotp = lforw(curwp->w_dotp); | |||||
curwp->w_dotline++; | |||||
} while (1); | |||||
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) { | |||||
dobeep(); | |||||
ewprintf("Invalid fill column: %s", rep); | |||||
return (FALSE); | |||||
} | |||||
fillcol = nfill; | |||||
ewprintf("Fill column set to %d", fillcol); | |||||
} | |||||
return (TRUE); | |||||
} | |||||
int | |||||
sentencespace(int f, int n) | |||||
{ | |||||
if (f & FFARG) | |||||
dblspace = n > 1; | |||||
else | |||||
dblspace = !dblspace; | |||||
return (TRUE); | |||||
} |