Annotation of src/usr.bin/mg/paragraph.c, Revision 1.39
1.39 ! lum 1: /* $OpenBSD: paragraph.c,v 1.38 2015/09/24 07:07:59 lum Exp $ */
1.11 kjell 2:
3: /* This file is in the public domain. */
1.4 niklas 4:
1.1 deraadt 5: /*
6: * Code for dealing with paragraphs and filling. Adapted from MicroEMACS 3.6
7: * and GNU-ified by mwm@ucbvax. Several bug fixes by blarson@usc-oberon.
8: */
1.3 millert 9:
1.36 bcallah 10: #include <sys/queue.h>
1.24 lum 11: #include <ctype.h>
1.35 guenther 12: #include <limits.h>
1.36 bcallah 13: #include <signal.h>
14: #include <stdio.h>
15: #include <stdlib.h>
1.24 lum 16:
1.1 deraadt 17: #include "def.h"
18:
1.3 millert 19: static int fillcol = 70;
20:
1.1 deraadt 21: #define MAXWORD 256
22:
1.37 lum 23: static int findpara(void);
1.38 lum 24: static int do_gotoeop(int, int, int *);
1.37 lum 25:
1.1 deraadt 26: /*
1.27 lum 27: * Move to start of paragraph.
28: * Move backwards by line, checking from the 1st character forwards for the
29: * existence a non-space. If a non-space character is found, move to the
30: * preceding line. Keep doing this until a line with only spaces is found or
31: * the start of buffer.
1.1 deraadt 32: */
1.2 millert 33: /* ARGSUSED */
1.3 millert 34: int
1.8 cloder 35: gotobop(int f, int n)
1.1 deraadt 36: {
1.26 lum 37: int col, nospace;
1.24 lum 38:
1.3 millert 39: /* the other way... */
40: if (n < 0)
1.10 db 41: return (gotoeop(f, -n));
1.1 deraadt 42:
1.3 millert 43: while (n-- > 0) {
1.31 florian 44: nospace = 0;
1.24 lum 45: while (lback(curwp->w_dotp) != curbp->b_headp) {
46: curwp->w_doto = 0;
47: col = 0;
48:
49: while (col < llength(curwp->w_dotp) &&
50: (isspace(lgetc(curwp->w_dotp, col))))
51: col++;
52:
53: if (col >= llength(curwp->w_dotp)) {
54: if (nospace)
55: break;
56: } else
57: nospace = 1;
1.32 lum 58:
59: curwp->w_dotline--;
60: curwp->w_dotp = lback(curwp->w_dotp);
1.24 lum 61: }
1.1 deraadt 62: }
1.3 millert 63: /* force screen update */
1.19 kjell 64: curwp->w_rflag |= WFMOVE;
1.10 db 65: return (TRUE);
1.1 deraadt 66: }
67:
68: /*
1.27 lum 69: * Move to end of paragraph.
70: * See comments for gotobop(). Same, but moving forwards.
1.1 deraadt 71: */
1.2 millert 72: /* ARGSUSED */
1.3 millert 73: int
1.8 cloder 74: gotoeop(int f, int n)
1.1 deraadt 75: {
1.38 lum 76: int i;
77:
78: return(do_gotoeop(f, n, &i));
79: }
80:
81: int
82: do_gotoeop(int f, int n, int *i)
83: {
84: int col, nospace, j = 0;
1.25 lum 85:
1.3 millert 86: /* the other way... */
87: if (n < 0)
1.10 db 88: return (gotobop(f, -n));
1.1 deraadt 89:
1.3 millert 90: /* for each one asked for */
91: while (n-- > 0) {
1.38 lum 92: *i = ++j;
1.31 florian 93: nospace = 0;
1.25 lum 94: while (lforw(curwp->w_dotp) != curbp->b_headp) {
95: col = 0;
96: curwp->w_doto = 0;
97:
98: while (col < llength(curwp->w_dotp) &&
99: (isspace(lgetc(curwp->w_dotp, col))))
100: col++;
101:
102: if (col >= llength(curwp->w_dotp)) {
103: if (nospace)
104: break;
1.23 lum 105: } else
1.25 lum 106: nospace = 1;
107:
108: curwp->w_dotp = lforw(curwp->w_dotp);
109: curwp->w_dotline++;
1.34 lum 110:
111: /* do not continue after end of buffer */
112: if (lforw(curwp->w_dotp) == curbp->b_headp) {
113: gotoeol(FFRAND, 1);
114: curwp->w_rflag |= WFMOVE;
115: return (FALSE);
116: }
1.1 deraadt 117: }
118: }
1.33 lum 119:
1.3 millert 120: /* force screen update */
1.19 kjell 121: curwp->w_rflag |= WFMOVE;
1.10 db 122: return (TRUE);
1.1 deraadt 123: }
124:
125: /*
1.6 mickey 126: * Justify a paragraph. Fill the current paragraph according to the current
1.3 millert 127: * fill column.
1.1 deraadt 128: */
1.2 millert 129: /* ARGSUSED */
1.3 millert 130: int
1.8 cloder 131: fillpara(int f, int n)
1.1 deraadt 132: {
1.10 db 133: int c; /* current char during scan */
1.3 millert 134: int wordlen; /* length of current word */
135: int clength; /* position on line during fill */
136: int i; /* index during word copy */
137: int eopflag; /* Are we at the End-Of-Paragraph? */
138: int firstflag; /* first word? (needs no space) */
139: int newlength; /* tentative new line length */
140: int eolflag; /* was at end of line */
1.12 kjell 141: int retval; /* return value */
1.13 deraadt 142: struct line *eopline; /* pointer to line just past EOP */
1.3 millert 143: char wbuf[MAXWORD]; /* buffer for current word */
1.1 deraadt 144:
1.17 kjell 145: undo_boundary_enable(FFRAND, 0);
1.12 kjell 146:
1.1 deraadt 147: /* record the pointer to the line just past the EOP */
1.5 art 148: (void)gotoeop(FFRAND, 1);
1.2 millert 149: if (curwp->w_doto != 0) {
1.1 deraadt 150: /* paragraph ends at end of buffer */
1.5 art 151: (void)lnewline();
1.1 deraadt 152: eopline = lforw(curwp->w_dotp);
1.2 millert 153: } else
154: eopline = curwp->w_dotp;
1.1 deraadt 155:
156: /* and back top the begining of the paragraph */
1.5 art 157: (void)gotobop(FFRAND, 1);
1.1 deraadt 158:
159: /* initialize various info */
1.9 avsm 160: while (inword() == 0 && forwchar(FFRAND, 1));
1.3 millert 161:
1.1 deraadt 162: clength = curwp->w_doto;
163: wordlen = 0;
164:
165: /* scan through lines, filling words */
166: firstflag = TRUE;
167: eopflag = FALSE;
168: while (!eopflag) {
1.3 millert 169:
1.1 deraadt 170: /* get the next character in the paragraph */
1.3 millert 171: if ((eolflag = (curwp->w_doto == llength(curwp->w_dotp)))) {
1.1 deraadt 172: c = ' ';
173: if (lforw(curwp->w_dotp) == eopline)
174: eopflag = TRUE;
175: } else
176: c = lgetc(curwp->w_dotp, curwp->w_doto);
177:
178: /* and then delete it */
1.12 kjell 179: if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag) {
180: retval = FALSE;
181: goto cleanup;
182: }
1.1 deraadt 183:
184: /* if not a separator, just add it in */
185: if (c != ' ' && c != '\t') {
186: if (wordlen < MAXWORD - 1)
187: wbuf[wordlen++] = c;
188: else {
1.2 millert 189: /*
1.21 matthew 190: * You lose chars beyond MAXWORD if the word
191: * is too long. I'm too lazy to fix it now; it
1.2 millert 192: * just silently truncated the word before,
193: * so I get to feel smug.
1.1 deraadt 194: */
195: ewprintf("Word too long!");
196: }
197: } else if (wordlen) {
1.3 millert 198:
1.10 db 199: /* calculate tentative new length with word added */
1.1 deraadt 200: newlength = clength + 1 + wordlen;
1.3 millert 201:
1.2 millert 202: /*
203: * if at end of line or at doublespace and previous
1.1 deraadt 204: * character was one of '.','?','!' doublespace here.
1.20 lum 205: * behave the same way if a ')' is preceded by a
206: * [.?!] and followed by a doublespace.
1.1 deraadt 207: */
1.6 mickey 208: if ((eolflag ||
209: curwp->w_doto == llength(curwp->w_dotp) ||
1.3 millert 210: (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' '
1.20 lum 211: || c == '\t') && (ISEOSP(wbuf[wordlen - 1]) ||
1.22 lum 212: (wbuf[wordlen - 1] == ')' && wordlen >= 2 &&
1.20 lum 213: ISEOSP(wbuf[wordlen - 2]))) &&
1.3 millert 214: wordlen < MAXWORD - 1)
1.1 deraadt 215: wbuf[wordlen++] = ' ';
1.3 millert 216:
1.1 deraadt 217: /* at a word break with a word waiting */
218: if (newlength <= fillcol) {
219: /* add word to current line */
220: if (!firstflag) {
1.5 art 221: (void)linsert(1, ' ');
1.1 deraadt 222: ++clength;
223: }
224: firstflag = FALSE;
225: } else {
1.2 millert 226: if (curwp->w_doto > 0 &&
227: lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') {
1.1 deraadt 228: curwp->w_doto -= 1;
1.5 art 229: (void)ldelete((RSIZE) 1, KNONE);
1.1 deraadt 230: }
231: /* start a new line */
1.5 art 232: (void)lnewline();
1.1 deraadt 233: clength = 0;
234: }
235:
236: /* and add the word in in either case */
1.2 millert 237: for (i = 0; i < wordlen; i++) {
1.5 art 238: (void)linsert(1, wbuf[i]);
1.1 deraadt 239: ++clength;
240: }
241: wordlen = 0;
242: }
243: }
244: /* and add a last newline for the end of our new paragraph */
1.5 art 245: (void)lnewline();
1.3 millert 246:
1.2 millert 247: /*
1.10 db 248: * We really should wind up where we started, (which is hard to keep
1.1 deraadt 249: * track of) but I think the end of the last line is better than the
1.10 db 250: * beginning of the blank line.
1.2 millert 251: */
1.5 art 252: (void)backchar(FFRAND, 1);
1.12 kjell 253: retval = TRUE;
254: cleanup:
1.17 kjell 255: undo_boundary_enable(FFRAND, 1);
1.12 kjell 256: return (retval);
1.1 deraadt 257: }
258:
1.6 mickey 259: /*
1.39 ! lum 260: * Delete n paragraphs. Move to the beginning of the current paragraph, or if
! 261: * the cursor is on an empty line, move down the buffer to the first line with
! 262: * non-space characters. Then mark n paragraphs and delete.
1.3 millert 263: */
1.2 millert 264: /* ARGSUSED */
1.3 millert 265: int
1.8 cloder 266: killpara(int f, int n)
1.1 deraadt 267: {
1.37 lum 268: int lineno, status;
269:
270: if (findpara() == FALSE)
271: return (TRUE);
272:
273: /* go to the beginning of the paragraph */
274: (void)gotobop(FFRAND, 1);
1.1 deraadt 275:
1.37 lum 276: /* take a note of the line number for after deletions and set mark */
277: lineno = curwp->w_dotline;
278: curwp->w_markp = curwp->w_dotp;
279: curwp->w_marko = curwp->w_doto;
1.1 deraadt 280:
1.37 lum 281: (void)gotoeop(FFRAND, n);
1.1 deraadt 282:
1.37 lum 283: if ((status = killregion(FFRAND, 1)) != TRUE)
284: return (status);
1.1 deraadt 285:
1.37 lum 286: curwp->w_dotline = lineno;
1.38 lum 287: return (TRUE);
288: }
289:
290: /*
1.39 ! lum 291: * Mark n paragraphs starting with the n'th and working our way backwards.
1.38 lum 292: * This leaves the cursor at the beginning of the paragraph where markpara()
293: * was invoked.
294: */
295: /* ARGSUSED */
296: int
297: markpara(int f, int n)
298: {
299: int i = 0;
300:
301: clearmark(FFARG, 0);
302:
303: if (findpara() == FALSE)
304: return (TRUE);
305:
306: (void)do_gotoeop(FFRAND, n, &i);
307:
308: /* set the mark here */
309: curwp->w_markp = curwp->w_dotp;
310: curwp->w_marko = curwp->w_doto;
311:
312: (void)gotobop(FFRAND, i);
313:
1.37 lum 314: return (TRUE);
315: }
316:
317: /*
1.39 ! lum 318: * Go down the buffer until we find a line with non-space characters.
1.37 lum 319: */
320: int
321: findpara(void)
322: {
323: int col, nospace = 0;
1.3 millert 324:
1.37 lum 325: /* we move forward to find a para to mark */
326: do {
1.3 millert 327: curwp->w_doto = 0;
1.37 lum 328: col = 0;
329:
330: /* check if we are on a blank line */
331: while (col < llength(curwp->w_dotp)) {
332: if (!isspace(lgetc(curwp->w_dotp, col)))
333: nospace = 1;
334: col++;
335: }
336: if (nospace)
337: break;
1.1 deraadt 338:
1.37 lum 339: if (lforw(curwp->w_dotp) == curbp->b_headp)
340: return (FALSE);
341:
342: curwp->w_dotp = lforw(curwp->w_dotp);
343: curwp->w_dotline++;
344: } while (1);
1.34 lum 345:
1.10 db 346: return (TRUE);
1.1 deraadt 347: }
348:
349: /*
1.3 millert 350: * Insert char with work wrap. Check to see if we're past fillcol, and if so,
351: * justify this line. As a last step, justify the line.
1.1 deraadt 352: */
1.2 millert 353: /* ARGSUSED */
1.3 millert 354: int
1.8 cloder 355: fillword(int f, int n)
1.1 deraadt 356: {
1.3 millert 357: char c;
358: int col, i, nce;
1.1 deraadt 359:
360: for (i = col = 0; col <= fillcol; ++i, ++col) {
1.2 millert 361: if (i == curwp->w_doto)
362: return selfinsert(f, n);
1.1 deraadt 363: c = lgetc(curwp->w_dotp, i);
364: if (c == '\t'
1.3 millert 365: #ifdef NOTAB
1.2 millert 366: && !(curbp->b_flag & BFNOTAB)
1.1 deraadt 367: #endif
1.2 millert 368: )
369: col |= 0x07;
370: else if (ISCTRL(c) != FALSE)
371: ++col;
1.1 deraadt 372: }
373: if (curwp->w_doto != llength(curwp->w_dotp)) {
1.5 art 374: (void)selfinsert(f, n);
1.1 deraadt 375: nce = llength(curwp->w_dotp) - curwp->w_doto;
1.2 millert 376: } else
377: nce = 0;
1.1 deraadt 378: curwp->w_doto = i;
379:
380: if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t')
381: do {
1.5 art 382: (void)backchar(FFRAND, 1);
1.7 deraadt 383: } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
384: c != '\t' && curwp->w_doto > 0);
1.1 deraadt 385:
386: if (curwp->w_doto == 0)
387: do {
1.5 art 388: (void)forwchar(FFRAND, 1);
1.7 deraadt 389: } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
390: c != '\t' && curwp->w_doto < llength(curwp->w_dotp));
1.1 deraadt 391:
1.5 art 392: (void)delwhite(FFRAND, 1);
393: (void)lnewline();
1.1 deraadt 394: i = llength(curwp->w_dotp) - nce;
1.2 millert 395: curwp->w_doto = i > 0 ? i : 0;
1.19 kjell 396: curwp->w_rflag |= WFMOVE;
1.2 millert 397: if (nce == 0 && curwp->w_doto != 0)
1.10 db 398: return (fillword(f, n));
399: return (TRUE);
1.1 deraadt 400: }
401:
1.3 millert 402: /*
403: * Set fill column to n for justify.
404: */
405: int
1.8 cloder 406: setfillcol(int f, int n)
1.2 millert 407: {
1.16 kjell 408: char buf[32], *rep;
409: const char *es;
1.18 kjell 410: int nfill;
1.16 kjell 411:
412: if ((f & FFARG) != 0) {
413: fillcol = n;
414: } else {
415: if ((rep = eread("Set fill-column: ", buf, sizeof(buf),
416: EFNEW | EFCR)) == NULL)
417: return (ABORT);
418: else if (rep[0] == '\0')
419: return (FALSE);
1.18 kjell 420: nfill = strtonum(rep, 0, INT_MAX, &es);
421: if (es != NULL) {
1.30 lum 422: dobeep();
1.18 kjell 423: ewprintf("Invalid fill column: %s", rep);
1.16 kjell 424: return (FALSE);
1.18 kjell 425: }
426: fillcol = nfill;
1.16 kjell 427: ewprintf("Fill column set to %d", fillcol);
428: }
1.10 db 429: return (TRUE);
1.1 deraadt 430: }