/*
* User text formatting functions
* Copyright
* (C) 1992 Joseph H. Allen
*
* This file is part of JOE (Joe's Own Editor)
*/
#include "types.h"
/* Center line cursor is on and move cursor to beginning of next line */
int ucenter(W *w, int k)
{
BW *bw;
P *p, *q;
off_t endcol, begcol, x;
int c;
WIND_BW(bw, w);
p = bw->cursor;
p_goto_eol(p);
while (joe_isblank(bw->b->o.charmap, (c = prgetc(p))))
/* do nothing */;
if (c == '\n') {
pgetc(p);
goto done;
}
if (c == NO_MORE_DATA)
goto done;
pgetc(p);
endcol = piscol(p);
p_goto_bol(p);
while (joe_isblank(bw->b->o.charmap, (c = pgetc(p))))
/* do nothing */;
if (c == '\n') {
prgetc(p);
goto done;
}
if (c == NO_MORE_DATA)
goto done;
prgetc(p);
begcol = piscol(p);
if (endcol - begcol > bw->o.rmargin + bw->o.lmargin)
goto done;
q = pdup(p, "ucenter");
p_goto_bol(q);
bdel(q, p);
prm(q);
for (x = 0; x != (bw->o.lmargin + bw->o.rmargin) / 2 - (endcol - begcol) / 2; ++x)
binsc(p, ' ');
done:
if (!pnextl(p)) {
binsc(p, '\n');
pgetc(p);
return -1;
} else
return 0;
}
/* Return true if c is a character which can indent a paragraph */
/* > is for mail/news
* * is for C comments
* / is for C++ comments
* # is for shell script comments
* % is for TeX comments
*/
static int cpara(BW *bw, int c)
{
if (c == ' ' || c == '\t')
return 1;
if (bw->o.cpara) {
const char *s = bw->o.cpara;
while (*s) {
if (utf8_decode_fwrd(&s, NULL) == c)
return 1;
}
}
return 0;
#ifdef junk
if (c == ' ' || c == '\t' || c == '\\' ||
c == '>' || c == '|' || c == ':' || c == '*' || c == '/' ||
c == ',' || c == '.' || c == '?' || c == ';' || c == ']' ||
c == '}' || c == '=' || c == '+' || c == '-' || c == '_' ||
c == ')' || c == '&' || c == '^' || c == '%' || c == '$' ||
c == '#' || c == '@' || c == '!' || c == '~')
return 1;
else
return 0;
#endif
}
/* Return true if this first non-whitespace character means
we are not a paragraph for sure (for example, '.' in nroff) */
static int cnotpara(BW *bw, int c)
{
const char *s;
if (bw->o.cnotpara) {
s = bw->o.cnotpara;
while (*s) {
if (c == utf8_decode_fwrd(&s, NULL))
return 1;
}
}
return 0;
}
/* Return true if line is definitly not a paragraph line.
* Lines which arn't paragraph lines:
* 1) Blank lines
* 2) Lines which begin with '.'
*/
static int pisnpara(BW *bw, P *p)
{
P *q;
int c;
q = pdup(p, "pisnpara");
p_goto_bol(q);
while (cpara(bw, c = pgetc(q)))
/* do nothing */;
prm(q);
if (cnotpara(bw, c) || c == '\r' || c == '\n')
return 1;
else
return 0;
}
/* Determine amount of indentation on current line. Set first
to include '-' and '*' bullets. */
static off_t nindent(BW *bw, P *p, int first)
{
P *q = pdup(p, "nindent");
off_t col;
int c;
p_goto_bol(q);
do {
col = q->col;
} while (cpara(bw, (c = pgetc(q))));
if (first && (c == '-' || c == '*')) {
c = pgetc(q);
if (c == ' ') {
col = q->col;
}
}
prm(q);
return col;
}
/* Get indentation prefix column */
static off_t prefix(BW *bw, P *p,int up)
{
off_t len;
P *q = pdup(p, "prefix");
p_goto_bol(q);
while (cpara(bw, brch(q)))
pgetc(q);
while (!pisbol(q)) {
/* int c; */
if (!joe_isblank(p->b->o.charmap, ( /* c = */ prgetc(q)))) {
/*
if (up && (c == '*' || c == '-')) {
if (!pisbol(q)) {
c = prgetc(q);
pgetc(q);
if (c == ' ' || c == '\t')
goto skip;
} else
goto skip;
}
pgetc(q);
*/
break;
/* skip:; */
}
}
len = piscol(q);
prm(q);
return len;
}
/* Move pointer to beginning of paragraph
*
* This function simply moves backwards until it sees:
* 0) The beginning of the file
* 1) A blank line
* 2) A line with a different indentation prefix
* 3) A line with indentation greater than that of the line we started with
* 4) A line with indentation less than that of the starting line, but with
* a blank line (or beginning of file) preceeding it.
*/
int within = 0;
P *pbop(BW *bw, P *p)
{
off_t indent;
off_t prelen;
P *last;
p_goto_bol(p);
indent = nindent(bw, p, 0);
prelen = prefix(bw, p, 0);
last = pdup(p, "pbop");
while (!pisbof(p) && (!within || !markb || p->byte > markb->byte)) {
off_t ind;
off_t len;
pprevl(p);
p_goto_bol(p);
ind = nindent(bw, p, 0);
len = prefix(bw, p, 0);
if (pisnpara(bw, p) || len != prelen) {
pset(p, last);
break;
}
if (ind > indent) {
/*
int ok = 1;
P *q = pdup(p, "pbop");
if (pprevl(q)) {
p_goto_bol(q);
if (nindent(bw, q, 0) == ind)
ok = 0;
}
prm(q);
if (!ok)
pnextl(p);
*/
break;
}
if (ind < indent) {
pset(p, last);
break;
/* if (pisbof(p)) {
break;
}
pprevl(p);
p_goto_bol(p);
if (pisnpara(bw, p)) {
pnextl(p);
break;
} else {
pnextl(p);
pnextl(p);
break;
} */
}
pset(last, p);
}
prm(last);
return p;
}
/* Move pointer to end of paragraph. Pointer must already be on first
* line of paragraph for this to work correctly.
*
* This function moves forwards until it sees:
* 0) The end of the file.
* 1) A blank line
* 2) A line with indentation different from the second line of the paragraph
* 3) A line with prefix column different from first line
*/
P *peop(BW *bw, P *p)
{
off_t indent;
off_t prelen;
if (!pnextl(p) || pisnpara(bw, p) || (within && markk && p->byte >= markk->byte))
return p;
indent = nindent(bw, p, 0);
prelen = prefix(bw, p, 0);
while (pnextl(p) && (!within || !markk || p->byte < markk->byte)) {
off_t ind = nindent(bw, p, 0);
off_t len = prefix(bw, p, 0);
if (ind != indent || len != prelen || pisnpara(bw, p))
break;
}
return p;
}
/* Motion commands */
int ubop(W *w, int k)
{
BW *bw;
P *q;
WIND_BW(bw, w);
q = pdup(bw->cursor, "ubop");
up:
while (pisnpara(bw, q) && !pisbof(q) && (!within || !markb || q->byte > markb->byte))
pprevl(q);
pbop(bw, q);
if (q->byte != bw->cursor->byte) {
pset(bw->cursor, q);
prm(q);
return 0;
} else if (!pisbof(q)) {
prgetc(q);
goto up;
} else {
prm(q);
return -1;
}
}
int ueop(W *w, int k)
{
P *q;
BW *bw;
WIND_BW(bw, w);
q = pdup(bw->cursor, "ueop");
up:
while (pisnpara(bw, q) && !piseof(q))
pnextl(q);
pbop(bw, q);
peop(bw, q);
if (q->byte != bw->cursor->byte) {
pset(bw->cursor, q);
prm(q);
return 0;
} else if (!piseof(q)) {
pnextl(q);
goto up;
} else {
prm(q);
return -1;
}
}
/* Wrap word. If 'french' is set, only one space will be placed
* after . ? or !
*/
void wrapword(BW *bw, P *p, off_t indent, int french, int no_over, char *indents)
{
P *q;
P *r;
P *s;
int rmf = 0;
int c;
off_t to = p->byte;
int my_indents = 0;
/* autoindent when called by utype */
if (!indents) {
/* Get indentation prefix from beginning of line */
s = pdup(p, "wrapword");
p_goto_bol(s);
pbop(bw, s);
/* Record indentation of second line of paragraph, of first
* line if there is only one line */
q = pdup(s, "wrapword");
pnextl(q);
if (q->line < p->line) {
/* Second line */
P *tr = pdup(q, "wrapword");
indent = nindent(bw, q, 0);
pcol(tr, indent);
indents = brs(q, tr->byte - q->byte); /* Risky */
prm(tr);
} else {
/* First line */
P *tr = pdup(s, "uformat");
ptrdiff_t x, y;
indent = nindent(bw, s, 1);
pcol(tr, indent);
indents = brs(s, tr->byte - s->byte); /* Risky */
prm(tr);
if (!bw->o.autoindent) {
/* Don't indent second line of single-line paragraphs if autoindent is off */
ptrdiff_t tx = zlen(indents);
ptrdiff_t orgx = tx;
while (tx && (indents[tx - 1] == ' ' || indents[tx - 1] == '\t'))
indents[--tx] = 0;
if (tx && orgx != tx) {
indents[tx++] = ' ';
indents[tx] = 0;
}
indent = txtwidth1(bw->o.charmap, bw->o.tab, indents, tx);
}
for (x = 0; indents[x] && (indents[x] == ' ' || indents[x] == '\t'); ++x);
y = zlen(indents);
while (y && (indents[y - 1] == ' ' || indents[y - 1] == '\t'))
--y;
/* Don't duplicate bullet */
if (y && ((indents[y - 1] == '*' && !cpara(bw, '*')) || (indents[y - 1] == '-' && !cpara(bw, '-'))) &&
(y == 1 || indents[y - 2] == ' ' || indents[y - 2] == '\t'))
indents[y - 1] = ' ';
/* Fix C comment */
if (indents[x] == '/' && indents[x + 1] == '*')
indents[x] = ' ';
}
if (bw->o.lmargin > indent) {
int x;
for (x = 0; indents[x] == ' ' || indents[x] == '\t'; ++x);
if (!indents[x]) {
joe_free(indents);
indent = bw->o.lmargin;
indents = (char *)joe_malloc(indent+1); /* Risky */
for (x = 0; x != indent; ++x)
indents[x] = ' ';
indents[x] = 0;
}
}
my_indents = 1;
prm(q);
prm(s);
}
/*
if(!indents) {
int f = 0;
P *r = pdup(p);
p_goto_bol(r);
q = pdup(r);
while(cpara(c = brc(q))) {
if(!joe_isblank(c))
f = 1;
pgetc(q);
}
if(f) {
indents = brs(r, q->byte-r->byte);
rmf = 1;
if(indents[0] == '/' && indents[1] == '*')
indents[0] = ' ';
}
prm(r);
prm(q);
}
*/
/* Get to beginning of word */
while (!pisbol(p) && piscol(p) > indent && !joe_isblank(p->b->o.charmap, prgetc(p)))
/* do nothing */;
/* If we found the beginning of a word... */
if (!pisbol(p) && piscol(p) > indent) {
/* Move q to two (or one if 'french' is set) spaces after end of previous
word */
q = pdup(p, "wrapword");
while (!pisbol(q))
if (!joe_isblank(p->b->o.charmap, (c = prgetc(q)))) {
pgetc(q);
if ((c == '.' || c == '?' || c == '!')
&& q->byte != p->byte && !french)
pgetc(q);
break;
}
pgetc(p);
/* Delete space between start of word and end of previous word */
to -= p->byte - q->byte;
bdel(q, p);
prm(q);
if (bw->o.flowed) {
binsc(p, ' ');
pgetc(p);
++to;
}
/* Move word to beginning of next line */
binsc(p, '\n');
/* When overtype is on, do not insert lines */
if (!no_over && p->b->o.overtype){
/* delete the next line break which is unnecessary */
r = pdup(p, "wrapword");
/* p_goto_eol(r); */
pgetc(r);
p_goto_eol(r);
s = pdup(r, "wrapword");
pgetc(r);
bdel(s,r);
binsc(r, ' ');
/* Now we got to take care that all subsequent lines are not longer than the right margin */
/* Move cursor to right margin */
pfwrd(r, r->b->o.rmargin - r->col);
/* Make a copy of the cursor and move the copied cursor to the end of the line */
prm(s);
s = pdup(r, "wrapword");
p_goto_eol(s);
/* If s is located behind r then the line goes beyond the right margin and we need to call wordwrap() for that line. */
/*
if (r->byte < s->byte){
wrapword(bw, r, indent, french, 1, indents);
}
*/
prm(r);
prm(s);
}
++to;
if (p->b->o.crlf)
++to;
pgetc(p);
/* Indent to left margin */
if (indents) {
binss(p, indents);
to += zlen(indents);
} else
while (indent--) {
binsc(p, ' ');
++to;
}
if (rmf)
joe_free(indents);
}
/* Move cursor back to original position */
pfwrd(p, to - p->byte);
if (my_indents)
joe_free(indents);
}
/* Reformat paragraph */
int uformat(W *w, int k)
{
off_t indent;
char *indents;
B *buf;
P *b;
off_t curoff;
int c;
P *p, *q;
BW *bw;
int flag;
WIND_BW(bw, w);
p = pdup(bw->cursor, "uformat");
p_goto_bol(p);
/* Do nothing if we're not on a paragraph line */
if (pisnpara(bw, p)) {
prm(p);
return 0;
}
/* Move p to beginning of paragraph, bw->cursor to end of paragraph and
* set curoff to original cursor offset within the paragraph */
pbop(bw, p);
curoff = bw->cursor->byte - p->byte;
pset(bw->cursor, p);
peop(bw, bw->cursor);
/* Ensure that paragraph ends on a beginning of a line */
if (!pisbol(bw->cursor))
binsc(bw->cursor, '\n'), pgetc(bw->cursor);
/* Record indentation of second line of paragraph, of first line if there
* is only one line */
q = pdup(p, "uformat");
pnextl(q);
if (q->line != bw->cursor->line) {
P *r = pdup(q, "uformat");
indent = nindent(bw, q, 0);
pcol(r, indent);
indents = brs(q, r->byte - q->byte); /* Risky */
prm(r);
} else {
P *r = pdup(p, "uformat");
ptrdiff_t x, y;
indent = nindent(bw, p, 1); /* allowing * and - here */
pcol(r, indent);
indents = brs(p, r->byte - p->byte); /* Risky */
prm(r);
if (!bw->o.autoindent) {
/* Don't indent second line of single-line paragraphs if autoindent is off */
ptrdiff_t tx = zlen(indents);
while (tx && (indents[tx - 1] == ' ' || indents[tx - 1] == '\t'))
indents[--tx] = 0;
if (tx) {
indents[tx++] = ' ';
indents[tx] = 0;
}
indent = txtwidth1(bw->o.charmap, bw->o.tab, indents, tx);
}
for (x = 0; indents[x] && (indents[x] == ' ' || indents[x] == '\t'); ++x);
y = zlen(indents);
while (y && (indents[y - 1] == ' ' || indents[y - 1] == '\t'))
--y;
/* Don't duplicate if it looks like a bullet */
if (y && ((indents[y - 1] == '*' && !cpara(bw, '*')) || (indents[y - 1] == '-' && !cpara(bw, '-'))) &&
(y == 1 || indents[y - 2] == ' ' || indents[y - 2] == '\t'))
indents[y - 1] = ' ';
/* Fix C comment */
if (indents[x] == '/' && indents[x + 1] == '*')
indents[x] = ' ';
}
prm(q);
/* But if the left margin is greater, we use that instead */
if (bw->o.lmargin > indent) {
int x;
for (x = 0; indents[x] == ' ' || indents[x] == '\t'; ++x);
if (!indents[x]) {
joe_free(indents);
indent = bw->o.lmargin;
indents = (char *)joe_malloc(indent+1); /* Risky, indent could be very large in theory */
for (x = 0; x != indent; ++x)
indents[x] = ' ';
indents[x] = 0;
}
}
/* Cut paragraph into new buffer */
/* New buffer needs to inherit UTF-8 and CR-LF options */
buf = bcpy(p, bw->cursor);
buf->o.crlf = p->b->o.crlf;
buf->o.charmap = p->b->o.charmap;
bdel(p, bw->cursor);
/* text is in buffer. insert it at cursor */
b = pdup(buf->bof, "uformat");
/* First line: preserve whitespace within this line
(but still apply french spacing after periods) */
flag = 0;
while (!piseof(b)) {
c = brch(b);
if (joe_isblank(b->b->o.charmap,c) || c == '\n') {
int f = 0;
/* First space after end of word */
if (flag && piscol(p) > bw->o.rmargin)
wrapword(bw, p, indent, bw->o.french, 1, indents);
flag = 0;
/* Stop if we're at end of line */
if (c == '\n' || piseolblank(b))
break;
/* Set f if previous character was '.', '?' or '!' */
if (!pisbof(b)) {
P *d = pdup(b, "uformat");
int g = prgetc(d);
if (g=='.' || g=='?' || g=='!') {
f = 1;
}
prm(d);
}
if (f) {
/* Skip past the whitespace. */
while (joe_isblank(b->b->o.charmap, brc(b))) {
if(b->byte == curoff)
pset(bw->cursor, p);
pgetc(b);
}
/* Insert proper amount of whitespace */
if (!piseof(b)) {
if (!bw->o.french)
binsc(p, ' '), pgetc(p);
binsc(p, ' ');
pgetc(p);
}
} else {
/* Insert whitespace character, advance pointer */
binsc(p, pgetc(b));
pgetc(p);
}
} else {
/* Insert characters of word and wrap if necessary */
if (b->byte == curoff)
pset(bw->cursor, p);
binsc(p, pgetc(b));
pgetc(p);
flag = 1;
}
}
/* Remaining lines: collapse whitespace */
flag = 0;
while (!piseof(b)) {
c = brc(b);
if (joe_isblank(b->b->o.charmap,c) || c == '\n') {
int f = 0;
P *d;
int g;
/* First space at end of word, wrap it */
if (flag && piscol(p) > bw->o.rmargin)
wrapword(bw, p, indent, bw->o.french, 1, indents);
flag = 0;
/* Detect end of sentence */
d=pdup(b, "uformat");
g=prgetc(d);
if (g=='.' || g=='?' || g=='!') {
f = 1;
}
prm(d);
/* Skip past the whitespace. Skip over indentations */
loop:
c = brc(b);
if (c == '\n') {
if (b->byte == curoff)
pset(bw->cursor, p);
pgetc(b);
while (cpara(bw, (c=brch(b)))) {
if (b->byte == curoff)
pset(bw->cursor, p);
pgetc(b);
}
}
if (joe_isblank(b->b->o.charmap,c)) {
if(b->byte == curoff)
pset(bw->cursor, p);
pgetc(b);
goto loop;
}
/* Insert proper amount of whitespace */
if (!piseof(b)) {
if (f && !bw->o.french)
binsc(p, ' '), pgetc(p);
binsc(p, ' ');
pgetc(p);
}
} else {
/* Insert characters of word and wrap if necessary */
if (b->byte == curoff)
pset(bw->cursor, p);
binsc(p, pgetc(b));
pgetc(p);
flag = 1;
}
}
if (flag && piscol(p) > bw->o.rmargin)
wrapword(bw, p, indent, bw->o.french, 1, indents);
binsc(p, '\n');
prm(p);
brm(buf);
joe_free(indents);
return 0;
}
/* Format entire block */
int ufmtblk(W *w, int k)
{
BW *bw;
WIND_BW(bw, w);
if (markv(1) && bw->cursor->byte >= markb->byte && bw->cursor->byte <= markk->byte) {
markk->end = 1;
utomarkk(w, 0);
within = 1;
do {
ubop(w, 0), uformat(w, 0);
} while (bw->cursor->byte > markb->byte);
within = 0;
markk->end = 0;
if (lightoff)
unmark(w, 0);
return 0;
} else
return uformat(w, 0);
}