Annotation of src/usr.bin/mg/paragraph.c, Revision 1.42
1.42 ! mmcc 1: /* $OpenBSD: paragraph.c,v 1.41 2015/10/10 09:13:14 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.41 lum 145: if (n == 0)
146: return (TRUE);
147:
1.17 kjell 148: undo_boundary_enable(FFRAND, 0);
1.12 kjell 149:
1.1 deraadt 150: /* record the pointer to the line just past the EOP */
1.5 art 151: (void)gotoeop(FFRAND, 1);
1.2 millert 152: if (curwp->w_doto != 0) {
1.1 deraadt 153: /* paragraph ends at end of buffer */
1.5 art 154: (void)lnewline();
1.1 deraadt 155: eopline = lforw(curwp->w_dotp);
1.2 millert 156: } else
157: eopline = curwp->w_dotp;
1.1 deraadt 158:
1.42 ! mmcc 159: /* and back top the beginning of the paragraph */
1.5 art 160: (void)gotobop(FFRAND, 1);
1.1 deraadt 161:
162: /* initialize various info */
1.9 avsm 163: while (inword() == 0 && forwchar(FFRAND, 1));
1.3 millert 164:
1.1 deraadt 165: clength = curwp->w_doto;
166: wordlen = 0;
167:
168: /* scan through lines, filling words */
169: firstflag = TRUE;
170: eopflag = FALSE;
171: while (!eopflag) {
1.3 millert 172:
1.1 deraadt 173: /* get the next character in the paragraph */
1.3 millert 174: if ((eolflag = (curwp->w_doto == llength(curwp->w_dotp)))) {
1.1 deraadt 175: c = ' ';
176: if (lforw(curwp->w_dotp) == eopline)
177: eopflag = TRUE;
178: } else
179: c = lgetc(curwp->w_dotp, curwp->w_doto);
180:
181: /* and then delete it */
1.12 kjell 182: if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag) {
183: retval = FALSE;
184: goto cleanup;
185: }
1.1 deraadt 186:
187: /* if not a separator, just add it in */
188: if (c != ' ' && c != '\t') {
189: if (wordlen < MAXWORD - 1)
190: wbuf[wordlen++] = c;
191: else {
1.2 millert 192: /*
1.21 matthew 193: * You lose chars beyond MAXWORD if the word
194: * is too long. I'm too lazy to fix it now; it
1.2 millert 195: * just silently truncated the word before,
196: * so I get to feel smug.
1.1 deraadt 197: */
198: ewprintf("Word too long!");
199: }
200: } else if (wordlen) {
1.3 millert 201:
1.10 db 202: /* calculate tentative new length with word added */
1.1 deraadt 203: newlength = clength + 1 + wordlen;
1.3 millert 204:
1.2 millert 205: /*
206: * if at end of line or at doublespace and previous
1.1 deraadt 207: * character was one of '.','?','!' doublespace here.
1.20 lum 208: * behave the same way if a ')' is preceded by a
209: * [.?!] and followed by a doublespace.
1.1 deraadt 210: */
1.6 mickey 211: if ((eolflag ||
212: curwp->w_doto == llength(curwp->w_dotp) ||
1.3 millert 213: (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' '
1.20 lum 214: || c == '\t') && (ISEOSP(wbuf[wordlen - 1]) ||
1.22 lum 215: (wbuf[wordlen - 1] == ')' && wordlen >= 2 &&
1.20 lum 216: ISEOSP(wbuf[wordlen - 2]))) &&
1.3 millert 217: wordlen < MAXWORD - 1)
1.1 deraadt 218: wbuf[wordlen++] = ' ';
1.3 millert 219:
1.1 deraadt 220: /* at a word break with a word waiting */
221: if (newlength <= fillcol) {
222: /* add word to current line */
223: if (!firstflag) {
1.5 art 224: (void)linsert(1, ' ');
1.1 deraadt 225: ++clength;
226: }
227: firstflag = FALSE;
228: } else {
1.2 millert 229: if (curwp->w_doto > 0 &&
230: lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') {
1.1 deraadt 231: curwp->w_doto -= 1;
1.5 art 232: (void)ldelete((RSIZE) 1, KNONE);
1.1 deraadt 233: }
234: /* start a new line */
1.5 art 235: (void)lnewline();
1.1 deraadt 236: clength = 0;
237: }
238:
239: /* and add the word in in either case */
1.2 millert 240: for (i = 0; i < wordlen; i++) {
1.5 art 241: (void)linsert(1, wbuf[i]);
1.1 deraadt 242: ++clength;
243: }
244: wordlen = 0;
245: }
246: }
247: /* and add a last newline for the end of our new paragraph */
1.5 art 248: (void)lnewline();
1.3 millert 249:
1.2 millert 250: /*
1.10 db 251: * We really should wind up where we started, (which is hard to keep
1.1 deraadt 252: * track of) but I think the end of the last line is better than the
1.10 db 253: * beginning of the blank line.
1.2 millert 254: */
1.5 art 255: (void)backchar(FFRAND, 1);
1.12 kjell 256: retval = TRUE;
257: cleanup:
1.17 kjell 258: undo_boundary_enable(FFRAND, 1);
1.12 kjell 259: return (retval);
1.1 deraadt 260: }
261:
1.6 mickey 262: /*
1.39 lum 263: * Delete n paragraphs. Move to the beginning of the current paragraph, or if
264: * the cursor is on an empty line, move down the buffer to the first line with
265: * non-space characters. Then mark n paragraphs and delete.
1.3 millert 266: */
1.2 millert 267: /* ARGSUSED */
1.3 millert 268: int
1.8 cloder 269: killpara(int f, int n)
1.1 deraadt 270: {
1.37 lum 271: int lineno, status;
272:
1.41 lum 273: if (n == 0)
274: return (TRUE);
275:
1.37 lum 276: if (findpara() == FALSE)
277: return (TRUE);
278:
279: /* go to the beginning of the paragraph */
280: (void)gotobop(FFRAND, 1);
1.1 deraadt 281:
1.37 lum 282: /* take a note of the line number for after deletions and set mark */
283: lineno = curwp->w_dotline;
284: curwp->w_markp = curwp->w_dotp;
285: curwp->w_marko = curwp->w_doto;
1.1 deraadt 286:
1.37 lum 287: (void)gotoeop(FFRAND, n);
1.1 deraadt 288:
1.37 lum 289: if ((status = killregion(FFRAND, 1)) != TRUE)
290: return (status);
1.1 deraadt 291:
1.37 lum 292: curwp->w_dotline = lineno;
1.38 lum 293: return (TRUE);
294: }
295:
296: /*
1.39 lum 297: * Mark n paragraphs starting with the n'th and working our way backwards.
1.38 lum 298: * This leaves the cursor at the beginning of the paragraph where markpara()
299: * was invoked.
300: */
301: /* ARGSUSED */
302: int
303: markpara(int f, int n)
304: {
305: int i = 0;
306:
1.41 lum 307: if (n == 0)
308: return (TRUE);
309:
1.38 lum 310: clearmark(FFARG, 0);
311:
312: if (findpara() == FALSE)
313: return (TRUE);
314:
315: (void)do_gotoeop(FFRAND, n, &i);
316:
317: /* set the mark here */
318: curwp->w_markp = curwp->w_dotp;
319: curwp->w_marko = curwp->w_doto;
320:
321: (void)gotobop(FFRAND, i);
1.40 lum 322:
323: return (TRUE);
324: }
325:
326: /*
327: * Transpose the current paragraph with the following paragraph. If invoked
328: * multiple times, transpose to the n'th paragraph. If invoked between
329: * paragraphs, move to the previous paragraph, then continue.
330: */
331: /* ARGSUSED */
332: int
333: transposepara(int f, int n)
334: {
335: int i = 0, status;
336: char flg;
1.41 lum 337:
338: if (n == 0)
339: return (TRUE);
1.40 lum 340:
341: /* find a paragraph, set mark, then goto the end */
342: gotobop(FFRAND, 1);
343: curwp->w_markp = curwp->w_dotp;
344: curwp->w_marko = curwp->w_doto;
345: (void)gotoeop(FFRAND, 1);
346:
347: /* take a note of buffer flags - we may need them */
348: flg = curbp->b_flag;
349:
350: /* clean out kill buffer then kill region */
351: kdelete();
352: if ((status = killregion(FFRAND, 1)) != TRUE)
353: return (status);
354:
355: /*
356: * Now step through n paragraphs. If we reach the end of buffer,
357: * stop and paste the killed region back, then display a message.
358: */
359: if (do_gotoeop(FFRAND, n, &i) == FALSE) {
360: ewprintf("Cannot transpose paragraph, end of buffer reached.");
361: (void)gotobop(FFRAND, i);
362: (void)yank(FFRAND, 1);
363: curbp->b_flag = flg;
364: return (FALSE);
365: }
366: (void)yank(FFRAND, 1);
1.38 lum 367:
1.37 lum 368: return (TRUE);
369: }
370:
371: /*
1.39 lum 372: * Go down the buffer until we find a line with non-space characters.
1.37 lum 373: */
374: int
375: findpara(void)
376: {
377: int col, nospace = 0;
1.3 millert 378:
1.37 lum 379: /* we move forward to find a para to mark */
380: do {
1.3 millert 381: curwp->w_doto = 0;
1.37 lum 382: col = 0;
383:
384: /* check if we are on a blank line */
385: while (col < llength(curwp->w_dotp)) {
386: if (!isspace(lgetc(curwp->w_dotp, col)))
387: nospace = 1;
388: col++;
389: }
390: if (nospace)
391: break;
1.1 deraadt 392:
1.37 lum 393: if (lforw(curwp->w_dotp) == curbp->b_headp)
394: return (FALSE);
395:
396: curwp->w_dotp = lforw(curwp->w_dotp);
397: curwp->w_dotline++;
398: } while (1);
1.34 lum 399:
1.10 db 400: return (TRUE);
1.1 deraadt 401: }
402:
403: /*
1.3 millert 404: * Insert char with work wrap. Check to see if we're past fillcol, and if so,
405: * justify this line. As a last step, justify the line.
1.1 deraadt 406: */
1.2 millert 407: /* ARGSUSED */
1.3 millert 408: int
1.8 cloder 409: fillword(int f, int n)
1.1 deraadt 410: {
1.3 millert 411: char c;
412: int col, i, nce;
1.1 deraadt 413:
414: for (i = col = 0; col <= fillcol; ++i, ++col) {
1.2 millert 415: if (i == curwp->w_doto)
416: return selfinsert(f, n);
1.1 deraadt 417: c = lgetc(curwp->w_dotp, i);
418: if (c == '\t'
1.3 millert 419: #ifdef NOTAB
1.2 millert 420: && !(curbp->b_flag & BFNOTAB)
1.1 deraadt 421: #endif
1.2 millert 422: )
423: col |= 0x07;
424: else if (ISCTRL(c) != FALSE)
425: ++col;
1.1 deraadt 426: }
427: if (curwp->w_doto != llength(curwp->w_dotp)) {
1.5 art 428: (void)selfinsert(f, n);
1.1 deraadt 429: nce = llength(curwp->w_dotp) - curwp->w_doto;
1.2 millert 430: } else
431: nce = 0;
1.1 deraadt 432: curwp->w_doto = i;
433:
434: if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t')
435: do {
1.5 art 436: (void)backchar(FFRAND, 1);
1.7 deraadt 437: } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
438: c != '\t' && curwp->w_doto > 0);
1.1 deraadt 439:
440: if (curwp->w_doto == 0)
441: do {
1.5 art 442: (void)forwchar(FFRAND, 1);
1.7 deraadt 443: } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
444: c != '\t' && curwp->w_doto < llength(curwp->w_dotp));
1.1 deraadt 445:
1.5 art 446: (void)delwhite(FFRAND, 1);
447: (void)lnewline();
1.1 deraadt 448: i = llength(curwp->w_dotp) - nce;
1.2 millert 449: curwp->w_doto = i > 0 ? i : 0;
1.19 kjell 450: curwp->w_rflag |= WFMOVE;
1.2 millert 451: if (nce == 0 && curwp->w_doto != 0)
1.10 db 452: return (fillword(f, n));
453: return (TRUE);
1.1 deraadt 454: }
455:
1.3 millert 456: /*
457: * Set fill column to n for justify.
458: */
459: int
1.8 cloder 460: setfillcol(int f, int n)
1.2 millert 461: {
1.16 kjell 462: char buf[32], *rep;
463: const char *es;
1.18 kjell 464: int nfill;
1.16 kjell 465:
466: if ((f & FFARG) != 0) {
467: fillcol = n;
468: } else {
469: if ((rep = eread("Set fill-column: ", buf, sizeof(buf),
470: EFNEW | EFCR)) == NULL)
471: return (ABORT);
472: else if (rep[0] == '\0')
473: return (FALSE);
1.18 kjell 474: nfill = strtonum(rep, 0, INT_MAX, &es);
475: if (es != NULL) {
1.30 lum 476: dobeep();
1.18 kjell 477: ewprintf("Invalid fill column: %s", rep);
1.16 kjell 478: return (FALSE);
1.18 kjell 479: }
480: fillcol = nfill;
1.16 kjell 481: ewprintf("Fill column set to %d", fillcol);
482: }
1.10 db 483: return (TRUE);
1.1 deraadt 484: }