GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/paragraph.c Lines: 0 193 0.0 %
Date: 2017-11-07 Branches: 0 144 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: paragraph.c,v 1.45 2016/09/06 16:25:47 lum 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 <sys/queue.h>
11
#include <ctype.h>
12
#include <limits.h>
13
#include <signal.h>
14
#include <stdio.h>
15
#include <stdlib.h>
16
17
#include "def.h"
18
19
static int	fillcol = 70;
20
21
#define MAXWORD 256
22
23
static int	findpara(void);
24
static int 	do_gotoeop(int, int, int *);
25
26
/*
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.
32
 */
33
/* ARGSUSED */
34
int
35
gotobop(int f, int n)
36
{
37
	int col, nospace;
38
39
	/* the other way... */
40
	if (n < 0)
41
		return (gotoeop(f, -n));
42
43
	while (n-- > 0) {
44
		nospace = 0;
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;
58
59
			curwp->w_dotline--;
60
			curwp->w_dotp = lback(curwp->w_dotp);
61
		}
62
	}
63
	/* force screen update */
64
	curwp->w_rflag |= WFMOVE;
65
	return (TRUE);
66
}
67
68
/*
69
 * Move to end of paragraph.
70
 * See comments for gotobop(). Same, but moving forwards.
71
 */
72
/* ARGSUSED */
73
int
74
gotoeop(int f, int n)
75
{
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;
85
86
	/* the other way... */
87
	if (n < 0)
88
		return (gotobop(f, -n));
89
90
	/* for each one asked for */
91
	while (n-- > 0) {
92
		*i = ++j;
93
		nospace = 0;
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;
105
			} else
106
				nospace = 1;
107
108
			curwp->w_dotp = lforw(curwp->w_dotp);
109
			curwp->w_dotline++;
110
111
		}
112
	}
113
	/* do not continue after end of buffer */
114
	if (lforw(curwp->w_dotp) == curbp->b_headp) {
115
		gotoeol(FFRAND, 1);
116
		curwp->w_rflag |= WFMOVE;
117
		return (FALSE);
118
	}
119
120
	/* force screen update */
121
	curwp->w_rflag |= WFMOVE;
122
	return (TRUE);
123
}
124
125
/*
126
 * Justify a paragraph.  Fill the current paragraph according to the current
127
 * fill column.
128
 */
129
/* ARGSUSED */
130
int
131
fillpara(int f, int n)
132
{
133
	int	 c;		/* current char during scan		*/
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			*/
141
	int	 retval;	/* return value				*/
142
	struct line	*eopline;	/* pointer to line just past EOP	*/
143
	char	 wbuf[MAXWORD];	/* buffer for current word		*/
144
145
	if (n == 0)
146
		return (TRUE);
147
148
	undo_boundary_enable(FFRAND, 0);
149
150
	/* record the pointer to the line just past the EOP */
151
	(void)gotoeop(FFRAND, 1);
152
	if (curwp->w_doto != 0) {
153
		/* paragraph ends at end of buffer */
154
		(void)lnewline();
155
		eopline = lforw(curwp->w_dotp);
156
	} else
157
		eopline = curwp->w_dotp;
158
159
	/* and back top the beginning of the paragraph */
160
	(void)gotobop(FFRAND, 1);
161
162
	/* initialize various info */
163
	while (inword() == 0 && forwchar(FFRAND, 1));
164
165
	clength = curwp->w_doto;
166
	wordlen = 0;
167
168
	/* scan through lines, filling words */
169
	firstflag = TRUE;
170
	eopflag = FALSE;
171
	while (!eopflag) {
172
173
		/* get the next character in the paragraph */
174
		if ((eolflag = (curwp->w_doto == llength(curwp->w_dotp)))) {
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 */
182
		if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag) {
183
			retval = FALSE;
184
			goto cleanup;
185
		}
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 {
192
				/*
193
				 * You lose chars beyond MAXWORD if the word
194
				 * is too long. I'm too lazy to fix it now; it
195
				 * just silently truncated the word before,
196
				 * so I get to feel smug.
197
				 */
198
				ewprintf("Word too long!");
199
			}
200
		} else if (wordlen) {
201
202
			/* calculate tentative new length with word added */
203
			newlength = clength + 1 + wordlen;
204
205
			/*
206
			 * if at end of line or at doublespace and previous
207
			 * character was one of '.','?','!' doublespace here.
208
			 * behave the same way if a ')' is preceded by a
209
			 * [.?!] and followed by a doublespace.
210
			 */
211
			if (dblspace && (!eopflag && ((eolflag ||
212
			    curwp->w_doto == llength(curwp->w_dotp) ||
213
			    (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' '
214
			    || c == '\t') && (ISEOSP(wbuf[wordlen - 1]) ||
215
			    (wbuf[wordlen - 1] == ')' && wordlen >= 2 &&
216
			    ISEOSP(wbuf[wordlen - 2])))) &&
217
			    wordlen < MAXWORD - 1))
218
				wbuf[wordlen++] = ' ';
219
220
			/* at a word break with a word waiting */
221
			if (newlength <= fillcol) {
222
				/* add word to current line */
223
				if (!firstflag) {
224
					(void)linsert(1, ' ');
225
					++clength;
226
				}
227
				firstflag = FALSE;
228
			} else {
229
				if (curwp->w_doto > 0 &&
230
				    lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') {
231
					curwp->w_doto -= 1;
232
					(void)ldelete((RSIZE) 1, KNONE);
233
				}
234
				/* start a new line */
235
				(void)lnewline();
236
				clength = 0;
237
			}
238
239
			/* and add the word in in either case */
240
			for (i = 0; i < wordlen; i++) {
241
				(void)linsert(1, wbuf[i]);
242
				++clength;
243
			}
244
			wordlen = 0;
245
		}
246
	}
247
	/* and add a last newline for the end of our new paragraph */
248
	(void)lnewline();
249
250
	/*
251
	 * We really should wind up where we started, (which is hard to keep
252
	 * track of) but I think the end of the last line is better than the
253
	 * beginning of the blank line.
254
	 */
255
	(void)backchar(FFRAND, 1);
256
	retval = TRUE;
257
cleanup:
258
	undo_boundary_enable(FFRAND, 1);
259
	return (retval);
260
}
261
262
/*
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.
266
 */
