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