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