Annotation of src/usr.bin/mg/paragraph.c, Revision 1.40
1.40 ! lum 1: /* $OpenBSD: paragraph.c,v 1.39 2015/09/24 07:20:12 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);
1.40 ! lum 313:
! 314: return (TRUE);
! 315: }
! 316:
! 317: /*
! 318: * Transpose the current paragraph with the following paragraph. If invoked
! 319: * multiple times, transpose to the n'th paragraph. If invoked between
! 320: * paragraphs, move to the previous paragraph, then continue.
! 321: */
! 322: /* ARGSUSED */
! 323: int
! 324: transposepara(int f, int n)
! 325: {
! 326: int i = 0, status;
! 327: char flg;
! 328:
! 329: /* find a paragraph, set mark, then goto the end */
! 330: gotobop(FFRAND, 1);
! 331: curwp->w_markp = curwp->w_dotp;
! 332: curwp->w_marko = curwp->w_doto;
! 333: (void)gotoeop(FFRAND, 1);
! 334:
! 335: /* take a note of buffer flags - we may need them */
! 336: flg = curbp->b_flag;
! 337:
! 338: /* clean out kill buffer then kill region */
! 339: kdelete();
! 340: if ((status = killregion(FFRAND, 1)) != TRUE)
! 341: return (status);
! 342:
! 343: /*
! 344: * Now step through n paragraphs. If we reach the end of buffer,
! 345: * stop and paste the killed region back, then display a message.
! 346: */
! 347: if (do_gotoeop(FFRAND, n, &i) == FALSE) {
! 348: ewprintf("Cannot transpose paragraph, end of buffer reached.");
! 349: (void)gotobop(FFRAND, i);
! 350: (void)yank(FFRAND, 1);
! 351: curbp->b_flag = flg;
! 352: return (FALSE);
! 353: }
! 354: (void)yank(FFRAND, 1);
1.38 lum 355:
1.37 lum 356: return (TRUE);
357: }
358:
359: /*
1.39 lum 360: * Go down the buffer until we find a line with non-space characters.
1.37 lum 361: */
362: int
363: findpara(void)
364: {
365: int col, nospace = 0;
1.3 millert 366:
1.37 lum 367: /* we move forward to find a para to mark */
368: do {
1.3 millert 369: curwp->w_doto = 0;
1.37 lum 370: col = 0;
371:
372: /* check if we are on a blank line */
373: while (col < llength(curwp->w_dotp)) {
374: if (!isspace(lgetc(curwp->w_dotp, col)))
375: nospace = 1;
376: col++;
377: }
378: if (nospace)
379: break;
1.1 deraadt 380:
1.37 lum 381: if (lforw(curwp->w_dotp) == curbp->b_headp)
382: return (FALSE);
383:
384: curwp->w_dotp = lforw(curwp->w_dotp);
385: curwp->w_dotline++;
386: } while (1);
1.34 lum 387:
1.10 db 388: return (TRUE);
1.1 deraadt 389: }
390:
391: /*
1.3 millert 392: * Insert char with work wrap. Check to see if we're past fillcol, and if so,
393: * justify this line. As a last step, justify the line.
1.1 deraadt 394: */
1.2 millert 395: /* ARGSUSED */
1.3 millert 396: int
1.8 cloder 397: fillword(int f, int n)
1.1 deraadt 398: {
1.3 millert 399: char c;
400: int col, i, nce;
1.1 deraadt 401:
402: for (i = col = 0; col <= fillcol; ++i, ++col) {
1.2 millert 403: if (i == curwp->w_doto)
404: return selfinsert(f, n);
1.1 deraadt 405: c = lgetc(curwp->w_dotp, i);
406: if (c == '\t'
1.3 millert 407: #ifdef NOTAB
1.2 millert 408: && !(curbp->b_flag & BFNOTAB)
1.1 deraadt 409: #endif
1.2 millert 410: )
411: col |= 0x07;
412: else if (ISCTRL(c) != FALSE)
413: ++col;
1.1 deraadt 414: }
415: if (curwp->w_doto != llength(curwp->w_dotp)) {
1.5 art 416: (void)selfinsert(f, n);
1.1 deraadt 417: nce = llength(curwp->w_dotp) - curwp->w_doto;
1.2 millert 418: } else
419: nce = 0;
1.1 deraadt 420: curwp->w_doto = i;
421:
422: if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t')
423: do {
1.5 art 424: (void)backchar(FFRAND, 1);
1.7 deraadt 425: } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
426: c != '\t' && curwp->w_doto > 0);
1.1 deraadt 427:
428: if (curwp->w_doto == 0)
429: do {
1.5 art 430: (void)forwchar(FFRAND, 1);
1.7 deraadt 431: } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
432: c != '\t' && curwp->w_doto < llength(curwp->w_dotp));
1.1 deraadt 433:
1.5 art 434: (void)delwhite(FFRAND, 1);
435: (void)lnewline();
1.1 deraadt 436: i = llength(curwp->w_dotp) - nce;
1.2 millert 437: curwp->w_doto = i > 0 ? i : 0;
1.19 kjell 438: curwp->w_rflag |= WFMOVE;
1.2 millert 439: if (nce == 0 && curwp->w_doto != 0)
1.10 db 440: return (fillword(f, n));
441: return (TRUE);
1.1 deraadt 442: }
443:
1.3 millert 444: /*
445: * Set fill column to n for justify.
446: */
447: int
1.8 cloder 448: setfillcol(int f, int n)
1.2 millert 449: {
1.16 kjell 450: char buf[32], *rep;
451: const char *es;
1.18 kjell 452: int nfill;
1.16 kjell 453:
454: if ((f & FFARG) != 0) {
455: fillcol = n;
456: } else {
457: if ((rep = eread("Set fill-column: ", buf, sizeof(buf),
458: EFNEW | EFCR)) == NULL)
459: return (ABORT);
460: else if (rep[0] == '\0')
461: return (FALSE);
1.18 kjell 462: nfill = strtonum(rep, 0, INT_MAX, &es);
463: if (es != NULL) {
1.30 lum 464: dobeep();
1.18 kjell 465: ewprintf("Invalid fill column: %s", rep);
1.16 kjell 466: return (FALSE);
1.18 kjell 467: }
468: fillcol = nfill;
1.16 kjell 469: ewprintf("Fill column set to %d", fillcol);
470: }
1.10 db 471: return (TRUE);
1.1 deraadt 472: }