xref: /openbsd-src/usr.bin/mg/paragraph.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: paragraph.c,v 1.17 2008/09/15 16:13:35 kjell Exp $	*/
2 
3 /* This file is in the public domain. */
4 
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  */
9 
10 #include "def.h"
11 
12 static int	fillcol = 70;
13 
14 #define MAXWORD 256
15 
16 /*
17  * Move to start of paragraph.  Go back to the beginning of the current
18  * paragraph here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
19  * combination to delimit the beginning of a paragraph.
20  */
21 /* ARGSUSED */
22 int
23 gotobop(int f, int n)
24 {
25 	/* the other way... */
26 	if (n < 0)
27 		return (gotoeop(f, -n));
28 
29 	while (n-- > 0) {
30 		/* first scan back until we are in a word */
31 		while (backchar(FFRAND, 1) && inword() == 0);
32 
33 		/* and go to the B-O-Line */
34 		curwp->w_doto = 0;
35 
36 		/*
37 		 * and scan back until we hit a <NL><SP> <NL><TAB> or
38 		 * <NL><NL>
39 		 */
40 		while (lback(curwp->w_dotp) != curbp->b_headp)
41 			if (llength(lback(curwp->w_dotp)) &&
42 			    lgetc(curwp->w_dotp, 0) != ' ' &&
43 			    lgetc(curwp->w_dotp, 0) != '.' &&
44 			    lgetc(curwp->w_dotp, 0) != '\t')
45 				curwp->w_dotp = lback(curwp->w_dotp);
46 			else {
47 				if (llength(lback(curwp->w_dotp)) &&
48 				    lgetc(curwp->w_dotp, 0) == '.') {
49 					curwp->w_dotp = lforw(curwp->w_dotp);
50 					if (curwp->w_dotp == curbp->b_headp) {
51 						/*
52 						 * beyond end of buffer,
53 						 * cleanup time
54 						 */
55 						curwp->w_dotp =
56 						    lback(curwp->w_dotp);
57 						curwp->w_doto =
58 						    llength(curwp->w_dotp);
59 					}
60 				}
61 				break;
62 			}
63 	}
64 	/* force screen update */
65 	curwp->w_flag |= WFMOVE;
66 	return (TRUE);
67 }
68 
69 /*
70  * Move to end of paragraph.  Go forward to the end of the current paragraph
71  * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE> combination to
72  * delimit the beginning of a paragraph.
73  */
74 /* ARGSUSED */
75 int
76 gotoeop(int f, int n)
77 {
78 	/* the other way... */
79 	if (n < 0)
80 		return (gotobop(f, -n));
81 
82 	/* for each one asked for */
83 	while (n-- > 0) {
84 		/* Find the first word on/after the current line */
85 		curwp->w_doto = 0;
86 		while (forwchar(FFRAND, 1) && inword() == 0);
87 
88 		curwp->w_doto = 0;
89 		curwp->w_dotp = lforw(curwp->w_dotp);
90 
91 		/* and scan forword until we hit a <NL><SP> or ... */
92 		while (curwp->w_dotp != curbp->b_headp) {
93 			if (llength(curwp->w_dotp) &&
94 			    lgetc(curwp->w_dotp, 0) != ' ' &&
95 			    lgetc(curwp->w_dotp, 0) != '.' &&
96 			    lgetc(curwp->w_dotp, 0) != '\t')
97 				curwp->w_dotp = lforw(curwp->w_dotp);
98 			else
99 				break;
100 		}
101 		if (curwp->w_dotp == curbp->b_headp) {
102 			/* beyond end of buffer, cleanup time */
103 			curwp->w_dotp = lback(curwp->w_dotp);
104 			curwp->w_doto = llength(curwp->w_dotp);
105 			break;
106 		}
107 	}
108 	/* force screen update */
109 	curwp->w_flag |= WFMOVE;
110 	return (TRUE);
111 }
112 
113 /*
114  * Justify a paragraph.  Fill the current paragraph according to the current
115  * fill column.
116  */
117 /* ARGSUSED */
118 int
119 fillpara(int f, int n)
120 {
121 	int	 c;		/* current char during scan		*/
122 	int	 wordlen;	/* length of current word		*/
123 	int	 clength;	/* position on line during fill		*/
124 	int	 i;		/* index during word copy		*/
125 	int	 eopflag;	/* Are we at the End-Of-Paragraph?	*/
126 	int	 firstflag;	/* first word? (needs no space)		*/
127 	int	 newlength;	/* tentative new line length		*/
128 	int	 eolflag;	/* was at end of line			*/
129 	int	 retval;	/* return value				*/
130 	struct line	*eopline;	/* pointer to line just past EOP	*/
131 	char	 wbuf[MAXWORD];	/* buffer for current word		*/
132 
133 	undo_boundary_enable(FFRAND, 0);
134 
135 	/* record the pointer to the line just past the EOP */
136 	(void)gotoeop(FFRAND, 1);
137 	if (curwp->w_doto != 0) {
138 		/* paragraph ends at end of buffer */
139 		(void)lnewline();
140 		eopline = lforw(curwp->w_dotp);
141 	} else
142 		eopline = curwp->w_dotp;
143 
144 	/* and back top the begining of the paragraph */
145 	(void)gotobop(FFRAND, 1);
146 
147 	/* initialize various info */
148 	while (inword() == 0 && forwchar(FFRAND, 1));
149 
150 	clength = curwp->w_doto;
151 	wordlen = 0;
152 
153 	/* scan through lines, filling words */
154 	firstflag = TRUE;
155 	eopflag = FALSE;
156 	while (!eopflag) {
157 
158 		/* get the next character in the paragraph */
159 		if ((eolflag = (curwp->w_doto == llength(curwp->w_dotp)))) {
160 			c = ' ';
161 			if (lforw(curwp->w_dotp) == eopline)
162 				eopflag = TRUE;
163 		} else
164 			c = lgetc(curwp->w_dotp, curwp->w_doto);
165 
166 		/* and then delete it */
167 		if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag) {
168 			retval = FALSE;
169 			goto cleanup;
170 		}
171 
172 		/* if not a separator, just add it in */
173 		if (c != ' ' && c != '\t') {
174 			if (wordlen < MAXWORD - 1)
175 				wbuf[wordlen++] = c;
176 			else {
177 				/*
178 				 * You loose chars beyond MAXWORD if the word
179 				 * is to long. I'm to lazy to fix it now; it
180 				 * just silently truncated the word before,
181 				 * so I get to feel smug.
182 				 */
183 				ewprintf("Word too long!");
184 			}
185 		} else if (wordlen) {
186 
187 			/* calculate tentative new length with word added */
188 			newlength = clength + 1 + wordlen;
189 
190 			/*
191 			 * if at end of line or at doublespace and previous
192 			 * character was one of '.','?','!' doublespace here.
193 			 */
194 			if ((eolflag ||
195 			    curwp->w_doto == llength(curwp->w_dotp) ||
196 			    (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' '
197 			    || c == '\t') && ISEOSP(wbuf[wordlen - 1]) &&
198 			    wordlen < MAXWORD - 1)
199 				wbuf[wordlen++] = ' ';
200 
201 			/* at a word break with a word waiting */
202 			if (newlength <= fillcol) {
203 				/* add word to current line */
204 				if (!firstflag) {
205 					(void)linsert(1, ' ');
206 					++clength;
207 				}
208 				firstflag = FALSE;
209 			} else {
210 				if (curwp->w_doto > 0 &&
211 				    lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') {
212 					curwp->w_doto -= 1;
213 					(void)ldelete((RSIZE) 1, KNONE);
214 				}
215 				/* start a new line */
216 				(void)lnewline();
217 				clength = 0;
218 			}
219 
220 			/* and add the word in in either case */
221 			for (i = 0; i < wordlen; i++) {
222 				(void)linsert(1, wbuf[i]);
223 				++clength;
224 			}
225 			wordlen = 0;
226 		}
227 	}
228 	/* and add a last newline for the end of our new paragraph */
229 	(void)lnewline();
230 
231 	/*
232 	 * We really should wind up where we started, (which is hard to keep
233 	 * track of) but I think the end of the last line is better than the
234 	 * beginning of the blank line.
235 	 */
236 	(void)backchar(FFRAND, 1);
237 	retval = TRUE;
238 cleanup:
239 	undo_boundary_enable(FFRAND, 1);
240 	return (retval);
241 }
242 
243 /*
244  * Delete a paragraph.  Delete n paragraphs starting with the current one.
245  */
246 /* ARGSUSED */
247 int
248 killpara(int f, int n)
249 {
250 	int	status;		/* returned status of functions */
251 
252 	/* for each paragraph to delete */
253 	while (n--) {
254 
255 		/* mark out the end and beginning of the para to delete */
256 		(void)gotoeop(FFRAND, 1);
257 
258 		/* set the mark here */
259 		curwp->w_markp = curwp->w_dotp;
260 		curwp->w_marko = curwp->w_doto;
261 
262 		/* go to the beginning of the paragraph */
263 		(void)gotobop(FFRAND, 1);
264 
265 		/* force us to the beginning of line */
266 		curwp->w_doto = 0;
267 
268 		/* and delete it */
269 		if ((status = killregion(FFRAND, 1)) != TRUE)
270 			return (status);
271 
272 		/* and clean up the 2 extra lines */
273 		(void)ldelete((RSIZE) 1, KFORW);
274 	}
275 	return (TRUE);
276 }
277 
278 /*
279  * Insert char with work wrap.  Check to see if we're past fillcol, and if so,
280  * justify this line.  As a last step, justify the line.
281  */
282 /* ARGSUSED */
283 int
284 fillword(int f, int n)
285 {
286 	char	c;
287 	int	col, i, nce;
288 
289 	for (i = col = 0; col <= fillcol; ++i, ++col) {
290 		if (i == curwp->w_doto)
291 			return selfinsert(f, n);
292 		c = lgetc(curwp->w_dotp, i);
293 		if (c == '\t'
294 #ifdef NOTAB
295 		    && !(curbp->b_flag & BFNOTAB)
296 #endif
297 			)
298 			col |= 0x07;
299 		else if (ISCTRL(c) != FALSE)
300 			++col;
301 	}
302 	if (curwp->w_doto != llength(curwp->w_dotp)) {
303 		(void)selfinsert(f, n);
304 		nce = llength(curwp->w_dotp) - curwp->w_doto;
305 	} else
306 		nce = 0;
307 	curwp->w_doto = i;
308 
309 	if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t')
310 		do {
311 			(void)backchar(FFRAND, 1);
312 		} while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
313 		    c != '\t' && curwp->w_doto > 0);
314 
315 	if (curwp->w_doto == 0)
316 		do {
317 			(void)forwchar(FFRAND, 1);
318 		} while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
319 		    c != '\t' && curwp->w_doto < llength(curwp->w_dotp));
320 
321 	(void)delwhite(FFRAND, 1);
322 	(void)lnewline();
323 	i = llength(curwp->w_dotp) - nce;
324 	curwp->w_doto = i > 0 ? i : 0;
325 	curwp->w_flag |= WFMOVE;
326 	if (nce == 0 && curwp->w_doto != 0)
327 		return (fillword(f, n));
328 	return (TRUE);
329 }
330 
331 /*
332  * Set fill column to n for justify.
333  */
334 int
335 setfillcol(int f, int n)
336 {
337 	char buf[32], *rep;
338 	const char *es;
339 
340 	if ((f & FFARG) != 0) {
341 		fillcol = n;
342 	} else {
343 		if ((rep = eread("Set fill-column: ", buf, sizeof(buf),
344 		    EFNEW | EFCR)) == NULL)
345 			return (ABORT);
346 		else if (rep[0] == '\0')
347 			return (FALSE);
348 		fillcol = strtonum(rep, 0, INT_MAX, &es);
349 		if (es != NULL)
350 			return (FALSE);
351 		ewprintf("Fill column set to %d", fillcol);
352 	}
353 	return (TRUE);
354 }
355