Annotation of src/usr.bin/mg/paragraph.c, Revision 1.48
1.48 ! op 1: /* $OpenBSD: paragraph.c,v 1.47 2023/03/08 04:43:11 guenther 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.3 millert 33: int
1.8 cloder 34: gotobop(int f, int n)
1.1 deraadt 35: {
1.26 lum 36: int col, nospace;
1.24 lum 37:
1.3 millert 38: /* the other way... */
39: if (n < 0)
1.10 db 40: return (gotoeop(f, -n));
1.1 deraadt 41:
1.3 millert 42: while (n-- > 0) {
1.31 florian 43: nospace = 0;
1.24 lum 44: while (lback(curwp->w_dotp) != curbp->b_headp) {
45: curwp->w_doto = 0;
46: col = 0;
47:
48: while (col < llength(curwp->w_dotp) &&
49: (isspace(lgetc(curwp->w_dotp, col))))
50: col++;
51:
52: if (col >= llength(curwp->w_dotp)) {
53: if (nospace)
54: break;
55: } else
56: nospace = 1;
1.32 lum 57:
58: curwp->w_dotline--;
59: curwp->w_dotp = lback(curwp->w_dotp);
1.24 lum 60: }
1.1 deraadt 61: }
1.3 millert 62: /* force screen update */
1.19 kjell 63: curwp->w_rflag |= WFMOVE;
1.10 db 64: return (TRUE);
1.1 deraadt 65: }
66:
67: /*
1.27 lum 68: * Move to end of paragraph.
69: * See comments for gotobop(). Same, but moving forwards.
1.1 deraadt 70: */
1.3 millert 71: int
1.8 cloder 72: gotoeop(int f, int n)
1.1 deraadt 73: {
1.38 lum 74: int i;
75:
76: return(do_gotoeop(f, n, &i));
77: }
78:
79: int
80: do_gotoeop(int f, int n, int *i)
81: {
82: int col, nospace, j = 0;
1.25 lum 83:
1.3 millert 84: /* the other way... */
85: if (n < 0)
1.10 db 86: return (gotobop(f, -n));
1.1 deraadt 87:
1.3 millert 88: /* for each one asked for */
89: while (n-- > 0) {
1.38 lum 90: *i = ++j;
1.31 florian 91: nospace = 0;
1.25 lum 92: while (lforw(curwp->w_dotp) != curbp->b_headp) {
93: col = 0;
94: curwp->w_doto = 0;
95:
96: while (col < llength(curwp->w_dotp) &&
97: (isspace(lgetc(curwp->w_dotp, col))))
98: col++;
99:
100: if (col >= llength(curwp->w_dotp)) {
101: if (nospace)
102: break;
1.23 lum 103: } else
1.25 lum 104: nospace = 1;
105:
106: curwp->w_dotp = lforw(curwp->w_dotp);
107: curwp->w_dotline++;
1.34 lum 108:
1.1 deraadt 109: }
1.45 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);
1.1 deraadt 116: }
1.33 lum 117:
1.3 millert 118: /* force screen update */
1.19 kjell 119: curwp->w_rflag |= WFMOVE;
1.10 db 120: return (TRUE);
1.1 deraadt 121: }
122:
123: /*
1.6 mickey 124: * Justify a paragraph. Fill the current paragraph according to the current
1.3 millert 125: * fill column.
1.1 deraadt 126: */
1.3 millert 127: int
1.8 cloder 128: fillpara(int f, int n)
1.1 deraadt 129: {
1.10 db 130: int c; /* current char during scan */
1.3 millert 131: int wordlen; /* length of current word */
132: int clength; /* position on line during fill */
133: int i; /* index during word copy */
134: int eopflag; /* Are we at the End-Of-Paragraph? */
135: int firstflag; /* first word? (needs no space) */
136: int newlength; /* tentative new line length */
137: int eolflag; /* was at end of line */
1.12 kjell 138: int retval; /* return value */
1.13 deraadt 139: struct line *eopline; /* pointer to line just past EOP */
1.3 millert 140: char wbuf[MAXWORD]; /* buffer for current word */
1.1 deraadt 141:
1.41 lum 142: if (n == 0)
143: return (TRUE);
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:
1.42 mmcc 156: /* and back top the beginning 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.44 lum 208: if (dblspace && (!eopflag && ((eolflag ||
1.6 mickey 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.44 lum 213: ISEOSP(wbuf[wordlen - 2])))) &&
1.43 lum 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: */
264: int
1.8 cloder 265: killpara(int f, int n)
1.1 deraadt 266: {
1.37 lum 267: int lineno, status;
268:
1.41 lum 269: if (n == 0)
270: return (TRUE);
271:
1.37 lum 272: if (findpara() == FALSE)
273: return (TRUE);
274:
275: /* go to the beginning of the paragraph */
276: (void)gotobop(FFRAND, 1);
1.1 deraadt 277:
1.37 lum 278: /* take a note of the line number for after deletions and set mark */
279: lineno = curwp->w_dotline;
280: curwp->w_markp = curwp->w_dotp;
281: curwp->w_marko = curwp->w_doto;
1.1 deraadt 282:
1.37 lum 283: (void)gotoeop(FFRAND, n);
1.1 deraadt 284:
1.37 lum 285: if ((status = killregion(FFRAND, 1)) != TRUE)
286: return (status);
1.1 deraadt 287:
1.37 lum 288: curwp->w_dotline = lineno;
1.38 lum 289: return (TRUE);
290: }
291:
292: /*
1.39 lum 293: * Mark n paragraphs starting with the n'th and working our way backwards.
1.38 lum 294: * This leaves the cursor at the beginning of the paragraph where markpara()
295: * was invoked.
296: */
297: int
298: markpara(int f, int n)
299: {
300: int i = 0;
301:
1.41 lum 302: if (n == 0)
303: return (TRUE);
304:
1.38 lum 305: clearmark(FFARG, 0);
306:
307: if (findpara() == FALSE)
308: return (TRUE);
309:
310: (void)do_gotoeop(FFRAND, n, &i);
311:
312: /* set the mark here */
313: curwp->w_markp = curwp->w_dotp;
314: curwp->w_marko = curwp->w_doto;
315:
316: (void)gotobop(FFRAND, i);
1.40 lum 317:
318: return (TRUE);
319: }
320:
321: /*
322: * Transpose the current paragraph with the following paragraph. If invoked
323: * multiple times, transpose to the n'th paragraph. If invoked between
324: * paragraphs, move to the previous paragraph, then continue.
325: */
326: int
327: transposepara(int f, int n)
328: {
329: int i = 0, status;
330: char flg;
1.41 lum 331:
332: if (n == 0)
333: return (TRUE);
1.40 lum 334:
1.46 lum 335: undo_boundary_enable(FFRAND, 0);
336:
1.40 lum 337: /* find a paragraph, set mark, then goto the end */
338: gotobop(FFRAND, 1);
339: curwp->w_markp = curwp->w_dotp;
340: curwp->w_marko = curwp->w_doto;
341: (void)gotoeop(FFRAND, 1);
342:
343: /* take a note of buffer flags - we may need them */
344: flg = curbp->b_flag;
345:
346: /* clean out kill buffer then kill region */
347: kdelete();
348: if ((status = killregion(FFRAND, 1)) != TRUE)
349: return (status);
350:
351: /*
352: * Now step through n paragraphs. If we reach the end of buffer,
353: * stop and paste the killed region back, then display a message.
354: */
355: if (do_gotoeop(FFRAND, n, &i) == FALSE) {
356: ewprintf("Cannot transpose paragraph, end of buffer reached.");
357: (void)gotobop(FFRAND, i);
358: (void)yank(FFRAND, 1);
359: curbp->b_flag = flg;
360: return (FALSE);
361: }
362: (void)yank(FFRAND, 1);
1.46 lum 363:
364: undo_boundary_enable(FFRAND, 1);
1.38 lum 365:
1.37 lum 366: return (TRUE);
367: }
368:
369: /*
1.39 lum 370: * Go down the buffer until we find a line with non-space characters.
1.37 lum 371: */
372: int
373: findpara(void)
374: {
375: int col, nospace = 0;
1.3 millert 376:
1.37 lum 377: /* we move forward to find a para to mark */
378: do {
1.3 millert 379: curwp->w_doto = 0;
1.37 lum 380: col = 0;
381:
382: /* check if we are on a blank line */
383: while (col < llength(curwp->w_dotp)) {
384: if (!isspace(lgetc(curwp->w_dotp, col)))
385: nospace = 1;
386: col++;
387: }
388: if (nospace)
389: break;
1.1 deraadt 390:
1.37 lum 391: if (lforw(curwp->w_dotp) == curbp->b_headp)
392: return (FALSE);
393:
394: curwp->w_dotp = lforw(curwp->w_dotp);
395: curwp->w_dotline++;
396: } while (1);
1.34 lum 397:
1.10 db 398: return (TRUE);
1.1 deraadt 399: }
400:
401: /*
1.3 millert 402: * Insert char with work wrap. Check to see if we're past fillcol, and if so,
403: * justify this line. As a last step, justify the line.
1.1 deraadt 404: */
1.3 millert 405: int
1.8 cloder 406: fillword(int f, int n)
1.1 deraadt 407: {
1.3 millert 408: char c;
409: int col, i, nce;
1.1 deraadt 410:
411: for (i = col = 0; col <= fillcol; ++i, ++col) {
1.2 millert 412: if (i == curwp->w_doto)
413: return selfinsert(f, n);
1.1 deraadt 414: c = lgetc(curwp->w_dotp, i);
1.48 ! op 415: if (c == '\t')
1.2 millert 416: col |= 0x07;
417: else if (ISCTRL(c) != FALSE)
418: ++col;
1.1 deraadt 419: }
420: if (curwp->w_doto != llength(curwp->w_dotp)) {
1.5 art 421: (void)selfinsert(f, n);
1.1 deraadt 422: nce = llength(curwp->w_dotp) - curwp->w_doto;
1.2 millert 423: } else
424: nce = 0;
1.1 deraadt 425: curwp->w_doto = i;
426:
427: if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t')
428: do {
1.5 art 429: (void)backchar(FFRAND, 1);
1.7 deraadt 430: } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
431: c != '\t' && curwp->w_doto > 0);
1.1 deraadt 432:
433: if (curwp->w_doto == 0)
434: do {
1.5 art 435: (void)forwchar(FFRAND, 1);
1.7 deraadt 436: } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
437: c != '\t' && curwp->w_doto < llength(curwp->w_dotp));
1.1 deraadt 438:
1.5 art 439: (void)delwhite(FFRAND, 1);
440: (void)lnewline();
1.1 deraadt 441: i = llength(curwp->w_dotp) - nce;
1.2 millert 442: curwp->w_doto = i > 0 ? i : 0;
1.19 kjell 443: curwp->w_rflag |= WFMOVE;
1.2 millert 444: if (nce == 0 && curwp->w_doto != 0)
1.10 db 445: return (fillword(f, n));
446: return (TRUE);
1.1 deraadt 447: }
448:
1.3 millert 449: /*
450: * Set fill column to n for justify.
451: */
452: int
1.8 cloder 453: setfillcol(int f, int n)
1.2 millert 454: {
1.16 kjell 455: char buf[32], *rep;
456: const char *es;
1.18 kjell 457: int nfill;
1.16 kjell 458:
459: if ((f & FFARG) != 0) {
460: fillcol = n;
461: } else {
462: if ((rep = eread("Set fill-column: ", buf, sizeof(buf),
463: EFNEW | EFCR)) == NULL)
464: return (ABORT);
465: else if (rep[0] == '\0')
466: return (FALSE);
1.18 kjell 467: nfill = strtonum(rep, 0, INT_MAX, &es);
468: if (es != NULL) {
1.30 lum 469: dobeep();
1.18 kjell 470: ewprintf("Invalid fill column: %s", rep);
1.16 kjell 471: return (FALSE);
1.18 kjell 472: }
473: fillcol = nfill;
1.16 kjell 474: ewprintf("Fill column set to %d", fillcol);
475: }
1.44 lum 476: return (TRUE);
477: }
478:
479: int
480: sentencespace(int f, int n)
481: {
482: if (f & FFARG)
483: dblspace = n > 1;
484: else
485: dblspace = !dblspace;
486:
1.10 db 487: return (TRUE);
1.1 deraadt 488: }