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