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