1 |
|
|
/* $OpenBSD: v_ex.c,v 1.13 2016/01/06 22:28:52 millert Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 1992, 1993, 1994 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* Copyright (c) 1992, 1993, 1994, 1995, 1996 |
7 |
|
|
* Keith Bostic. All rights reserved. |
8 |
|
|
* |
9 |
|
|
* See the LICENSE file for redistribution information. |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
#include "config.h" |
13 |
|
|
|
14 |
|
|
#include <sys/types.h> |
15 |
|
|
#include <sys/queue.h> |
16 |
|
|
#include <sys/time.h> |
17 |
|
|
|
18 |
|
|
#include <bitstring.h> |
19 |
|
|
#include <limits.h> |
20 |
|
|
#include <stdio.h> |
21 |
|
|
#include <stdlib.h> |
22 |
|
|
#include <string.h> |
23 |
|
|
#include <unistd.h> |
24 |
|
|
|
25 |
|
|
#include "../common/common.h" |
26 |
|
|
#include "vi.h" |
27 |
|
|
|
28 |
|
|
static int v_ecl(SCR *); |
29 |
|
|
static int v_ecl_init(SCR *); |
30 |
|
|
static int v_ecl_log(SCR *, TEXT *); |
31 |
|
|
static int v_ex_done(SCR *, VICMD *); |
32 |
|
|
static int v_exec_ex(SCR *, VICMD *, EXCMD *); |
33 |
|
|
|
34 |
|
|
/* |
35 |
|
|
* v_again -- & |
36 |
|
|
* Repeat the previous substitution. |
37 |
|
|
* |
38 |
|
|
* PUBLIC: int v_again(SCR *, VICMD *); |
39 |
|
|
*/ |
40 |
|
|
int |
41 |
|
|
v_again(SCR *sp, VICMD *vp) |
42 |
|
|
{ |
43 |
|
|
ARGS *ap[2], a; |
44 |
|
|
EXCMD cmd; |
45 |
|
|
|
46 |
|
|
ex_cinit(&cmd, C_SUBAGAIN, 2, vp->m_start.lno, vp->m_start.lno, 1, ap); |
47 |
|
|
ex_cadd(&cmd, &a, "", 1); |
48 |
|
|
|
49 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
50 |
|
|
} |
51 |
|
|
|
52 |
|
|
/* |
53 |
|
|
* v_exmode -- Q |
54 |
|
|
* Switch the editor into EX mode. |
55 |
|
|
* |
56 |
|
|
* PUBLIC: int v_exmode(SCR *, VICMD *); |
57 |
|
|
*/ |
58 |
|
|
int |
59 |
|
|
v_exmode(SCR *sp, VICMD *vp) |
60 |
|
|
{ |
61 |
|
|
GS *gp; |
62 |
|
|
|
63 |
|
|
gp = sp->gp; |
64 |
|
|
|
65 |
|
|
/* Try and switch screens -- the screen may not permit it. */ |
66 |
|
|
if (gp->scr_screen(sp, SC_EX)) { |
67 |
|
|
msgq(sp, M_ERR, |
68 |
|
|
"The Q command requires the ex terminal interface"); |
69 |
|
|
return (1); |
70 |
|
|
} |
71 |
|
|
(void)gp->scr_attr(sp, SA_ALTERNATE, 0); |
72 |
|
|
|
73 |
|
|
/* Save the current cursor position. */ |
74 |
|
|
sp->frp->lno = sp->lno; |
75 |
|
|
sp->frp->cno = sp->cno; |
76 |
|
|
F_SET(sp->frp, FR_CURSORSET); |
77 |
|
|
|
78 |
|
|
/* Switch to ex mode. */ |
79 |
|
|
F_CLR(sp, SC_VI | SC_SCR_VI); |
80 |
|
|
F_SET(sp, SC_EX); |
81 |
|
|
|
82 |
|
|
/* Move out of the vi screen. */ |
83 |
|
|
(void)ex_puts(sp, "\n"); |
84 |
|
|
|
85 |
|
|
return (0); |
86 |
|
|
} |
87 |
|
|
|
88 |
|
|
/* |
89 |
|
|
* v_join -- [count]J |
90 |
|
|
* Join lines together. |
91 |
|
|
* |
92 |
|
|
* PUBLIC: int v_join(SCR *, VICMD *); |
93 |
|
|
*/ |
94 |
|
|
int |
95 |
|
|
v_join(SCR *sp, VICMD *vp) |
96 |
|
|
{ |
97 |
|
|
EXCMD cmd; |
98 |
|
|
int lno; |
99 |
|
|
|
100 |
|
|
/* |
101 |
|
|
* YASC. |
102 |
|
|
* The general rule is that '#J' joins # lines, counting the current |
103 |
|
|
* line. However, 'J' and '1J' are the same as '2J', i.e. join the |
104 |
|
|
* current and next lines. This doesn't map well into the ex command |
105 |
|
|
* (which takes two line numbers), so we handle it here. Note that |
106 |
|
|
* we never test for EOF -- historically going past the end of file |
107 |
|
|
* worked just fine. |
108 |
|
|
*/ |
109 |
|
|
lno = vp->m_start.lno + 1; |
110 |
|
|
if (F_ISSET(vp, VC_C1SET) && vp->count > 2) |
111 |
|
|
lno = vp->m_start.lno + (vp->count - 1); |
112 |
|
|
|
113 |
|
|
ex_cinit(&cmd, C_JOIN, 2, vp->m_start.lno, lno, 0, NULL); |
114 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
115 |
|
|
} |
116 |
|
|
|
117 |
|
|
/* |
118 |
|
|
* v_shiftl -- [count]<motion |
119 |
|
|
* Shift lines left. |
120 |
|
|
* |
121 |
|
|
* PUBLIC: int v_shiftl(SCR *, VICMD *); |
122 |
|
|
*/ |
123 |
|
|
int |
124 |
|
|
v_shiftl(SCR *sp, VICMD *vp) |
125 |
|
|
{ |
126 |
|
|
ARGS *ap[2], a; |
127 |
|
|
EXCMD cmd; |
128 |
|
|
|
129 |
|
|
ex_cinit(&cmd, C_SHIFTL, 2, vp->m_start.lno, vp->m_stop.lno, 0, ap); |
130 |
|
|
ex_cadd(&cmd, &a, "<", 1); |
131 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
132 |
|
|
} |
133 |
|
|
|
134 |
|
|
/* |
135 |
|
|
* v_shiftr -- [count]>motion |
136 |
|
|
* Shift lines right. |
137 |
|
|
* |
138 |
|
|
* PUBLIC: int v_shiftr(SCR *, VICMD *); |
139 |
|
|
*/ |
140 |
|
|
int |
141 |
|
|
v_shiftr(SCR *sp, VICMD *vp) |
142 |
|
|
{ |
143 |
|
|
ARGS *ap[2], a; |
144 |
|
|
EXCMD cmd; |
145 |
|
|
|
146 |
|
|
ex_cinit(&cmd, C_SHIFTR, 2, vp->m_start.lno, vp->m_stop.lno, 0, ap); |
147 |
|
|
ex_cadd(&cmd, &a, ">", 1); |
148 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
149 |
|
|
} |
150 |
|
|
|
151 |
|
|
/* |
152 |
|
|
* v_suspend -- ^Z |
153 |
|
|
* Suspend vi. |
154 |
|
|
* |
155 |
|
|
* PUBLIC: int v_suspend(SCR *, VICMD *); |
156 |
|
|
*/ |
157 |
|
|
int |
158 |
|
|
v_suspend(SCR *sp, VICMD *vp) |
159 |
|
|
{ |
160 |
|
|
ARGS *ap[2], a; |
161 |
|
|
EXCMD cmd; |
162 |
|
|
|
163 |
|
|
ex_cinit(&cmd, C_STOP, 0, OOBLNO, OOBLNO, 0, ap); |
164 |
|
|
ex_cadd(&cmd, &a, "suspend", sizeof("suspend") - 1); |
165 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
/* |
169 |
|
|
* v_switch -- ^^ |
170 |
|
|
* Switch to the previous file. |
171 |
|
|
* |
172 |
|
|
* PUBLIC: int v_switch(SCR *, VICMD *); |
173 |
|
|
*/ |
174 |
|
|
int |
175 |
|
|
v_switch(SCR *sp, VICMD *vp) |
176 |
|
|
{ |
177 |
|
|
ARGS *ap[2], a; |
178 |
|
|
EXCMD cmd; |
179 |
|
|
char *name; |
180 |
|
|
|
181 |
|
|
/* |
182 |
|
|
* Try the alternate file name, then the previous file |
183 |
|
|
* name. Use the real name, not the user's current name. |
184 |
|
|
*/ |
185 |
|
|
if (sp->alt_name == NULL) { |
186 |
|
|
msgq(sp, M_ERR, "No previous file to edit"); |
187 |
|
|
return (1); |
188 |
|
|
} |
189 |
|
|
if ((name = strdup(sp->alt_name)) == NULL) { |
190 |
|
|
msgq(sp, M_SYSERR, NULL); |
191 |
|
|
return (1); |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
/* If autowrite is set, write out the file. */ |
195 |
|
|
if (file_m1(sp, 0, FS_ALL)) { |
196 |
|
|
free(name); |
197 |
|
|
return (1); |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
ex_cinit(&cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0, ap); |
201 |
|
|
ex_cadd(&cmd, &a, name, strlen(name)); |
202 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
203 |
|
|
} |
204 |
|
|
|
205 |
|
|
/* |
206 |
|
|
* v_tagpush -- ^[ |
207 |
|
|
* Do a tag search on the cursor keyword. |
208 |
|
|
* |
209 |
|
|
* PUBLIC: int v_tagpush(SCR *, VICMD *); |
210 |
|
|
*/ |
211 |
|
|
int |
212 |
|
|
v_tagpush(SCR *sp, VICMD *vp) |
213 |
|
|
{ |
214 |
|
|
ARGS *ap[2], a; |
215 |
|
|
EXCMD cmd; |
216 |
|
|
|
217 |
|
|
ex_cinit(&cmd, C_TAG, 0, OOBLNO, 0, 0, ap); |
218 |
|
|
ex_cadd(&cmd, &a, VIP(sp)->keyw, strlen(VIP(sp)->keyw)); |
219 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
220 |
|
|
} |
221 |
|
|
|
222 |
|
|
/* |
223 |
|
|
* v_tagpop -- ^T |
224 |
|
|
* Pop the tags stack. |
225 |
|
|
* |
226 |
|
|
* PUBLIC: int v_tagpop(SCR *, VICMD *); |
227 |
|
|
*/ |
228 |
|
|
int |
229 |
|
|
v_tagpop(SCR *sp, VICMD *vp) |
230 |
|
|
{ |
231 |
|
|
EXCMD cmd; |
232 |
|
|
|
233 |
|
|
ex_cinit(&cmd, C_TAGPOP, 0, OOBLNO, 0, 0, NULL); |
234 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
235 |
|
|
} |
236 |
|
|
|
237 |
|
|
/* |
238 |
|
|
* v_filter -- [count]!motion command(s) |
239 |
|
|
* Run range through shell commands, replacing text. |
240 |
|
|
* |
241 |
|
|
* PUBLIC: int v_filter(SCR *, VICMD *); |
242 |
|
|
*/ |
243 |
|
|
int |
244 |
|
|
v_filter(SCR *sp, VICMD *vp) |
245 |
|
|
{ |
246 |
|
|
EXCMD cmd; |
247 |
|
|
TEXT *tp; |
248 |
|
|
|
249 |
|
|
/* |
250 |
|
|
* !!! |
251 |
|
|
* Historical vi permitted "!!" in an empty file, and it's handled |
252 |
|
|
* as a special case in the ex_bang routine. Don't modify this setup |
253 |
|
|
* without understanding that one. In particular, note that we're |
254 |
|
|
* manipulating the ex argument structures behind ex's back. |
255 |
|
|
* |
256 |
|
|
* !!! |
257 |
|
|
* Historical vi did not permit the '!' command to be associated with |
258 |
|
|
* a non-line oriented motion command, in general, although it did |
259 |
|
|
* with search commands. So, !f; and !w would fail, but !/;<CR> |
260 |
|
|
* would succeed, even if they all moved to the same location in the |
261 |
|
|
* current line. I don't see any reason to disallow '!' using any of |
262 |
|
|
* the possible motion commands. |
263 |
|
|
* |
264 |
|
|
* !!! |
265 |
|
|
* Historical vi ran the last bang command if N or n was used as the |
266 |
|
|
* search motion. |
267 |
|
|
*/ |
268 |
|
|
if (F_ISSET(vp, VC_ISDOT) || |
269 |
|
|
ISCMD(vp->rkp, 'N') || ISCMD(vp->rkp, 'n')) { |
270 |
|
|
ex_cinit(&cmd, C_BANG, |
271 |
|
|
2, vp->m_start.lno, vp->m_stop.lno, 0, NULL); |
272 |
|
|
EXP(sp)->argsoff = 0; /* XXX */ |
273 |
|
|
|
274 |
|
|
if (argv_exp1(sp, &cmd, "!", 1, 1)) |
275 |
|
|
return (1); |
276 |
|
|
cmd.argc = EXP(sp)->argsoff; /* XXX */ |
277 |
|
|
cmd.argv = EXP(sp)->args; /* XXX */ |
278 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
/* Get the command from the user. */ |
282 |
|
|
if (v_tcmd(sp, vp, |
283 |
|
|
'!', TXT_BS | TXT_CR | TXT_ESCAPE | TXT_FILEC | TXT_PROMPT)) |
284 |
|
|
return (1); |
285 |
|
|
|
286 |
|
|
/* |
287 |
|
|
* Check to see if the user changed their mind. |
288 |
|
|
* |
289 |
|
|
* !!! |
290 |
|
|
* Entering <escape> on an empty line was historically an error, |
291 |
|
|
* this implementation doesn't bother. |
292 |
|
|
*/ |
293 |
|
|
tp = TAILQ_FIRST(&sp->tiq); |
294 |
|
|
if (tp->term != TERM_OK) { |
295 |
|
|
vp->m_final.lno = sp->lno; |
296 |
|
|
vp->m_final.cno = sp->cno; |
297 |
|
|
return (0); |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
/* Home the cursor. */ |
301 |
|
|
vs_home(sp); |
302 |
|
|
|
303 |
|
|
ex_cinit(&cmd, C_BANG, 2, vp->m_start.lno, vp->m_stop.lno, 0, NULL); |
304 |
|
|
EXP(sp)->argsoff = 0; /* XXX */ |
305 |
|
|
|
306 |
|
|
if (argv_exp1(sp, &cmd, tp->lb + 1, tp->len - 1, 1)) |
307 |
|
|
return (1); |
308 |
|
|
cmd.argc = EXP(sp)->argsoff; /* XXX */ |
309 |
|
|
cmd.argv = EXP(sp)->args; /* XXX */ |
310 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
311 |
|
|
} |
312 |
|
|
|
313 |
|
|
/* |
314 |
|
|
* v_event_exec -- |
315 |
|
|
* Execute some command(s) based on an event. |
316 |
|
|
* |
317 |
|
|
* PUBLIC: int v_event_exec(SCR *, VICMD *); |
318 |
|
|
*/ |
319 |
|
|
int |
320 |
|
|
v_event_exec(SCR *sp, VICMD *vp) |
321 |
|
|
{ |
322 |
|
|
EXCMD cmd; |
323 |
|
|
|
324 |
|
|
switch (vp->ev.e_event) { |
325 |
|
|
case E_QUIT: |
326 |
|
|
ex_cinit(&cmd, C_QUIT, 0, OOBLNO, OOBLNO, 0, NULL); |
327 |
|
|
break; |
328 |
|
|
case E_WRITE: |
329 |
|
|
ex_cinit(&cmd, C_WRITE, 0, OOBLNO, OOBLNO, 0, NULL); |
330 |
|
|
break; |
331 |
|
|
default: |
332 |
|
|
abort(); |
333 |
|
|
} |
334 |
|
|
return (v_exec_ex(sp, vp, &cmd)); |
335 |
|
|
} |
336 |
|
|
|
337 |
|
|
/* |
338 |
|
|
* v_exec_ex -- |
339 |
|
|
* Execute an ex command. |
340 |
|
|
*/ |
341 |
|
|
static int |
342 |
|
|
v_exec_ex(SCR *sp, VICMD *vp, EXCMD *exp) |
343 |
|
|
{ |
344 |
|
|
int rval; |
345 |
|
|
|
346 |
|
|
rval = exp->cmd->fn(sp, exp); |
347 |
|
|
return (v_ex_done(sp, vp) || rval); |
348 |
|
|
} |
349 |
|
|
|
350 |
|
|
/* |
351 |
|
|
* v_ex -- : |
352 |
|
|
* Execute a colon command line. |
353 |
|
|
* |
354 |
|
|
* PUBLIC: int v_ex(SCR *, VICMD *); |
355 |
|
|
*/ |
356 |
|
|
int |
357 |
|
|
v_ex(SCR *sp, VICMD *vp) |
358 |
|
|
{ |
359 |
|
|
GS *gp; |
360 |
|
|
TEXT *tp; |
361 |
|
|
int do_cedit, do_resolution, ifcontinue; |
362 |
|
|
|
363 |
|
|
gp = sp->gp; |
364 |
|
|
|
365 |
|
|
/* |
366 |
|
|
* !!! |
367 |
|
|
* If we put out more than a single line of messages, or ex trashes |
368 |
|
|
* the screen, the user may continue entering ex commands. We find |
369 |
|
|
* this out when we do the screen/message resolution. We can't enter |
370 |
|
|
* completely into ex mode however, because the user can elect to |
371 |
|
|
* return into vi mode by entering any key, i.e. we have to be in raw |
372 |
|
|
* mode. |
373 |
|
|
*/ |
374 |
|
|
for (do_cedit = do_resolution = 0;;) { |
375 |
|
|
/* |
376 |
|
|
* !!! |
377 |
|
|
* There may already be an ex command waiting to run. If |
378 |
|
|
* so, we continue with it. |
379 |
|
|
*/ |
380 |
|
|
if (!EXCMD_RUNNING(gp)) { |
381 |
|
|
/* Get a command. */ |
382 |
|
|
if (v_tcmd(sp, vp, ':', |
383 |
|
|
TXT_BS | TXT_CEDIT | TXT_FILEC | TXT_PROMPT)) |
384 |
|
|
return (1); |
385 |
|
|
tp = TAILQ_FIRST(&sp->tiq); |
386 |
|
|
|
387 |
|
|
/* |
388 |
|
|
* If the user entered a single <esc>, they want to |
389 |
|
|
* edit their colon command history. If they already |
390 |
|
|
* entered some text, move it into the edit history. |
391 |
|
|
*/ |
392 |
|
|
if (tp->term == TERM_CEDIT) { |
393 |
|
|
if (tp->len > 1 && v_ecl_log(sp, tp)) |
394 |
|
|
return (1); |
395 |
|
|
do_cedit = 1; |
396 |
|
|
break; |
397 |
|
|
} |
398 |
|
|
|
399 |
|
|
/* If the user changed their mind, return. */ |
400 |
|
|
if (tp->term != TERM_OK) |
401 |
|
|
break; |
402 |
|
|
|
403 |
|
|
/* Log the command. */ |
404 |
|
|
if (O_STR(sp, O_CEDIT) != NULL && v_ecl_log(sp, tp)) |
405 |
|
|
return (1); |
406 |
|
|
|
407 |
|
|
/* Push a command on the command stack. */ |
408 |
|
|
if (ex_run_str(sp, NULL, tp->lb, tp->len, 0, 1)) |
409 |
|
|
return (1); |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
/* Home the cursor. */ |
413 |
|
|
vs_home(sp); |
414 |
|
|
|
415 |
|
|
/* |
416 |
|
|
* !!! |
417 |
|
|
* If the editor wrote the screen behind curses back, put out |
418 |
|
|
* a <newline> so that we don't overwrite the user's command |
419 |
|
|
* with its output or the next want-to-continue? message. This |
420 |
|
|
* doesn't belong here, but I can't find another place to put |
421 |
|
|
* it. See, we resolved the output from the last ex command, |
422 |
|
|
* and the user entered another one. This is the only place |
423 |
|
|
* where we have control before the ex command writes output. |
424 |
|
|
* We could get control in vs_msg(), but we have no way to know |
425 |
|
|
* if command didn't put out any output when we try and resolve |
426 |
|
|
* this command. This fixes a bug where combinations of ex |
427 |
|
|
* commands, e.g. ":set<CR>:!date<CR>:set" didn't look right. |
428 |
|
|
*/ |
429 |
|
|
if (F_ISSET(sp, SC_SCR_EXWROTE)) |
430 |
|
|
(void)putchar('\n'); |
431 |
|
|
|
432 |
|
|
/* Call the ex parser. */ |
433 |
|
|
(void)ex_cmd(sp); |
434 |
|
|
|
435 |
|
|
/* Flush ex messages. */ |
436 |
|
|
(void)ex_fflush(sp); |
437 |
|
|
|
438 |
|
|
/* Resolve any messages. */ |
439 |
|
|
if (vs_ex_resolve(sp, &ifcontinue)) |
440 |
|
|
return (1); |
441 |
|
|
|
442 |
|
|
/* |
443 |
|
|
* Continue or return. If continuing, make sure that we |
444 |
|
|
* eventually do resolution. |
445 |
|
|
*/ |
446 |
|
|
if (!ifcontinue) |
447 |
|
|
break; |
448 |
|
|
do_resolution = 1; |
449 |
|
|
|
450 |
|
|
/* If we're continuing, it's a new command. */ |
451 |
|
|
++sp->ccnt; |
452 |
|
|
} |
453 |
|
|
|
454 |
|
|
/* |
455 |
|
|
* If the user previously continued an ex command, we have to do |
456 |
|
|
* resolution to clean up the screen. Don't wait, we already did |
457 |
|
|
* that. |
458 |
|
|
*/ |
459 |
|
|
if (do_resolution) { |
460 |
|
|
F_SET(sp, SC_EX_WAIT_NO); |
461 |
|
|
if (vs_ex_resolve(sp, &ifcontinue)) |
462 |
|
|
return (1); |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
/* Cleanup from the ex command. */ |
466 |
|
|
if (v_ex_done(sp, vp)) |
467 |
|
|
return (1); |
468 |
|
|
|
469 |
|
|
/* The user may want to edit their colon command history. */ |
470 |
|
|
if (do_cedit) |
471 |
|
|
return (v_ecl(sp)); |
472 |
|
|
|
473 |
|
|
return (0); |
474 |
|
|
} |
475 |
|
|
|
476 |
|
|
/* |
477 |
|
|
* v_ex_done -- |
478 |
|
|
* Cleanup from an ex command. |
479 |
|
|
*/ |
480 |
|
|
static int |
481 |
|
|
v_ex_done(SCR *sp, VICMD *vp) |
482 |
|
|
{ |
483 |
|
|
size_t len; |
484 |
|
|
|
485 |
|
|
/* |
486 |
|
|
* The only cursor modifications are real, however, the underlying |
487 |
|
|
* line may have changed; don't trust anything. This code has been |
488 |
|
|
* a remarkably fertile place for bugs. Do a reality check on a |
489 |
|
|
* cursor value, and make sure it's okay. If necessary, change it. |
490 |
|
|
* Ex keeps track of the line number, but it cares less about the |
491 |
|
|
* column and it may have disappeared. |
492 |
|
|
* |
493 |
|
|
* Don't trust ANYTHING. |
494 |
|
|
* |
495 |
|
|
* XXX |
496 |
|
|
* Ex will soon have to start handling the column correctly; see |
497 |
|
|
* the POSIX 1003.2 standard. |
498 |
|
|
*/ |
499 |
|
|
if (db_eget(sp, sp->lno, NULL, &len, NULL)) { |
500 |
|
|
sp->lno = 1; |
501 |
|
|
sp->cno = 0; |
502 |
|
|
} else if (sp->cno >= len) |
503 |
|
|
sp->cno = len ? len - 1 : 0; |
504 |
|
|
|
505 |
|
|
vp->m_final.lno = sp->lno; |
506 |
|
|
vp->m_final.cno = sp->cno; |
507 |
|
|
|
508 |
|
|
/* |
509 |
|
|
* Don't re-adjust the cursor after executing an ex command, |
510 |
|
|
* and ex movements are permanent. |
511 |
|
|
*/ |
512 |
|
|
F_CLR(vp, VM_RCM_MASK); |
513 |
|
|
F_SET(vp, VM_RCM_SET); |
514 |
|
|
|
515 |
|
|
return (0); |
516 |
|
|
} |
517 |
|
|
|
518 |
|
|
/* |
519 |
|
|
* v_ecl -- |
520 |
|
|
* Start an edit window on the colon command-line commands. |
521 |
|
|
*/ |
522 |
|
|
static int |
523 |
|
|
v_ecl(SCR *sp) |
524 |
|
|
{ |
525 |
|
|
GS *gp; |
526 |
|
|
SCR *new; |
527 |
|
|
|
528 |
|
|
/* Initialize the screen, if necessary. */ |
529 |
|
|
gp = sp->gp; |
530 |
|
|
if (gp->ccl_sp == NULL && v_ecl_init(sp)) |
531 |
|
|
return (1); |
532 |
|
|
|
533 |
|
|
/* Get a new screen. */ |
534 |
|
|
if (screen_init(gp, sp, &new)) |
535 |
|
|
return (1); |
536 |
|
|
if (vs_split(sp, new, 1)) { |
537 |
|
|
(void)screen_end(new); |
538 |
|
|
return (1); |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
/* Attach to the screen. */ |
542 |
|
|
new->ep = gp->ccl_sp->ep; |
543 |
|
|
++new->ep->refcnt; |
544 |
|
|
|
545 |
|
|
new->frp = gp->ccl_sp->frp; |
546 |
|
|
new->frp->flags = sp->frp->flags; |
547 |
|
|
|
548 |
|
|
/* Move the cursor to the end. */ |
549 |
|
|
(void)db_last(new, &new->lno); |
550 |
|
|
if (new->lno == 0) |
551 |
|
|
new->lno = 1; |
552 |
|
|
|
553 |
|
|
/* Remember the originating window. */ |
554 |
|
|
sp->ccl_parent = sp; |
555 |
|
|
|
556 |
|
|
/* It's a special window. */ |
557 |
|
|
F_SET(new, SC_COMEDIT); |
558 |
|
|
|
559 |
|
|
/* Set up the switch. */ |
560 |
|
|
sp->nextdisp = new; |
561 |
|
|
F_SET(sp, SC_SSWITCH); |
562 |
|
|
return (0); |
563 |
|
|
} |
564 |
|
|
|
565 |
|
|
/* |
566 |
|
|
* v_ecl_exec -- |
567 |
|
|
* Execute a command from a colon command-line window. |
568 |
|
|
* |
569 |
|
|
* PUBLIC: int v_ecl_exec(SCR *); |
570 |
|
|
*/ |
571 |
|
|
int |
572 |
|
|
v_ecl_exec(SCR *sp) |
573 |
|
|
{ |
574 |
|
|
size_t len; |
575 |
|
|
char *p; |
576 |
|
|
|
577 |
|
|
if (db_get(sp, sp->lno, 0, &p, &len) && sp->lno == 1) { |
578 |
|
|
v_emsg(sp, NULL, VIM_EMPTY); |
579 |
|
|
return (1); |
580 |
|
|
} |
581 |
|
|
if (len == 0) { |
582 |
|
|
msgq(sp, M_BERR, "No ex command to execute"); |
583 |
|
|
return (1); |
584 |
|
|
} |
585 |
|
|
|
586 |
|
|
/* Push the command on the command stack. */ |
587 |
|
|
if (ex_run_str(sp, NULL, p, len, 0, 0)) |
588 |
|
|
return (1); |
589 |
|
|
|
590 |
|
|
/* Set up the switch. */ |
591 |
|
|
sp->nextdisp = sp->ccl_parent; |
592 |
|
|
F_SET(sp, SC_EXIT); |
593 |
|
|
return (0); |
594 |
|
|
} |
595 |
|
|
|
596 |
|
|
/* |
597 |
|
|
* v_ecl_log -- |
598 |
|
|
* Log a command into the colon command-line log file. |
599 |
|
|
*/ |
600 |
|
|
static int |
601 |
|
|
v_ecl_log(SCR *sp, TEXT *tp) |
602 |
|
|
{ |
603 |
|
|
EXF *save_ep; |
604 |
|
|
recno_t lno; |
605 |
|
|
int rval; |
606 |
|
|
|
607 |
|
|
/* Initialize the screen, if necessary. */ |
608 |
|
|
if (sp->gp->ccl_sp == NULL && v_ecl_init(sp)) |
609 |
|
|
return (1); |
610 |
|
|
|
611 |
|
|
/* |
612 |
|
|
* Don't log colon command window commands into the colon command |
613 |
|
|
* window... |
614 |
|
|
*/ |
615 |
|
|
if (sp->ep == sp->gp->ccl_sp->ep) |
616 |
|
|
return (0); |
617 |
|
|
|
618 |
|
|
/* |
619 |
|
|
* XXX |
620 |
|
|
* Swap the current EXF with the colon command file EXF. This |
621 |
|
|
* isn't pretty, but too many routines "know" that sp->ep points |
622 |
|
|
* to the current EXF. |
623 |
|
|
*/ |
624 |
|
|
save_ep = sp->ep; |
625 |
|
|
sp->ep = sp->gp->ccl_sp->ep; |
626 |
|
|
if (db_last(sp, &lno)) { |
627 |
|
|
sp->ep = save_ep; |
628 |
|
|
return (1); |
629 |
|
|
} |
630 |
|
|
rval = db_append(sp, 0, lno, tp->lb, tp->len); |
631 |
|
|
sp->ep = save_ep; |
632 |
|
|
return (rval); |
633 |
|
|
} |
634 |
|
|
|
635 |
|
|
/* |
636 |
|
|
* v_ecl_init -- |
637 |
|
|
* Initialize the colon command-line log file. |
638 |
|
|
*/ |
639 |
|
|
static int |
640 |
|
|
v_ecl_init(SCR *sp) |
641 |
|
|
{ |
642 |
|
|
FREF *frp; |
643 |
|
|
GS *gp; |
644 |
|
|
|
645 |
|
|
gp = sp->gp; |
646 |
|
|
|
647 |
|
|
/* Get a temporary file. */ |
648 |
|
|
if ((frp = file_add(sp, NULL)) == NULL) |
649 |
|
|
return (1); |
650 |
|
|
|
651 |
|
|
/* |
652 |
|
|
* XXX |
653 |
|
|
* Create a screen -- the file initialization code wants one. |
654 |
|
|
*/ |
655 |
|
|
if (screen_init(gp, sp, &gp->ccl_sp)) |
656 |
|
|
return (1); |
657 |
|
|
if (file_init(gp->ccl_sp, frp, NULL, 0)) { |
658 |
|
|
(void)screen_end(gp->ccl_sp); |
659 |
|
|
return (1); |
660 |
|
|
} |
661 |
|
|
|
662 |
|
|
/* The underlying file isn't recoverable. */ |
663 |
|
|
F_CLR(gp->ccl_sp->ep, F_RCV_ON); |
664 |
|
|
|
665 |
|
|
return (0); |
666 |
|
|
} |