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