2 * Copyright (c) 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
13 static const char sccsid[] = "@(#)vs_msg.c 10.77 (Berkeley) 10/13/96";
16 #include <sys/types.h>
17 #include <sys/queue.h>
20 #include <bitstring.h>
27 #include "../common/common.h"
31 SCROLL_W, /* User wait. */
32 SCROLL_W_EX, /* User wait, or enter : to continue. */
33 SCROLL_W_QUIT /* User wait, or enter q to quit. */
35 * SCROLL_W_QUIT has another semantic
36 * -- only wait if the screen is full
40 static void vs_divider __P((SCR *));
41 static void vs_msgsave __P((SCR *, mtype_t, char *, size_t));
42 static void vs_output __P((SCR *, mtype_t, const char *, int));
43 static void vs_scroll __P((SCR *, int *, sw_t));
44 static void vs_wait __P((SCR *, int *, sw_t));
48 * Display, update or clear a busy message.
50 * This routine is the default editor interface for vi busy messages. It
51 * implements a standard strategy of stealing lines from the bottom of the
52 * vi text screen. Screens using an alternate method of displaying busy
53 * messages, e.g. X11 clock icons, should set their scr_busy function to the
54 * correct function before calling the main editor routine.
56 * PUBLIC: void vs_busy __P((SCR *, const char *, busy_t));
59 vs_busy(sp, msg, btype)
66 static const char flagc[] = "|/-\\";
71 /* Ex doesn't display busy messages. */
72 if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
79 * Most of this routine is to deal with the screen sharing real estate
80 * between the normal edit messages and the busy messages. Logically,
81 * all that's needed is something that puts up a message, periodically
82 * updates it, and then goes away.
87 if (vip->totalcount != 0 || vip->busy_ref != 1)
90 /* Initialize state for updates. */
92 (void)gettimeofday(&vip->busy_tv, NULL);
94 /* Save the current cursor. */
95 (void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx);
97 /* Display the busy message. */
98 p = msg_cat(sp, msg, &len);
99 (void)gp->scr_move(sp, LASTLINE(sp), 0);
100 (void)gp->scr_addstr(sp, p, len);
101 (void)gp->scr_cursor(sp, ¬used, &vip->busy_fx);
102 (void)gp->scr_clrtoeol(sp);
103 (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
106 if (vip->busy_ref == 0)
111 * If the line isn't in use for another purpose, clear it.
112 * Always return to the original position.
114 if (vip->totalcount == 0 && vip->busy_ref == 0) {
115 (void)gp->scr_move(sp, LASTLINE(sp), 0);
116 (void)gp->scr_clrtoeol(sp);
118 (void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx);
121 if (vip->totalcount != 0 || vip->busy_ref == 0)
124 /* Update no more than every 1/8 of a second. */
125 (void)gettimeofday(&tv, NULL);
126 if (((tv.tv_sec - vip->busy_tv.tv_sec) * 1000000 +
127 (tv.tv_usec - vip->busy_tv.tv_usec)) < 125000)
131 /* Display the update. */
132 if (vip->busy_ch == sizeof(flagc) - 1)
134 (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
135 (void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1);
136 (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
139 (void)gp->scr_refresh(sp, 0);
144 * Home the cursor to the bottom row, left-most column.
146 * PUBLIC: void vs_home __P((SCR *));
152 (void)sp->gp->scr_move(sp, LASTLINE(sp), 0);
153 (void)sp->gp->scr_refresh(sp, 0);
160 * PUBLIC: void vs_update __P((SCR *, const char *, const char *));
163 vs_update(sp, m1, m2)
168 size_t len, mlen, oldx, oldy;
173 * This routine displays a message on the bottom line of the screen,
174 * without updating any of the command structures that would keep it
175 * there for any period of time, i.e. it is overwritten immediately.
177 * It's used by the ex read and ! commands when the user's command is
178 * expanded, and by the ex substitution confirmation prompt.
180 if (F_ISSET(sp, SC_SCR_EXWROTE)) {
182 "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : m2);
187 * Save the cursor position, the substitute-with-confirmation code
188 * will have already set it correctly.
190 (void)gp->scr_cursor(sp, &oldy, &oldx);
192 /* Clear the bottom line. */
193 (void)gp->scr_move(sp, LASTLINE(sp), 0);
194 (void)gp->scr_clrtoeol(sp);
198 * Don't let long file names screw up the screen.
201 mlen = len = strlen(m1);
202 if (len > sp->cols - 2)
203 mlen = len = sp->cols - 2;
204 (void)gp->scr_addstr(sp, m1, mlen);
209 if (len + mlen > sp->cols - 2)
210 mlen = (sp->cols - 2) - len;
211 (void)gp->scr_addstr(sp, m2, mlen);
214 (void)gp->scr_move(sp, oldy, oldx);
215 (void)gp->scr_refresh(sp, 0);
220 * Display ex output or error messages for the screen.
222 * This routine is the default editor interface for all ex output, and all ex
223 * and vi error/informational messages. It implements the standard strategy
224 * of stealing lines from the bottom of the vi text screen. Screens using an
225 * alternate method of displaying messages, e.g. dialog boxes, should set their
226 * scr_msg function to the correct function before calling the editor.
228 * PUBLIC: void vs_msg __P((SCR *, mtype_t, char *, size_t));
231 vs_msg(sp, mtype, line, len)
239 size_t maxcols, oldx, oldy, padding;
240 const char *e, *s, *t;
246 * Ring the bell if it's scheduled.
249 * Shouldn't we save this, too?
251 if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED))
252 if (F_ISSET(sp, SC_SCR_VI)) {
253 F_CLR(gp, G_BELLSCHED);
254 (void)gp->scr_bell(sp);
256 F_SET(gp, G_BELLSCHED);
259 * If vi is using the error line for text input, there's no screen
260 * real-estate for the error message. Nothing to do without some
261 * information as to how important the error message is.
263 if (F_ISSET(sp, SC_TINPUT_INFO))
267 * Ex or ex controlled screen output.
269 * If output happens during startup, e.g., a .exrc file, we may be
270 * in ex mode but haven't initialized the screen. Initialize here,
271 * and in this case, stay in ex mode.
273 * If the SC_SCR_EXWROTE bit is set, then we're switching back and
274 * forth between ex and vi, but the screen is trashed and we have
275 * to respect that. Switch to ex mode long enough to put out the
278 * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to
279 * the screen, so previous opinions are ignored.
281 if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
282 if (!F_ISSET(sp, SC_SCR_EX))
283 if (F_ISSET(sp, SC_SCR_EXWROTE)) {
284 if (sp->gp->scr_screen(sp, SC_EX))
291 (void)gp->scr_attr(sp, SA_INVERSE, 1);
292 (void)printf("%.*s", (int)len, line);
294 (void)gp->scr_attr(sp, SA_INVERSE, 0);
295 (void)fflush(stdout);
297 F_CLR(sp, SC_EX_WAIT_NO);
299 if (!F_ISSET(sp, SC_SCR_EX))
300 (void)sp->gp->scr_screen(sp, SC_VI);
304 /* If the vi screen isn't ready, save the message. */
305 if (!F_ISSET(sp, SC_SCR_VI)) {
306 (void)vs_msgsave(sp, mtype, line, len);
310 /* Save the cursor position. */
311 (void)gp->scr_cursor(sp, &oldy, &oldx);
313 /* If it's an ex output message, just write it out. */
314 if (mtype == M_NONE) {
315 vs_output(sp, mtype, line, len);
320 * If it's a vi message, strip the trailing <newline> so we can
321 * try and paste messages together.
323 if (line[len - 1] == '\n')
327 * If a message won't fit on a single line, try to split on a <blank>.
328 * If a subsequent message fits on the same line, write a separator
329 * and output it. Otherwise, put out a newline.
331 * Need up to two padding characters normally; a semi-colon and a
332 * separating space. If only a single line on the screen, add some
333 * more for the trailing continuation message.
336 * Assume that periods and semi-colons take up a single column on the
340 * There are almost certainly pathological cases that will break this
344 (void)msg_cmsg(sp, CMSG_CONT_S, &padding);
349 maxcols = sp->cols - 1;
350 if (vip->lcontinue != 0)
351 if (len + vip->lcontinue + padding > maxcols)
352 vs_output(sp, vip->mtype, ".\n", 2);
354 vs_output(sp, vip->mtype, ";", 1);
355 vs_output(sp, M_NONE, " ", 1);
358 for (s = line;; s = t) {
359 for (; len > 0 && isblank(*s); --len, ++s);
362 if (len + vip->lcontinue > maxcols) {
363 for (e = s + (maxcols - vip->lcontinue);
364 e > s && !isblank(*e); --e);
366 e = t = s + (maxcols - vip->lcontinue);
368 for (t = e; isblank(e[-1]); --e);
373 * If the message ends in a period, discard it, we want to
374 * gang messages where possible.
377 if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.')
379 vs_output(sp, mtype, s, e - s);
382 vs_output(sp, M_NONE, "\n", 1);
388 ret: (void)gp->scr_move(sp, oldy, oldx);
389 (void)gp->scr_refresh(sp, 0);
394 * Output the text to the screen.
397 vs_output(sp, mtype, line, llen)
406 size_t chlen, notused;
407 int ch, len, rlen, tlen;
409 char *cbp, *ecbp, cbuf[128];
413 for (p = line, rlen = llen; llen > 0;) {
414 /* Get the next physical line. */
415 if ((p = memchr(line, '\n', llen)) == NULL)
421 * The max is sp->cols characters, and we may have already
422 * written part of the line.
424 if (len + vip->lcontinue > sp->cols)
425 len = sp->cols - vip->lcontinue;
428 * If the first line output, do nothing. If the second line
429 * output, draw the divider line. If drew a full screen, we
430 * remove the divider line. If it's a continuation line, move
431 * to the continuation point, else, move the screen up.
433 if (vip->lcontinue == 0) {
434 if (!IS_ONELINE(sp)) {
435 if (vip->totalcount == 1) {
436 (void)gp->scr_move(sp,
437 LASTLINE(sp) - 1, 0);
438 (void)gp->scr_clrtoeol(sp);
439 (void)vs_divider(sp);
440 F_SET(vip, VIP_DIVIDER);
444 if (vip->totalcount == sp->t_maxrows &&
445 F_ISSET(vip, VIP_DIVIDER)) {
448 F_CLR(vip, VIP_DIVIDER);
451 if (vip->totalcount != 0)
452 vs_scroll(sp, NULL, SCROLL_W_QUIT);
454 (void)gp->scr_move(sp, LASTLINE(sp), 0);
461 (void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue);
463 /* Error messages are in inverse video. */
465 (void)gp->scr_attr(sp, SA_INVERSE, 1);
467 /* Display the line, doing character translation. */
470 (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); \
473 ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
474 for (t = line, tlen = len; tlen--; ++t) {
477 * Replace tabs with spaces, there are places in
478 * ex that do column calculations without looking
479 * at <tabs> -- and all routines that care about
480 * <tabs> do their own expansions. This catches
481 * <tabs> in things like tag search strings.
485 chlen = KEY_LEN(sp, ch);
486 if (cbp + chlen >= ecbp)
488 for (kp = KEY_NAME(sp, ch); chlen--;)
494 (void)gp->scr_attr(sp, SA_INVERSE, 0);
496 /* Clear the rest of the line. */
497 (void)gp->scr_clrtoeol(sp);
499 /* If we loop, it's a new line. */
502 /* Reset for the next line. */
511 /* Set up next continuation line. */
513 gp->scr_cursor(sp, ¬used, &vip->lcontinue);
518 * Deal with ex message output.
520 * This routine is called when exiting a colon command to resolve any ex
521 * output that may have occurred.
523 * PUBLIC: int vs_ex_resolve __P((SCR *, int *));
526 vs_ex_resolve(sp, continuep)
539 /* If we ran any ex command, we can't trust the cursor position. */
540 F_SET(vip, VIP_CUR_INVALID);
542 /* Terminate any partially written message. */
543 if (vip->lcontinue != 0) {
544 vs_output(sp, vip->mtype, ".", 1);
551 * If we switched out of the vi screen into ex, switch back while we
552 * figure out what to do with the screen and potentially get another
553 * command to execute.
555 * If we didn't switch into ex, we're not required to wait, and less
556 * than 2 lines of output, we can continue without waiting for the
559 * Note, all other code paths require waiting, so we leave the report
560 * of modified lines until later, so that we won't wait for no other
561 * reason than a threshold number of lines were modified. This means
562 * we display cumulative line modification reports for groups of ex
563 * commands. That seems right to me (well, at least not wrong).
565 if (F_ISSET(sp, SC_SCR_EXWROTE)) {
566 if (sp->gp->scr_screen(sp, SC_VI))
569 if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) {
570 F_CLR(sp, SC_EX_WAIT_NO);
574 /* Clear the required wait flag, it's no longer needed. */
575 F_CLR(sp, SC_EX_WAIT_YES);
578 * Wait, unless explicitly told not to wait or the user interrupted
579 * the command. If the user is leaving the screen, for any reason,
580 * they can't continue with further ex commands.
582 if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) {
583 wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE |
584 SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX;
585 if (F_ISSET(sp, SC_SCR_EXWROTE))
586 vs_wait(sp, continuep, wtype);
588 vs_scroll(sp, continuep, wtype);
593 /* If ex wrote on the screen, refresh the screen image. */
594 if (F_ISSET(sp, SC_SCR_EXWROTE))
595 F_SET(vip, VIP_N_EX_PAINT);
598 * If we're not the bottom of the split screen stack, the screen
599 * image itself is wrong, so redraw everything.
601 if (sp->q.cqe_next != (void *)&sp->gp->dq)
602 F_SET(sp, SC_SCR_REDRAW);
604 /* If ex changed the underlying file, the map itself is wrong. */
605 if (F_ISSET(vip, VIP_N_EX_REDRAW))
606 F_SET(sp, SC_SCR_REFORMAT);
608 /* Ex may have switched out of the alternate screen, return. */
609 (void)gp->scr_attr(sp, SA_ALTERNATE, 1);
612 * Whew. We're finally back home, after what feels like years.
615 F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO);
618 * We may need to repaint some of the screen, e.g.:
623 * gives us a combination of some lines that are "wrong", and a need
624 * for a full refresh.
626 if (vip->totalcount > 1) {
627 /* Set up the redraw of the overwritten lines. */
628 ev.e_event = E_REPAINT;
629 ev.e_flno = vip->totalcount >=
630 sp->rows ? 1 : sp->rows - vip->totalcount;
631 ev.e_tlno = sp->rows;
633 /* Reset the count of overwriting lines. */
634 vip->linecount = vip->lcontinue = vip->totalcount = 0;
637 (void)vs_repaint(sp, &ev);
639 /* Reset the count of overwriting lines. */
640 vip->linecount = vip->lcontinue = vip->totalcount = 0;
647 * Deal with message output.
649 * PUBLIC: int vs_resolve __P((SCR *, SCR *, int));
652 vs_resolve(sp, csp, forcewait)
664 * Vs_resolve is called from the main vi loop and the refresh function
665 * to periodically ensure that the user has seen any messages that have
666 * been displayed and that any status lines are correct. The sp screen
667 * is the screen we're checking, usually the current screen. When it's
668 * not, csp is the current screen, used for final cursor positioning.
675 /* Save the cursor position. */
676 (void)gp->scr_cursor(csp, &oldy, &oldx);
678 /* Ring the bell if it's scheduled. */
679 if (F_ISSET(gp, G_BELLSCHED)) {
680 F_CLR(gp, G_BELLSCHED);
681 (void)gp->scr_bell(sp);
684 /* Display new file status line. */
685 if (F_ISSET(sp, SC_STATUS)) {
686 F_CLR(sp, SC_STATUS);
687 msgq_status(sp, sp->lno, MSTAT_TRUNCATE);
690 /* Report on line modifications. */
694 * Flush any saved messages. If the screen isn't ready, refresh
695 * it. (A side-effect of screen refresh is that we can display
696 * messages.) Once this is done, don't trust the cursor. That
697 * extra refresh screwed the pooch.
699 if (gp->msgq.lh_first != NULL) {
700 if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1))
702 while ((mp = gp->msgq.lh_first) != NULL) {
703 gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
708 F_SET(vip, VIP_CUR_INVALID);
711 switch (vip->totalcount) {
717 * If we're switching screens, we have to wait for messages,
718 * regardless. If we don't wait, skip updating the modeline.
721 vs_scroll(sp, NULL, SCROLL_W);
723 F_SET(vip, VIP_S_MODELINE);
729 * If >1 message line in use, prompt the user to continue and
730 * repaint overwritten lines.
732 vs_scroll(sp, NULL, SCROLL_W);
734 ev.e_event = E_REPAINT;
735 ev.e_flno = vip->totalcount >=
736 sp->rows ? 1 : sp->rows - vip->totalcount;
737 ev.e_tlno = sp->rows;
743 /* Reset the count of overwriting lines. */
744 vip->linecount = vip->lcontinue = vip->totalcount = 0;
748 (void)vs_repaint(sp, &ev);
750 /* Restore the cursor position. */
751 (void)gp->scr_move(csp, oldy, oldx);
758 * Scroll the screen for output.
761 vs_scroll(sp, continuep, wtype)
771 if (!IS_ONELINE(sp)) {
773 * Scroll the screen. Instead of scrolling the entire screen,
774 * delete the line above the first line output so preserve the
775 * maximum amount of the screen.
777 (void)gp->scr_move(sp, vip->totalcount <
778 sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0);
779 (void)gp->scr_deleteln(sp);
781 /* If there are screens below us, push them back into place. */
782 if (sp->q.cqe_next != (void *)&sp->gp->dq) {
783 (void)gp->scr_move(sp, LASTLINE(sp), 0);
784 (void)gp->scr_insertln(sp);
787 if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows)
789 vs_wait(sp, continuep, wtype);
794 * Prompt the user to continue.
797 vs_wait(sp, continuep, wtype)
811 (void)gp->scr_move(sp, LASTLINE(sp), 0);
813 p = msg_cmsg(sp, CMSG_CONT_S, &len);
817 p = msg_cmsg(sp, CMSG_CONT_Q, &len);
820 p = msg_cmsg(sp, CMSG_CONT_EX, &len);
823 p = msg_cmsg(sp, CMSG_CONT, &len);
829 (void)gp->scr_addstr(sp, p, len);
834 (void)gp->scr_clrtoeol(sp);
835 (void)gp->scr_refresh(sp, 0);
837 /* Get a single character from the terminal. */
838 if (continuep != NULL)
841 if (v_event_get(sp, &ev, 0, 0))
843 if (ev.e_event == E_CHARACTER)
845 if (ev.e_event == E_INTERRUPT) {
847 F_SET(gp, G_INTERRUPTED);
850 (void)gp->scr_bell(sp);
854 if (ev.e_c == CH_QUIT)
855 F_SET(gp, G_INTERRUPTED);
858 if (ev.e_c == ':' && continuep != NULL)
868 * Draw a dividing line between the screen and the output.
877 #define DIVIDESTR "+=+=+=+=+=+=+=+"
879 sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1;
881 (void)gp->scr_attr(sp, SA_INVERSE, 1);
882 (void)gp->scr_addstr(sp, DIVIDESTR, len);
883 (void)gp->scr_attr(sp, SA_INVERSE, 0);
888 * Save a message for later display.
891 vs_msgsave(sp, mt, p, len)
901 * We have to handle messages before we have any place to put them.
902 * If there's no screen support yet, allocate a msg structure, copy
903 * in the message, and queue it on the global structure. If we can't
904 * allocate memory here, we're genuinely screwed, dump the message
905 * to stderr in the (probably) vain hope that someone will see it.
907 CALLOC_GOTO(sp, mp_n, MSGS *, 1, sizeof(MSGS));
908 MALLOC_GOTO(sp, mp_n->buf, char *, len);
910 memmove(mp_n->buf, p, len);
915 if ((mp_c = gp->msgq.lh_first) == NULL) {
916 LIST_INSERT_HEAD(&gp->msgq, mp_n, q);
918 for (; mp_c->q.le_next != NULL; mp_c = mp_c->q.le_next);
919 LIST_INSERT_AFTER(mp_c, mp_n, q);
926 (void)fprintf(stderr, "%.*s\n", (int)len, p);