1 |
|
|
/* $OpenBSD: vs_msg.c,v 1.20 2017/04/18 01:45:35 deraadt Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 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 <ctype.h> |
20 |
|
|
#include <stdio.h> |
21 |
|
|
#include <stdlib.h> |
22 |
|
|
#include <string.h> |
23 |
|
|
#include <time.h> |
24 |
|
|
#include <unistd.h> |
25 |
|
|
|
26 |
|
|
#include "../common/common.h" |
27 |
|
|
#include "vi.h" |
28 |
|
|
|
29 |
|
|
typedef enum { |
30 |
|
|
SCROLL_W, /* User wait. */ |
31 |
|
|
SCROLL_W_EX, /* User wait, or enter : to continue. */ |
32 |
|
|
SCROLL_W_QUIT /* User wait, or enter q to quit. */ |
33 |
|
|
/* |
34 |
|
|
* SCROLL_W_QUIT has another semantic |
35 |
|
|
* -- only wait if the screen is full |
36 |
|
|
*/ |
37 |
|
|
} sw_t; |
38 |
|
|
|
39 |
|
|
static void vs_divider(SCR *); |
40 |
|
|
static void vs_msgsave(SCR *, mtype_t, char *, size_t); |
41 |
|
|
static void vs_output(SCR *, mtype_t, const char *, int); |
42 |
|
|
static void vs_scroll(SCR *, int *, sw_t); |
43 |
|
|
static void vs_wait(SCR *, int *, sw_t); |
44 |
|
|
|
45 |
|
|
/* |
46 |
|
|
* vs_busy -- |
47 |
|
|
* Display, update or clear a busy message. |
48 |
|
|
* |
49 |
|
|
* This routine is the default editor interface for vi busy messages. It |
50 |
|
|
* implements a standard strategy of stealing lines from the bottom of the |
51 |
|
|
* vi text screen. Screens using an alternate method of displaying busy |
52 |
|
|
* messages, e.g. X11 clock icons, should set their scr_busy function to the |
53 |
|
|
* correct function before calling the main editor routine. |
54 |
|
|
* |
55 |
|
|
* PUBLIC: void vs_busy(SCR *, const char *, busy_t); |
56 |
|
|
*/ |
57 |
|
|
void |
58 |
|
|
vs_busy(SCR *sp, const char *msg, busy_t btype) |
59 |
|
|
{ |
60 |
|
|
GS *gp; |
61 |
|
|
VI_PRIVATE *vip; |
62 |
|
|
static const char flagc[] = "|/-\\"; |
63 |
|
|
struct timespec ts, ts_diff; |
64 |
|
|
size_t notused; |
65 |
|
|
|
66 |
|
|
/* Ex doesn't display busy messages. */ |
67 |
|
|
if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) |
68 |
|
|
return; |
69 |
|
|
|
70 |
|
|
gp = sp->gp; |
71 |
|
|
vip = VIP(sp); |
72 |
|
|
|
73 |
|
|
/* |
74 |
|
|
* Most of this routine is to deal with the screen sharing real estate |
75 |
|
|
* between the normal edit messages and the busy messages. Logically, |
76 |
|
|
* all that's needed is something that puts up a message, periodically |
77 |
|
|
* updates it, and then goes away. |
78 |
|
|
*/ |
79 |
|
|
switch (btype) { |
80 |
|
|
case BUSY_ON: |
81 |
|
|
++vip->busy_ref; |
82 |
|
|
if (vip->totalcount != 0 || vip->busy_ref != 1) |
83 |
|
|
break; |
84 |
|
|
|
85 |
|
|
/* Initialize state for updates. */ |
86 |
|
|
vip->busy_ch = 0; |
87 |
|
|
(void)clock_gettime(CLOCK_MONOTONIC, &vip->busy_ts); |
88 |
|
|
|
89 |
|
|
/* Save the current cursor. */ |
90 |
|
|
(void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx); |
91 |
|
|
|
92 |
|
|
/* Display the busy message. */ |
93 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), 0); |
94 |
|
|
(void)gp->scr_addstr(sp, msg, strlen(msg)); |
95 |
|
|
(void)gp->scr_cursor(sp, ¬used, &vip->busy_fx); |
96 |
|
|
(void)gp->scr_clrtoeol(sp); |
97 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); |
98 |
|
|
break; |
99 |
|
|
case BUSY_OFF: |
100 |
|
|
if (vip->busy_ref == 0) |
101 |
|
|
break; |
102 |
|
|
--vip->busy_ref; |
103 |
|
|
|
104 |
|
|
/* |
105 |
|
|
* If the line isn't in use for another purpose, clear it. |
106 |
|
|
* Always return to the original position. |
107 |
|
|
*/ |
108 |
|
|
if (vip->totalcount == 0 && vip->busy_ref == 0) { |
109 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), 0); |
110 |
|
|
(void)gp->scr_clrtoeol(sp); |
111 |
|
|
} |
112 |
|
|
(void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx); |
113 |
|
|
break; |
114 |
|
|
case BUSY_UPDATE: |
115 |
|
|
if (vip->totalcount != 0 || vip->busy_ref == 0) |
116 |
|
|
break; |
117 |
|
|
|
118 |
|
|
/* Update no more than every 1/8 of a second. */ |
119 |
|
|
(void)clock_gettime(CLOCK_MONOTONIC, &ts); |
120 |
|
|
ts_diff = ts; |
121 |
|
|
ts_diff.tv_sec -= vip->busy_ts.tv_sec; |
122 |
|
|
ts_diff.tv_nsec -= vip->busy_ts.tv_nsec; |
123 |
|
|
if (ts_diff.tv_nsec < 0) { |
124 |
|
|
ts_diff.tv_sec--; |
125 |
|
|
ts_diff.tv_nsec += 1000000000; |
126 |
|
|
} |
127 |
|
|
if ((ts_diff.tv_sec == 0 && ts_diff.tv_nsec < 125000000) || |
128 |
|
|
ts_diff.tv_sec < 0) |
129 |
|
|
return; |
130 |
|
|
vip->busy_ts = ts; |
131 |
|
|
|
132 |
|
|
/* Display the update. */ |
133 |
|
|
if (vip->busy_ch == sizeof(flagc) - 1) |
134 |
|
|
vip->busy_ch = 0; |
135 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); |
136 |
|
|
(void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1); |
137 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); |
138 |
|
|
break; |
139 |
|
|
} |
140 |
|
|
(void)gp->scr_refresh(sp, 0); |
141 |
|
|
} |
142 |
|
|
|
143 |
|
|
/* |
144 |
|
|
* vs_home -- |
145 |
|
|
* Home the cursor to the bottom row, left-most column. |
146 |
|
|
* |
147 |
|
|
* PUBLIC: void vs_home(SCR *); |
148 |
|
|
*/ |
149 |
|
|
void |
150 |
|
|
vs_home(SCR *sp) |
151 |
|
|
{ |
152 |
|
|
(void)sp->gp->scr_move(sp, LASTLINE(sp), 0); |
153 |
|
|
(void)sp->gp->scr_refresh(sp, 0); |
154 |
|
|
} |
155 |
|
|
|
156 |
|
|
/* |
157 |
|
|
* vs_update -- |
158 |
|
|
* Update a command. |
159 |
|
|
* |
160 |
|
|
* PUBLIC: void vs_update(SCR *, const char *, const char *); |
161 |
|
|
*/ |
162 |
|
|
void |
163 |
|
|
vs_update(SCR *sp, const char *m1, const char *m2) |
164 |
|
|
{ |
165 |
|
|
GS *gp; |
166 |
|
|
size_t len, mlen, oldx, oldy; |
167 |
|
|
|
168 |
|
|
gp = sp->gp; |
169 |
|
|
|
170 |
|
|
/* |
171 |
|
|
* This routine displays a message on the bottom line of the screen, |
172 |
|
|
* without updating any of the command structures that would keep it |
173 |
|
|
* there for any period of time, i.e. it is overwritten immediately. |
174 |
|
|
* |
175 |
|
|
* It's used by the ex read and ! commands when the user's command is |
176 |
|
|
* expanded, and by the ex substitution confirmation prompt. |
177 |
|
|
*/ |
178 |
|
|
if (F_ISSET(sp, SC_SCR_EXWROTE)) { |
179 |
|
|
(void)ex_printf(sp, |
180 |
|
|
"%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : m2); |
181 |
|
|
(void)ex_fflush(sp); |
182 |
|
|
} |
183 |
|
|
|
184 |
|
|
/* |
185 |
|
|
* Save the cursor position, the substitute-with-confirmation code |
186 |
|
|
* will have already set it correctly. |
187 |
|
|
*/ |
188 |
|
|
(void)gp->scr_cursor(sp, &oldy, &oldx); |
189 |
|
|
|
190 |
|
|
/* Clear the bottom line. */ |
191 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), 0); |
192 |
|
|
(void)gp->scr_clrtoeol(sp); |
193 |
|
|
|
194 |
|
|
/* |
195 |
|
|
* XXX |
196 |
|
|
* Don't let long file names screw up the screen. |
197 |
|
|
*/ |
198 |
|
|
if (m1 != NULL) { |
199 |
|
|
mlen = len = strlen(m1); |
200 |
|
|
if (len > sp->cols - 2) |
201 |
|
|
mlen = len = sp->cols - 2; |
202 |
|
|
(void)gp->scr_addstr(sp, m1, mlen); |
203 |
|
|
} else |
204 |
|
|
len = 0; |
205 |
|
|
if (m2 != NULL) { |
206 |
|
|
mlen = strlen(m2); |
207 |
|
|
if (len + mlen > sp->cols - 2) |
208 |
|
|
mlen = (sp->cols - 2) - len; |
209 |
|
|
(void)gp->scr_addstr(sp, m2, mlen); |
210 |
|
|
} |
211 |
|
|
|
212 |
|
|
(void)gp->scr_move(sp, oldy, oldx); |
213 |
|
|
(void)gp->scr_refresh(sp, 0); |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
/* |
217 |
|
|
* vs_msg -- |
218 |
|
|
* Display ex output or error messages for the screen. |
219 |
|
|
* |
220 |
|
|
* This routine is the default editor interface for all ex output, and all ex |
221 |
|
|
* and vi error/informational messages. It implements the standard strategy |
222 |
|
|
* of stealing lines from the bottom of the vi text screen. Screens using an |
223 |
|
|
* alternate method of displaying messages, e.g. dialog boxes, should set their |
224 |
|
|
* scr_msg function to the correct function before calling the editor. |
225 |
|
|
* |
226 |
|
|
* PUBLIC: void vs_msg(SCR *, mtype_t, char *, size_t); |
227 |
|
|
*/ |
228 |
|
|
void |
229 |
|
|
vs_msg(SCR *sp, mtype_t mtype, char *line, size_t len) |
230 |
|
|
{ |
231 |
|
|
GS *gp; |
232 |
|
|
VI_PRIVATE *vip; |
233 |
|
|
size_t maxcols, oldx, oldy, padding; |
234 |
|
|
const char *e, *s, *t; |
235 |
|
|
|
236 |
|
|
gp = sp->gp; |
237 |
|
|
vip = VIP(sp); |
238 |
|
|
|
239 |
|
|
/* |
240 |
|
|
* Ring the bell if it's scheduled. |
241 |
|
|
* |
242 |
|
|
* XXX |
243 |
|
|
* Shouldn't we save this, too? |
244 |
|
|
*/ |
245 |
|
|
if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED)) { |
246 |
|
|
if (F_ISSET(sp, SC_SCR_VI)) { |
247 |
|
|
F_CLR(gp, G_BELLSCHED); |
248 |
|
|
(void)gp->scr_bell(sp); |
249 |
|
|
} else |
250 |
|
|
F_SET(gp, G_BELLSCHED); |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
/* |
254 |
|
|
* If vi is using the error line for text input, there's no screen |
255 |
|
|
* real-estate for the error message. Nothing to do without some |
256 |
|
|
* information as to how important the error message is. |
257 |
|
|
*/ |
258 |
|
|
if (F_ISSET(sp, SC_TINPUT_INFO)) |
259 |
|
|
return; |
260 |
|
|
|
261 |
|
|
/* |
262 |
|
|
* Ex or ex controlled screen output. |
263 |
|
|
* |
264 |
|
|
* If output happens during startup, e.g., a .exrc file, we may be |
265 |
|
|
* in ex mode but haven't initialized the screen. Initialize here, |
266 |
|
|
* and in this case, stay in ex mode. |
267 |
|
|
* |
268 |
|
|
* If the SC_SCR_EXWROTE bit is set, then we're switching back and |
269 |
|
|
* forth between ex and vi, but the screen is trashed and we have |
270 |
|
|
* to respect that. Switch to ex mode long enough to put out the |
271 |
|
|
* message. |
272 |
|
|
* |
273 |
|
|
* If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to |
274 |
|
|
* the screen, so previous opinions are ignored. |
275 |
|
|
*/ |
276 |
|
|
if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) { |
277 |
|
|
if (!F_ISSET(sp, SC_SCR_EX)) { |
278 |
|
|
if (F_ISSET(sp, SC_SCR_EXWROTE)) { |
279 |
|
|
if (sp->gp->scr_screen(sp, SC_EX)) |
280 |
|
|
return; |
281 |
|
|
} else |
282 |
|
|
if (ex_init(sp)) |
283 |
|
|
return; |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
if (mtype == M_ERR) |
287 |
|
|
(void)gp->scr_attr(sp, SA_INVERSE, 1); |
288 |
|
|
(void)printf("%.*s", (int)len, line); |
289 |
|
|
if (mtype == M_ERR) |
290 |
|
|
(void)gp->scr_attr(sp, SA_INVERSE, 0); |
291 |
|
|
(void)fflush(stdout); |
292 |
|
|
|
293 |
|
|
F_CLR(sp, SC_EX_WAIT_NO); |
294 |
|
|
|
295 |
|
|
if (!F_ISSET(sp, SC_SCR_EX)) |
296 |
|
|
(void)sp->gp->scr_screen(sp, SC_VI); |
297 |
|
|
return; |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
/* If the vi screen isn't ready, save the message. */ |
301 |
|
|
if (!F_ISSET(sp, SC_SCR_VI)) { |
302 |
|
|
(void)vs_msgsave(sp, mtype, line, len); |
303 |
|
|
return; |
304 |
|
|
} |
305 |
|
|
|
306 |
|
|
/* Save the cursor position. */ |
307 |
|
|
(void)gp->scr_cursor(sp, &oldy, &oldx); |
308 |
|
|
|
309 |
|
|
/* If it's an ex output message, just write it out. */ |
310 |
|
|
if (mtype == M_NONE) { |
311 |
|
|
vs_output(sp, mtype, line, len); |
312 |
|
|
goto ret; |
313 |
|
|
} |
314 |
|
|
|
315 |
|
|
/* |
316 |
|
|
* If it's a vi message, strip the trailing <newline> so we can |
317 |
|
|
* try and paste messages together. |
318 |
|
|
*/ |
319 |
|
|
if (line[len - 1] == '\n') |
320 |
|
|
--len; |
321 |
|
|
|
322 |
|
|
/* |
323 |
|
|
* If a message won't fit on a single line, try to split on a <blank>. |
324 |
|
|
* If a subsequent message fits on the same line, write a separator |
325 |
|
|
* and output it. Otherwise, put out a newline. |
326 |
|
|
* |
327 |
|
|
* Need up to two padding characters normally; a semi-colon and a |
328 |
|
|
* separating space. If only a single line on the screen, add some |
329 |
|
|
* more for the trailing continuation message. |
330 |
|
|
* |
331 |
|
|
* XXX |
332 |
|
|
* Assume that periods and semi-colons take up a single column on the |
333 |
|
|
* screen. |
334 |
|
|
* |
335 |
|
|
* XXX |
336 |
|
|
* There are almost certainly pathological cases that will break this |
337 |
|
|
* code. |
338 |
|
|
*/ |
339 |
|
|
if (IS_ONELINE(sp)) |
340 |
|
|
(void)msg_cmsg(sp, CMSG_CONT_S, &padding); |
341 |
|
|
else |
342 |
|
|
padding = 0; |
343 |
|
|
padding += 2; |
344 |
|
|
|
345 |
|
|
maxcols = sp->cols - 1; |
346 |
|
|
if (vip->lcontinue != 0) { |
347 |
|
|
if (len + vip->lcontinue + padding > maxcols) |
348 |
|
|
vs_output(sp, vip->mtype, ".\n", 2); |
349 |
|
|
else { |
350 |
|
|
vs_output(sp, vip->mtype, ";", 1); |
351 |
|
|
vs_output(sp, M_NONE, " ", 1); |
352 |
|
|
} |
353 |
|
|
} |
354 |
|
|
vip->mtype = mtype; |
355 |
|
|
for (s = line;; s = t) { |
356 |
|
|
for (; len > 0 && isblank(*s); --len, ++s); |
357 |
|
|
if (len == 0) |
358 |
|
|
break; |
359 |
|
|
if (len + vip->lcontinue > maxcols) { |
360 |
|
|
for (e = s + (maxcols - vip->lcontinue); |
361 |
|
|
e > s && !isblank(*e); --e); |
362 |
|
|
if (e == s) |
363 |
|
|
e = t = s + (maxcols - vip->lcontinue); |
364 |
|
|
else |
365 |
|
|
for (t = e; isblank(e[-1]); --e); |
366 |
|
|
} else |
367 |
|
|
e = t = s + len; |
368 |
|
|
|
369 |
|
|
/* |
370 |
|
|
* If the message ends in a period, discard it, we want to |
371 |
|
|
* gang messages where possible. |
372 |
|
|
*/ |
373 |
|
|
len -= t - s; |
374 |
|
|
if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.') |
375 |
|
|
--e; |
376 |
|
|
vs_output(sp, mtype, s, e - s); |
377 |
|
|
|
378 |
|
|
if (len != 0) |
379 |
|
|
vs_output(sp, M_NONE, "\n", 1); |
380 |
|
|
|
381 |
|
|
if (INTERRUPTED(sp)) |
382 |
|
|
break; |
383 |
|
|
} |
384 |
|
|
|
385 |
|
|
ret: (void)gp->scr_move(sp, oldy, oldx); |
386 |
|
|
(void)gp->scr_refresh(sp, 0); |
387 |
|
|
} |
388 |
|
|
|
389 |
|
|
/* |
390 |
|
|
* vs_output -- |
391 |
|
|
* Output the text to the screen. |
392 |
|
|
*/ |
393 |
|
|
static void |
394 |
|
|
vs_output(SCR *sp, mtype_t mtype, const char *line, int llen) |
395 |
|
|
{ |
396 |
|
|
CHAR_T *kp; |
397 |
|
|
GS *gp; |
398 |
|
|
VI_PRIVATE *vip; |
399 |
|
|
size_t chlen, notused; |
400 |
|
|
int ch, len, tlen; |
401 |
|
|
const char *p, *t; |
402 |
|
|
char *cbp, *ecbp, cbuf[128]; |
403 |
|
|
|
404 |
|
|
gp = sp->gp; |
405 |
|
|
vip = VIP(sp); |
406 |
|
|
for (p = line; llen > 0;) { |
407 |
|
|
/* Get the next physical line. */ |
408 |
|
|
if ((p = memchr(line, '\n', llen)) == NULL) |
409 |
|
|
len = llen; |
410 |
|
|
else |
411 |
|
|
len = p - line; |
412 |
|
|
|
413 |
|
|
/* |
414 |
|
|
* The max is sp->cols characters, and we may have already |
415 |
|
|
* written part of the line. |
416 |
|
|
*/ |
417 |
|
|
if (len + vip->lcontinue > sp->cols) |
418 |
|
|
len = sp->cols - vip->lcontinue; |
419 |
|
|
|
420 |
|
|
/* |
421 |
|
|
* If the first line output, do nothing. If the second line |
422 |
|
|
* output, draw the divider line. If drew a full screen, we |
423 |
|
|
* remove the divider line. If it's a continuation line, move |
424 |
|
|
* to the continuation point, else, move the screen up. |
425 |
|
|
*/ |
426 |
|
|
if (vip->lcontinue == 0) { |
427 |
|
|
if (!IS_ONELINE(sp)) { |
428 |
|
|
if (vip->totalcount == 1) { |
429 |
|
|
(void)gp->scr_move(sp, |
430 |
|
|
LASTLINE(sp) - 1, 0); |
431 |
|
|
(void)gp->scr_clrtoeol(sp); |
432 |
|
|
(void)vs_divider(sp); |
433 |
|
|
F_SET(vip, VIP_DIVIDER); |
434 |
|
|
++vip->totalcount; |
435 |
|
|
++vip->linecount; |
436 |
|
|
} |
437 |
|
|
if (vip->totalcount == sp->t_maxrows && |
438 |
|
|
F_ISSET(vip, VIP_DIVIDER)) { |
439 |
|
|
--vip->totalcount; |
440 |
|
|
--vip->linecount; |
441 |
|
|
F_CLR(vip, VIP_DIVIDER); |
442 |
|
|
} |
443 |
|
|
} |
444 |
|
|
if (vip->totalcount != 0) |
445 |
|
|
vs_scroll(sp, NULL, SCROLL_W_QUIT); |
446 |
|
|
|
447 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), 0); |
448 |
|
|
++vip->totalcount; |
449 |
|
|
++vip->linecount; |
450 |
|
|
|
451 |
|
|
if (INTERRUPTED(sp)) |
452 |
|
|
break; |
453 |
|
|
} else |
454 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue); |
455 |
|
|
|
456 |
|
|
/* Error messages are in inverse video. */ |
457 |
|
|
if (mtype == M_ERR) |
458 |
|
|
(void)gp->scr_attr(sp, SA_INVERSE, 1); |
459 |
|
|
|
460 |
|
|
/* Display the line, doing character translation. */ |
461 |
|
|
#define FLUSH { \ |
462 |
|
|
*cbp = '\0'; \ |
463 |
|
|
(void)gp->scr_addstr(sp, cbuf, cbp - cbuf); \ |
464 |
|
|
cbp = cbuf; \ |
465 |
|
|
} |
466 |
|
|
ecbp = (cbp = cbuf) + sizeof(cbuf) - 1; |
467 |
|
|
for (t = line, tlen = len; tlen--; ++t) { |
468 |
|
|
ch = *t; |
469 |
|
|
/* |
470 |
|
|
* Replace tabs with spaces, there are places in |
471 |
|
|
* ex that do column calculations without looking |
472 |
|
|
* at <tabs> -- and all routines that care about |
473 |
|
|
* <tabs> do their own expansions. This catches |
474 |
|
|
* <tabs> in things like tag search strings. |
475 |
|
|
*/ |
476 |
|
|
if (ch == '\t') |
477 |
|
|
ch = ' '; |
478 |
|
|
chlen = KEY_LEN(sp, ch); |
479 |
|
|
if (cbp + chlen >= ecbp) |
480 |
|
|
FLUSH; |
481 |
|
|
for (kp = KEY_NAME(sp, ch); chlen--;) |
482 |
|
|
*cbp++ = *kp++; |
483 |
|
|
} |
484 |
|
|
if (cbp > cbuf) |
485 |
|
|
FLUSH; |
486 |
|
|
if (mtype == M_ERR) |
487 |
|
|
(void)gp->scr_attr(sp, SA_INVERSE, 0); |
488 |
|
|
|
489 |
|
|
/* Clear the rest of the line. */ |
490 |
|
|
(void)gp->scr_clrtoeol(sp); |
491 |
|
|
|
492 |
|
|
/* If we loop, it's a new line. */ |
493 |
|
|
vip->lcontinue = 0; |
494 |
|
|
|
495 |
|
|
/* Reset for the next line. */ |
496 |
|
|
line += len; |
497 |
|
|
llen -= len; |
498 |
|
|
if (p != NULL) { |
499 |
|
|
++line; |
500 |
|
|
--llen; |
501 |
|
|
} |
502 |
|
|
} |
503 |
|
|
|
504 |
|
|
/* Set up next continuation line. */ |
505 |
|
|
if (p == NULL) |
506 |
|
|
gp->scr_cursor(sp, ¬used, &vip->lcontinue); |
507 |
|
|
} |
508 |
|
|
|
509 |
|
|
/* |
510 |
|
|
* vs_ex_resolve -- |
511 |
|
|
* Deal with ex message output. |
512 |
|
|
* |
513 |
|
|
* This routine is called when exiting a colon command to resolve any ex |
514 |
|
|
* output that may have occurred. |
515 |
|
|
* |
516 |
|
|
* PUBLIC: int vs_ex_resolve(SCR *, int *); |
517 |
|
|
*/ |
518 |
|
|
int |
519 |
|
|
vs_ex_resolve(SCR *sp, int *continuep) |
520 |
|
|
{ |
521 |
|
|
EVENT ev; |
522 |
|
|
GS *gp; |
523 |
|
|
VI_PRIVATE *vip; |
524 |
|
|
sw_t wtype; |
525 |
|
|
|
526 |
|
|
gp = sp->gp; |
527 |
|
|
vip = VIP(sp); |
528 |
|
|
*continuep = 0; |
529 |
|
|
|
530 |
|
|
/* If we ran any ex command, we can't trust the cursor position. */ |
531 |
|
|
F_SET(vip, VIP_CUR_INVALID); |
532 |
|
|
|
533 |
|
|
/* Terminate any partially written message. */ |
534 |
|
|
if (vip->lcontinue != 0) { |
535 |
|
|
vs_output(sp, vip->mtype, ".", 1); |
536 |
|
|
vip->lcontinue = 0; |
537 |
|
|
|
538 |
|
|
vip->mtype = M_NONE; |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
/* |
542 |
|
|
* If we switched out of the vi screen into ex, switch back while we |
543 |
|
|
* figure out what to do with the screen and potentially get another |
544 |
|
|
* command to execute. |
545 |
|
|
* |
546 |
|
|
* If we didn't switch into ex, we're not required to wait, and less |
547 |
|
|
* than 2 lines of output, we can continue without waiting for the |
548 |
|
|
* wait. |
549 |
|
|
* |
550 |
|
|
* Note, all other code paths require waiting, so we leave the report |
551 |
|
|
* of modified lines until later, so that we won't wait for no other |
552 |
|
|
* reason than a threshold number of lines were modified. This means |
553 |
|
|
* we display cumulative line modification reports for groups of ex |
554 |
|
|
* commands. That seems right to me (well, at least not wrong). |
555 |
|
|
*/ |
556 |
|
|
if (F_ISSET(sp, SC_SCR_EXWROTE)) { |
557 |
|
|
if (sp->gp->scr_screen(sp, SC_VI)) |
558 |
|
|
return (1); |
559 |
|
|
} else |
560 |
|
|
if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) { |
561 |
|
|
F_CLR(sp, SC_EX_WAIT_NO); |
562 |
|
|
return (0); |
563 |
|
|
} |
564 |
|
|
|
565 |
|
|
/* Clear the required wait flag, it's no longer needed. */ |
566 |
|
|
F_CLR(sp, SC_EX_WAIT_YES); |
567 |
|
|
|
568 |
|
|
/* |
569 |
|
|
* Wait, unless explicitly told not to wait or the user interrupted |
570 |
|
|
* the command. If the user is leaving the screen, for any reason, |
571 |
|
|
* they can't continue with further ex commands. |
572 |
|
|
*/ |
573 |
|
|
if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) { |
574 |
|
|
wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | |
575 |
|
|
SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX; |
576 |
|
|
if (F_ISSET(sp, SC_SCR_EXWROTE)) |
577 |
|
|
vs_wait(sp, continuep, wtype); |
578 |
|
|
else |
579 |
|
|
vs_scroll(sp, continuep, wtype); |
580 |
|
|
if (*continuep) |
581 |
|
|
return (0); |
582 |
|
|
} |
583 |
|
|
|
584 |
|
|
/* If ex wrote on the screen, refresh the screen image. */ |
585 |
|
|
if (F_ISSET(sp, SC_SCR_EXWROTE)) |
586 |
|
|
F_SET(vip, VIP_N_EX_PAINT); |
587 |
|
|
|
588 |
|
|
/* |
589 |
|
|
* If we're not the bottom of the split screen stack, the screen |
590 |
|
|
* image itself is wrong, so redraw everything. |
591 |
|
|
*/ |
592 |
|
|
if (TAILQ_NEXT(sp, q)) |
593 |
|
|
F_SET(sp, SC_SCR_REDRAW); |
594 |
|
|
|
595 |
|
|
/* If ex changed the underlying file, the map itself is wrong. */ |
596 |
|
|
if (F_ISSET(vip, VIP_N_EX_REDRAW)) |
597 |
|
|
F_SET(sp, SC_SCR_REFORMAT); |
598 |
|
|
|
599 |
|
|
/* Ex may have switched out of the alternate screen, return. */ |
600 |
|
|
(void)gp->scr_attr(sp, SA_ALTERNATE, 1); |
601 |
|
|
|
602 |
|
|
/* |
603 |
|
|
* Whew. We're finally back home, after what feels like years. |
604 |
|
|
* Kiss the ground. |
605 |
|
|
*/ |
606 |
|
|
F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO); |
607 |
|
|
|
608 |
|
|
/* |
609 |
|
|
* We may need to repaint some of the screen, e.g.: |
610 |
|
|
* |
611 |
|
|
* :set |
612 |
|
|
* :!ls |
613 |
|
|
* |
614 |
|
|
* gives us a combination of some lines that are "wrong", and a need |
615 |
|
|
* for a full refresh. |
616 |
|
|
*/ |
617 |
|
|
if (vip->totalcount > 1) { |
618 |
|
|
/* Set up the redraw of the overwritten lines. */ |
619 |
|
|
ev.e_event = E_REPAINT; |
620 |
|
|
ev.e_flno = vip->totalcount >= |
621 |
|
|
sp->rows ? 1 : sp->rows - vip->totalcount; |
622 |
|
|
ev.e_tlno = sp->rows; |
623 |
|
|
|
624 |
|
|
/* Reset the count of overwriting lines. */ |
625 |
|
|
vip->linecount = vip->lcontinue = vip->totalcount = 0; |
626 |
|
|
|
627 |
|
|
/* Redraw. */ |
628 |
|
|
(void)vs_repaint(sp, &ev); |
629 |
|
|
} else |
630 |
|
|
/* Reset the count of overwriting lines. */ |
631 |
|
|
vip->linecount = vip->lcontinue = vip->totalcount = 0; |
632 |
|
|
|
633 |
|
|
return (0); |
634 |
|
|
} |
635 |
|
|
|
636 |
|
|
/* |
637 |
|
|
* vs_resolve -- |
638 |
|
|
* Deal with message output. |
639 |
|
|
* |
640 |
|
|
* PUBLIC: int vs_resolve(SCR *, SCR *, int); |
641 |
|
|
*/ |
642 |
|
|
int |
643 |
|
|
vs_resolve(SCR *sp, SCR *csp, int forcewait) |
644 |
|
|
{ |
645 |
|
|
EVENT ev; |
646 |
|
|
GS *gp; |
647 |
|
|
MSGS *mp; |
648 |
|
|
VI_PRIVATE *vip; |
649 |
|
|
size_t oldy, oldx; |
650 |
|
|
int redraw; |
651 |
|
|
|
652 |
|
|
/* |
653 |
|
|
* Vs_resolve is called from the main vi loop and the refresh function |
654 |
|
|
* to periodically ensure that the user has seen any messages that have |
655 |
|
|
* been displayed and that any status lines are correct. The sp screen |
656 |
|
|
* is the screen we're checking, usually the current screen. When it's |
657 |
|
|
* not, csp is the current screen, used for final cursor positioning. |
658 |
|
|
*/ |
659 |
|
|
gp = sp->gp; |
660 |
|
|
vip = VIP(sp); |
661 |
|
|
if (csp == NULL) |
662 |
|
|
csp = sp; |
663 |
|
|
|
664 |
|
|
/* Save the cursor position. */ |
665 |
|
|
(void)gp->scr_cursor(csp, &oldy, &oldx); |
666 |
|
|
|
667 |
|
|
/* Ring the bell if it's scheduled. */ |
668 |
|
|
if (F_ISSET(gp, G_BELLSCHED)) { |
669 |
|
|
F_CLR(gp, G_BELLSCHED); |
670 |
|
|
(void)gp->scr_bell(sp); |
671 |
|
|
} |
672 |
|
|
|
673 |
|
|
/* Display new file status line. */ |
674 |
|
|
if (F_ISSET(sp, SC_STATUS)) { |
675 |
|
|
F_CLR(sp, SC_STATUS); |
676 |
|
|
msgq_status(sp, sp->lno, MSTAT_TRUNCATE); |
677 |
|
|
} |
678 |
|
|
|
679 |
|
|
/* Report on line modifications. */ |
680 |
|
|
mod_rpt(sp); |
681 |
|
|
|
682 |
|
|
/* |
683 |
|
|
* Flush any saved messages. If the screen isn't ready, refresh |
684 |
|
|
* it. (A side-effect of screen refresh is that we can display |
685 |
|
|
* messages.) Once this is done, don't trust the cursor. That |
686 |
|
|
* extra refresh screwed the pooch. |
687 |
|
|
*/ |
688 |
|
|
if (LIST_FIRST(&gp->msgq) != NULL) { |
689 |
|
|
if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1)) |
690 |
|
|
return (1); |
691 |
|
|
while ((mp = LIST_FIRST(&gp->msgq)) != NULL) { |
692 |
|
|
gp->scr_msg(sp, mp->mtype, mp->buf, mp->len); |
693 |
|
|
LIST_REMOVE(mp, q); |
694 |
|
|
free(mp->buf); |
695 |
|
|
free(mp); |
696 |
|
|
} |
697 |
|
|
F_SET(vip, VIP_CUR_INVALID); |
698 |
|
|
} |
699 |
|
|
|
700 |
|
|
switch (vip->totalcount) { |
701 |
|
|
case 0: |
702 |
|
|
redraw = 0; |
703 |
|
|
break; |
704 |
|
|
case 1: |
705 |
|
|
/* |
706 |
|
|
* If we're switching screens, we have to wait for messages, |
707 |
|
|
* regardless. If we don't wait, skip updating the modeline. |
708 |
|
|
*/ |
709 |
|
|
if (forcewait) |
710 |
|
|
vs_scroll(sp, NULL, SCROLL_W); |
711 |
|
|
else |
712 |
|
|
F_SET(vip, VIP_S_MODELINE); |
713 |
|
|
|
714 |
|
|
redraw = 0; |
715 |
|
|
break; |
716 |
|
|
default: |
717 |
|
|
/* |
718 |
|
|
* If >1 message line in use, prompt the user to continue and |
719 |
|
|
* repaint overwritten lines. |
720 |
|
|
*/ |
721 |
|
|
vs_scroll(sp, NULL, SCROLL_W); |
722 |
|
|
|
723 |
|
|
ev.e_event = E_REPAINT; |
724 |
|
|
ev.e_flno = vip->totalcount >= |
725 |
|
|
sp->rows ? 1 : sp->rows - vip->totalcount; |
726 |
|
|
ev.e_tlno = sp->rows; |
727 |
|
|
|
728 |
|
|
redraw = 1; |
729 |
|
|
break; |
730 |
|
|
} |
731 |
|
|
|
732 |
|
|
/* Reset the count of overwriting lines. */ |
733 |
|
|
vip->linecount = vip->lcontinue = vip->totalcount = 0; |
734 |
|
|
|
735 |
|
|
/* Redraw. */ |
736 |
|
|
if (redraw) |
737 |
|
|
(void)vs_repaint(sp, &ev); |
738 |
|
|
|
739 |
|
|
/* Restore the cursor position. */ |
740 |
|
|
(void)gp->scr_move(csp, oldy, oldx); |
741 |
|
|
|
742 |
|
|
return (0); |
743 |
|
|
} |
744 |
|
|
|
745 |
|
|
/* |
746 |
|
|
* vs_scroll -- |
747 |
|
|
* Scroll the screen for output. |
748 |
|
|
*/ |
749 |
|
|
static void |
750 |
|
|
vs_scroll(SCR *sp, int *continuep, sw_t wtype) |
751 |
|
|
{ |
752 |
|
|
GS *gp; |
753 |
|
|
VI_PRIVATE *vip; |
754 |
|
|
|
755 |
|
|
gp = sp->gp; |
756 |
|
|
vip = VIP(sp); |
757 |
|
|
if (!IS_ONELINE(sp)) { |
758 |
|
|
/* |
759 |
|
|
* Scroll the screen. Instead of scrolling the entire screen, |
760 |
|
|
* delete the line above the first line output so preserve the |
761 |
|
|
* maximum amount of the screen. |
762 |
|
|
*/ |
763 |
|
|
(void)gp->scr_move(sp, vip->totalcount < |
764 |
|
|
sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0); |
765 |
|
|
(void)gp->scr_deleteln(sp); |
766 |
|
|
|
767 |
|
|
/* If there are screens below us, push them back into place. */ |
768 |
|
|
if (TAILQ_NEXT(sp, q)) { |
769 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), 0); |
770 |
|
|
(void)gp->scr_insertln(sp); |
771 |
|
|
} |
772 |
|
|
} |
773 |
|
|
if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows) |
774 |
|
|
return; |
775 |
|
|
vs_wait(sp, continuep, wtype); |
776 |
|
|
} |
777 |
|
|
|
778 |
|
|
/* |
779 |
|
|
* vs_wait -- |
780 |
|
|
* Prompt the user to continue. |
781 |
|
|
*/ |
782 |
|
|
static void |
783 |
|
|
vs_wait(SCR *sp, int *continuep, sw_t wtype) |
784 |
|
|
{ |
785 |
|
|
EVENT ev; |
786 |
|
|
VI_PRIVATE *vip; |
787 |
|
|
const char *p; |
788 |
|
|
GS *gp; |
789 |
|
|
size_t len; |
790 |
|
|
|
791 |
|
|
gp = sp->gp; |
792 |
|
|
vip = VIP(sp); |
793 |
|
|
|
794 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), 0); |
795 |
|
|
if (IS_ONELINE(sp)) |
796 |
|
|
p = msg_cmsg(sp, CMSG_CONT_S, &len); |
797 |
|
|
else |
798 |
|
|
switch (wtype) { |
799 |
|
|
case SCROLL_W_QUIT: |
800 |
|
|
p = msg_cmsg(sp, CMSG_CONT_Q, &len); |
801 |
|
|
break; |
802 |
|
|
case SCROLL_W_EX: |
803 |
|
|
p = msg_cmsg(sp, CMSG_CONT_EX, &len); |
804 |
|
|
break; |
805 |
|
|
case SCROLL_W: |
806 |
|
|
p = msg_cmsg(sp, CMSG_CONT, &len); |
807 |
|
|
break; |
808 |
|
|
default: |
809 |
|
|
abort(); |
810 |
|
|
/* NOTREACHED */ |
811 |
|
|
} |
812 |
|
|
(void)gp->scr_addstr(sp, p, len); |
813 |
|
|
|
814 |
|
|
++vip->totalcount; |
815 |
|
|
vip->linecount = 0; |
816 |
|
|
|
817 |
|
|
(void)gp->scr_clrtoeol(sp); |
818 |
|
|
(void)gp->scr_refresh(sp, 0); |
819 |
|
|
|
820 |
|
|
/* Get a single character from the terminal. */ |
821 |
|
|
if (continuep != NULL) |
822 |
|
|
*continuep = 0; |
823 |
|
|
for (;;) { |
824 |
|
|
if (v_event_get(sp, &ev, 0, 0)) |
825 |
|
|
return; |
826 |
|
|
if (ev.e_event == E_CHARACTER) |
827 |
|
|
break; |
828 |
|
|
if (ev.e_event == E_INTERRUPT) { |
829 |
|
|
ev.e_c = CH_QUIT; |
830 |
|
|
F_SET(gp, G_INTERRUPTED); |
831 |
|
|
break; |
832 |
|
|
} |
833 |
|
|
(void)gp->scr_bell(sp); |
834 |
|
|
} |
835 |
|
|
switch (wtype) { |
836 |
|
|
case SCROLL_W_QUIT: |
837 |
|
|
if (ev.e_c == CH_QUIT) |
838 |
|
|
F_SET(gp, G_INTERRUPTED); |
839 |
|
|
break; |
840 |
|
|
case SCROLL_W_EX: |
841 |
|
|
if (ev.e_c == ':' && continuep != NULL) |
842 |
|
|
*continuep = 1; |
843 |
|
|
break; |
844 |
|
|
case SCROLL_W: |
845 |
|
|
break; |
846 |
|
|
} |
847 |
|
|
} |
848 |
|
|
|
849 |
|
|
/* |
850 |
|
|
* vs_divider -- |
851 |
|
|
* Draw a dividing line between the screen and the output. |
852 |
|
|
*/ |
853 |
|
|
static void |
854 |
|
|
vs_divider(SCR *sp) |
855 |
|
|
{ |
856 |
|
|
GS *gp; |
857 |
|
|
size_t len; |
858 |
|
|
|
859 |
|
|
#define DIVIDESTR "+=+=+=+=+=+=+=+" |
860 |
|
|
len = |
861 |
|
|
sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1; |
862 |
|
|
gp = sp->gp; |
863 |
|
|
(void)gp->scr_attr(sp, SA_INVERSE, 1); |
864 |
|
|
(void)gp->scr_addstr(sp, DIVIDESTR, len); |
865 |
|
|
(void)gp->scr_attr(sp, SA_INVERSE, 0); |
866 |
|
|
} |
867 |
|
|
|
868 |
|
|
/* |
869 |
|
|
* vs_msgsave -- |
870 |
|
|
* Save a message for later display. |
871 |
|
|
*/ |
872 |
|
|
static void |
873 |
|
|
vs_msgsave(SCR *sp, mtype_t mt, char *p, size_t len) |
874 |
|
|
{ |
875 |
|
|
GS *gp; |
876 |
|
|
MSGS *mp_c, *mp_n; |
877 |
|
|
|
878 |
|
|
/* |
879 |
|
|
* We have to handle messages before we have any place to put them. |
880 |
|
|
* If there's no screen support yet, allocate a msg structure, copy |
881 |
|
|
* in the message, and queue it on the global structure. If we can't |
882 |
|
|
* allocate memory here, we're genuinely screwed, dump the message |
883 |
|
|
* to stderr in the (probably) vain hope that someone will see it. |
884 |
|
|
*/ |
885 |
|
|
CALLOC_GOTO(sp, mp_n, 1, sizeof(MSGS)); |
886 |
|
|
MALLOC_GOTO(sp, mp_n->buf, len); |
887 |
|
|
|
888 |
|
|
memmove(mp_n->buf, p, len); |
889 |
|
|
mp_n->len = len; |
890 |
|
|
mp_n->mtype = mt; |
891 |
|
|
|
892 |
|
|
gp = sp->gp; |
893 |
|
|
if ((mp_c = LIST_FIRST(&gp->msgq)) == NULL) { |
894 |
|
|
LIST_INSERT_HEAD(&gp->msgq, mp_n, q); |
895 |
|
|
} else { |
896 |
|
|
for (; LIST_NEXT(mp_c, q) != NULL; mp_c = LIST_NEXT(mp_c, q)); |
897 |
|
|
LIST_INSERT_AFTER(mp_c, mp_n, q); |
898 |
|
|
} |
899 |
|
|
return; |
900 |
|
|
|
901 |
|
|
alloc_err: |
902 |
|
|
free(mp_n); |
903 |
|
|
(void)fprintf(stderr, "%.*s\n", (int)len, p); |
904 |
|
|
} |