Bug Summary

File:src/usr.bin/vi/build/../vi/vs_msg.c
Warning:line 692, column 20
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name vs_msg.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/vi/build/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/vi/build -I /usr/src/usr.bin/vi/build/../include -I . -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/vi/build/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/vi/build/../vi/vs_msg.c
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
29typedef 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
39static void vs_divider(SCR *);
40static void vs_msgsave(SCR *, mtype_t, char *, size_t);
41static void vs_output(SCR *, mtype_t, const char *, int);
42static void vs_scroll(SCR *, int *, sw_t);
43static 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 */
57void
58vs_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, &notused, &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 */
149void
150vs_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 */
162void
163vs_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 */
228void
229vs_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
385ret: (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 */
393static void
394vs_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, &notused, &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 */
518int
519vs_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 */
642int
643vs_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))
1
Assuming 'csp' is not equal to NULL
2
Taking false branch
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)))) {
3
Assuming the condition is false
4
Taking false branch
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)))) {
5
Assuming the condition is false
6
Taking false branch
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)) {
7
Assuming field 'lh_first' is not equal to NULL
8
Taking true branch
689 if (!F_ISSET(sp, SC_SCR_VI)(((sp)->flags) & ((0x00000008))) && vs_refresh(sp, 1))
9
Assuming the condition is false
690 return (1);
691 while ((mp = LIST_FIRST(&gp->msgq)((&gp->msgq)->lh_first)) != NULL((void *)0)) {
10
Loop condition is true. Entering loop body
15
Loop condition is true. Entering loop body
692 gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
16
Use of memory after it is freed
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)
;
11
Assuming field 'le_next' is equal to null
12
Taking false branch
13
Loop condition is false. Exiting loop
694 free(mp->buf);
695 free(mp);
14
Memory is released
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 */
749static void
750vs_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 */
782static void
783vs_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 */
853static void
854vs_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 */
872static void
873vs_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
901alloc_err:
902 free(mp_n);
903 (void)fprintf(stderr(&__sF[2]), "%.*s\n", (int)len, p);
904}