xref: /openbsd-src/usr.bin/mg/util.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: util.c,v 1.33 2014/03/26 22:02:06 lum Exp $	*/
2 
3 /* This file is in the public domain. */
4 
5 /*
6  *		Assorted commands.
7  * This file contains the command processors for a large assortment of
8  * unrelated commands.  The only thing they have in common is that they
9  * are all command processors.
10  */
11 
12 #include "def.h"
13 
14 #include <ctype.h>
15 
16 /*
17  * Display a bunch of useful information about the current location of dot.
18  * The character under the cursor (in octal), the current line, row, and
19  * column, and approximate position of the cursor in the file (as a
20  * percentage) is displayed.  The column position assumes an infinite
21  * position display; it does not truncate just because the screen does.
22  * This is normally bound to "C-X =".
23  */
24 /* ARGSUSED */
25 int
26 showcpos(int f, int n)
27 {
28 	struct line	*clp;
29 	long	 nchar, cchar;
30 	int	 nline, row;
31 	int	 cline, cbyte;		/* Current line/char/byte */
32 	int	 ratio;
33 
34 	/* collect the data */
35 	clp = bfirstlp(curbp);
36 	cchar = 0;
37 	cline = 0;
38 	cbyte = 0;
39 	nchar = 0;
40 	nline = 0;
41 	for (;;) {
42 		/* count this line */
43 		++nline;
44 		if (clp == curwp->w_dotp) {
45 			/* mark line */
46 			cline = nline;
47 			cchar = nchar + curwp->w_doto;
48 			if (curwp->w_doto == llength(clp))
49 				cbyte = '\n';
50 			else
51 				cbyte = lgetc(clp, curwp->w_doto);
52 		}
53 		/* now count the chars */
54 		nchar += llength(clp);
55 		clp = lforw(clp);
56 		if (clp == curbp->b_headp)
57 			break;
58 		/* count the newline */
59 		nchar++;
60 	}
61 	/* determine row */
62 	row = curwp->w_toprow + 1;
63 	clp = curwp->w_linep;
64 	while (clp != curbp->b_headp && clp != curwp->w_dotp) {
65 		++row;
66 		clp = lforw(clp);
67 	}
68 	/* NOSTRICT */
69 	ratio = nchar ? (100L * cchar) / nchar : 100;
70 	ewprintf("Char: %c (0%o)  point=%ld(%d%%)  line=%d  row=%d  col=%d",
71 	    cbyte, cbyte, cchar, ratio, cline, row, getcolpos(curwp));
72 	return (TRUE);
73 }
74 
75 int
76 getcolpos(struct mgwin *wp)
77 {
78 	int	col, i, c;
79 	char tmp[5];
80 
81 	/* determine column */
82 	col = 0;
83 
84 	for (i = 0; i < wp->w_doto; ++i) {
85 		c = lgetc(wp->w_dotp, i);
86 		if (c == '\t'
87 #ifdef NOTAB
88 		    && !(wp->w_bufp->b_flag & BFNOTAB)
89 #endif /* NOTAB */
90 			) {
91 			col |= 0x07;
92 			col++;
93 		} else if (ISCTRL(c) != FALSE)
94 			col += 2;
95 		else if (isprint(c)) {
96 			col++;
97 		} else {
98 			col += snprintf(tmp, sizeof(tmp), "\\%o", c);
99 		}
100 
101 	}
102 	return (col);
103 }
104 
105 /*
106  * Twiddle the two characters in front of and under dot, then move forward
107  * one character.  Treat new-line characters the same as any other.
108  * Normally bound to "C-t".  This always works within a line, so "WFEDIT"
109  * is good enough.
110  */
111 /* ARGSUSED */
112 int
113 twiddle(int f, int n)
114 {
115 	struct line	*dotp;
116 	int	 doto, cr;
117 
118 	dotp = curwp->w_dotp;
119 	doto = curwp->w_doto;
120 
121 	/* Don't twiddle if the dot is on the first char of buffer */
122 	if (doto == 0 && lback(dotp) == curbp->b_headp) {
123 		dobeep();
124 		ewprintf("Beginning of buffer");
125 		return(FALSE);
126 	}
127 	/* Don't twiddle if the dot is on the last char of buffer */
128 	if (doto == llength(dotp) && lforw(dotp) == curbp->b_headp) {
129 		dobeep();
130 		return(FALSE);
131 	}
132 	undo_boundary_enable(FFRAND, 0);
133 	if (doto == 0 && doto == llength(dotp)) { /* only '\n' on this line */
134 		(void)forwline(FFRAND, 1);
135 		curwp->w_doto = 0;
136 	} else {
137 		if (doto == 0) { /* 1st twiddle is on 1st character of a line */
138 			cr = lgetc(dotp, doto);
139 			(void)backdel(FFRAND, 1);
140 			(void)forwchar(FFRAND, 1);
141 			lnewline();
142 			linsert(1, cr);
143 			(void)backdel(FFRAND, 1);
144 		} else {	/* twiddle is elsewhere in line */
145 			cr = lgetc(dotp, doto - 1);
146 			(void)backdel(FFRAND, 1);
147 			(void)forwchar(FFRAND, 1);
148 			linsert(1, cr);
149 		}
150 	}
151 	undo_boundary_enable(FFRAND, 1);
152 	lchange(WFEDIT);
153 	return (TRUE);
154 }
155 
156 /*
157  * Open up some blank space.  The basic plan is to insert a bunch of
158  * newlines, and then back up over them.  Everything is done by the
159  * subcommand processors.  They even handle the looping.  Normally this
160  * is bound to "C-O".
161  */
162 /* ARGSUSED */
163 int
164 openline(int f, int n)
165 {
166 	int	i, s;
167 
168 	if (n < 0)
169 		return (FALSE);
170 	if (n == 0)
171 		return (TRUE);
172 
173 	/* insert newlines */
174 	undo_boundary_enable(FFRAND, 0);
175 	i = n;
176 	do {
177 		s = lnewline();
178 	} while (s == TRUE && --i);
179 
180 	/* then go back up overtop of them all */
181 	if (s == TRUE)
182 		s = backchar(f | FFRAND, n);
183 	undo_boundary_enable(FFRAND, 1);
184 	return (s);
185 }
186 
187 /*
188  * Insert a newline.
189  */
190 /* ARGSUSED */
191 int
192 newline(int f, int n)
193 {
194 	int	 s;
195 
196 	if (n < 0)
197 		return (FALSE);
198 
199 	while (n--) {
200 		if ((s = lnewline()) != TRUE)
201 			return (s);
202 	}
203 	return (TRUE);
204 }
205 
206 /*
207  * Delete blank lines around dot. What this command does depends if dot is
208  * sitting on a blank line. If dot is sitting on a blank line, this command
209  * deletes all the blank lines above and below the current line. If it is
210  * sitting on a non blank line then it deletes all of the blank lines after
211  * the line. Normally this command is bound to "C-X C-O". Any argument is
212  * ignored.
213  */
214 /* ARGSUSED */
215 int
216 deblank(int f, int n)
217 {
218 	struct line	*lp1, *lp2;
219 	RSIZE	 nld;
220 
221 	lp1 = curwp->w_dotp;
222 	while (llength(lp1) == 0 && (lp2 = lback(lp1)) != curbp->b_headp)
223 		lp1 = lp2;
224 	lp2 = lp1;
225 	nld = (RSIZE)0;
226 	while ((lp2 = lforw(lp2)) != curbp->b_headp && llength(lp2) == 0)
227 		++nld;
228 	if (nld == 0)
229 		return (TRUE);
230 	curwp->w_dotp = lforw(lp1);
231 	curwp->w_doto = 0;
232 	return (ldelete((RSIZE)nld, KNONE));
233 }
234 
235 /*
236  * Delete any whitespace around dot, then insert a space.
237  */
238 int
239 justone(int f, int n)
240 {
241 	undo_boundary_enable(FFRAND, 0);
242 	(void)delwhite(f, n);
243 	linsert(1, ' ');
244 	undo_boundary_enable(FFRAND, 1);
245 	return (TRUE);
246 }
247 
248 /*
249  * Delete any whitespace around dot.
250  */
251 /* ARGSUSED */
252 int
253 delwhite(int f, int n)
254 {
255 	int	col, s;
256 
257 	col = curwp->w_doto;
258 
259 	while (col < llength(curwp->w_dotp) &&
260 	    (isspace(lgetc(curwp->w_dotp, col))))
261 		++col;
262 	do {
263 		if (curwp->w_doto == 0) {
264 			s = FALSE;
265 			break;
266 		}
267 		if ((s = backchar(FFRAND, 1)) != TRUE)
268 			break;
269 	} while (isspace(lgetc(curwp->w_dotp, curwp->w_doto)));
270 
271 	if (s == TRUE)
272 		(void)forwchar(FFRAND, 1);
273 	(void)ldelete((RSIZE)(col - curwp->w_doto), KNONE);
274 	return (TRUE);
275 }
276 
277 /*
278  * Delete any leading whitespace on the current line
279  */
280 int
281 delleadwhite(int f, int n)
282 {
283 	int soff, ls;
284 	struct line *slp;
285 
286 	/* Save current position */
287 	slp = curwp->w_dotp;
288 	soff = curwp->w_doto;
289 
290 	for (ls = 0; ls < llength(slp); ls++)
291                  if (!isspace(lgetc(slp, ls)))
292                         break;
293 	gotobol(FFRAND, 1);
294 	forwdel(FFRAND, ls);
295 	soff -= ls;
296 	if (soff < 0)
297 		soff = 0;
298 	forwchar(FFRAND, soff);
299 
300 	return (TRUE);
301 }
302 
303 /*
304  * Delete any trailing whitespace on the current line
305  */
306 int
307 deltrailwhite(int f, int n)
308 {
309 	int soff;
310 
311 	/* Save current position */
312 	soff = curwp->w_doto;
313 
314 	gotoeol(FFRAND, 1);
315 	delwhite(FFRAND, 1);
316 
317 	/* restore original position, if possible */
318 	if (soff < curwp->w_doto)
319 		curwp->w_doto = soff;
320 
321 	return (TRUE);
322 }
323 
324 
325 
326 /*
327  * Insert a newline, then enough tabs and spaces to duplicate the indentation
328  * of the previous line.  Assumes tabs are every eight characters.  Quite
329  * simple.  Figure out the indentation of the current line.  Insert a newline
330  * by calling the standard routine.  Insert the indentation by inserting the
331  * right number of tabs and spaces.  Return TRUE if all ok.  Return FALSE if
332  * one of the subcommands failed. Normally bound to "C-M".
333  */
334 /* ARGSUSED */
335 int
336 lfindent(int f, int n)
337 {
338 	int	c, i, nicol;
339 	int	s = TRUE;
340 
341 	if (n < 0)
342 		return (FALSE);
343 
344 	undo_boundary_enable(FFRAND, 0);
345 	while (n--) {
346 		nicol = 0;
347 		for (i = 0; i < llength(curwp->w_dotp); ++i) {
348 			c = lgetc(curwp->w_dotp, i);
349 			if (c != ' ' && c != '\t')
350 				break;
351 			if (c == '\t')
352 				nicol |= 0x07;
353 			++nicol;
354 		}
355 		if (lnewline() == FALSE || ((
356 #ifdef	NOTAB
357 		    curbp->b_flag & BFNOTAB) ? linsert(nicol, ' ') == FALSE : (
358 #endif /* NOTAB */
359 		    ((i = nicol / 8) != 0 && linsert(i, '\t') == FALSE) ||
360 		    ((i = nicol % 8) != 0 && linsert(i, ' ') == FALSE)))) {
361 			s = FALSE;
362 			break;
363 		}
364 	}
365 	undo_boundary_enable(FFRAND, 1);
366 	return (s);
367 }
368 
369 /*
370  * Indent the current line. Delete existing leading whitespace,
371  * and use tabs/spaces to achieve correct indentation. Try
372  * to leave dot where it started.
373  */
374 int
375 indent(int f, int n)
376 {
377 	int soff, i;
378 
379 	if (n < 0)
380 		return (FALSE);
381 
382 	delleadwhite(FFRAND, 1);
383 
384 	/* If not invoked with a numerical argument, done */
385 	if (!(f & FFARG))
386 		return (TRUE);
387 
388 	/* insert appropriate whitespace */
389 	soff = curwp->w_doto;
390 	(void)gotobol(FFRAND, 1);
391 	if (
392 #ifdef	NOTAB
393 	    (curbp->b_flag & BFNOTAB) ? linsert(n, ' ') == FALSE :
394 #endif /* NOTAB */
395 	    (((i = n / 8) != 0 && linsert(i, '\t') == FALSE) ||
396 	    ((i = n % 8) != 0 && linsert(i, ' ') == FALSE)))
397 		return (FALSE);
398 
399 	forwchar(FFRAND, soff);
400 
401 	return (TRUE);
402 }
403 
404 
405 /*
406  * Delete forward.  This is real easy, because the basic delete routine does
407  * all of the work.  Watches for negative arguments, and does the right thing.
408  * If any argument is present, it kills rather than deletes, to prevent loss
409  * of text if typed with a big argument.  Normally bound to "C-D".
410  */
411 /* ARGSUSED */
412 int
413 forwdel(int f, int n)
414 {
415 	if (n < 0)
416 		return (backdel(f | FFRAND, -n));
417 
418 	/* really a kill */
419 	if (f & FFARG) {
420 		if ((lastflag & CFKILL) == 0)
421 			kdelete();
422 		thisflag |= CFKILL;
423 	}
424 
425 	return (ldelete((RSIZE) n, (f & FFARG) ? KFORW : KNONE));
426 }
427 
428 /*
429  * Delete backwards.  This is quite easy too, because it's all done with
430  * other functions.  Just move the cursor back, and delete forwards.  Like
431  * delete forward, this actually does a kill if presented with an argument.
432  */
433 /* ARGSUSED */
434 int
435 backdel(int f, int n)
436 {
437 	int	s;
438 
439 	if (n < 0)
440 		return (forwdel(f | FFRAND, -n));
441 
442 	/* really a kill */
443 	if (f & FFARG) {
444 		if ((lastflag & CFKILL) == 0)
445 			kdelete();
446 		thisflag |= CFKILL;
447 	}
448 	if ((s = backchar(f | FFRAND, n)) == TRUE)
449 		s = ldelete((RSIZE)n, (f & FFARG) ? KFORW : KNONE);
450 
451 	return (s);
452 }
453 
454 #ifdef	NOTAB
455 /* ARGSUSED */
456 int
457 space_to_tabstop(int f, int n)
458 {
459 	if (n < 0)
460 		return (FALSE);
461 	if (n == 0)
462 		return (TRUE);
463 	return (linsert((n << 3) - (curwp->w_doto & 7), ' '));
464 }
465 #endif /* NOTAB */
466 
467 /*
468  * Move the dot to the first non-whitespace character of the current line.
469  */
470 int
471 backtoindent(int f, int n)
472 {
473 	gotobol(FFRAND, 1);
474 	while (curwp->w_doto < llength(curwp->w_dotp) &&
475 	    (isspace(lgetc(curwp->w_dotp, curwp->w_doto))))
476 		++curwp->w_doto;
477 	return (TRUE);
478 }
479 
480 /*
481  * Join the current line to the previous, or with arg, the next line
482  * to the current one.  If the former line is not empty, leave exactly
483  * one space at the joint.  Otherwise, leave no whitespace.
484  */
485 int
486 joinline(int f, int n)
487 {
488 	int doto;
489 
490 	undo_boundary_enable(FFRAND, 0);
491 	if (f & FFARG) {
492 		gotoeol(FFRAND, 1);
493 		forwdel(FFRAND, 1);
494 	} else {
495 		gotobol(FFRAND, 1);
496 		backdel(FFRAND, 1);
497 	}
498 
499 	delwhite(FFRAND, 1);
500 
501 	if ((doto = curwp->w_doto) > 0) {
502 		linsert(1, ' ');
503 		curwp->w_doto = doto;
504 	}
505 	undo_boundary_enable(FFRAND, 1);
506 
507 	return (TRUE);
508 }
509