267
/* ARGSUSED */
268
int
269
killpara(int f, int n)
270
{
271
	int	lineno, status;
272
273
	if (n == 0)
274
		return (TRUE);
275
276
	if (findpara() == FALSE)
277
		return (TRUE);
278
279
	/* go to the beginning of the paragraph */
280
	(void)gotobop(FFRAND, 1);
281
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;
286
287
	(void)gotoeop(FFRAND, n);
288
289
	if ((status = killregion(FFRAND, 1)) != TRUE)
290
		return (status);
291
292
	curwp->w_dotline = lineno;
293
	return (TRUE);
294
}
295
296
/*
297
 * Mark n paragraphs starting with the n'th and working our way backwards.
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
307
	if (n == 0)
308
		return (TRUE);
309
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);
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;
337
338
	if (n == 0)
339
		return (TRUE);
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);
367
368
	return (TRUE);
369
}
370
371
/*
372
 * Go down the buffer until we find a line with non-space characters.
373
 */
374
int
375
findpara(void)
376
{
377
	int	col, nospace = 0;
378
379
	/* we move forward to find a para to mark */
380
	do {
381
		curwp->w_doto = 0;
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;
392
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);
399
400
	return (TRUE);
401
}
402
403
/*
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.
406
 */
407
/* ARGSUSED */
408
int
409
fillword(int f, int n)
410
{
411
	char	c;
412
	int	col, i, nce;
413
414
	for (i = col = 0; col <= fillcol; ++i, ++col) {
415
		if (i == curwp->w_doto)
416
			return selfinsert(f, n);
417
		c = lgetc(curwp->w_dotp, i);
418
		if (c == '\t'
419
#ifdef NOTAB
420
		    && !(curbp->b_flag & BFNOTAB)
421
#endif
422
			)
423
			col |= 0x07;
424
		else if (ISCTRL(c) != FALSE)
425
			++col;
426
	}
427
	if (curwp->w_doto != llength(curwp->w_dotp)) {
428
		(void)selfinsert(f, n);
429
		nce = llength(curwp->w_dotp) - curwp->w_doto;
430
	} else
431
		nce = 0;
432
	curwp->w_doto = i;
433
434
	if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t')
435
		do {
436
			(void)backchar(FFRAND, 1);
437
		} while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
438
		    c != '\t' && curwp->w_doto > 0);
439
440
	if (curwp->w_doto == 0)
441
		do {
442
			(void)forwchar(FFRAND, 1);
443
		} while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
444
		    c != '\t' && curwp->w_doto < llength(curwp->w_dotp));
445
446
	(void)delwhite(FFRAND, 1);
447
	(void)lnewline();
448
	i = llength(curwp->w_dotp) - nce;
449
	curwp->w_doto = i > 0 ? i : 0;
450
	curwp->w_rflag |= WFMOVE;
451
	if (nce == 0 && curwp->w_doto != 0)
452
		return (fillword(f, n));
453
	return (TRUE);
454
}
455
456
/*
457
 * Set fill column to n for justify.
458
 */
459
int
460
setfillcol(int f, int n)
461
{
462
	char buf[32], *rep;
463
	const char *es;
464
	int nfill;
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);
474
		nfill = strtonum(rep, 0, INT_MAX, &es);
475
		if (es != NULL) {
476
			dobeep();
477
			ewprintf("Invalid fill column: %s", rep);
478
			return (FALSE);
479
		}
480
		fillcol = nfill;
481
		ewprintf("Fill column set to %d", fillcol);
482
	}
483
	return (TRUE);
484
}
485
486
int
487
sentencespace(int f, int n)
488
{
489
	if (f & FFARG)
490
		dblspace = n > 1;
491
	else
492
		dblspace = !dblspace;
493
494
	return (TRUE);
495
}