File: | src/usr.bin/vi/build/../vi/vs_msg.c |
Warning: | line 692, column 20 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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)(((sp)->flags) & ((0x00000001 | 0x00000010)))) | |||
68 | return; | |||
69 | ||||
70 | gp = sp->gp; | |||
71 | vip = VIP(sp)((VI_PRIVATE *)((sp)->vi_private)); | |||
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_MONOTONIC3, &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)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), 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)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), 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)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), 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_MONOTONIC3, &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)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), vip->busy_fx); | |||
136 | (void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1); | |||
137 | (void)gp->scr_move(sp, LASTLINE(sp)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), 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)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), 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)(((sp)->flags) & ((0x00000010)))) { | |||
179 | (void)ex_printf(sp, | |||
180 | "%s\n", m1 == NULL((void *)0)? "" : m1, m2 == NULL((void *)0) ? "" : 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)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), 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((void *)0)) { | |||
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((void *)0)) { | |||
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)((VI_PRIVATE *)((sp)->vi_private)); | |||
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)(((sp)->flags) & ((0x10000000))) || F_ISSET(gp, G_BELLSCHED)(((gp)->flags) & ((0x0002)))) { | |||
246 | if (F_ISSET(sp, SC_SCR_VI)(((sp)->flags) & ((0x00000008)))) { | |||
247 | F_CLR(gp, G_BELLSCHED)(((gp)->flags) &= ~((0x0002))); | |||
248 | (void)gp->scr_bell(sp); | |||
249 | } else | |||
250 | F_SET(gp, G_BELLSCHED)(((gp)->flags) |= ((0x0002))); | |||
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)(((sp)->flags) & ((0x10000000)))) | |||
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)(((sp)->flags) & ((0x00000001 | 0x00000010)))) { | |||
277 | if (!F_ISSET(sp, SC_SCR_EX)(((sp)->flags) & ((0x00000004)))) { | |||
278 | if (F_ISSET(sp, SC_SCR_EXWROTE)(((sp)->flags) & ((0x00000010)))) { | |||
279 | if (sp->gp->scr_screen(sp, SC_EX0x00000001)) | |||
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(&__sF[1])); | |||
292 | ||||
293 | F_CLR(sp, SC_EX_WAIT_NO)(((sp)->flags) &= ~((0x00080000))); | |||
294 | ||||
295 | if (!F_ISSET(sp, SC_SCR_EX)(((sp)->flags) & ((0x00000004)))) | |||
296 | (void)sp->gp->scr_screen(sp, SC_VI0x00000002); | |||
297 | return; | |||
298 | } | |||
299 | ||||
300 | /* If the vi screen isn't ready, save the message. */ | |||
301 | if (!F_ISSET(sp, SC_SCR_VI)(((sp)->flags) & ((0x00000008)))) { | |||
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)((sp)->rows == 1)) | |||
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)(((((sp)->gp)->flags) & ((0x0004))) || (!v_event_get ((sp), ((void *)0), 0, 0x001) && ((((sp)->gp)-> flags) & ((0x0004)))))) | |||
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)((VI_PRIVATE *)((sp)->vi_private)); | |||
406 | for (p = line; llen > 0;) { | |||
407 | /* Get the next physical line. */ | |||
408 | if ((p = memchr(line, '\n', llen)) == NULL((void *)0)) | |||
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)((sp)->rows == 1)) { | |||
428 | if (vip->totalcount == 1) { | |||
429 | (void)gp->scr_move(sp, | |||
430 | LASTLINE(sp)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1) - 1, 0); | |||
431 | (void)gp->scr_clrtoeol(sp); | |||
432 | (void)vs_divider(sp); | |||
433 | F_SET(vip, VIP_DIVIDER)(((vip)->flags) |= ((0x0002))); | |||
434 | ++vip->totalcount; | |||
435 | ++vip->linecount; | |||
436 | } | |||
437 | if (vip->totalcount == sp->t_maxrows && | |||
438 | F_ISSET(vip, VIP_DIVIDER)(((vip)->flags) & ((0x0002)))) { | |||
439 | --vip->totalcount; | |||
440 | --vip->linecount; | |||
441 | F_CLR(vip, VIP_DIVIDER)(((vip)->flags) &= ~((0x0002))); | |||
442 | } | |||
443 | } | |||
444 | if (vip->totalcount != 0) | |||
445 | vs_scroll(sp, NULL((void *)0), SCROLL_W_QUIT); | |||
446 | ||||
447 | (void)gp->scr_move(sp, LASTLINE(sp)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), 0); | |||
448 | ++vip->totalcount; | |||
449 | ++vip->linecount; | |||
450 | ||||
451 | if (INTERRUPTED(sp)(((((sp)->gp)->flags) & ((0x0004))) || (!v_event_get ((sp), ((void *)0), 0, 0x001) && ((((sp)->gp)-> flags) & ((0x0004)))))) | |||
452 | break; | |||
453 | } else | |||
454 | (void)gp->scr_move(sp, LASTLINE(sp)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), 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{ *cbp = '\0'; (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); cbp = cbuf; } { \ | |||
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)((unsigned char)(ch) <= 254 ? (sp)->gp->cname[(unsigned char)(ch)].len : v_key_len((sp), (ch))); | |||
479 | if (cbp + chlen >= ecbp) | |||
480 | FLUSH{ *cbp = '\0'; (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); cbp = cbuf; }; | |||
481 | for (kp = KEY_NAME(sp, ch)((unsigned char)(ch) <= 254 ? (sp)->gp->cname[(unsigned char)(ch)].name : v_key_name((sp), (ch))); chlen--;) | |||
482 | *cbp++ = *kp++; | |||
483 | } | |||
484 | if (cbp > cbuf) | |||
485 | FLUSH{ *cbp = '\0'; (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); cbp = cbuf; }; | |||
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((void *)0)) { | |||
499 | ++line; | |||
500 | --llen; | |||
501 | } | |||
502 | } | |||
503 | ||||
504 | /* Set up next continuation line. */ | |||
505 | if (p == NULL((void *)0)) | |||
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)((VI_PRIVATE *)((sp)->vi_private)); | |||
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)(((vip)->flags) |= ((0x0001))); | |||
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)(((sp)->flags) & ((0x00000010)))) { | |||
557 | if (sp->gp->scr_screen(sp, SC_VI0x00000002)) | |||
558 | return (1); | |||
559 | } else | |||
560 | if (!F_ISSET(sp, SC_EX_WAIT_YES)(((sp)->flags) & ((0x00100000))) && vip->totalcount < 2) { | |||
561 | F_CLR(sp, SC_EX_WAIT_NO)(((sp)->flags) &= ~((0x00080000))); | |||
562 | return (0); | |||
563 | } | |||
564 | ||||
565 | /* Clear the required wait flag, it's no longer needed. */ | |||
566 | F_CLR(sp, SC_EX_WAIT_YES)(((sp)->flags) &= ~((0x00100000))); | |||
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)(((sp)->flags) & ((0x00080000))) && !INTERRUPTED(sp)(((((sp)->gp)->flags) & ((0x0004))) || (!v_event_get ((sp), ((void *)0), 0, 0x001) && ((((sp)->gp)-> flags) & ((0x0004)))))) { | |||
574 | wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE |(((sp)->flags) & ((0x00000200 | 0x00000400 | 0x00000800 | 0x00001000))) | |||
575 | SC_FSWITCH | SC_SSWITCH)(((sp)->flags) & ((0x00000200 | 0x00000400 | 0x00000800 | 0x00001000))) ? SCROLL_W : SCROLL_W_EX; | |||
576 | if (F_ISSET(sp, SC_SCR_EXWROTE)(((sp)->flags) & ((0x00000010)))) | |||
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)(((sp)->flags) & ((0x00000010)))) | |||
586 | F_SET(vip, VIP_N_EX_PAINT)(((vip)->flags) |= ((0x0004))); | |||
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)((sp)->q.tqe_next)) | |||
593 | F_SET(sp, SC_SCR_REDRAW)(((sp)->flags) |= ((0x00000040))); | |||
594 | ||||
595 | /* If ex changed the underlying file, the map itself is wrong. */ | |||
596 | if (F_ISSET(vip, VIP_N_EX_REDRAW)(((vip)->flags) & ((0x0008)))) | |||
597 | F_SET(sp, SC_SCR_REFORMAT)(((sp)->flags) |= ((0x00000020))); | |||
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)(((sp)->flags) &= ~((0x00000010 | 0x00080000))); | |||
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_u_event._e_mark.lno1 = vip->totalcount >= | |||
621 | sp->rows ? 1 : sp->rows - vip->totalcount; | |||
622 | ev.e_tlno_u_event._e_mark.lno2 = 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)((VI_PRIVATE *)((sp)->vi_private)); | |||
661 | if (csp == NULL((void *)0)) | |||
| ||||
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)(((gp)->flags) & ((0x0002)))) { | |||
669 | F_CLR(gp, G_BELLSCHED)(((gp)->flags) &= ~((0x0002))); | |||
670 | (void)gp->scr_bell(sp); | |||
671 | } | |||
672 | ||||
673 | /* Display new file status line. */ | |||
674 | if (F_ISSET(sp, SC_STATUS)(((sp)->flags) & ((0x02000000)))) { | |||
675 | F_CLR(sp, SC_STATUS)(((sp)->flags) &= ~((0x02000000))); | |||
676 | msgq_status(sp, sp->lno, MSTAT_TRUNCATE0x02); | |||
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)((&gp->msgq)->lh_first) != NULL((void *)0)) { | |||
689 | if (!F_ISSET(sp, SC_SCR_VI)(((sp)->flags) & ((0x00000008))) && vs_refresh(sp, 1)) | |||
690 | return (1); | |||
691 | while ((mp = LIST_FIRST(&gp->msgq)((&gp->msgq)->lh_first)) != NULL((void *)0)) { | |||
692 | gp->scr_msg(sp, mp->mtype, mp->buf, mp->len); | |||
| ||||
693 | LIST_REMOVE(mp, q)do { if ((mp)->q.le_next != ((void *)0)) (mp)->q.le_next ->q.le_prev = (mp)->q.le_prev; *(mp)->q.le_prev = (mp )->q.le_next; ; ; } while (0); | |||
694 | free(mp->buf); | |||
695 | free(mp); | |||
696 | } | |||
697 | F_SET(vip, VIP_CUR_INVALID)(((vip)->flags) |= ((0x0001))); | |||
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((void *)0), SCROLL_W); | |||
711 | else | |||
712 | F_SET(vip, VIP_S_MODELINE)(((vip)->flags) |= ((0x0080))); | |||
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((void *)0), SCROLL_W); | |||
722 | ||||
723 | ev.e_event = E_REPAINT; | |||
724 | ev.e_flno_u_event._e_mark.lno1 = vip->totalcount >= | |||
725 | sp->rows ? 1 : sp->rows - vip->totalcount; | |||
726 | ev.e_tlno_u_event._e_mark.lno2 = 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)((VI_PRIVATE *)((sp)->vi_private)); | |||
757 | if (!IS_ONELINE(sp)((sp)->rows == 1)) { | |||
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)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1) - 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)((sp)->q.tqe_next)) { | |||
769 | (void)gp->scr_move(sp, LASTLINE(sp)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), 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)((VI_PRIVATE *)((sp)->vi_private)); | |||
793 | ||||
794 | (void)gp->scr_move(sp, LASTLINE(sp)((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1), 0); | |||
795 | if (IS_ONELINE(sp)((sp)->rows == 1)) | |||
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((void *)0)) | |||
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_u_event._e_ch.c = CH_QUIT'q'; | |||
830 | F_SET(gp, G_INTERRUPTED)(((gp)->flags) |= ((0x0004))); | |||
831 | break; | |||
832 | } | |||
833 | (void)gp->scr_bell(sp); | |||
834 | } | |||
835 | switch (wtype) { | |||
836 | case SCROLL_W_QUIT: | |||
837 | if (ev.e_c_u_event._e_ch.c == CH_QUIT'q') | |||
838 | F_SET(gp, G_INTERRUPTED)(((gp)->flags) |= ((0x0004))); | |||
839 | break; | |||
840 | case SCROLL_W_EX: | |||
841 | if (ev.e_c_u_event._e_ch.c == ':' && continuep != NULL((void *)0)) | |||
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)){ if (((mp_n) = calloc((1), (sizeof(MSGS)))) == ((void *)0)) goto alloc_err; }; | |||
886 | MALLOC_GOTO(sp, mp_n->buf, len){ if (((mp_n->buf) = malloc(len)) == ((void *)0)) goto alloc_err ; }; | |||
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)((&gp->msgq)->lh_first)) == NULL((void *)0)) { | |||
894 | LIST_INSERT_HEAD(&gp->msgq, mp_n, q)do { if (((mp_n)->q.le_next = (&gp->msgq)->lh_first ) != ((void *)0)) (&gp->msgq)->lh_first->q.le_prev = &(mp_n)->q.le_next; (&gp->msgq)->lh_first = (mp_n); (mp_n)->q.le_prev = &(&gp->msgq)-> lh_first; } while (0); | |||
895 | } else { | |||
896 | for (; LIST_NEXT(mp_c, q)((mp_c)->q.le_next) != NULL((void *)0); mp_c = LIST_NEXT(mp_c, q)((mp_c)->q.le_next)); | |||
897 | LIST_INSERT_AFTER(mp_c, mp_n, q)do { if (((mp_n)->q.le_next = (mp_c)->q.le_next) != ((void *)0)) (mp_c)->q.le_next->q.le_prev = &(mp_n)->q .le_next; (mp_c)->q.le_next = (mp_n); (mp_n)->q.le_prev = &(mp_c)->q.le_next; } while (0); | |||
898 | } | |||
899 | return; | |||
900 | ||||
901 | alloc_err: | |||
902 | free(mp_n); | |||
903 | (void)fprintf(stderr(&__sF[2]), "%.*s\n", (int)len, p); | |||
904 | } |